rvasp

package
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 14, 2021 License: MIT Imports: 38 Imported by: 0

README

rVASP

Robot VASP for TRISA demonstration and integration

This is a simple gRPC server that implements a UI workflow and messaging framework to demonstrate sending and receiving transactions using the TRISA InterVASP protocol. The server was built to support a demonstration UI that requires a streaming interface so that a live message flow is achieved. However, the communication protocol between rVASPs also demonstrates how implementers might go about writing InterVASP services in their own codebases. The architecture is as follows:

Architecture

Generating Protocol Buffers

To regenerate the Go and Python code from the protocol buffers:

$ go generate ./...

This will generate the Go code in pb/ and the Python code in pb/rvaspy. Alternatively you can manually generate the code to specify different directories using the following commands:

$ protoc -I . --go_out=plugins=grpc:. api.proto
$ python -m grpc_tools.protoc -I . --python_out=./rvaspy --grpc_python_out=./rvaspy api.proto

Quick Start

To get started using the rVASP, you can run the local server as follows:

$ go run ./cmd/rvasp serve

The server should now be listening for TRISADemo RPC messages. To send messages using the python API, make sure you can import the modules from rvaspy - the simplest way to do this is to install the package in editable mode as follows:

$ pip install -e ./rvaspy/

This will use the setup.py file in the rvaspy directory to install the package to your $PYTHON_PATH. Because it is in editable mode, any time you regenerate the protocol buffers or pull the repository, the module should be updated on the next time you import. An example script for using the package is as follows:

import rvaspy

api = rvaspy.connect("localhost:4434")

cmds = [
    api.account_request("robert@bobvasp.co.uk"),
    api.transfer_request("robert@bobvasp.co.uk", "mary@alicevasp.us", 42.99)
]

for msg in api.stub.LiveUpdates(iter(cmds)):
    print(msg)

Note that the RVASP api client is not fully implemented yet.

Containers

We are currently not using a container repository, so to build the docker images locally, please run the following steps in order:

  1. Build the root Docker image tagged as trisacrypto/rvasp:latest
  2. Build the alice, bob, and evil containers in containers/, tagging them appropriately
  3. Use docker-compose to run the three rVASPs locally

To simplify the build process, we have added a script that builds all 4 images. You can execute the building script as follows:

$ ./containers/rebuild.sh

Then all that's needed is to run docker-compose up to get the robot VASPs running locally.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func LookupAccount

func LookupAccount(db *gorm.DB, account string) *gorm.DB

LookupAccount by email address or wallet address.

func LookupBeneficiary

func LookupBeneficiary(db *gorm.DB, beneficiary string) *gorm.DB

LookupBeneficiary by email address or wallet address.

func MigrateDB

func MigrateDB(db *gorm.DB) (err error)

MigrateDB the schema based on the models defined above.

Types

type Account

type Account struct {
	gorm.Model
	Name          string          `gorm:"not null"`
	Email         string          `gorm:"uniqueIndex;not null"`
	WalletAddress string          `gorm:"uniqueIndex;not null;column:wallet_address"`
	Wallet        Wallet          `gorm:"foreignKey:WalletAddress;references:Address"`
	Balance       decimal.Decimal `gorm:"type:numeric(15,2);default:0.0"`
	Completed     uint64          `gorm:"not null;default:0"`
	Pending       uint64          `gorm:"not null;default:0"`
	IVMS101       string          `gorm:"column:ivms101;not null"`
}

Account contains details about the transactions that are served by the local VASP. It also contains the IVMS 101 data for KYC verification, in this table it is just stored as a JSON string rather than breaking it down to the field level. Only customers of the VASP have accounts.

func (Account) BalanceFloat

func (a Account) BalanceFloat() float32

BalanceFloat converts the balance decmial into an exact two precision float32 for use with the protocol buffers.

func (Account) LoadIdentity

func (a Account) LoadIdentity() (person *ivms101.Person, err error)

LoadIdentity returns the ivms101.Person for the Account.

func (Account) TableName

func (Account) TableName() string

TableName explicitly defines the name of the table for the model

func (Account) Transactions

func (a Account) Transactions(db *gorm.DB) (records []Transaction, err error)

Transactions returns an ordered list of transactions associated with the account ordered by the timestamp of the transaction, listing any pending transactions at the top. This function may also support pagination and limiting functions, which is why we're using it rather than having a direct relationship on the model.

type Identity

type Identity struct {
	gorm.Model
	WalletAddress string `gorm:"not null;column:wallet_address"`
	Email         string `gorm:"uniqueIndex"`
	Provider      string `gorm:"not null"`
}

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 the identity information could change between transactions. This intermediate table is designed to more closely mimic data storage as part of a blockchain transaction.

