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-7425 Improve beneficiary identity check #109

Merged
merged 1 commit into from
Aug 5, 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
11 changes: 11 additions & 0 deletions pkg/rvasp/db/fixtures_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ func TestLoadVASPs(t *testing.T) {
require.NoError(t, err)

require.Equal(t, 4, len(vasps))
for _, vasp := range vasps {
identity, err := vasp.LoadIdentity()
require.NoError(t, err, "failed to load identity for vasp fixture %s", vasp.Name)
require.NoError(t, identity.GetLegalPerson().Validate(), "identity for vasp fixture %s must be a valid ivms101.LegalPerson", vasp.Name)
}
}

func TestLoadWallets(t *testing.T) {
Expand All @@ -21,4 +26,10 @@ func TestLoadWallets(t *testing.T) {

require.Equal(t, 16, len(wallets))
require.Equal(t, 16, len(accounts))
for _, account := range accounts {
identity, err := account.LoadIdentity()
require.NoError(t, err, "failed to load identity for account fixture %s", account.Name)
require.NoError(t, identity.GetNaturalPerson().Validate(), "identity for account fixture %s must be a valid ivms101.NaturalPerson", account.Name)
require.NotEmpty(t, account.WalletAddress, "account fixture %s must have a wallet address", account.Name)
}
}
7 changes: 4 additions & 3 deletions pkg/rvasp/rvasp.go
Original file line number Diff line number Diff line change
Expand Up @@ -227,9 +227,7 @@ func (s *Server) Transfer(ctx context.Context, req *pb.TransferRequest) (reply *
}

// Build the transfer response
reply = &pb.TransferReply{
Transaction: xfer.Proto(),
}
reply = &pb.TransferReply{}

// Handle rVASP errors and TRISA protocol errors
if transferError != nil {
Expand All @@ -248,6 +246,9 @@ func (s *Server) Transfer(ctx context.Context, req *pb.TransferRequest) (reply *
}
}

// Populate the transfer response with the transaction details
reply.Transaction = xfer.Proto()
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This is just an ordering change so that we populate the transfer response for the CLI with the correct transaction state.


// Save the updated transaction
// TODO: Clean up completed transactions in the database
if err = s.db.Save(xfer).Error; err != nil {
Expand Down
40 changes: 36 additions & 4 deletions pkg/rvasp/transfer.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ func (s *Server) createIdentityPayload(originatorAccount db.Account, beneficiary
// Create the beneficiary account information if provided
if beneficiaryAccount.WalletAddress != "" {
identity.Beneficiary = &ivms101.Beneficiary{
BeneficiaryPersons: make([]*ivms101.Person, 0, 1),
BeneficiaryPersons: make([]*ivms101.Person, 0),
AccountNumbers: []string{beneficiaryAccount.WalletAddress},
}
}
Expand Down Expand Up @@ -187,7 +187,7 @@ func parsePayload(payload *protocol.Payload, response bool) (identity *ivms101.I
}

// Validate an identity payload, returning an error if the payload is not valid.
func validateIdentityPayload(identity *ivms101.IdentityPayload, requireBeneficiary bool) (err *protocol.Error) {
func validateIdentityPayload(identity *ivms101.IdentityPayload, requireBeneficiary bool) *protocol.Error {
// Verify the identity payload is not nil
if identity == nil {
log.Warn().Msg("identity payload is nil")
Expand All @@ -207,17 +207,49 @@ func validateIdentityPayload(identity *ivms101.IdentityPayload, requireBeneficia
}

if requireBeneficiary {
// Validate that the beneficiary is present
var err error

// Check that the beneficiary is present
if identity.Beneficiary == nil {
log.Warn().Msg("identity payload missing beneficiary")
return protocol.Errorf(protocol.IncompleteIdentity, "missing beneficiary")
}

// Validate that the beneficiary vasp is present
// Check that the beneficiary person is present
if len(identity.Beneficiary.BeneficiaryPersons) == 0 {
log.Warn().Msg("identity payload missing beneficiary person")
return protocol.Errorf(protocol.IncompleteIdentity, "missing beneficiary person")
}

// Validate the beneficiary person
if err = identity.Beneficiary.BeneficiaryPersons[0].GetNaturalPerson().Validate(); err != nil {
log.Warn().Err(err).Msg("beneficiary person validation error")
return protocol.Errorf(protocol.ValidationError, "beneficiary person validation error: %s", err)
}

// Check that the account number is present
if len(identity.Beneficiary.AccountNumbers) == 0 || identity.Beneficiary.AccountNumbers[0] == "" {
log.Warn().Msg("identity payload missing account number")
return protocol.Errorf(protocol.IncompleteIdentity, "missing beneficiary account number")
}

// Check that the beneficiary vasp is present
if identity.BeneficiaryVasp == nil {
log.Warn().Msg("identity payload missing beneficiary vasp")
return protocol.Errorf(protocol.IncompleteIdentity, "missing beneficiary vasp")
}

// Check that the beneficiary vasp entity is present
if identity.BeneficiaryVasp.BeneficiaryVasp == nil {
log.Warn().Msg("identity payload missing beneficiary vasp entity")
return protocol.Errorf(protocol.IncompleteIdentity, "missing beneficiary vasp entity")
}

// Validate the beneficiary vasp entity
if err = identity.BeneficiaryVasp.BeneficiaryVasp.GetLegalPerson().Validate(); err != nil {
log.Warn().Err(err).Msg("beneficiary vasp entity validation error")
return protocol.Errorf(protocol.ValidationError, "beneficiary vasp entity validation error: %s", err)
}
}
return nil
}
3 changes: 2 additions & 1 deletion pkg/rvasp/trisa.go
Original file line number Diff line number Diff line change
Expand Up @@ -420,7 +420,8 @@ func (s *TRISA) handleTransaction(ctx context.Context, peer *peers.Peer, in *pro
return nil, protocol.Errorf(protocol.InternalError, "unknown policy '%s' for wallet '%s'", policy, account.WalletAddress)
}

if transferError != nil {
// Mark transaction as failed if it was not rejected but an error occurred
if xfer.State != pb.TransactionState_REJECTED && transferError != nil {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This doesn't impact how the rVASPs operate but it does make sure that the transaction state gets updated correctly in the database, which makes it easier to manually verify our "integration" tests.

log.Debug().Err(transferError).Msg("transfer failed")
xfer.SetState(pb.TransactionState_FAILED)
}
Expand Down
71 changes: 55 additions & 16 deletions pkg/rvasp/trisa_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/trisacrypto/testnet/pkg/rvasp"
"github.com/trisacrypto/testnet/pkg/rvasp/bufconn"
"github.com/trisacrypto/testnet/pkg/rvasp/config"
"github.com/trisacrypto/testnet/pkg/rvasp/db"
"github.com/trisacrypto/trisa/pkg/ivms101"
protocol "github.com/trisacrypto/trisa/pkg/trisa/api/v1beta1"
generic "github.com/trisacrypto/trisa/pkg/trisa/data/generic/v1beta1"
Expand All @@ -21,19 +22,25 @@ import (
"google.golang.org/protobuf/types/known/anypb"
)

const bufSize = 1024 * 1024
const (
bufSize = 1024 * 1024
fixturesPath = "fixtures"
)

// rVASPTestSuite tests interactions with the rvasp servers to ensure that they return
// the expected error codes and messages.
type rVASPTestSuite struct {
suite.Suite
grpc *bufconn.GRPCListener
db sqlmock.Sqlmock
trisa *rvasp.TRISA
certs *trust.Provider
chain trust.ProviderPool
peers *peers.Peers
conf *config.Config
grpc *bufconn.GRPCListener
db sqlmock.Sqlmock
trisa *rvasp.TRISA
certs *trust.Provider
chain trust.ProviderPool
peers *peers.Peers
conf *config.Config
vasps []db.VASP
wallets []db.Wallet
accounts []db.Account
}

func TestRVASP(t *testing.T) {
Expand All @@ -49,6 +56,15 @@ func (s *rVASPTestSuite) SetupSuite() {
certPath := filepath.Join("testdata", "cert.pem")
s.conf.CertPath = certPath
s.conf.TrustChainPath = certPath

s.vasps, err = db.LoadVASPs(fixturesPath)
require.NoError(err, "could not load VASP fixtures")
require.Greater(len(s.vasps), 0, "no VASPs loaded")

s.wallets, s.accounts, err = db.LoadWallets(fixturesPath)
require.NoError(err, "could not load wallet fixtures")
require.Greater(len(s.wallets), 0, "no wallets loaded")
require.Greater(len(s.accounts), 0, "no accounts loaded")
}

func (s *rVASPTestSuite) BeforeTest(suiteName, testName string) {
Expand All @@ -74,6 +90,36 @@ func expectStandardQuery(db sqlmock.Sqlmock, kind string) {
db.ExpectQuery(kind).WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(1))
}

// createIdentityPayload which has a valid beneficiary identity and can be used for
// SyncRequire transfers.
func (s *rVASPTestSuite) createIdentityPayload() (identity *ivms101.IdentityPayload) {
require := s.Require()

identity = &ivms101.IdentityPayload{
Originator: &ivms101.Originator{},
OriginatingVasp: &ivms101.OriginatingVasp{},
Beneficiary: &ivms101.Beneficiary{},
BeneficiaryVasp: &ivms101.BeneficiaryVasp{},
}

// For unit testing it does not matter which VASP fixture is used although it must
// have a valid ivms101.LegalPerson
var err error
identity.BeneficiaryVasp.BeneficiaryVasp, err = s.vasps[0].LoadIdentity()
require.NoError(err, "could not load beneficiary VASP identity")
require.NoError(identity.BeneficiaryVasp.BeneficiaryVasp.GetLegalPerson().Validate(), "VASP ivms101.LegalPerson fixture failed validation")

// The account fixture must have a valid ivms101.NaturalPerson
beneficiary, err := s.accounts[0].LoadIdentity()
require.NoError(err, "could not load beneficiary account identity")
require.NoError(beneficiary.GetNaturalPerson().Validate(), "account ivms101.NaturalPerson fixture failed validation")

identity.Beneficiary.BeneficiaryPersons = []*ivms101.Person{beneficiary}
identity.Beneficiary.AccountNumbers = []string{s.accounts[0].WalletAddress}

return identity
}

// Test that the TRISA server returns a valid envelope when a valid request is sent for
// SyncRequire.
func (s *rVASPTestSuite) TestValidTransfer() {
Expand All @@ -88,14 +134,7 @@ func (s *rVASPTestSuite) TestValidTransfer() {
SentAt: time.Now().Format(time.RFC3339),
}

identity := &ivms101.IdentityPayload{
Originator: &ivms101.Originator{},
OriginatingVasp: &ivms101.OriginatingVasp{},
Beneficiary: &ivms101.Beneficiary{},
BeneficiaryVasp: &ivms101.BeneficiaryVasp{
BeneficiaryVasp: &ivms101.Person{},
},
}
identity := s.createIdentityPayload()
payload.Identity, err = anypb.New(identity)
require.NoError(err)

Expand Down