From 5de3b3427b8c47f1de98a4bfeec9491ce8f3e7a8 Mon Sep 17 00:00:00 2001 From: Patrick Deziel <42919891+pdeziel@users.noreply.github.com> Date: Fri, 15 Jul 2022 14:30:20 -0500 Subject: [PATCH] Peer endpoint lookup and error handling (#104) --- containers/docker-compose.yml | 10 +- pkg/rvasp/mock.go | 1 + pkg/rvasp/peer.go | 76 +++++++++++++ pkg/rvasp/rvasp.go | 92 ++++++++------- pkg/rvasp/transfer.go | 19 ++-- pkg/rvasp/trisa.go | 172 +++++++++++++++-------------- pkg/rvasp/trisa_test.go | 5 + scripts/fixtures/gds-fixture.py | 29 +++-- scripts/fixtures/rvasp-fixtures.py | 42 +++++++ scripts/generate-fixtures.sh | 50 ++++----- 10 files changed, 324 insertions(+), 172 deletions(-) create mode 100644 pkg/rvasp/peer.go create mode 100644 scripts/fixtures/rvasp-fixtures.py diff --git a/containers/docker-compose.yml b/containers/docker-compose.yml index 826bd13..1000977 100644 --- a/containers/docker-compose.yml +++ b/containers/docker-compose.yml @@ -21,7 +21,7 @@ services: - RVASP_FIXTURES_PATH=/fixtures - RVASP_DATABASE_MAX_RETRIES=5 volumes: - - ../pkg/rvasp/fixtures:/fixtures + - ../fixtures/rvasps:/fixtures gds: image: trisa/gds:latest @@ -59,7 +59,7 @@ services: - 5434:4434 - 5435:4435 environment: - - RVASP_NAME=api.alice.vaspbot.net + - RVASP_NAME=alice - RVASP_DATABASE_DSN=postgres://postgres:postgres@db:5432/rvasp?sslmode=disable - RVASP_CERT_PATH=/certs/cert.pem - RVASP_TRUST_CHAIN_PATH=/certs/cert.pem @@ -83,7 +83,7 @@ services: - 6434:4434 - 6435:4435 environment: - - RVASP_NAME=api.bob.vaspbot.net + - RVASP_NAME=bob - RVASP_DATABASE_DSN=postgres://postgres:postgres@db:5432/rvasp?sslmode=disable - RVASP_CERT_PATH=/certs/cert.pem - RVASP_TRUST_CHAIN_PATH=/certs/cert.pem @@ -107,7 +107,7 @@ services: - 7434:4434 - 7435:4435 environment: - - RVASP_NAME=api.evil.vaspbot.net + - RVASP_NAME=evil - RVASP_DATABASE_DSN=postgres://postgres:postgres@db:5432/rvasp?sslmode=disable - RVASP_CERT_PATH=/certs/cert.pem - RVASP_TRUST_CHAIN_PATH=/certs/cert.pem @@ -131,7 +131,7 @@ services: - 8434:4434 - 8435:4435 environment: - - RVASP_NAME=api.charlie.vaspbot.net + - RVASP_NAME=charlie - RVASP_DATABASE_DSN=postgres://postgres:postgres@db:5432/rvasp?sslmode=disable - RVASP_CERT_PATH=/certs/cert.pem - RVASP_TRUST_CHAIN_PATH=/certs/cert.pem diff --git a/pkg/rvasp/mock.go b/pkg/rvasp/mock.go index 2beea91..76134c3 100644 --- a/pkg/rvasp/mock.go +++ b/pkg/rvasp/mock.go @@ -34,6 +34,7 @@ func NewTRISAMock(conf *config.Config) (s *TRISA, remotePeers *peers.Peers, mock remotePeers = peers.NewMock(s.certs, s.chain, conf.GDS.URL) remotePeers.Add(&peers.PeerInfo{ CommonName: "alice", + Endpoint: "gds.example.io:443", SigningKey: &s.sign.PublicKey, }) parent.peers = remotePeers diff --git a/pkg/rvasp/peer.go b/pkg/rvasp/peer.go new file mode 100644 index 0000000..71ce4e5 --- /dev/null +++ b/pkg/rvasp/peer.go @@ -0,0 +1,76 @@ +package rvasp + +import ( + "crypto/rsa" + "fmt" + + "github.com/rs/zerolog/log" + "github.com/trisacrypto/trisa/pkg/trisa/peers" +) + +// TODO: The peers cache needs to be flushed periodically to prevent the situation +// where remote peers change their endpoints or certificates and the rVASPs are stuck +// using the old info. + +// fetchPeer returns the peer with the common name from the cache and performs a lookup +// against the directory service if the peer does not have an endpoint. +func (s *Server) fetchPeer(commonName string) (peer *peers.Peer, err error) { + // Retrieve or create the peer from the cache + if peer, err = s.peers.Get(commonName); err != nil { + log.Error().Err(err).Msg("could not create or fetch peer") + return nil, fmt.Errorf("could not create or fetch peer: %s", err) + } + + // Ensure that the remote peer has an endpoint to connect to + if err = s.resolveEndpoint(peer); err != nil { + log.Warn().Err(err).Msg("could not fetch endpoint from remote peer") + return nil, fmt.Errorf("could not fetch endpoint from remote peer: %s", err) + } + + return peer, nil +} + +// fetchSigningKey returns the signing key for the peer, performing an endpoint lookup +// and key exchange if necessary. +func (s *Server) fetchSigningKey(peer *peers.Peer) (key *rsa.PublicKey, err error) { + // Ensure that the remote peer has an endpoint to connect to + if err = s.resolveEndpoint(peer); err != nil { + log.Warn().Err(err).Msg("could not fetch endpoint from remote peer") + return nil, fmt.Errorf("could not fetch endpoint from remote peer: %s", err) + } + + if peer.SigningKey() == nil { + // If no key is available, perform a key exchange with the remote peer + if peer.ExchangeKeys(true); err != nil { + log.Warn().Str("common_name", peer.String()).Err(err).Msg("could not exchange keys with remote peer") + return nil, fmt.Errorf("could not exchange keys with remote peer: %s", err) + } + + // Verify the key is now available on the peer + if peer.SigningKey() == nil { + log.Error().Str("common_name", peer.String()).Msg("peer has no key after key exchange") + return nil, fmt.Errorf("peer has no key after key exchange") + } + } + + return peer.SigningKey(), nil +} + +// resolveEndpoint ensures that the peer has an endpoint to connect to, and performs a +// lookup against the directory service to set the endpoint on the peer if necessary. +func (s *Server) resolveEndpoint(peer *peers.Peer) (err error) { + // If the endpoint is not in the peer, do the lookup to fetch the endpoint + if peer.Info().Endpoint == "" { + var remote *peers.Peer + if remote, err = s.peers.Lookup(peer.String()); err != nil { + log.Warn().Str("peer", peer.String()).Err(err).Msg("could not lookup peer") + return fmt.Errorf("could not lookup peer: %s", err) + } + + if remote.Info().Endpoint == "" { + log.Error().Str("peer", peer.String()).Msg("peer has no endpoint after lookup") + return fmt.Errorf("peer has no endpoint after lookup") + } + } + return nil +} diff --git a/pkg/rvasp/rvasp.go b/pkg/rvasp/rvasp.go index 9800392..c03bb29 100644 --- a/pkg/rvasp/rvasp.go +++ b/pkg/rvasp/rvasp.go @@ -291,18 +291,18 @@ func (s *Server) fetchBeneficiaryWallet(req *pb.TransferRequest) (wallet *db.Wal // information is not included in the payload. This function handles pending responses // from the beneficiary saving the transaction in an "await" state in the database. func (s *Server) sendTransfer(xfer *db.Transaction, beneficiary *db.Wallet, partial bool) (err error) { - // Conduct a GDS lookup if necessary to get the endpoint + // Fetch the remote peer var peer *peers.Peer - if peer, err = s.peers.Search(beneficiary.Provider.Name); err != nil { - log.Warn().Err(err).Msg("could not search peer from directory service") - return status.Errorf(codes.FailedPrecondition, "could not search peer from directory service: %s", err) + if peer, err = s.fetchPeer(beneficiary.Provider.Name); err != nil { + log.Warn().Err(err).Msg("could not fetch beneficiary peer") + return status.Errorf(codes.FailedPrecondition, "could not fetch beneficiary peer: %s", err) } - // Ensure that the local RVASP has signing keys for the remote, otherwise perform key exchange + // Fetch the signing key var signKey *rsa.PublicKey - if signKey, err = peer.ExchangeKeys(true); err != nil { - log.Warn().Err(err).Msg("could not exchange keys with remote peer") - return status.Errorf(codes.FailedPrecondition, "could not exchange keys with remote peer: %s", err) + if signKey, err = s.fetchSigningKey(peer); err != nil { + log.Warn().Err(err).Msg("could not fetch signing key from benficiary peer") + return status.Errorf(codes.FailedPrecondition, "could not fetch signing key from beneficiary peer: %s", err) } if err = s.db.Create(xfer).Error; err != nil { @@ -387,9 +387,10 @@ func (s *Server) sendTransfer(xfer *db.Transaction, beneficiary *db.Wallet, part // Parse the response payload var pending *generic.Pending - if identity, transaction, pending, err = parsePayload(payload, true); err != nil { - log.Warn().Err(err).Msg("TRISA protocol error while parsing payload") - return status.Errorf(codes.FailedPrecondition, "TRISA protocol error: %s", err) + var parseError *protocol.Error + if identity, transaction, pending, parseError = parsePayload(payload, true); parseError != nil { + log.Warn().Str("message", parseError.Message).Msg("TRISA protocol error while parsing payload") + return status.Errorf(codes.FailedPrecondition, "TRISA protocol error: %s", parseError.Message) } // Update the transaction record with the identity payload @@ -462,11 +463,11 @@ func (s *Server) sendTransfer(xfer *db.Transaction, beneficiary *db.Wallet, part // sendError sends a TRISA error to the beneficiary. func (s *Server) sendError(xfer *db.Transaction, beneficiary *db.Wallet) (err error) { - // Conduct a GDS lookup if necessary to get the endpoint + // Fetch the remote peer var peer *peers.Peer - if peer, err = s.peers.Search(beneficiary.Provider.Name); err != nil { - log.Warn().Err(err).Msg("could not search peer from directory service") - return status.Errorf(codes.FailedPrecondition, "could not search peer from directory service: %s", err) + if peer, err = s.fetchPeer(beneficiary.Provider.Name); err != nil { + log.Warn().Err(err).Msg("could not fetch beneficiary peer") + return status.Errorf(codes.FailedPrecondition, "could not fetch beneficiary peer: %s", err) } reject := protocol.Errorf(protocol.ComplianceCheckFail, "rVASP mock compliance check failed") @@ -494,7 +495,7 @@ func (s *Server) sendError(xfer *db.Transaction, beneficiary *db.Wallet) (err er // respondAsync responds to a serviced transfer request from the beneficiary by // continuing or completing the asynchronous handshake. -func (s *Server) respondAsync(peer *peers.Peer, payload *protocol.Payload, identity *ivms101.IdentityPayload, transaction *generic.Transaction, xfer *db.Transaction) (out *protocol.SecureEnvelope, err error) { +func (s *Server) respondAsync(peer *peers.Peer, payload *protocol.Payload, identity *ivms101.IdentityPayload, transaction *generic.Transaction, xfer *db.Transaction) (out *protocol.SecureEnvelope, transferError *protocol.Error) { // Secure envelope was successfully received now := time.Now() @@ -504,46 +505,54 @@ func (s *Server) respondAsync(peer *peers.Peer, payload *protocol.Payload, ident return nil, protocol.Errorf(protocol.ComplianceCheckFail, "received expired transaction") } + // Fetch the signing key from the remote peer + var signKey *rsa.PublicKey + var err error + if signKey, err = s.fetchSigningKey(peer); err != nil { + log.Warn().Err(err).Msg("could not fetch signing key from beneficiary peer") + return nil, protocol.Errorf(protocol.NoSigningKey, "could not fetch signing key from beneficiary peer: %s", err) + } + // Marshal the identity payload into the transaction record var data []byte if data, err = protojson.Marshal(identity); err != nil { log.Error().Err(err).Msg("could not marshal identity payload") - return nil, status.Errorf(codes.Internal, "could not marshal identity payload: %s", err) + return nil, protocol.Errorf(protocol.InternalError, "could not marshal identity payload: %s", err) } xfer.Identity = string(data) // Marshal the transaction payload into the transaction record if data, err = protojson.Marshal(transaction); err != nil { log.Error().Err(err).Msg("could not marshal transaction payload") - return nil, status.Errorf(codes.Internal, "could not marshal transaction payload: %s", err) + return nil, protocol.Errorf(protocol.InternalError, "could not marshal transaction payload: %s", err) } xfer.Transaction = string(data) // Fetch the beneficiary identity if err = s.db.LookupIdentity(transaction.Beneficiary).First(&xfer.Beneficiary).Error; err != nil { log.Error().Err(err).Str("wallet_address", transaction.Beneficiary).Msg("could not lookup beneficiary identity") - return nil, status.Errorf(codes.Internal, "could not lookup beneficiary identity: %s", err) + return nil, protocol.Errorf(protocol.InternalError, "could not lookup beneficiary identity: %s", err) } // Save the peer name so we can access it later xfer.Beneficiary.Provider = peer.String() if err = s.db.Save(&xfer.Beneficiary).Error; err != nil { log.Error().Err(err).Msg("could not save beneficiary identity") - return nil, status.Errorf(codes.Internal, "could not save beneficiary identity: %s", err) + return nil, protocol.Errorf(protocol.InternalError, "could not save beneficiary identity: %s", err) } // Create the response envelope - out, reject, err := envelope.Seal(payload, envelope.WithRSAPublicKey(peer.SigningKey())) + out, reject, err := envelope.Seal(payload, envelope.WithRSAPublicKey(signKey)) if err != nil { if reject != nil { if out, err = envelope.Reject(reject); err != nil { - return nil, status.Errorf(codes.FailedPrecondition, "TRISA protocol error: %s", err) + return nil, protocol.Errorf(protocol.EnvelopeDecodeFail, "TRISA protocol error: %s", err) } xfer.SetState(pb.TransactionState_REJECTED) return out, nil } log.Warn().Err(err).Msg("TRISA protocol error while sealing envelope") - return nil, status.Errorf(codes.FailedPrecondition, "TRISA protocol error: %s", err) + return nil, protocol.Errorf(protocol.EnvelopeDecodeFail, "TRISA protocol error: %s", err) } // Check the transaction state @@ -566,7 +575,7 @@ func (s *Server) respondAsync(peer *peers.Peer, payload *protocol.Payload, ident account.Balance = account.Balance.Sub(xfer.Amount) if err = s.db.Save(&account).Error; err != nil { log.Error().Err(err).Msg("could not save originator account") - return nil, status.Errorf(codes.Internal, "could not save originator account: %s", err) + return nil, protocol.Errorf(protocol.InternalError, "could not save originator account: %s", err) } xfer.SetState(pb.TransactionState_COMPLETED) default: @@ -601,6 +610,20 @@ func (s *Server) continueAsync(xfer *db.Transaction) (err error) { return fmt.Errorf("could not fetch beneficiary address") } + // Fetch the remote peer + var peer *peers.Peer + if peer, err = s.fetchPeer(beneficiary.Provider); err != nil { + log.Error().Err(err).Msg("could not fetch beneficiary peer") + return fmt.Errorf("could not fetch beneficiary peer: %s", err) + } + + // Fetch the signing key from the remote peer + var signKey *rsa.PublicKey + if signKey, err = s.fetchSigningKey(peer); err != nil { + log.Warn().Err(err).Msg("could not fetch signing key from beneficiary peer") + return fmt.Errorf("could not fetch signing key from beneficiary peer: %s", err) + } + // Fill the transaction with a new TxID to continue the handshake var payload *protocol.Payload transaction.Txid = uuid.New().String() @@ -609,20 +632,6 @@ func (s *Server) continueAsync(xfer *db.Transaction) (err error) { return fmt.Errorf("could not create transfer payload: %s", err) } - // Conduct a GDS lookup if necessary to get the endpoint - var peer *peers.Peer - if peer, err = s.peers.Search(beneficiary.Provider); err != nil { - log.Error().Err(err).Msg("could not search peer from directory service") - return fmt.Errorf("could not search peer from directory service: %s", err) - } - - // Ensure that the local RVASP has signing keys for the remote, otherwise perform key exchange - var signKey *rsa.PublicKey - if signKey, err = peer.ExchangeKeys(true); err != nil { - log.Warn().Err(err).Msg("could not exchange keys with remote peer") - return fmt.Errorf("could not exchange keys with remote peer: %s", err) - } - // Secure the envelope with the remote beneficiary's signing keys msg, _, err := envelope.Seal(payload, envelope.WithEnvelopeID(xfer.Envelope), envelope.WithRSAPublicKey(signKey)) if err != nil { @@ -645,9 +654,10 @@ func (s *Server) continueAsync(xfer *db.Transaction) (err error) { // Parse the response payload var pending *generic.Pending - if identity, _, pending, err = parsePayload(payload, true); err != nil { - log.Warn().Err(err).Msg("TRISA protocol error while parsing payload") - return fmt.Errorf("TRISA protocol error: %s", err) + var parseError *protocol.Error + if identity, _, pending, parseError = parsePayload(payload, true); parseError != nil { + log.Warn().Str("message", parseError.Message).Msg("TRISA protocol error while parsing payload") + return fmt.Errorf("TRISA protocol error: %s", parseError.Message) } if pending == nil { diff --git a/pkg/rvasp/transfer.go b/pkg/rvasp/transfer.go index 915eee9..2cf891d 100644 --- a/pkg/rvasp/transfer.go +++ b/pkg/rvasp/transfer.go @@ -133,43 +133,44 @@ func createPendingPayload(pending *generic.Pending, identity *ivms101.IdentityPa // Parse a TRISA transfer payload, returning the identity payload and either the // transaction payload or the pending message. -func parsePayload(payload *protocol.Payload, response bool) (identity *ivms101.IdentityPayload, transaction *generic.Transaction, pending *generic.Pending, err error) { +func parsePayload(payload *protocol.Payload, response bool) (identity *ivms101.IdentityPayload, transaction *generic.Transaction, pending *generic.Pending, parseError *protocol.Error) { // Verify the sent_at timestamp if this is an accepted response payload if payload.SentAt == "" { log.Warn().Msg("missing sent at timestamp") - return nil, nil, nil, fmt.Errorf("missing sent_at timestamp") + return nil, nil, nil, protocol.Errorf(protocol.MissingFields, "missing sent_at timestamp") } // Payload must contain an identity if payload.Identity == nil { log.Warn().Msg("payload does not contain an identity") - return nil, nil, nil, fmt.Errorf("missing identity payload") + return nil, nil, nil, protocol.Errorf(protocol.MissingFields, "missing identity payload") } // Payload must contain a transaction if payload.Transaction == nil { log.Warn().Msg("payload does not contain a transaction") - return nil, nil, nil, fmt.Errorf("missing transaction payload") + return nil, nil, nil, protocol.Errorf(protocol.MissingFields, "missing transaction payload") } // Parse the identity payload identity = &ivms101.IdentityPayload{} + var err error if err = payload.Identity.UnmarshalTo(identity); err != nil { log.Warn().Err(err).Msg("could not unmarshal identity") - return nil, nil, nil, fmt.Errorf("could non unmarshal identity: %s", err) + return nil, nil, nil, protocol.Errorf(protocol.UnparseableIdentity, "could non unmarshal identity: %s", err) } // Validate identity fields if identity.Originator == nil || identity.OriginatingVasp == nil || identity.BeneficiaryVasp == nil || identity.Beneficiary == nil { log.Warn().Msg("incomplete identity payload") - return nil, nil, nil, fmt.Errorf("incomplete identity payload") + return nil, nil, nil, protocol.Errorf(protocol.IncompleteIdentity, "incomplete identity payload") } // Parse the transaction message type var msgTx proto.Message if msgTx, err = payload.Transaction.UnmarshalNew(); err != nil { log.Warn().Err(err).Str("transaction_type", payload.Transaction.TypeUrl).Msg("could not unmarshal incoming transaction payload") - return nil, nil, nil, status.Errorf(codes.FailedPrecondition, "could not unmarshal transaction payload: %s", err) + return nil, nil, nil, protocol.Errorf(protocol.UnparseableTransaction, "could not unmarshal transaction payload: %s", err) } switch tx := msgTx.(type) { @@ -180,13 +181,13 @@ func parsePayload(payload *protocol.Payload, response bool) (identity *ivms101.I // NOTE: pending messages and intermediate messages will not contain received_at if response && payload.ReceivedAt == "" { log.Warn().Msg("missing received at timestamp") - return nil, nil, nil, fmt.Errorf("missing received_at timestamp") + return nil, nil, nil, protocol.Errorf(protocol.MissingFields, "missing received_at timestamp") } case *generic.Pending: pending = tx default: log.Warn().Str("transaction_type", payload.Transaction.TypeUrl).Msg("could not handle incoming transaction payload") - return nil, nil, nil, fmt.Errorf("unexpected transaction payload type: %s", payload.Transaction.TypeUrl) + return nil, nil, nil, protocol.Errorf(protocol.UnparseableTransaction, "unexpected transaction payload type: %s", payload.Transaction.TypeUrl) } return identity, transaction, pending, nil } diff --git a/pkg/rvasp/trisa.go b/pkg/rvasp/trisa.go index 6bf01d1..936e9a9 100644 --- a/pkg/rvasp/trisa.go +++ b/pkg/rvasp/trisa.go @@ -133,35 +133,42 @@ func (s *TRISA) Transfer(ctx context.Context, in *protocol.SecureEnvelope) (out var peer *peers.Peer if peer, err = s.parent.peers.FromContext(ctx); err != nil { log.Error().Err(err).Msg("could not verify peer from context") - return nil, &protocol.Error{ - Code: protocol.Unverified, - Message: err.Error(), + reject := protocol.Errorf(protocol.Unverified, "could not verify peer from context: %v", err) + var msg *protocol.SecureEnvelope + if msg, err = envelope.Reject(reject, envelope.WithEnvelopeID(in.Id)); err != nil { + log.Error().Err(err).Msg("could not create TRISA error envelope") + return nil, status.Errorf(codes.Internal, "could not create TRISA error envelope: %v", err) } + return msg, nil } log.Info().Str("peer", peer.String()).Msg("unary transfer request received") s.parent.updates.Broadcast(0, fmt.Sprintf("received secure exchange from %s", peer), pb.MessageCategory_TRISAP2P) - // Check signing key is available to send an encrypted response - if peer.SigningKey() == nil { - log.Warn().Str("peer", peer.String()).Msg("no remote signing key available, attempting key exchange") - s.parent.updates.Broadcast(0, "no remote signing key available, attempting key exchange", pb.MessageCategory_TRISAP2P) - - if _, err = peer.ExchangeKeys(false); err != nil { - log.Warn().Err(err).Str("peer", peer.String()).Msg("no remote signing key available, key exchange failed") - s.parent.updates.Broadcast(0, fmt.Sprintf("key exchange failed: %s", err), pb.MessageCategory_TRISAP2P) + // Fetch the signing key from the peer to ensure we can encrypt envelopes + if _, err = s.parent.fetchSigningKey(peer); err != nil { + log.Warn().Err(err).Msg("could not fetch signing key from remote peer") + reject := protocol.Errorf(protocol.Rejected, "could not fetch signing key from remote peer: %v", err) + var msg *protocol.SecureEnvelope + if msg, err = envelope.Reject(reject, envelope.WithEnvelopeID(in.Id)); err != nil { + log.Error().Err(err).Msg("could not create TRISA error envelope") + return nil, status.Errorf(codes.Internal, "could not create TRISA error envelope: %s", err) } + return msg, nil + } - // Second check for signing keys, if they're not available then reject messages - if peer.SigningKey() == nil { - return nil, &protocol.Error{ - Code: protocol.NoSigningKey, - Message: "please retry transfer after key exchange", - Retry: true, - } + var transferError *protocol.Error + if out, transferError = s.handleTransaction(ctx, peer, in); err != nil { + log.Warn().Err(err).Msg("could not complete transfer") + var msg *protocol.SecureEnvelope + if msg, err = envelope.Reject(transferError, envelope.WithEnvelopeID(in.Id)); err != nil { + log.Error().Err(err).Msg("could not create TRISA error envelope") + return nil, status.Errorf(codes.Internal, "could not create TRISA error envelope: %s", err) } + return msg, nil } - return s.handleTransaction(ctx, peer, in) + log.Info().Str("peer", peer.String()).Msg("unary transfer completed") + return out, nil } // TransferStream allows for high-throughput transactions. @@ -221,16 +228,12 @@ func (s *TRISA) TransferStream(stream protocol.TRISANetwork_TransferStreamServer if err != nil { // Do not close the stream, send the error in the secure envelope if the // Error is a TRISA coded error. - pbErr, ok := err.(*protocol.Error) - if !ok { - return err - } out = &protocol.SecureEnvelope{ - Error: pbErr, + Error: err, } } - if err = stream.Send(out); err != nil { + if err := stream.Send(out); err != nil { log.Error().Err(err).Msg("send stream error") return err } @@ -238,7 +241,7 @@ func (s *TRISA) TransferStream(stream protocol.TRISANetwork_TransferStreamServer } } -func (s *TRISA) handleTransaction(ctx context.Context, peer *peers.Peer, in *protocol.SecureEnvelope) (out *protocol.SecureEnvelope, err error) { +func (s *TRISA) handleTransaction(ctx context.Context, peer *peers.Peer, in *protocol.SecureEnvelope) (out *protocol.SecureEnvelope, transferError *protocol.Error) { var ( identity *ivms101.IdentityPayload transaction *generic.Transaction @@ -247,14 +250,15 @@ func (s *TRISA) handleTransaction(ctx context.Context, peer *peers.Peer, in *pro // Check for TRISA rejection errors reject, isErr := envelope.Check(in) if isErr { + var err error if reject != nil { if out, err = envelope.Reject(reject, envelope.WithEnvelopeID(in.Id)); err != nil { - return nil, status.Errorf(codes.FailedPrecondition, "TRISA protocol error: %s", err) + return nil, protocol.Errorf(protocol.EnvelopeDecodeFail, "TRISA protocol error: %s", err) } // Lookup the indicated transaction in the database xfer := &db.Transaction{} - if err = s.parent.db.LookupTransaction(in.Id).First(xfer).Error; err != nil { + if err := s.parent.db.LookupTransaction(in.Id).First(xfer).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { log.Warn().Err(err).Str("id", in.Id).Msg("transaction not found") } else { @@ -267,12 +271,12 @@ func (s *TRISA) handleTransaction(ctx context.Context, peer *peers.Peer, in *pro xfer.SetState(pb.TransactionState_REJECTED) if err = s.parent.db.Save(xfer).Error; err != nil { log.Error().Err(err).Msg("could not save transaction") - return nil, status.Errorf(codes.Internal, "could not save transaction: %s", err) + return nil, protocol.Errorf(protocol.InternalError, "could not save transaction: %s", err) } return out, nil } log.Warn().Err(err).Msg("TRISA protocol error while checking envelope") - return nil, status.Errorf(codes.FailedPrecondition, "TRISA protocol error: %s", err) + return nil, protocol.Errorf(protocol.EnvelopeDecodeFail, "TRISA protocol error: %s", err) } // Check the algorithms to make sure they're supported @@ -291,17 +295,17 @@ func (s *TRISA) handleTransaction(ctx context.Context, peer *peers.Peer, in *pro if err != nil { if reject != nil { if out, err = envelope.Reject(reject, envelope.WithEnvelopeID(in.Id)); err != nil { - return nil, status.Errorf(codes.FailedPrecondition, "TRISA protocol error: %s", err) + return nil, protocol.Errorf(protocol.EnvelopeDecodeFail, "TRISA protocol error: %s", err) } return out, nil } log.Warn().Err(err).Msg("TRISA protocol error while opening envelope") - return nil, status.Errorf(codes.FailedPrecondition, "TRISA protocol error: %s", err) + return nil, protocol.Errorf(protocol.EnvelopeDecodeFail, "TRISA protocol error: %s", err) } - if identity, transaction, _, err = parsePayload(payload, false); err != nil { - log.Warn().Err(err).Msg("TRISA protocol error while parsing payload") - return nil, status.Errorf(codes.FailedPrecondition, "TRISA protocol error: %s", err) + if identity, transaction, _, transferError = parsePayload(payload, false); transferError != nil { + log.Warn().Str("message", transferError.Message).Msg("TRISA protocol error while parsing payload") + return nil, transferError } s.parent.updates.Broadcast(0, fmt.Sprintf("secure envelope %s opened and payload decrypted and parsed", in.Id), pb.MessageCategory_TRISAP2P) @@ -324,8 +328,7 @@ func (s *TRISA) handleTransaction(ctx context.Context, peer *peers.Peer, in *pro } // Perform the transfer back to the originator - var transferErr error - if out, transferErr = s.parent.respondAsync(peer, payload, identity, transaction, xfer); transferErr != nil { + if out, transferError = s.parent.respondAsync(peer, payload, identity, transaction, xfer); transferError != nil { log.Warn().Err(err).Msg("TRISA protocol error while responding to async transaction") xfer.SetState(pb.TransactionState_FAILED) } @@ -333,10 +336,10 @@ func (s *TRISA) handleTransaction(ctx context.Context, peer *peers.Peer, in *pro // Save the updated transaction if err = s.parent.db.Save(xfer).Error; err != nil { log.Error().Err(err).Msg("could not save transaction") - return nil, status.Errorf(codes.Internal, "could not save transaction: %s", err) + return nil, protocol.Errorf(protocol.InternalError, "could not save transaction: %s", err) } - return out, transferErr + return out, transferError } // Lookup the beneficiary in the local VASP database. @@ -394,7 +397,6 @@ func (s *TRISA) handleTransaction(ctx context.Context, peer *peers.Peer, in *pro } // Run the scenario for the wallet's configured policy - var transferError error policy := wallet.BeneficiaryPolicy log.Debug().Str("wallet", account.WalletAddress).Str("policy", string(policy)).Msg("received transfer request") switch policy { @@ -460,7 +462,15 @@ func (s *TRISA) repairBeneficiary(identity *ivms101.IdentityPayload, account db. // the payload with the beneficiary identity information. If requireBeneficiary is // true, the beneficiary identity must be filled in, or the transfer is rejected. If // requireBeneficiary is false, the partial beneficiary identity is repaired. -func (s *TRISA) respondTransfer(in *protocol.SecureEnvelope, peer *peers.Peer, identity *ivms101.IdentityPayload, transaction *generic.Transaction, xfer *db.Transaction, account db.Account, requireBeneficiary bool) (out *protocol.SecureEnvelope, err error) { +func (s *TRISA) respondTransfer(in *protocol.SecureEnvelope, peer *peers.Peer, identity *ivms101.IdentityPayload, transaction *generic.Transaction, xfer *db.Transaction, account db.Account, requireBeneficiary bool) (out *protocol.SecureEnvelope, transferError *protocol.Error) { + // Fetch the signing key from the remote peer + var signKey *rsa.PublicKey + var err error + if signKey, err = s.parent.fetchSigningKey(peer); err != nil { + log.Warn().Err(err).Msg("could not fetch signing key from originator peer") + return nil, protocol.Errorf(protocol.NoSigningKey, "could not fetch signing key from originator peer") + } + // Save the pending transaction on the account account.Pending++ if err = s.parent.db.Save(&account).Error; err != nil { @@ -524,17 +534,17 @@ func (s *TRISA) respondTransfer(in *protocol.SecureEnvelope, peer *peers.Peer, i s.parent.updates.Broadcast(0, "sealing beneficiary information and returning", pb.MessageCategory_TRISAP2P) - out, reject, err := envelope.Seal(payload, envelope.WithRSAPublicKey(peer.SigningKey())) + out, reject, err := envelope.Seal(payload, envelope.WithRSAPublicKey(signKey)) if err != nil { if reject != nil { if out, err = envelope.Reject(reject, envelope.WithEnvelopeID(in.Id)); err != nil { - return nil, status.Errorf(codes.FailedPrecondition, "TRISA protocol error: %s", err) + return nil, protocol.Errorf(protocol.EnvelopeDecodeFail, "TRISA protocol error: %s", err) } xfer.SetState(pb.TransactionState_REJECTED) return out, nil } log.Warn().Err(err).Msg("TRISA protocol error while sealing envelope") - return nil, status.Errorf(codes.FailedPrecondition, "TRISA protocol error: %s", err) + return nil, protocol.Errorf(protocol.EnvelopeDecodeFail, "TRISA protocol error: %s", err) } s.parent.updates.Broadcast(0, fmt.Sprintf("%04d new account balance: %s", account.ID, account.Balance), pb.MessageCategory_LEDGER) @@ -547,12 +557,20 @@ func (s *TRISA) respondTransfer(in *protocol.SecureEnvelope, peer *peers.Peer, i // respondPending responds to a transfer request from the originator by returning a // pending message and saving the pending transaction in the database. -func (s *TRISA) respondPending(in *protocol.SecureEnvelope, peer *peers.Peer, identity *ivms101.IdentityPayload, transaction *generic.Transaction, xfer *db.Transaction, account db.Account) (out *protocol.SecureEnvelope, err error) { +func (s *TRISA) respondPending(in *protocol.SecureEnvelope, peer *peers.Peer, identity *ivms101.IdentityPayload, transaction *generic.Transaction, xfer *db.Transaction, account db.Account) (out *protocol.SecureEnvelope, transferError *protocol.Error) { now := time.Now() xfer.NotBefore = now.Add(s.parent.conf.AsyncNotBefore) xfer.NotAfter = now.Add(s.parent.conf.AsyncNotAfter) + // Fetch the signing key from the remote peer + var signKey *rsa.PublicKey + var err error + if signKey, err = s.parent.fetchSigningKey(peer); err != nil { + log.Warn().Err(err).Msg("could not fetch signing key from originator peer") + return nil, protocol.Errorf(protocol.NoSigningKey, "could not fetch signing key from originator peer") + } + // Marshal the identity info into the local transaction var data []byte if data, err = protojson.Marshal(identity); err != nil { @@ -607,17 +625,17 @@ func (s *TRISA) respondPending(in *protocol.SecureEnvelope, peer *peers.Peer, id return nil, protocol.Errorf(protocol.InternalError, "request could not be processed") } - out, reject, err := envelope.Seal(payload, envelope.WithRSAPublicKey(peer.SigningKey())) + out, reject, err := envelope.Seal(payload, envelope.WithRSAPublicKey(signKey)) if err != nil { if reject != nil { if out, err = envelope.Reject(reject, envelope.WithEnvelopeID(in.Id)); err != nil { - return nil, status.Errorf(codes.FailedPrecondition, "TRISA protocol error: %s", err) + return nil, protocol.Errorf(protocol.EnvelopeDecodeFail, "TRISA protocol error: %s", err) } xfer.SetState(pb.TransactionState_REJECTED) return out, nil } log.Warn().Err(err).Msg("TRISA protocol error while sealing envelope") - return nil, status.Errorf(codes.FailedPrecondition, "TRISA protocol error: %s", err) + return nil, protocol.Errorf(protocol.EnvelopeDecodeFail, "TRISA protocol error: %s", err) } // Mark the transaction as pending for the async routine @@ -637,7 +655,21 @@ func (s *TRISA) sendAsync(tx *db.Transaction) (err error) { var originator *db.Identity if originator, err = tx.GetOriginator(s.parent.db); err != nil { log.Error().Err(err).Msg("could not fetch originator address") - return fmt.Errorf("could not fetch originator address") + return fmt.Errorf("could not fetch originator address: %s", err) + } + + // Fetch the remote peer + var peer *peers.Peer + if peer, err = s.parent.fetchPeer(originator.Provider); err != nil { + log.Warn().Err(err).Msg("could not fetch originator peer") + return fmt.Errorf("could not fetch originator peer: %s", err) + } + + // Fetch the signing key from the remote peer + var signKey *rsa.PublicKey + if signKey, err = s.parent.fetchSigningKey(peer); err != nil { + log.Warn().Err(err).Msg("could not fetch signing key from originator peer") + return fmt.Errorf("could not fetch signing key from originator peer: %s", err) } // Create the identity for the payload @@ -680,20 +712,6 @@ func (s *TRISA) sendAsync(tx *db.Transaction) (err error) { } payload.ReceivedAt = time.Now().Format(time.RFC3339) - // Conduct a GDS lookup if necessary to get the endpoint - var peer *peers.Peer - if peer, err = s.parent.peers.Search(originator.Provider); err != nil { - log.Error().Err(err).Msg("could not search peer from directory service") - return fmt.Errorf("could not search peer from directory service: %s", err) - } - - // Ensure that the local RVASP has signing keys for the remote, otherwise perform key exchange - var signKey *rsa.PublicKey - if signKey, err = peer.ExchangeKeys(true); err != nil { - log.Warn().Err(err).Msg("could not exchange keys with remote peer") - return fmt.Errorf("could not exchange keys with remote peer: %s", err) - } - // Secure the envelope with the remote originator's signing keys msg, _, err := envelope.Seal(payload, envelope.WithEnvelopeID(tx.Envelope), envelope.WithRSAPublicKey(signKey)) if err != nil { @@ -701,7 +719,7 @@ func (s *TRISA) sendAsync(tx *db.Transaction) (err error) { return fmt.Errorf("TRISA protocol error: %s", err) } - // Conduct the TRISA transfer, handle errors + // Conduct the TRISA exchange, handle errors if msg, err = peer.Transfer(msg); err != nil { log.Warn().Err(err).Msg("could not perform TRISA exchange") return fmt.Errorf("could not perform TRISA exchange: %s", err) @@ -714,9 +732,10 @@ func (s *TRISA) sendAsync(tx *db.Transaction) (err error) { return fmt.Errorf("TRISA protocol error: %s", err) } - if _, transaction, _, err = parsePayload(payload, true); err != nil { - log.Warn().Err(err).Msg("TRISA protocol error while parsing payload") - return fmt.Errorf("TRISA protocol error while parsing payload: %s", err) + var parseError *protocol.Error + if _, transaction, _, parseError = parsePayload(payload, true); parseError != nil { + log.Warn().Str("message", parseError.Message).Msg("TRISA protocol error while parsing payload") + return fmt.Errorf("TRISA protocol error while parsing payload: %s", parseError.Message) } if transaction == nil { @@ -772,28 +791,21 @@ func (s *TRISA) sendRejected(tx *db.Transaction) (err error) { return fmt.Errorf("could not fetch originator address") } - // Conduct a GDS lookup if necessary to get the endpoint + // Fetch the remote peer var peer *peers.Peer - if peer, err = s.parent.peers.Search(originator.Provider); err != nil { - log.Error().Err(err).Msg("could not search peer from directory service") - return fmt.Errorf("could not search peer from directory service: %s", err) - } - - // Ensure that the local RVASP has signing keys for the remote, otherwise perform key exchange - var signKey *rsa.PublicKey - if signKey, err = peer.ExchangeKeys(true); err != nil { - log.Warn().Err(err).Msg("could not exchange keys with remote peer") - return status.Errorf(codes.FailedPrecondition, "could not exchange keys with remote peer: %s", err) + if peer, err = s.parent.fetchPeer(originator.Provider); err != nil { + log.Warn().Err(err).Msg("could not fetch originator peer") + return fmt.Errorf("could not fetch originator peer: %s", err) } // Create the rejection message reject = protocol.Errorf(protocol.Rejected, "rejected by beneficiary") - if msg, err = envelope.Reject(reject, envelope.WithEnvelopeID(tx.Envelope), envelope.WithRSAPublicKey(signKey)); err != nil { + if msg, err = envelope.Reject(reject, envelope.WithEnvelopeID(tx.Envelope)); err != nil { log.Warn().Err(err).Msg("TRISA protocol error while creating reject envelope") return fmt.Errorf("TRISA protocol error: %s", err) } - // Conduct the TRISA transfer, handle errors + // Conduct the TRISA exchange, handle errors if msg, err = peer.Transfer(msg); err != nil { log.Warn().Err(err).Msg("could not perform TRISA exchange") return fmt.Errorf("could not perform TRISA exchange: %s", err) diff --git a/pkg/rvasp/trisa_test.go b/pkg/rvasp/trisa_test.go index 85417af..ce18978 100644 --- a/pkg/rvasp/trisa_test.go +++ b/pkg/rvasp/trisa_test.go @@ -147,6 +147,11 @@ func (s *rVASPTestSuite) TestValidTransfer() { require.NoError(err) require.NotNil(response) + // Verify that a valid envelope was returned + reject, isErr := envelope.Check(response) + require.Nil(reject) + require.False(isErr) + // Decrypt the response envelope payload, reject, err = envelope.Open(response, envelope.WithRSAPrivateKey(key)) require.NoError(err) diff --git a/scripts/fixtures/gds-fixture.py b/scripts/fixtures/gds-fixture.py index dda8d4c..4d42703 100644 --- a/scripts/fixtures/gds-fixture.py +++ b/scripts/fixtures/gds-fixture.py @@ -1,19 +1,26 @@ +import pem import json +import base64 import argparse -def generate_fixtures(template_path, output_path, name, person, id, endpoint): - # Load the template JSON - with open(template_path, 'r') as template_file: +def generate_fixtures(args): + # Load the GDS template JSON + with open(args.template, 'r') as template_file: template = json.load(template_file) # Insert the name, id, and TRISA endpoint into the template - template['common_name'] = name - template['entity']['name']['name_identifiers'][0]['legal_person_name'] = person - template['id'] = id - template['trisa_endpoint'] = endpoint + template['common_name'] = args.name + template['entity']['name']['name_identifiers'][0]['legal_person_name'] = args.name + template['id'] = args.id + template['trisa_endpoint'] = args.endpoint - # Write the fixture to the output path - with open(output_path, 'w') as output_file: + # Parse the pem certificate into a base64 string + certs = pem.parse_file(args.cert) + encoded = base64.b64encode(certs[0].as_bytes()) + template['signing_certificates'] = [{'data': encoded.decode('ascii')}] + + # Write the GDS fixture to the output path + with open(args.output, 'w') as output_file: json.dump(template, output_file, indent=4) def main(): @@ -21,12 +28,12 @@ def main(): parser.add_argument('-t', '--template', help='Path to template JSON to base fixture on', required=True) parser.add_argument('-o', '--output', help='Output path for the GDS VASP fixture', required=True) parser.add_argument('-n', '--name', help='Common name of the VASP', required=True) - parser.add_argument('-p', '--person', help='Legal person name of the VASP', required=True) parser.add_argument('-i', '--id', help='ID for the VASP', required=True) + parser.add_argument('-c', '--cert', help='Path to the public certificate to store in the VASP', required=True) parser.add_argument('-e', '--endpoint', help='TRISA endpoint for the VASP', required=True) args = parser.parse_args() - generate_fixtures(args.template, args.output, args.name, args.person, args.id, args.endpoint) + generate_fixtures(args) print('GDS VASP fixture generated at {}'.format(args.output)) diff --git a/scripts/fixtures/rvasp-fixtures.py b/scripts/fixtures/rvasp-fixtures.py new file mode 100644 index 0000000..0ea6818 --- /dev/null +++ b/scripts/fixtures/rvasp-fixtures.py @@ -0,0 +1,42 @@ +import os +import json +import shutil +import argparse + +VASPS_FILE = 'vasps.json' +WALLETS_FILE = 'wallets.json' + +def migrate_fixtures(args): + # Load the VASPs fixtures + with open(os.path.join(args.fixtures, VASPS_FILE), 'r') as rvasp_file: + rvasps = json.load(rvasp_file) + + names = args.names.split(',') + + # Insert the rVASP name into the fixture + for record in rvasps: + for n in names: + if n in record['common_name']: + record['common_name'] = n + break + + # Write the rVASP fixtures to the output path + with open(os.path.join(args.output, VASPS_FILE), 'w') as output_file: + json.dump(rvasps, output_file, indent=4) + + # Copy the wallets fixtures to the output path + shutil.copy(os.path.join(args.fixtures, WALLETS_FILE), os.path.join(args.output, WALLETS_FILE)) + +def main(): + parser = argparse.ArgumentParser(description='rVASP Fixture Migrator') + parser.add_argument('-f', '--fixtures', help='Path to the rVASP fixtures', required=True) + parser.add_argument('-o', '--output', help='Path to the output directory for the fixtures', required=True) + parser.add_argument('-n', '--names', help='Comma-separated list of VASP names', required=True) + + args = parser.parse_args() + migrate_fixtures(args) + + print('rVASP fixtures migrated to {}'.format(args.output)) + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/scripts/generate-fixtures.sh b/scripts/generate-fixtures.sh index d7721f7..5f05b3c 100755 --- a/scripts/generate-fixtures.sh +++ b/scripts/generate-fixtures.sh @@ -1,11 +1,5 @@ #!/bin/bash -# rVASP common names -ALICE_NAME="api.alice.vaspbot.net" -BOB_NAME="api.bob.vaspbot.net" -EVIL_NAME="api.evil.vaspbot.net" -CHARLIE_NAME="api.charlie.vaspbot.net" - # rVASP UUIDs in GDS VASP_PREFIX="vasps::" ALICE_ID="alice0a0-a0a0-a0a0-a0a0-a0a0a0a0a0a0" @@ -13,11 +7,11 @@ BOB_ID="bob0b0b0-b0b0-b0b0-b0b0-b0b0b0b0b0b0" EVIL_ID="evile0e0-e0e0-e0e0-e0e0-e0e0e0e0e0e0" CHARLIE_ID="charlie0-c0c0-c0c0-c0c0-c0c0c0c0c0c0" -# rVASP endpoints -ALICE_ENDPOINT="alice" -BOB_ENDPOINT="bob" -EVIL_ENDPOINT="evil" -CHARLIE_ENDPOINT="charlie" +# rVASP common names - these should match the service names in docker compose +ALICE_NAME="alice" +BOB_NAME="bob" +EVIL_NAME="evil" +CHARLIE_NAME="charlie" TRISA_PORT="4435" # GDS database DSN @@ -41,28 +35,32 @@ mkdir -p fixtures/certs certs init -c fixtures/certs/ca.gz mkdir -p fixtures/certs/alice -certs issue -c fixtures/certs/ca.gz -o fixtures/certs/alice/cert.pem -n $ALICE_ENDPOINT -O localhost -C $COUNTRY_CODE +certs issue -c fixtures/certs/ca.gz -o fixtures/certs/alice/cert.pem -n $ALICE_NAME -O localhost -C $COUNTRY_CODE mkdir -p fixtures/certs/bob -certs issue -c fixtures/certs/ca.gz -o fixtures/certs/bob/cert.pem -n $BOB_ENDPOINT -O localhost -C $COUNTRY_CODE +certs issue -c fixtures/certs/ca.gz -o fixtures/certs/bob/cert.pem -n $BOB_NAME -O localhost -C $COUNTRY_CODE mkdir -p fixtures/certs/charlie -certs issue -c fixtures/certs/ca.gz -o fixtures/certs/charlie/cert.pem -n $CHARLIE_ENDPOINT -O localhost -C $COUNTRY_CODE +certs issue -c fixtures/certs/ca.gz -o fixtures/certs/charlie/cert.pem -n $CHARLIE_NAME -O localhost -C $COUNTRY_CODE certs init -c fixtures/certs/ca.evil.gz mkdir -p fixtures/certs/evil -certs issue -c fixtures/certs/ca.evil.gz -o fixtures/certs/evil/cert.pem -n $EVIL_ENDPOINT -O localhost -C $COUNTRY_CODE +certs issue -c fixtures/certs/ca.evil.gz -o fixtures/certs/evil/cert.pem -n $EVIL_NAME -O localhost -C $COUNTRY_CODE + +# Generate the GDS fixtures from the template +mkdir -p fixtures/gds +python3 scripts/fixtures/gds-fixture.py -t scripts/fixtures/template.json -o fixtures/gds/alice.json -n $ALICE_NAME -i $ALICE_ID -c fixtures/certs/alice/cert.pem -e $ALICE_NAME:$TRISA_PORT +python3 scripts/fixtures/gds-fixture.py -t scripts/fixtures/template.json -o fixtures/gds/bob.json -n $BOB_NAME -i $BOB_ID -c fixtures/certs/bob/cert.pem -e $BOB_NAME:$TRISA_PORT +python3 scripts/fixtures/gds-fixture.py -t scripts/fixtures/template.json -o fixtures/gds/evil.json -n $EVIL_NAME -i $EVIL_ID -c fixtures/certs/evil/cert.pem -e $EVIL_NAME:$TRISA_PORT +python3 scripts/fixtures/gds-fixture.py -t scripts/fixtures/template.json -o fixtures/gds/charlie.json -n $CHARLIE_NAME -i $CHARLIE_ID -c fixtures/certs/charlie/cert.pem -e $CHARLIE_NAME:$TRISA_PORT -# Generate the rVASP fixtures from the template -mkdir -p fixtures/vasps -python scripts/fixtures/gds-fixture.py -t scripts/fixtures/template.json -o fixtures/vasps/alice.json -n $ALICE_NAME -p $ALICE_ENDPOINT -i $ALICE_ID -e $ALICE_ENDPOINT:$TRISA_PORT -python scripts/fixtures/gds-fixture.py -t scripts/fixtures/template.json -o fixtures/vasps/bob.json -n $BOB_NAME -p $BOB_ENDPOINT -i $BOB_ID -e $BOB_ENDPOINT:$TRISA_PORT -python scripts/fixtures/gds-fixture.py -t scripts/fixtures/template.json -o fixtures/vasps/evil.json -n $EVIL_NAME -p $EVIL_ENDPOINT -i $EVIL_ID -e $EVIL_ENDPOINT:$TRISA_PORT -python scripts/fixtures/gds-fixture.py -t scripts/fixtures/template.json -o fixtures/vasps/charlie.json -n $CHARLIE_NAME -p $CHARLIE_ENDPOINT -i $CHARLIE_ID -e $CHARLIE_ENDPOINT:$TRISA_PORT +# Migrate the rVASP fixtures to the fixtures directory +mkdir -p fixtures/rvasps +python3 scripts/fixtures/rvasp-fixtures.py -f pkg/rvasp/fixtures -o fixtures/rvasps -n $ALICE_NAME,$BOB_NAME,$EVIL_NAME,$CHARLIE_NAME -# Store the fixtures in the GDS database -gdsutil ldb:put -d $GDS_DSN $VASP_PREFIX$ALICE_ID fixtures/vasps/alice.json -gdsutil ldb:put -d $GDS_DSN $VASP_PREFIX$BOB_ID fixtures/vasps/bob.json -gdsutil ldb:put -d $GDS_DSN $VASP_PREFIX$EVIL_ID fixtures/vasps/evil.json -gdsutil ldb:put -d $GDS_DSN $VASP_PREFIX$CHARLIE_ID fixtures/vasps/charlie.json +# Store the rVASP records in the GDS database +gdsutil ldb:put -d $GDS_DSN $VASP_PREFIX$ALICE_ID fixtures/gds/alice.json +gdsutil ldb:put -d $GDS_DSN $VASP_PREFIX$BOB_ID fixtures/gds/bob.json +gdsutil ldb:put -d $GDS_DSN $VASP_PREFIX$EVIL_ID fixtures/gds/evil.json +gdsutil ldb:put -d $GDS_DSN $VASP_PREFIX$CHARLIE_ID fixtures/gds/charlie.json # Confirm the keys in the database echo ""