func (Identity) TableName

func (Identity) TableName() string

TableName explicitly defines the name of the table for the model

type LogLevelDecoder

type LogLevelDecoder zerolog.Level

LogLevelDecoder deserializes the log level from a config string.

func (*LogLevelDecoder) Decode

func (ll *LogLevelDecoder) Decode(value string) error

Decode implements envconfig.Decoder

type Server

type Server struct {
	pb.UnimplementedTRISADemoServer
	pb.UnimplementedTRISAIntegrationServer
	// contains filtered or unexported fields
}

Server implements the GRPC TRISAIntegration and TRISADemo services.

func New

func New(conf *Settings) (s *Server, err error)

New creates a rVASP server with the specified configuration and prepares it to listen for and serve GRPC requests.

func (*Server) AccountStatus

func (s *Server) AccountStatus(ctx context.Context, req *pb.AccountRequest) (rep *pb.AccountReply, err error)

AccountStatus is a demo RPC to allow demo clients to fetch their recent transactions.

func (*Server) LiveUpdates

func (s *Server) LiveUpdates(stream pb.TRISADemo_LiveUpdatesServer) (err error)

LiveUpdates is a demo bidirectional RPC that allows demo clients to explicitly show the message interchange between VASPs during the InterVASP protocol. The demo client connects to both sides of a transaction and can push commands to the stream; any messages received by the VASP as they perform the protocol are sent down to the UI.

func (*Server) Serve

func (s *Server) Serve() (err error)

Serve GRPC requests on the specified address.

func (*Server) Shutdown

func (s *Server) Shutdown() (err error)

Shutdown the rVASP Service gracefully

func (*Server) Transfer

func (s *Server) Transfer(ctx context.Context, req *pb.TransferRequest) (rep *pb.TransferReply, err error)

Transfer accepts a transfer request from a beneficiary and begins the InterVASP protocol to perform identity verification prior to establishing the transaction in the blockchain between crypto wallet addresses.

type Settings

type Settings struct {
	Name                string          `envconfig:"RVASP_NAME"`
	BindAddr            string          `envconfig:"RVASP_BIND_ADDR" default:":4434"`
	TRISABindAddr       string          `envconfig:"RVASP_TRISA_BIND_ADDR" default:":4435"`
	DatabaseDSN         string          `envconfig:"RVASP_DATABASE"`
	CertPath            string          `envconfig:"RVASP_CERT_PATH"`
	TrustChainPath      string          `envconfig:"RVASP_TRUST_CHAIN_PATH"`
	DirectoryServiceURL string          `envconfig:"RVASP_DIRECTORY_SERVICE_URL" default:"api.vaspdirectory.net:443"`
	ConsoleLog          bool            `envconfig:"RVASP_CONSOLE_LOG" default:"false"`
	LogLevel            LogLevelDecoder `envconfig:"RVASP_LOG_LEVEL" default:"info"`
}

Settings uses envconfig to load required settings from the environment and validate them in preparation for running the rVASP.

TODO: also store separate signing key instead of using the cert key.

func Config

func Config() (_ *Settings, err error)

Config creates a new settings object, loading environment variables and defaults.

type TRISA

type TRISA struct {
	protocol.UnimplementedTRISANetworkServer
	protocol.UnimplementedTRISAHealthServer
	// contains filtered or unexported fields
}

TRISA implements the GRPC TRISANetwork and TRISAHealth services.

func NewTRISA

func NewTRISA(parent *Server) (svc *TRISA, err error)

NewTRISA from a parent server.

func (*TRISA) ConfirmAddress

func (s *TRISA) ConfirmAddress(ctx context.Context, in *protocol.Address) (out *protocol.AddressConfirmation, err error)

ConfirmAddress allows the rVASP to respond to proof-of-control requests.

func (*TRISA) KeyExchange

func (s *TRISA) KeyExchange(ctx context.Context, in *protocol.SigningKey) (out *protocol.SigningKey, err error)

KeyExchange facilitates signing key exchange between VASPs.

func (*TRISA) Serve

func (s *TRISA) Serve() (err error)

Serve initializes the GRPC server and returns any errors during intitialization, it then kicks off a go routine to handle requests. Not thread safe, should not be called multiple times.

func (*TRISA) Shutdown

func (s *TRISA) Shutdown() (err error)

Shutdown the TRISA server gracefully

func (*TRISA) Status

func (s *TRISA) Status(ctx context.Context, in *protocol.HealthCheck) (out *protocol.ServiceState, err error)

Status returns a directory health check status as online and requests half an hour checks.

