Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SC-6043 Improve error handling #102

Merged
merged 1 commit into from
Jul 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 18 additions & 12 deletions lib/python/rvaspy/rvaspy/api_pb2.py

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 7 additions & 5 deletions pkg/rvasp/async.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,9 @@ func (s *TRISA) handleAsync(ctx context.Context) {

now := time.Now()
txloop:
for _, tx := range transactions {
for _, transaction := range transactions {
tx := &transaction

// Verify pending transaction is old enough
if now.Before(tx.NotBefore) {
continue
Expand All @@ -78,21 +80,21 @@ txloop:
// Verify pending transaction has not expired
if now.After(tx.NotAfter) {
log.Info().Uint("id", tx.ID).Time("not_after", tx.NotAfter).Msg("transaction expired")
tx.State = pb.TransactionState_EXPIRED
tx.SetState(pb.TransactionState_EXPIRED)
if err = s.parent.db.Save(&tx).Error; err != nil {
log.Error().Err(err).Uint("id", tx.ID).Msg("could not save expired transaction")
}
continue txloop
}

// Acknowledge the transaction with the originator
if err = s.acknowledgeTransaction(&tx); err != nil {
if err = s.acknowledgeTransaction(tx); err != nil {
log.Warn().Err(err).Uint("id", tx.ID).Msg("could not acknowledge transaction")
tx.State = pb.TransactionState_FAILED
tx.SetState(pb.TransactionState_FAILED)
}

// Save the updated transaction in the database
if err = s.parent.db.Save(&tx).Error; err != nil {
if err = s.parent.db.Save(tx).Error; err != nil {
log.Error().Err(err).Uint("id", tx.ID).Msg("could not save completed transaction")
}
}
Expand Down
20 changes: 14 additions & 6 deletions pkg/rvasp/db/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,13 +111,13 @@ func (d *DB) LookupWallet(address string) *gorm.DB {
// MakeTransaction returns a new Transaction from the originator and beneficiary
// wallet addresses. Note: this does not store the transaction in the database to allow
// the caller to modify the transaction fields before storage.
func (d *DB) MakeTransaction(originator string, beneficiary string) (Transaction, error) {
func (d *DB) MakeTransaction(originator string, beneficiary string) (*Transaction, error) {
var originatorIdentity, beneficiaryIdentity Identity

// Fetch originator identity record
if err := d.LookupIdentity(originator).FirstOrInit(&originatorIdentity, Identity{}).Error; err != nil {
log.Error().Err(err).Msg("could not lookup originator identity")
return Transaction{}, status.Errorf(codes.FailedPrecondition, "could not lookup originator identity: %s", err)
return nil, status.Errorf(codes.FailedPrecondition, "could not lookup originator identity: %s", err)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is a little bit nicer - especially if you're checking nil (which is easier than checking iszero)

Another pattern if you're not checking nil that I like to do is:

func (d *DB) MakeTransaction(originator string, beneficiary string) (tx Transaction, err error) {
    return tx, errors.New("this is an error, but tx is guaranteed to be zero-valued") 
}

Copy link
Collaborator

@bbengfort bbengfort Jul 4, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Although now that I've read farther in the code, I guess the real reason for the pointer is to make sure the transaction is editable?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct, although the consistent nil return was an additional benefit.

}

// If originator identity does not exist then create it
Expand All @@ -127,14 +127,14 @@ func (d *DB) MakeTransaction(originator string, beneficiary string) (Transaction

if err := d.Create(&originatorIdentity).Error; err != nil {
log.Error().Err(err).Msg("could not save originator identity")
return Transaction{}, status.Errorf(codes.FailedPrecondition, "could not save originator identity: %s", err)
return nil, status.Errorf(codes.FailedPrecondition, "could not save originator identity: %s", err)
}
}

// Fetch beneficiary identity record
if err := d.LookupIdentity(beneficiary).FirstOrInit(&beneficiaryIdentity, Identity{}).Error; err != nil {
log.Error().Err(err).Msg("could not lookup beneficiary identity")
return Transaction{}, status.Errorf(codes.FailedPrecondition, "could not lookup beneficiary identity: %s", err)
return nil, status.Errorf(codes.FailedPrecondition, "could not lookup beneficiary identity: %s", err)
}

// If the beneficiary identity does not exist then create it
Expand All @@ -144,15 +144,16 @@ func (d *DB) MakeTransaction(originator string, beneficiary string) (Transaction

if err := d.Create(&beneficiaryIdentity).Error; err != nil {
log.Error().Err(err).Msg("could not save beneficiary identity")
return Transaction{}, status.Errorf(codes.FailedPrecondition, "could not save beneficiary identity: %s", err)
return nil, status.Errorf(codes.FailedPrecondition, "could not save beneficiary identity: %s", err)
}
}

return Transaction{
return &Transaction{
Envelope: uuid.New().String(),
Originator: originatorIdentity,
Beneficiary: beneficiaryIdentity,
State: pb.TransactionState_AWAITING,
StateString: pb.TransactionState_AWAITING.String(),
Timestamp: time.Now(),
Vasp: d.vasp,
}, nil
Expand Down Expand Up @@ -260,6 +261,7 @@ type Transaction struct {
Amount decimal.Decimal `gorm:"type:decimal(15,8)"`
Debit bool `gorm:"not null"`
State pb.TransactionState `gorm:"not null;default:0"`
StateString string `gorm:"column:state_string;not null"`
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added this so we see the actual transaction states from psql. My verification process for local development is to now run the test script, then query select amount, vasp_id, state_string, id, envelope from transactions order by amount; to get a list of all the transactions; they should be either COMPLETED or REJECTED.

Timestamp time.Time `gorm:"not null"`
NotBefore time.Time `gorm:"not null"`
NotAfter time.Time `gorm:"not null"`
Expand All @@ -274,6 +276,12 @@ func (Transaction) TableName() string {
return "transactions"
}

// Set the transaction state to a new value
func (t *Transaction) SetState(state pb.TransactionState) {
t.State = state
t.StateString = state.String()
}

// Identity holds raw data for an originator or a beneficiary that was sent as
// part of the transaction process. This should not be stored in the wallet since the
// wallet is a representation of the local VASPs knowledge about customers and bercause
Expand Down
Loading