diff --git a/pkg/rvasp/db/db.go b/pkg/rvasp/db/db.go index 06f975f..60fcb90 100644 --- a/pkg/rvasp/db/db.go +++ b/pkg/rvasp/db/db.go @@ -250,7 +250,6 @@ func (Account) TableName() string { // TODO: Add a field for the transaction payload marshaled as a string. type Transaction struct { gorm.Model - TxID string `gorm:"not null"` Envelope string `gorm:"not null"` AccountID uint `gorm:"not null"` Account Account `gorm:"foreignKey:AccountID"` @@ -258,13 +257,14 @@ type Transaction struct { Originator Identity `gorm:"foreignKey:OriginatorID"` BeneficiaryID uint `gorm:"column:beneficiary_id;not null"` Beneficiary Identity `gorm:"foreignKey:BeneficiaryID"` - Amount decimal.Decimal `gorm:"type:numeric(15,2)"` + Amount decimal.Decimal `gorm:"type:decimal(15,8)"` Debit bool `gorm:"not null"` State pb.TransactionState `gorm:"not null;default:0"` Timestamp time.Time `gorm:"not null"` NotBefore time.Time `gorm:"not null"` NotAfter time.Time `gorm:"not null"` Identity string `gorm:"not null"` + Transaction string `gorm:"not null"` VaspID uint `gorm:"not null"` Vasp VASP `gorm:"foreignKey:VaspID"` } diff --git a/pkg/rvasp/fixtures/wallets.json b/pkg/rvasp/fixtures/wallets.json index cef6fdf..9ed8cc6 100644 --- a/pkg/rvasp/fixtures/wallets.json +++ b/pkg/rvasp/fixtures/wallets.json @@ -407,7 +407,7 @@ "badnews@evilvasp.gg", 3, "SendFull", - "SyncRequire", + "AsyncRepair", { "natural_person": { "name": { diff --git a/pkg/rvasp/rvasp.go b/pkg/rvasp/rvasp.go index d9d7bfe..e604956 100644 --- a/pkg/rvasp/rvasp.go +++ b/pkg/rvasp/rvasp.go @@ -524,8 +524,7 @@ func (s *Server) respondAsync(peer *peers.Peer, payload *protocol.Payload, ident switch xfer.State { case pb.TransactionState_AWAITING: // Fill the transaction with a new TxID to continue the handshake - xfer.TxID = uuid.New().String() - transaction.Txid = xfer.TxID + transaction.Txid = uuid.New().String() if payload, err = createTransferPayload(identity, transaction); err != nil { log.Error().Err(err).Msg("could not create transfer payload") return nil, protocol.Errorf(protocol.InternalError, "could not create transfer payload: %s", err) @@ -578,6 +577,13 @@ func (s *Server) respondAsync(peer *peers.Peer, payload *protocol.Payload, ident } xfer.Identity = string(data) + // Update the transaction with the new generic.Transaction + if data, err = json.Marshal(transaction); err != nil { + log.Error().Err(err).Msg("could not marshal generic.Transaction") + return nil, status.Errorf(codes.Internal, "could not marshal generic.Transaction: %s", err) + } + xfer.Transaction = string(data) + // Update the Transaction in the database with the pending timestamps if xfer.NotBefore, err = time.Parse(time.RFC3339, pending.ReplyNotBefore); err != nil { log.Error().Err(err).Msg("TRISA protocol error: could not parse ReplyNotBefore timestamp") diff --git a/pkg/rvasp/trisa.go b/pkg/rvasp/trisa.go index 81ae8ec..3ab64df 100644 --- a/pkg/rvasp/trisa.go +++ b/pkg/rvasp/trisa.go @@ -362,6 +362,29 @@ func (s *TRISA) handleTransaction(ctx context.Context, peer *peers.Peer, in *pro } } +// Repair the identity payload in a received transfer request by filling in the +// beneficiary identity information. +func (s *TRISA) repairBeneficiary(identity *ivms101.IdentityPayload, account db.Account) (err error) { + identity.BeneficiaryVasp = &ivms101.BeneficiaryVasp{} + if identity.BeneficiaryVasp.BeneficiaryVasp, err = s.parent.vasp.LoadIdentity(); err != nil { + log.Error().Err(err).Msg("could not load beneficiary vasp") + return err + } + + identity.Beneficiary = &ivms101.Beneficiary{ + BeneficiaryPersons: make([]*ivms101.Person, 0, 1), + AccountNumbers: []string{account.WalletAddress}, + } + + var beneficiary *ivms101.Person + if beneficiary, err = account.LoadIdentity(); err != nil { + log.Error().Err(err).Msg("could not load beneficiary account identity") + return err + } + identity.Beneficiary.BeneficiaryPersons = append(identity.Beneficiary.BeneficiaryPersons, beneficiary) + return nil +} + // respondTransfer responds to a transfer request from the originator by sending back // the payload with the beneficiary identity information. If requireBeneficiary is // true, the beneficiary identity must be filled in, or the transfer is rejected. If @@ -381,24 +404,8 @@ func (s *TRISA) respondTransfer(in *protocol.SecureEnvelope, peer *peers.Peer, i return nil, status.Errorf(codes.FailedPrecondition, "TRISA protocol error: missing beneficiary vasp identity") } } else { - // Update the identity with the beneficiary information - identity.BeneficiaryVasp = &ivms101.BeneficiaryVasp{} - if identity.BeneficiaryVasp.BeneficiaryVasp, err = s.parent.vasp.LoadIdentity(); err != nil { - log.Error().Err(err).Msg("could not load beneficiary vasp") - return nil, protocol.Errorf(protocol.InternalError, "request could not be processed") - } - - identity.Beneficiary = &ivms101.Beneficiary{ - BeneficiaryPersons: make([]*ivms101.Person, 0, 1), - AccountNumbers: []string{account.WalletAddress}, - } - - var beneficiary *ivms101.Person - if beneficiary, err = account.LoadIdentity(); err != nil { - log.Error().Err(err).Msg("could not load beneficiary") - return nil, protocol.Errorf(protocol.InternalError, "request could not be processed") - } - identity.Beneficiary.BeneficiaryPersons = append(identity.Beneficiary.BeneficiaryPersons, beneficiary) + // Fill in the beneficiary identity information for the repair policy + s.repairBeneficiary(identity, account) } // Update the transaction with beneficiary information @@ -487,7 +494,6 @@ func (s *TRISA) respondPending(in *protocol.SecureEnvelope, peer *peers.Peer, id log.Error().Err(err).Msg("could not construct transaction") return nil, protocol.Errorf(protocol.InternalError, "request could not be processed") } - xfer.TxID = transaction.Txid xfer.Envelope = in.Id xfer.Account = account xfer.Amount = decimal.NewFromFloat(transaction.Amount) @@ -511,12 +517,19 @@ func (s *TRISA) respondPending(in *protocol.SecureEnvelope, peer *peers.Peer, id xfer.NotAfter = now.Add(s.parent.conf.AsyncNotAfter) // Marshal the identity info into the local transaction - var identityBytes []byte - if identityBytes, err = protojson.Marshal(identity); err != nil { + var data []byte + if data, err = protojson.Marshal(identity); err != nil { log.Error().Err(err).Msg("could not marshal identity") return nil, protocol.Errorf(protocol.InternalError, "request could not be processed") } - xfer.Identity = string(identityBytes) + xfer.Identity = string(data) + + // Marshal the generic.Transaction into the local transaction + if data, err = protojson.Marshal(transaction); err != nil { + log.Error().Err(err).Msg("could not marshal transaction") + return nil, protocol.Errorf(protocol.InternalError, "request could not be processed") + } + xfer.Transaction = string(data) // Save the updated transaction in the database if err = s.parent.db.Save(&xfer).Error; err != nil { @@ -582,13 +595,6 @@ func (s *TRISA) sendAsync(tx *db.Transaction) (err error) { return fmt.Errorf("could not fetch originator address") } - // Fetch the beneficiary address - var beneficiary *db.Identity - if beneficiary, err = tx.GetBeneficiary(s.parent.db); err != nil { - log.Error().Err(err).Msg("could not fetch beneficiary address") - return fmt.Errorf("could not fetch beneficiary address") - } - // Create the identity for the payload identity := &ivms101.IdentityPayload{} if err = protojson.Unmarshal([]byte(tx.Identity), identity); err != nil { @@ -596,13 +602,25 @@ func (s *TRISA) sendAsync(tx *db.Transaction) (err error) { return fmt.Errorf("could not unmarshal identity from transaction: %s", err) } - // Create the transaction for the payload - transaction := &generic.Transaction{ - Txid: tx.TxID, - Originator: originator.WalletAddress, - Beneficiary: beneficiary.WalletAddress, - Amount: float64(tx.AmountFloat()), - Timestamp: tx.Timestamp.Format(time.RFC3339), + // Repair the beneficiary information if this is the first handshake + if tx.State == pb.TransactionState_PENDING_SENT { + var account *db.Account + if account, err = tx.GetAccount(s.parent.db); err != nil { + log.Error().Err(err).Msg("could not fetch beneficiary account") + return fmt.Errorf("could not fetch beneficiary account: %s", err) + } + + if err = s.repairBeneficiary(identity, *account); err != nil { + log.Error().Err(err).Msg("could not repair beneficiary information") + return fmt.Errorf("could not repair beneficiary information: %s", err) + } + } + + // Create the generic.Transaction for the payload + transaction := &generic.Transaction{} + if err = protojson.Unmarshal([]byte(tx.Transaction), transaction); err != nil { + log.Error().Err(err).Msg("could not unmarshal generic.Transaction from transaction") + return fmt.Errorf("could not unmarshal generic.Transaction from transaction: %s", err) } // Create the payload diff --git a/scripts/rvasp-test.sh b/scripts/rvasp-test.sh index c231b57..b3093e4 100755 --- a/scripts/rvasp-test.sh +++ b/scripts/rvasp-test.sh @@ -1,113 +1,121 @@ #!/bin/bash # TODO: check codes +if [ "$1" == "--local" ]; then + ALICE_ENDPOINT=localhost:5434 + BOB_ENDPOINT=localhost:6434 +else + ALICE_ENDPOINT=admin.alice.vaspbot.net:443 + BOB_ENDPOINT=admin.bob.vaspbot.net:443 +fi + # Send from Alice to Bob # Partial Sync Repair: success expected echo "Alice --> Bob Partial Sync Repair" -rvasp transfer -e admin.alice.vaspbot.net:443 \ +rvasp transfer -e $ALICE_ENDPOINT \ -a 1ASkqdo1hvydosVRvRv2j6eNnWpWLHucMX -d 0.0001 \ -b 18nxAxBktHZDrMoJ3N2fk9imLX8xNnYbNh # Partial Sync Require: rejection expected echo "Alice --> Bob Partial Sync Require" -rvasp transfer -e admin.alice.vaspbot.net:443 \ +rvasp transfer -e $ALICE_ENDPOINT \ -a 1ASkqdo1hvydosVRvRv2j6eNnWpWLHucMX -d 0.0002 \ -b 1LgtLYkpaXhHDu1Ngh7x9fcBs5KuThbSzw # Full Sync Repair: success expected echo "Alice --> Bob Full Sync Repair" -rvasp transfer -e admin.alice.vaspbot.net:443 \ +rvasp transfer -e $ALICE_ENDPOINT \ -a 1MRCxvEpBoY8qajrmNTSrcfXSZ2wsrGeha -d 0.0003 \ -b 18nxAxBktHZDrMoJ3N2fk9imLX8xNnYbNh # Full Sync Require: success expected echo "Alice --> Bob Full Sync Require" -rvasp transfer -e admin.alice.vaspbot.net:443 \ +rvasp transfer -e $ALICE_ENDPOINT \ -a 14HmBSwec8XrcWge9Zi1ZngNia64u3Wd2v -d 0.0004 \ -b 1LgtLYkpaXhHDu1Ngh7x9fcBs5KuThbSzw # Partial Async Repair: success expected # TODO: how to test getting a message back? echo "Alice --> Bob Partial Async Repair" -rvasp transfer -e admin.alice.vaspbot.net:443 \ +rvasp transfer -e $ALICE_ENDPOINT \ -a 1ASkqdo1hvydosVRvRv2j6eNnWpWLHucMX -d 0.0005 \ -b 14WU745djqecaJ1gmtWQGeMCFim1W5MNp3 # Partial Async Reject: rejection expected echo "Alice --> Bob Partial Async Reject" -rvasp transfer -e admin.alice.vaspbot.net:443 \ +rvasp transfer -e $ALICE_ENDPOINT \ -a 1ASkqdo1hvydosVRvRv2j6eNnWpWLHucMX -d 0.0006 \ -b 1Hzej6a2VG7C8iCAD5DAdN72cZH5THSMt9 # Full Async Repair: success expected echo "Alice --> Bob Full Async Repair" -rvasp transfer -e admin.alice.vaspbot.net:443 \ +rvasp transfer -e $ALICE_ENDPOINT \ -a 1MRCxvEpBoY8qajrmNTSrcfXSZ2wsrGeha -d 0.0007 \ -b 14WU745djqecaJ1gmtWQGeMCFim1W5MNp3 # Full Async Require: reject expected echo "Alice --> Bob Full Async Reject" -rvasp transfer -e admin.alice.vaspbot.net:443 \ +rvasp transfer -e $ALICE_ENDPOINT \ -a 14HmBSwec8XrcWge9Zi1ZngNia64u3Wd2v -d 0.0008 \ -b 1Hzej6a2VG7C8iCAD5DAdN72cZH5THSMt9 # Send Error: rejection expected. echo "Alice --> Bob Send Error" -rvasp transfer -e admin.alice.vaspbot.net:443 \ +rvasp transfer -e $ALICE_ENDPOINT \ -a 19nFejdNSUhzkAAdwAvP3wc53o8dL326QQ -d 0.0009 \ -b 1Hzej6a2VG7C8iCAD5DAdN72cZH5THSMt9 # Send from Bob to Alice # Partial Sync Repair: success expected echo "Bob --> Alice Partial Sync Repair" -rvasp transfer -e admin.bob.vaspbot.net:443 \ +rvasp transfer -e $BOB_ENDPOINT \ -a 18nxAxBktHZDrMoJ3N2fk9imLX8xNnYbNh -d 0.00010 \ -b 1ASkqdo1hvydosVRvRv2j6eNnWpWLHucMX # Partial Sync Require: rejection expected echo "Bob --> Alice Partial Sync Require" -rvasp transfer -e admin.bob.vaspbot.net:443 \ +rvasp transfer -e $BOB_ENDPOINT \ -a 18nxAxBktHZDrMoJ3N2fk9imLX8xNnYbNh -d 0.00011 \ -b 1MRCxvEpBoY8qajrmNTSrcfXSZ2wsrGeha # Full Sync Repair: success expected echo "Bob --> Alice Full Sync Repair" -rvasp transfer -e admin.bob.vaspbot.net:443 \ +rvasp transfer -e $BOB_ENDPOINT \ -a 1LgtLYkpaXhHDu1Ngh7x9fcBs5KuThbSzw -d 0.00012 \ -b 1ASkqdo1hvydosVRvRv2j6eNnWpWLHucMX # Full Sync Require: success expected echo "Bob --> Alice Full Sync Require" -rvasp transfer -e admin.bob.vaspbot.net:443 \ +rvasp transfer -e $BOB_ENDPOINT \ -a 14WU745djqecaJ1gmtWQGeMCFim1W5MNp3 -d 0.00013 \ -b 1MRCxvEpBoY8qajrmNTSrcfXSZ2wsrGeha # Partial Async Repair: success expected echo "Bob --> Alice Partial Async Repair" -rvasp transfer -e admin.bob.vaspbot.net:443 \ +rvasp transfer -e $BOB_ENDPOINT \ -a 18nxAxBktHZDrMoJ3N2fk9imLX8xNnYbNh -d 0.00014 \ -b 14HmBSwec8XrcWge9Zi1ZngNia64u3Wd2v # Partial Async Require: rejection expected echo "Bob --> Alice Partial Async Reject" -rvasp transfer -e admin.bob.vaspbot.net:443 \ +rvasp transfer -e $BOB_ENDPOINT \ -a 18nxAxBktHZDrMoJ3N2fk9imLX8xNnYbNh -d 0.00015 \ -b 19nFejdNSUhzkAAdwAvP3wc53o8dL326QQ # Full Async Repair: success expected echo "Bob --> Alice Full Async Repair" -rvasp transfer -e admin.bob.vaspbot.net:443 \ +rvasp transfer -e $BOB_ENDPOINT \ -a 1LgtLYkpaXhHDu1Ngh7x9fcBs5KuThbSzw -d 0.00016 \ -b 14HmBSwec8XrcWge9Zi1ZngNia64u3Wd2v # Full Async Require: rejection expected echo "Bob --> Alice Full Async Reject" -rvasp transfer -e admin.bob.vaspbot.net:443 \ +rvasp transfer -e $BOB_ENDPOINT \ -a 14WU745djqecaJ1gmtWQGeMCFim1W5MNp3 -d 0.00017 \ -b 19nFejdNSUhzkAAdwAvP3wc53o8dL326QQ # Send Error: rejection expected. echo "Bob --> Alice Send Error" -rvasp transfer -e admin.bob.vaspbot.net:443 \ +rvasp transfer -e $BOB_ENDPOINT \ -a 1Hzej6a2VG7C8iCAD5DAdN72cZH5THSMt9 -d 0.00018 \ -b 14HmBSwec8XrcWge9Zi1ZngNia64u3Wd2v \ No newline at end of file