func (*TRISA) Transfer

func (s *TRISA) Transfer(ctx context.Context, in *protocol.SecureEnvelope) (out *protocol.SecureEnvelope, err error)

Transfer enables a quick one-off transaction between peers.

func (*TRISA) TransferStream

func (s *TRISA) TransferStream(stream protocol.TRISANetwork_TransferStreamServer) (err error)

TransferStream allows for high-throughput transactions.

type Transaction

type Transaction struct {
	gorm.Model
	Envelope      string          `gorm:"not null"`
	AccountID     uint            `gorm:"not null"`
	Account       Account         `gorm:"foreignKey:AccountID"`
	OriginatorID  uint            `gorm:"column:originator_id;not null"`
	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)"`
	Debit         bool            `gorm:"not null"`
	Completed     bool            `gorm:"not null;default:false"`
	Timestamp     time.Time       `gorm:"not null"`
	Identity      string          `gorm:"not null"`
}

Transaction holds exchange information to send money from one account to another. It also contains the decrypted identity payload that was sent as part of the TRISA protocol and the envelope ID that uniquely identifies the message chain.

func (Transaction) AmountFloat

func (t Transaction) AmountFloat() float32

AmountFloat converts the amount decmial into an exact two precision float32 for use with the protocol buffers.

func (Transaction) Proto

func (t Transaction) Proto() *pb.Transaction

Proto converts the transaction into a protocol buffer transaction

func (Transaction) TableName

func (Transaction) TableName() string

TableName explicitly defines the name of the table for the model

type UpdateManager

type UpdateManager struct {
	sync.RWMutex
	// contains filtered or unexported fields
}

UpdateManager sends update messages to all connected clients.

func NewUpdateManager

func NewUpdateManager() *UpdateManager

NewUpdateManager creates a new update manager ready to work. For thread safety, this is the only object that can send messages on update streams.

func (*UpdateManager) Add

func (u *UpdateManager) Add(client string, stream pb.TRISADemo_LiveUpdatesServer) (err error)

Add a new client update stream.

func (*UpdateManager) Broadcast

func (u *UpdateManager) Broadcast(requestID uint64, update string, cat pb.MessageCategory) (err error)

Broadcast a message to all streams.

func (*UpdateManager) Del

func (u *UpdateManager) Del(client string)

Del an old client update stream. No-op if client odesn't exist.

func (*UpdateManager) Send

func (u *UpdateManager) Send(client string, msg *pb.Message) (err error)

Send a message to a specific stream

func (*UpdateManager) SendTransferError

func (u *UpdateManager) SendTransferError(client string, id uint64, err *pb.Error) error

SendTransferError to client

type VASP

type VASP struct {
	gorm.Model
	Name      string     `gorm:"uniqueIndex;size:255;not null"`
	LegalName *string    `gorm:"column:legal_name;null"`
	URL       *string    `gorm:"null"`
	Country   *string    `gorm:"null"`
	Endpoint  *string    `gorm:"null"`
	PubKey    *string    `gorm:"null"`
	NotAfter  *time.Time `gorm:"null"`
	IsLocal   bool       `gorm:"column:is_local;default:false"`
	IVMS101   string     `gorm:"column:ivms101"`
}

VASP is a record of known partner VASPs and caches TRISA protocol information. This table also contains IVMS101 data that identifies the VASP (but only for the local VASP - we assume that VASPs do not have IVMS101 data on each other and have to use the directory service for that). TODO: modify VASP ID to a GUID

func (VASP) LoadIdentity

func (v VASP) LoadIdentity() (person *ivms101.Person, err error)

LoadIdentity returns the ivms101.Person for the VASP.

func (VASP) TableName

func (VASP) TableName() string

TableName explicitly defines the name of the table for the model

type Wallet

type Wallet struct {
	gorm.Model
	Address    string `gorm:"uniqueIndex"`
	Email      string `gorm:"uniqueIndex"`
	ProviderID uint   `gorm:"not null"`
	Provider   VASP   `gorm:"foreignKey:ProviderID"`
}

Wallet is a mapping of wallet IDs to VASPs to determine where to send transactions. Provider lookups can happen by wallet address or by email.

func (Wallet) TableName

func (Wallet) TableName() string

TableName explicitly defines the name of the table for the model

Directories

Path Synopsis
* Package jsonpb uses protojson (not the deprecated jsonpb module) to set defaults for * marshaling and unmarshaling rVASP protobuf messages to and from JSON format.
* Package jsonpb uses protojson (not the deprecated jsonpb module) to set defaults for * marshaling and unmarshaling rVASP protobuf messages to and from JSON format.
pb
v1

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL