diff --git a/README.md b/README.md index c64ca90..650f8af 100644 --- a/README.md +++ b/README.md @@ -718,10 +718,9 @@ import "github.com/bartossh/Computantis/configuration" - [type Configuration](<#type-configuration>) - [func Read(path string) (Configuration, error)](<#func-read>) -- [type DBConfig](<#type-dbconfig>) -## type [Configuration]() +## type [Configuration]() Configuration is the main configuration of the application that corresponds to the \*.yaml file that holds the configuration. @@ -729,14 +728,14 @@ Configuration is the main configuration of the application that corresponds to t type Configuration struct { Bookkeeper bookkeeping.Config `yaml:"bookkeeper"` Server server.Config `yaml:"server"` - Database DBConfig `yaml:"database"` + Database repohelper.DBConfig `yaml:"database"` DataProvider dataprovider.Config `yaml:"data_provider"` Validator validator.Config `yaml:"validator"` FileOperator fileoperations.Config `yaml:"file_operator"` } ``` -### func [Read]() +### func [Read]() ```go func Read(path string) (Configuration, error) @@ -744,19 +743,6 @@ func Read(path string) (Configuration, error) Read reads the configuration from the file and returns the Configuration with set fields according to the yaml setup. -## type [DBConfig]() - -Config contains configuration for the database. - -```go -type DBConfig struct { - ConnStr string `yaml:"conn_str"` // ConnStr is the connection string to the database. - DatabaseName string `yaml:"database_name"` // DatabaseName is the name of the database. - Token string `yaml:"token"` // Token is the token that is used to confirm api clients access. - TokenExpire int64 `yaml:"token_expiration"` // TokenExpire is the number of seconds after which token expires. -} -``` - # dataprovider ```go @@ -1048,6 +1034,158 @@ func (o *Observable[T]) Subscribe() *subscriber[T] Subscribe subscribes to the container. +# repohelper + +```go +import "github.com/bartossh/Computantis/repohelper" +``` + +## Index + +- [Variables](<#variables>) +- [type AddressWriteFindChecker](<#type-addresswritefindchecker>) +- [type BlockReadWriter](<#type-blockreadwriter>) +- [type ConnectionCloser](<#type-connectioncloser>) +- [type DBConfig](<#type-dbconfig>) + - [func (cfg DBConfig) Connect(ctx context.Context) (RepositoryProvider, error)](<#func-dbconfig-connect>) +- [type Migrator](<#type-migrator>) +- [type RepositoryProvider](<#type-repositoryprovider>) +- [type TokenWriteCheckInvalidator](<#type-tokenwritecheckinvalidator>) +- [type TransactionOperator](<#type-transactionoperator>) +- [type ValidatorStatusReader](<#type-validatorstatusreader>) + + +## Variables + +```go +var ( + ErrDatabaseNotSupported = fmt.Errorf("database not supported") +) +``` + +## type [AddressWriteFindChecker]() + +AddressWriteFindChecker abstracts address operations. + +```go +type AddressWriteFindChecker interface { + WriteAddress(ctx context.Context, addr string) error + CheckAddressExists(ctx context.Context, addr string) (bool, error) + FindAddress(ctx context.Context, search string, limit int) ([]string, error) +} +``` + +## type [BlockReadWriter]() + +BlockReadWriter abstracts block operations. + +```go +type BlockReadWriter interface { + LastBlock(ctx context.Context) (block.Block, error) + ReadBlockByHash(ctx context.Context, hash [32]byte) (block.Block, error) + WriteBlock(ctx context.Context, block block.Block) error +} +``` + +## type [ConnectionCloser]() + +ConnectionCloser abstracts connection closing operations. + +```go +type ConnectionCloser interface { + Disconnect(ctx context.Context) error +} +``` + +## type [DBConfig]() + +Config contains configuration for the database. + +```go +type DBConfig struct { + ConnStr string `yaml:"conn_str"` // ConnStr is the connection string to the database. + DatabaseName string `yaml:"database_name"` // DatabaseName is the name of the database. + Token string `yaml:"token"` // Token is the token that is used to confirm api clients access. + TokenExpire int64 `yaml:"token_expiration"` // TokenExpire is the number of seconds after which token expires. +} +``` + +### func \(DBConfig\) [Connect]() + +```go +func (cfg DBConfig) Connect(ctx context.Context) (RepositoryProvider, error) +``` + +Connect connects to the proper database and returns that connection. + +## type [Migrator]() + +MigrationRunner abstracts migration operations. + +```go +type Migrator interface { + RunMigration(ctx context.Context) error +} +``` + +## type [RepositoryProvider]() + +RepositoryProvider is an interface that ensures that all required methods to run computantis are implemented. + +```go +type RepositoryProvider interface { + AddressWriteFindChecker + BlockReadWriter + io.Writer + Migrator + TokenWriteCheckInvalidator + TransactionOperator + ValidatorStatusReader + ConnectionCloser +} +``` + +## type [TokenWriteCheckInvalidator]() + +TokenWriteCheckInvalidator abstracts token operations. + +```go +type TokenWriteCheckInvalidator interface { + CheckToken(ctx context.Context, tkn string) (bool, error) + WriteToken(ctx context.Context, tkn string, expirationDate int64) error + InvalidateToken(ctx context.Context, token string) error +} +``` + +## type [TransactionOperator]() + +TransactionOperator abstracts transaction operations. + +```go +type TransactionOperator interface { + WriteTransactionsInBlock(ctx context.Context, blockHash [32]byte, trxHash [][32]byte) error + FindTransactionInBlockHash(ctx context.Context, trxHash [32]byte) ([32]byte, error) + WriteTemporaryTransaction(ctx context.Context, trx *transaction.Transaction) error + RemoveAwaitingTransaction(ctx context.Context, trxHash [32]byte) error + WriteIssuerSignedTransactionForReceiver(ctx context.Context, receiverAddr string, trx *transaction.Transaction) error + ReadAwaitingTransactionsByReceiver(ctx context.Context, address string) ([]transaction.Transaction, error) + ReadAwaitingTransactionsByIssuer(ctx context.Context, address string) ([]transaction.Transaction, error) + MoveTransactionsFromTemporaryToPermanent(ctx context.Context, hash [][32]byte) error + ReadTemporaryTransactions(ctx context.Context) ([]transaction.Transaction, error) +} +``` + +## type [ValidatorStatusReader]() + +ValidatorStatusReader abstracts validator status operations. + +```go +type ValidatorStatusReader interface { + ReadLastNValidatorStatuses(ctx context.Context, last int64) ([]validator.Status, error) + WriteValidatorStatus(ctx context.Context, vs *validator.Status) error +} +``` + # repomongo ```go @@ -1057,7 +1195,7 @@ import "github.com/bartossh/Computantis/repomongo" ## Index - [type DataBase](<#type-database>) - - [func Connect(ctx context.Context, cfg configuration.DBConfig) (*DataBase, error)](<#func-connect>) + - [func Connect(ctx context.Context, conn, database string) (*DataBase, error)](<#func-connect>) - [func (db DataBase) CheckAddressExists(ctx context.Context, addr string) (bool, error)](<#func-database-checkaddressexists>) - [func (db DataBase) CheckToken(ctx context.Context, tkn string) (bool, error)](<#func-database-checktoken>) - [func (c DataBase) Disconnect(ctx context.Context) error](<#func-database-disconnect>) @@ -1084,7 +1222,7 @@ import "github.com/bartossh/Computantis/repomongo" - [type Migration](<#type-migration>) -## type [DataBase]() +## type [DataBase]() Database provides database access for read, write and delete of repository entities. @@ -1094,10 +1232,10 @@ type DataBase struct { } ``` -### func [Connect]() +### func [Connect]() ```go -func Connect(ctx context.Context, cfg configuration.DBConfig) (*DataBase, error) +func Connect(ctx context.Context, conn, database string) (*DataBase, error) ``` Connect creates new connection to the repository and returns pointer to the DataBase. @@ -1118,7 +1256,7 @@ func (db DataBase) CheckToken(ctx context.Context, tkn string) (bool, error) CheckToken checks if token exists in the database is valid and didn't expire. -### func \(DataBase\) [Disconnect]() +### func \(DataBase\) [Disconnect]() ```go func (c DataBase) Disconnect(ctx context.Context) error @@ -1306,7 +1444,7 @@ import "github.com/bartossh/Computantis/repopostgre" - [Variables](<#variables>) - [type DataBase](<#type-database>) - - [func Connect(ctx context.Context, cfg configuration.DBConfig) (*DataBase, error)](<#func-connect>) + - [func Connect(ctx context.Context, conn, database string) (*DataBase, error)](<#func-connect>) - [func (db DataBase) CheckAddressExists(ctx context.Context, addr string) (bool, error)](<#func-database-checkaddressexists>) - [func (db DataBase) CheckToken(ctx context.Context, tkn string) (bool, error)](<#func-database-checktoken>) - [func (db DataBase) Disconnect(ctx context.Context) error](<#func-database-disconnect>) @@ -1322,6 +1460,7 @@ import "github.com/bartossh/Computantis/repopostgre" - [func (db DataBase) ReadLastNValidatorStatuses(ctx context.Context, last int64) ([]validator.Status, error)](<#func-database-readlastnvalidatorstatuses>) - [func (db DataBase) ReadTemporaryTransactions(ctx context.Context) ([]transaction.Transaction, error)](<#func-database-readtemporarytransactions>) - [func (db DataBase) RemoveAwaitingTransaction(ctx context.Context, trxHash [32]byte) error](<#func-database-removeawaitingtransaction>) + - [func (DataBase) RunMigration(_ context.Context) error](<#func-database-runmigration>) - [func (db DataBase) Write(p []byte) (n int, err error)](<#func-database-write>) - [func (db DataBase) WriteAddress(ctx context.Context, addr string) error](<#func-database-writeaddress>) - [func (db DataBase) WriteBlock(ctx context.Context, block block.Block) error](<#func-database-writeblock>) @@ -1345,7 +1484,7 @@ var ( ) ``` -## type [DataBase]() +## type [DataBase]() Database provides database access for read, write and delete of repository entities. @@ -1355,10 +1494,10 @@ type DataBase struct { } ``` -### func [Connect]() +### func [Connect]() ```go -func Connect(ctx context.Context, cfg configuration.DBConfig) (*DataBase, error) +func Connect(ctx context.Context, conn, database string) (*DataBase, error) ``` Connect creates new connection to the repository and returns pointer to the DataBase. @@ -1379,7 +1518,7 @@ func (db DataBase) CheckToken(ctx context.Context, tkn string) (bool, error) CheckToken checks if token exists in the database is valid and didn't expire. -### func \(DataBase\) [Disconnect]() +### func \(DataBase\) [Disconnect]() ```go func (db DataBase) Disconnect(ctx context.Context) error @@ -1395,7 +1534,7 @@ func (db DataBase) FindAddress(ctx context.Context, search string, limit int) ([ FindAddress finds address in the database. -### func \(DataBase\) [FindTransactionInBlockHash]() +### func \(DataBase\) [FindTransactionInBlockHash]() ```go func (db DataBase) FindTransactionInBlockHash(ctx context.Context, trxHash [32]byte) ([32]byte, error) @@ -1403,7 +1542,7 @@ func (db DataBase) FindTransactionInBlockHash(ctx context.Context, trxHash [32]b FindTransactionInBlockHash finds Block hash in to which Transaction with given hash was added. -### func \(DataBase\) [InvalidateToken]() +### func \(DataBase\) [InvalidateToken]() ```go func (db DataBase) InvalidateToken(ctx context.Context, token string) error @@ -1411,7 +1550,7 @@ func (db DataBase) InvalidateToken(ctx context.Context, token string) error InvalidateToken invalidates token. -### func \(DataBase\) [LastBlock]() +### func \(DataBase\) [LastBlock]() ```go func (db DataBase) LastBlock(ctx context.Context) (block.Block, error) @@ -1419,7 +1558,7 @@ func (db DataBase) LastBlock(ctx context.Context) (block.Block, error) LastBlock returns last block from the database. -### func \(DataBase\) [MoveTransactionsFromTemporaryToPermanent]() +### func \(DataBase\) [MoveTransactionsFromTemporaryToPermanent]() ```go func (db DataBase) MoveTransactionsFromTemporaryToPermanent(ctx context.Context, hash [][32]byte) error @@ -1427,7 +1566,7 @@ func (db DataBase) MoveTransactionsFromTemporaryToPermanent(ctx context.Context, MoveTransactionsFromTemporaryToPermanent moves transactions from temporary storage to permanent storage. -### func \(DataBase\) [Ping]() +### func \(DataBase\) [Ping]() ```go func (db DataBase) Ping(ctx context.Context) error @@ -1435,7 +1574,7 @@ func (db DataBase) Ping(ctx context.Context) error Ping checks if the connection to the database is still alive. -### func \(DataBase\) [ReadAwaitingTransactionsByIssuer]() +### func \(DataBase\) [ReadAwaitingTransactionsByIssuer]() ```go func (db DataBase) ReadAwaitingTransactionsByIssuer(ctx context.Context, address string) ([]transaction.Transaction, error) @@ -1451,7 +1590,7 @@ func (db DataBase) ReadAwaitingTransactionsByReceiver(ctx context.Context, addre ReadAwaitingTransactionsByReceiver reads all transactions paired with given receiver address. -### func \(DataBase\) [ReadBlockByHash]() +### func \(DataBase\) [ReadBlockByHash]() ```go func (db DataBase) ReadBlockByHash(ctx context.Context, hash [32]byte) (block.Block, error) @@ -1467,7 +1606,7 @@ func (db DataBase) ReadLastNValidatorStatuses(ctx context.Context, last int64) ( ReadLastNValidatorStatuses reads last validator statuses from the database. -### func \(DataBase\) [ReadTemporaryTransactions]() +### func \(DataBase\) [ReadTemporaryTransactions]() ```go func (db DataBase) ReadTemporaryTransactions(ctx context.Context) ([]transaction.Transaction, error) @@ -1483,6 +1622,14 @@ func (db DataBase) RemoveAwaitingTransaction(ctx context.Context, trxHash [32]by RemoveAwaitingTransaction removes transaction from the awaiting transaction storage. +### func \(DataBase\) [RunMigration]() + +```go +func (DataBase) RunMigration(_ context.Context) error +``` + +RunMigration satisfies the RepositoryProvider interface as PostgreSQL migrations are run on when database is created in docker\-compose\-postgresql.yml. + ### func \(DataBase\) [Write]() ```go @@ -1499,7 +1646,7 @@ func (db DataBase) WriteAddress(ctx context.Context, addr string) error WriteAddress writes address to the database. -### func \(DataBase\) [WriteBlock]() +### func \(DataBase\) [WriteBlock]() ```go func (db DataBase) WriteBlock(ctx context.Context, block block.Block) error @@ -1523,7 +1670,7 @@ func (db DataBase) WriteTemporaryTransaction(ctx context.Context, trx *transacti WriteTemporaryTransaction writes transaction to the temporary storage. -### func \(DataBase\) [WriteToken]() +### func \(DataBase\) [WriteToken]() ```go func (db DataBase) WriteToken(ctx context.Context, tkn string, expirationDate int64) error diff --git a/client/client_test.go b/client/client_test.go index c31c7b3..b518098 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -58,7 +58,7 @@ func TestFullClientApiCycle(t *testing.T) { wallet.New) err := issuer.ValidateApiVersion() assert.Nil(t, err) - err = issuer.NewWallet("wpg6d0grqJjyRicC8oI0/w6IGivm5ypFNTO/wwPGW9A=") + err = issuer.NewWallet("G8OH7lHu5qfWVumWom0ySN29lakog8nhzSPEwROMjvhdI6VgZ6GoPcdJmoIo7sF3lxQNJMOTKxpYBr6zF992WN86uB7xTEJZ") assert.Nil(t, err) receiver := NewClient( @@ -72,7 +72,7 @@ func TestFullClientApiCycle(t *testing.T) { wallet.New) err = receiver.ValidateApiVersion() assert.Nil(t, err) - err = receiver.NewWallet("GWFuhvyFnmMg1/vhPCfoa9ct1pAMC1pWwlRg4kt0D/w=") + err = receiver.NewWallet("jykkeD6Tr6xikkYwC805kVoFThm8VGEHStTFk1lIU6RgEf7p3vjFpPQFI3VP9SYeARjYh2jecMSYsmgddjZZcy32iySHijJQ") assert.Nil(t, err) receiverAddr, err := receiver.Address() @@ -89,6 +89,9 @@ func TestFullClientApiCycle(t *testing.T) { err = receiver.ConfirmTransaction(&awaitedTrx[0]) assert.Nil(t, err) + if err != nil { + fmt.Printf("err: %v\n", err.Error()) + } issuer.FlushWalletFromMemory() receiver.FlushWalletFromMemory() diff --git a/cmd/central/main.go b/cmd/central/main.go index 3e426ca..ffac11c 100644 --- a/cmd/central/main.go +++ b/cmd/central/main.go @@ -13,7 +13,6 @@ import ( "github.com/bartossh/Computantis/dataprovider" "github.com/bartossh/Computantis/logging" "github.com/bartossh/Computantis/reactive" - "github.com/bartossh/Computantis/repomongo" "github.com/bartossh/Computantis/server" "github.com/bartossh/Computantis/wallet" ) @@ -39,7 +38,7 @@ func main() { cancel() }() - db, err := repomongo.Connect(ctx, cfg.Database) + db, err := cfg.Database.Connect(ctx) if err != nil { fmt.Println(err) c <- os.Interrupt diff --git a/cmd/validator/main.go b/cmd/validator/main.go index 260b669..0091a50 100644 --- a/cmd/validator/main.go +++ b/cmd/validator/main.go @@ -12,7 +12,6 @@ import ( "github.com/bartossh/Computantis/configuration" "github.com/bartossh/Computantis/fileoperations" "github.com/bartossh/Computantis/logging" - "github.com/bartossh/Computantis/repomongo" "github.com/bartossh/Computantis/validator" "github.com/bartossh/Computantis/wallet" "github.com/bartossh/Computantis/webhooks" @@ -30,7 +29,7 @@ func main() { c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt) - db, err := repomongo.Connect(ctx, cfg.Database) + db, err := cfg.Database.Connect(ctx) if err != nil { fmt.Println(err) c <- os.Interrupt diff --git a/configuration/configuration.go b/configuration/configuration.go index 270cb5d..cfc1b62 100644 --- a/configuration/configuration.go +++ b/configuration/configuration.go @@ -7,25 +7,18 @@ import ( "github.com/bartossh/Computantis/bookkeeping" "github.com/bartossh/Computantis/dataprovider" "github.com/bartossh/Computantis/fileoperations" + "github.com/bartossh/Computantis/repohelper" "github.com/bartossh/Computantis/server" "github.com/bartossh/Computantis/validator" "gopkg.in/yaml.v2" ) -// Config contains configuration for the database. -type DBConfig struct { - ConnStr string `yaml:"conn_str"` // ConnStr is the connection string to the database. - DatabaseName string `yaml:"database_name"` // DatabaseName is the name of the database. - Token string `yaml:"token"` // Token is the token that is used to confirm api clients access. - TokenExpire int64 `yaml:"token_expiration"` // TokenExpire is the number of seconds after which token expires. -} - // Configuration is the main configuration of the application that corresponds to the *.yaml file // that holds the configuration. type Configuration struct { Bookkeeper bookkeeping.Config `yaml:"bookkeeper"` Server server.Config `yaml:"server"` - Database DBConfig `yaml:"database"` + Database repohelper.DBConfig `yaml:"database"` DataProvider dataprovider.Config `yaml:"data_provider"` Validator validator.Config `yaml:"validator"` FileOperator fileoperations.Config `yaml:"file_operator"` diff --git a/docker_postgres_init.sql b/docker_postgres_init.sql index ac4c6d5..07bc3ab 100644 --- a/docker_postgres_init.sql +++ b/docker_postgres_init.sql @@ -7,9 +7,7 @@ CREATE DATABASE computantis TABLESPACE = pg_default CONNECTION LIMIT = -1; -CREATE USER computantis WITH PASSWORD 'computantis'; - -GRANT ALL PRIVILEGES ON DATABASE computantis TO computantis; +\c computantis CREATE TABLE IF NOT EXISTS addresses ( id serial PRIMARY KEY, @@ -67,8 +65,8 @@ CREATE INDEX transaction_awaiting_receiver_address ON transactionsAwaitingReceiv CREATE TABLE IF NOT EXISTS blocks ( id serial PRIMARY KEY, - index INTEGER UNIQUE NOT NULL, - created_at INTEGER NOT NULL, + index BIGINT UNIQUE NOT NULL, + timestamp BIGINT NOT NULL, nonce INTEGER NOT NULL, difficulty INTEGER NOT NULL, hash BYTEA UNIQUE NOT NULL, @@ -79,7 +77,7 @@ CREATE TABLE IF NOT EXISTS blocks ( CREATE INDEX block_index ON blocks USING HASH (index); CREATE INDEX block_hash ON blocks USING HASH (hash); CREATE INDEX block_prev_hash ON blocks USING HASH (prev_hash); -CREATE INDEX block_created_at ON blocks USING BTREE (created_at); +CREATE INDEX block_created_at ON blocks USING BTREE (timestamp); CREATE TABLE IF NOT EXISTS transactionsInBlock ( id serial PRIMARY KEY, @@ -94,7 +92,7 @@ CREATE TABLE IF NOT EXISTS tokens ( id serial PRIMARY KEY, token VARCHAR (100) UNIQUE NOT NULL, valid BOOLEAN NOT NULL, - expiration_date INTEGER NOT NULL + expiration_date BIGINT NOT NULL ); CREATE INDEX token_token ON tokens USING HASH (token); @@ -118,4 +116,12 @@ CREATE TABLE IF NOT EXISTS validatorStatus ( ); CREATE INDEX validator_index ON validatorStatus USING HASH (index); -CREATE INDEX validator_created_at ON validatorStatus USING BTREE (created_at); \ No newline at end of file +CREATE INDEX validator_created_at ON validatorStatus USING BTREE (created_at); + + +CREATE USER computantis WITH ENCRYPTED PASSWORD 'computantis'; + +GRANT ALL PRIVILEGES ON DATABASE computantis TO computantis; +GRANT ALL PRIVILEGES ON SCHEMA public TO computantis; +GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO computantis; +GRANT ALL ON ALL TABLES IN SCHEMA public TO computantis; diff --git a/docs/docs.md b/docs/docs.md index 2a44041..505b272 100644 --- a/docs/docs.md +++ b/docs/docs.md @@ -649,10 +649,9 @@ import "github.com/bartossh/Computantis/configuration" - [type Configuration](<#type-configuration>) - [func Read(path string) (Configuration, error)](<#func-read>) -- [type DBConfig](<#type-dbconfig>) -## type [Configuration]() +## type [Configuration]() Configuration is the main configuration of the application that corresponds to the \*.yaml file that holds the configuration. @@ -660,14 +659,14 @@ Configuration is the main configuration of the application that corresponds to t type Configuration struct { Bookkeeper bookkeeping.Config `yaml:"bookkeeper"` Server server.Config `yaml:"server"` - Database DBConfig `yaml:"database"` + Database repohelper.DBConfig `yaml:"database"` DataProvider dataprovider.Config `yaml:"data_provider"` Validator validator.Config `yaml:"validator"` FileOperator fileoperations.Config `yaml:"file_operator"` } ``` -### func [Read]() +### func [Read]() ```go func Read(path string) (Configuration, error) @@ -675,19 +674,6 @@ func Read(path string) (Configuration, error) Read reads the configuration from the file and returns the Configuration with set fields according to the yaml setup. -## type [DBConfig]() - -Config contains configuration for the database. - -```go -type DBConfig struct { - ConnStr string `yaml:"conn_str"` // ConnStr is the connection string to the database. - DatabaseName string `yaml:"database_name"` // DatabaseName is the name of the database. - Token string `yaml:"token"` // Token is the token that is used to confirm api clients access. - TokenExpire int64 `yaml:"token_expiration"` // TokenExpire is the number of seconds after which token expires. -} -``` - # dataprovider ```go @@ -979,6 +965,158 @@ func (o *Observable[T]) Subscribe() *subscriber[T] Subscribe subscribes to the container. +# repohelper + +```go +import "github.com/bartossh/Computantis/repohelper" +``` + +## Index + +- [Variables](<#variables>) +- [type AddressWriteFindChecker](<#type-addresswritefindchecker>) +- [type BlockReadWriter](<#type-blockreadwriter>) +- [type ConnectionCloser](<#type-connectioncloser>) +- [type DBConfig](<#type-dbconfig>) + - [func (cfg DBConfig) Connect(ctx context.Context) (RepositoryProvider, error)](<#func-dbconfig-connect>) +- [type Migrator](<#type-migrator>) +- [type RepositoryProvider](<#type-repositoryprovider>) +- [type TokenWriteCheckInvalidator](<#type-tokenwritecheckinvalidator>) +- [type TransactionOperator](<#type-transactionoperator>) +- [type ValidatorStatusReader](<#type-validatorstatusreader>) + + +## Variables + +```go +var ( + ErrDatabaseNotSupported = fmt.Errorf("database not supported") +) +``` + +## type [AddressWriteFindChecker]() + +AddressWriteFindChecker abstracts address operations. + +```go +type AddressWriteFindChecker interface { + WriteAddress(ctx context.Context, addr string) error + CheckAddressExists(ctx context.Context, addr string) (bool, error) + FindAddress(ctx context.Context, search string, limit int) ([]string, error) +} +``` + +## type [BlockReadWriter]() + +BlockReadWriter abstracts block operations. + +```go +type BlockReadWriter interface { + LastBlock(ctx context.Context) (block.Block, error) + ReadBlockByHash(ctx context.Context, hash [32]byte) (block.Block, error) + WriteBlock(ctx context.Context, block block.Block) error +} +``` + +## type [ConnectionCloser]() + +ConnectionCloser abstracts connection closing operations. + +```go +type ConnectionCloser interface { + Disconnect(ctx context.Context) error +} +``` + +## type [DBConfig]() + +Config contains configuration for the database. + +```go +type DBConfig struct { + ConnStr string `yaml:"conn_str"` // ConnStr is the connection string to the database. + DatabaseName string `yaml:"database_name"` // DatabaseName is the name of the database. + Token string `yaml:"token"` // Token is the token that is used to confirm api clients access. + TokenExpire int64 `yaml:"token_expiration"` // TokenExpire is the number of seconds after which token expires. +} +``` + +### func \(DBConfig\) [Connect]() + +```go +func (cfg DBConfig) Connect(ctx context.Context) (RepositoryProvider, error) +``` + +Connect connects to the proper database and returns that connection. + +## type [Migrator]() + +MigrationRunner abstracts migration operations. + +```go +type Migrator interface { + RunMigration(ctx context.Context) error +} +``` + +## type [RepositoryProvider]() + +RepositoryProvider is an interface that ensures that all required methods to run computantis are implemented. + +```go +type RepositoryProvider interface { + AddressWriteFindChecker + BlockReadWriter + io.Writer + Migrator + TokenWriteCheckInvalidator + TransactionOperator + ValidatorStatusReader + ConnectionCloser +} +``` + +## type [TokenWriteCheckInvalidator]() + +TokenWriteCheckInvalidator abstracts token operations. + +```go +type TokenWriteCheckInvalidator interface { + CheckToken(ctx context.Context, tkn string) (bool, error) + WriteToken(ctx context.Context, tkn string, expirationDate int64) error + InvalidateToken(ctx context.Context, token string) error +} +``` + +## type [TransactionOperator]() + +TransactionOperator abstracts transaction operations. + +```go +type TransactionOperator interface { + WriteTransactionsInBlock(ctx context.Context, blockHash [32]byte, trxHash [][32]byte) error + FindTransactionInBlockHash(ctx context.Context, trxHash [32]byte) ([32]byte, error) + WriteTemporaryTransaction(ctx context.Context, trx *transaction.Transaction) error + RemoveAwaitingTransaction(ctx context.Context, trxHash [32]byte) error + WriteIssuerSignedTransactionForReceiver(ctx context.Context, receiverAddr string, trx *transaction.Transaction) error + ReadAwaitingTransactionsByReceiver(ctx context.Context, address string) ([]transaction.Transaction, error) + ReadAwaitingTransactionsByIssuer(ctx context.Context, address string) ([]transaction.Transaction, error) + MoveTransactionsFromTemporaryToPermanent(ctx context.Context, hash [][32]byte) error + ReadTemporaryTransactions(ctx context.Context) ([]transaction.Transaction, error) +} +``` + +## type [ValidatorStatusReader]() + +ValidatorStatusReader abstracts validator status operations. + +```go +type ValidatorStatusReader interface { + ReadLastNValidatorStatuses(ctx context.Context, last int64) ([]validator.Status, error) + WriteValidatorStatus(ctx context.Context, vs *validator.Status) error +} +``` + # repomongo ```go @@ -988,7 +1126,7 @@ import "github.com/bartossh/Computantis/repomongo" ## Index - [type DataBase](<#type-database>) - - [func Connect(ctx context.Context, cfg configuration.DBConfig) (*DataBase, error)](<#func-connect>) + - [func Connect(ctx context.Context, conn, database string) (*DataBase, error)](<#func-connect>) - [func (db DataBase) CheckAddressExists(ctx context.Context, addr string) (bool, error)](<#func-database-checkaddressexists>) - [func (db DataBase) CheckToken(ctx context.Context, tkn string) (bool, error)](<#func-database-checktoken>) - [func (c DataBase) Disconnect(ctx context.Context) error](<#func-database-disconnect>) @@ -1015,7 +1153,7 @@ import "github.com/bartossh/Computantis/repomongo" - [type Migration](<#type-migration>) -## type [DataBase]() +## type [DataBase]() Database provides database access for read, write and delete of repository entities. @@ -1025,10 +1163,10 @@ type DataBase struct { } ``` -### func [Connect]() +### func [Connect]() ```go -func Connect(ctx context.Context, cfg configuration.DBConfig) (*DataBase, error) +func Connect(ctx context.Context, conn, database string) (*DataBase, error) ``` Connect creates new connection to the repository and returns pointer to the DataBase. @@ -1049,7 +1187,7 @@ func (db DataBase) CheckToken(ctx context.Context, tkn string) (bool, error) CheckToken checks if token exists in the database is valid and didn't expire. -### func \(DataBase\) [Disconnect]() +### func \(DataBase\) [Disconnect]() ```go func (c DataBase) Disconnect(ctx context.Context) error @@ -1237,7 +1375,7 @@ import "github.com/bartossh/Computantis/repopostgre" - [Variables](<#variables>) - [type DataBase](<#type-database>) - - [func Connect(ctx context.Context, cfg configuration.DBConfig) (*DataBase, error)](<#func-connect>) + - [func Connect(ctx context.Context, conn, database string) (*DataBase, error)](<#func-connect>) - [func (db DataBase) CheckAddressExists(ctx context.Context, addr string) (bool, error)](<#func-database-checkaddressexists>) - [func (db DataBase) CheckToken(ctx context.Context, tkn string) (bool, error)](<#func-database-checktoken>) - [func (db DataBase) Disconnect(ctx context.Context) error](<#func-database-disconnect>) @@ -1253,6 +1391,7 @@ import "github.com/bartossh/Computantis/repopostgre" - [func (db DataBase) ReadLastNValidatorStatuses(ctx context.Context, last int64) ([]validator.Status, error)](<#func-database-readlastnvalidatorstatuses>) - [func (db DataBase) ReadTemporaryTransactions(ctx context.Context) ([]transaction.Transaction, error)](<#func-database-readtemporarytransactions>) - [func (db DataBase) RemoveAwaitingTransaction(ctx context.Context, trxHash [32]byte) error](<#func-database-removeawaitingtransaction>) + - [func (DataBase) RunMigration(_ context.Context) error](<#func-database-runmigration>) - [func (db DataBase) Write(p []byte) (n int, err error)](<#func-database-write>) - [func (db DataBase) WriteAddress(ctx context.Context, addr string) error](<#func-database-writeaddress>) - [func (db DataBase) WriteBlock(ctx context.Context, block block.Block) error](<#func-database-writeblock>) @@ -1276,7 +1415,7 @@ var ( ) ``` -## type [DataBase]() +## type [DataBase]() Database provides database access for read, write and delete of repository entities. @@ -1286,10 +1425,10 @@ type DataBase struct { } ``` -### func [Connect]() +### func [Connect]() ```go -func Connect(ctx context.Context, cfg configuration.DBConfig) (*DataBase, error) +func Connect(ctx context.Context, conn, database string) (*DataBase, error) ``` Connect creates new connection to the repository and returns pointer to the DataBase. @@ -1310,7 +1449,7 @@ func (db DataBase) CheckToken(ctx context.Context, tkn string) (bool, error) CheckToken checks if token exists in the database is valid and didn't expire. -### func \(DataBase\) [Disconnect]() +### func \(DataBase\) [Disconnect]() ```go func (db DataBase) Disconnect(ctx context.Context) error @@ -1326,7 +1465,7 @@ func (db DataBase) FindAddress(ctx context.Context, search string, limit int) ([ FindAddress finds address in the database. -### func \(DataBase\) [FindTransactionInBlockHash]() +### func \(DataBase\) [FindTransactionInBlockHash]() ```go func (db DataBase) FindTransactionInBlockHash(ctx context.Context, trxHash [32]byte) ([32]byte, error) @@ -1334,7 +1473,7 @@ func (db DataBase) FindTransactionInBlockHash(ctx context.Context, trxHash [32]b FindTransactionInBlockHash finds Block hash in to which Transaction with given hash was added. -### func \(DataBase\) [InvalidateToken]() +### func \(DataBase\) [InvalidateToken]() ```go func (db DataBase) InvalidateToken(ctx context.Context, token string) error @@ -1342,7 +1481,7 @@ func (db DataBase) InvalidateToken(ctx context.Context, token string) error InvalidateToken invalidates token. -### func \(DataBase\) [LastBlock]() +### func \(DataBase\) [LastBlock]() ```go func (db DataBase) LastBlock(ctx context.Context) (block.Block, error) @@ -1350,7 +1489,7 @@ func (db DataBase) LastBlock(ctx context.Context) (block.Block, error) LastBlock returns last block from the database. -### func \(DataBase\) [MoveTransactionsFromTemporaryToPermanent]() +### func \(DataBase\) [MoveTransactionsFromTemporaryToPermanent]() ```go func (db DataBase) MoveTransactionsFromTemporaryToPermanent(ctx context.Context, hash [][32]byte) error @@ -1358,7 +1497,7 @@ func (db DataBase) MoveTransactionsFromTemporaryToPermanent(ctx context.Context, MoveTransactionsFromTemporaryToPermanent moves transactions from temporary storage to permanent storage. -### func \(DataBase\) [Ping]() +### func \(DataBase\) [Ping]() ```go func (db DataBase) Ping(ctx context.Context) error @@ -1366,7 +1505,7 @@ func (db DataBase) Ping(ctx context.Context) error Ping checks if the connection to the database is still alive. -### func \(DataBase\) [ReadAwaitingTransactionsByIssuer]() +### func \(DataBase\) [ReadAwaitingTransactionsByIssuer]() ```go func (db DataBase) ReadAwaitingTransactionsByIssuer(ctx context.Context, address string) ([]transaction.Transaction, error) @@ -1382,7 +1521,7 @@ func (db DataBase) ReadAwaitingTransactionsByReceiver(ctx context.Context, addre ReadAwaitingTransactionsByReceiver reads all transactions paired with given receiver address. -### func \(DataBase\) [ReadBlockByHash]() +### func \(DataBase\) [ReadBlockByHash]() ```go func (db DataBase) ReadBlockByHash(ctx context.Context, hash [32]byte) (block.Block, error) @@ -1398,7 +1537,7 @@ func (db DataBase) ReadLastNValidatorStatuses(ctx context.Context, last int64) ( ReadLastNValidatorStatuses reads last validator statuses from the database. -### func \(DataBase\) [ReadTemporaryTransactions]() +### func \(DataBase\) [ReadTemporaryTransactions]() ```go func (db DataBase) ReadTemporaryTransactions(ctx context.Context) ([]transaction.Transaction, error) @@ -1414,6 +1553,14 @@ func (db DataBase) RemoveAwaitingTransaction(ctx context.Context, trxHash [32]by RemoveAwaitingTransaction removes transaction from the awaiting transaction storage. +### func \(DataBase\) [RunMigration]() + +```go +func (DataBase) RunMigration(_ context.Context) error +``` + +RunMigration satisfies the RepositoryProvider interface as PostgreSQL migrations are run on when database is created in docker\-compose\-postgresql.yml. + ### func \(DataBase\) [Write]() ```go @@ -1430,7 +1577,7 @@ func (db DataBase) WriteAddress(ctx context.Context, addr string) error WriteAddress writes address to the database. -### func \(DataBase\) [WriteBlock]() +### func \(DataBase\) [WriteBlock]() ```go func (db DataBase) WriteBlock(ctx context.Context, block block.Block) error @@ -1454,7 +1601,7 @@ func (db DataBase) WriteTemporaryTransaction(ctx context.Context, trx *transacti WriteTemporaryTransaction writes transaction to the temporary storage. -### func \(DataBase\) [WriteToken]() +### func \(DataBase\) [WriteToken]() ```go func (db DataBase) WriteToken(ctx context.Context, tkn string, expirationDate int64) error diff --git a/mongodb_queries/create_tokens.js b/queries_mongo/create_tokens.js similarity index 100% rename from mongodb_queries/create_tokens.js rename to queries_mongo/create_tokens.js diff --git a/queries_postgresql/insert_token.sql b/queries_postgresql/insert_token.sql new file mode 100644 index 0000000..1cda359 --- /dev/null +++ b/queries_postgresql/insert_token.sql @@ -0,0 +1,3 @@ +INSERT INTO tokens (token, valid, expiration_date) VALUES ('80fda91a43989fa81347aa011e0f1e0fdde4eaabb408bf426166a62c80456c30', true, 9223372036854775807); +INSERT INTO tokens (token, valid, expiration_date) VALUES ('G8OH7lHu5qfWVumWom0ySN29lakog8nhzSPEwROMjvhdI6VgZ6GoPcdJmoIo7sF3lxQNJMOTKxpYBr6zF992WN86uB7xTEJZ', true, 9223372036854775807); +INSERT INTO tokens (token, valid, expiration_date) VALUES ('jykkeD6Tr6xikkYwC805kVoFThm8VGEHStTFk1lIU6RgEf7p3vjFpPQFI3VP9SYeARjYh2jecMSYsmgddjZZcy32iySHijJQ', true, 9223372036854775807); diff --git a/queries_postgresql/set_tokens _true.sql b/queries_postgresql/set_tokens _true.sql new file mode 100644 index 0000000..411b922 --- /dev/null +++ b/queries_postgresql/set_tokens _true.sql @@ -0,0 +1,3 @@ +UPDATE tokens SET valid = ? WHERE token = ?, true, "80fda91a43989fa81347aa011e0f1e0fdde4eaabb408bf426166a62c80456c30"; +UPDATE tokens SET valid = ? WHERE token = ?, true, "G8OH7lHu5qfWVumWom0ySN29lakog8nhzSPEwROMjvhdI6VgZ6GoPcdJmoIo7sF3lxQNJMOTKxpYBr6zF992WN86uB7xTEJZ"; +UPDATE tokens SET valid = ? WHERE token = ?, true, "jykkeD6Tr6xikkYwC805kVoFThm8VGEHStTFk1lIU6RgEf7p3vjFpPQFI3VP9SYeARjYh2jecMSYsmgddjZZcy32iySHijJQ"; \ No newline at end of file diff --git a/repohelper/repohelper.go b/repohelper/repohelper.go new file mode 100644 index 0000000..5072f9e --- /dev/null +++ b/repohelper/repohelper.go @@ -0,0 +1,100 @@ +package repohelper + +import ( + "context" + "fmt" + "io" + "strings" + + "github.com/bartossh/Computantis/block" + "github.com/bartossh/Computantis/repomongo" + "github.com/bartossh/Computantis/repopostgre" + "github.com/bartossh/Computantis/transaction" + "github.com/bartossh/Computantis/validator" +) + +var ( + ErrDatabaseNotSupported = fmt.Errorf("database not supported") +) + +// AddressWriteFindChecker abstracts address operations. +type AddressWriteFindChecker interface { + WriteAddress(ctx context.Context, addr string) error + CheckAddressExists(ctx context.Context, addr string) (bool, error) + FindAddress(ctx context.Context, search string, limit int) ([]string, error) +} + +// BlockReadWriter abstracts block operations. +type BlockReadWriter interface { + LastBlock(ctx context.Context) (block.Block, error) + ReadBlockByHash(ctx context.Context, hash [32]byte) (block.Block, error) + WriteBlock(ctx context.Context, block block.Block) error +} + +// MigrationRunner abstracts migration operations. +type Migrator interface { + RunMigration(ctx context.Context) error +} + +// TokenWriteCheckInvalidator abstracts token operations. +type TokenWriteCheckInvalidator interface { + CheckToken(ctx context.Context, tkn string) (bool, error) + WriteToken(ctx context.Context, tkn string, expirationDate int64) error + InvalidateToken(ctx context.Context, token string) error +} + +// TransactionOperator abstracts transaction operations. +type TransactionOperator interface { + WriteTransactionsInBlock(ctx context.Context, blockHash [32]byte, trxHash [][32]byte) error + FindTransactionInBlockHash(ctx context.Context, trxHash [32]byte) ([32]byte, error) + WriteTemporaryTransaction(ctx context.Context, trx *transaction.Transaction) error + RemoveAwaitingTransaction(ctx context.Context, trxHash [32]byte) error + WriteIssuerSignedTransactionForReceiver(ctx context.Context, receiverAddr string, trx *transaction.Transaction) error + ReadAwaitingTransactionsByReceiver(ctx context.Context, address string) ([]transaction.Transaction, error) + ReadAwaitingTransactionsByIssuer(ctx context.Context, address string) ([]transaction.Transaction, error) + MoveTransactionsFromTemporaryToPermanent(ctx context.Context, hash [][32]byte) error + ReadTemporaryTransactions(ctx context.Context) ([]transaction.Transaction, error) +} + +// ValidatorStatusReader abstracts validator status operations. +type ValidatorStatusReader interface { + ReadLastNValidatorStatuses(ctx context.Context, last int64) ([]validator.Status, error) + WriteValidatorStatus(ctx context.Context, vs *validator.Status) error +} + +// ConnectionCloser abstracts connection closing operations. +type ConnectionCloser interface { + Disconnect(ctx context.Context) error +} + +// RepositoryProvider is an interface that ensures that all required methods to run computantis are implemented. +type RepositoryProvider interface { + AddressWriteFindChecker + BlockReadWriter + io.Writer + Migrator + TokenWriteCheckInvalidator + TransactionOperator + ValidatorStatusReader + ConnectionCloser +} + +// Config contains configuration for the database. +type DBConfig struct { + ConnStr string `yaml:"conn_str"` // ConnStr is the connection string to the database. + DatabaseName string `yaml:"database_name"` // DatabaseName is the name of the database. + Token string `yaml:"token"` // Token is the token that is used to confirm api clients access. + TokenExpire int64 `yaml:"token_expiration"` // TokenExpire is the number of seconds after which token expires. +} + +// Connect connects to the proper database and returns that connection. +func (cfg DBConfig) Connect(ctx context.Context) (RepositoryProvider, error) { + switch { + case strings.Contains(cfg.ConnStr, "postgres"): + return repopostgre.Connect(ctx, cfg.ConnStr, cfg.DatabaseName) + case strings.Contains(cfg.ConnStr, "mongodb"): + return repomongo.Connect(ctx, cfg.ConnStr, cfg.DatabaseName) + } + + return nil, ErrDatabaseNotSupported +} diff --git a/repomongo/mongorepo.go b/repomongo/mongorepo.go index 045600c..98ceaa0 100644 --- a/repomongo/mongorepo.go +++ b/repomongo/mongorepo.go @@ -4,7 +4,6 @@ import ( "context" "time" - "github.com/bartossh/Computantis/configuration" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" "go.mongodb.org/mongo-driver/mongo/readpref" @@ -29,19 +28,19 @@ type DataBase struct { } // Connect creates new connection to the repository and returns pointer to the DataBase. -func Connect(ctx context.Context, cfg configuration.DBConfig) (*DataBase, error) { - conn, err := mongo.Connect(ctx, options.Client().ApplyURI(cfg.ConnStr)) +func Connect(ctx context.Context, conn, database string) (*DataBase, error) { + cli, err := mongo.Connect(ctx, options.Client().ApplyURI(conn)) if err != nil { return nil, err } ctxx, cancel := context.WithTimeout(ctx, time.Second*5) defer cancel() - if err := conn.Ping(ctxx, readpref.Primary()); err != nil { + if err := cli.Ping(ctxx, readpref.Primary()); err != nil { return nil, err } - return &DataBase{*conn.Database(cfg.DatabaseName)}, nil + return &DataBase{*cli.Database(database)}, nil } // Disconnect disconnects user from database diff --git a/repopostgre/address.go b/repopostgre/address.go index d7b10b5..af39b28 100644 --- a/repopostgre/address.go +++ b/repopostgre/address.go @@ -7,7 +7,7 @@ import ( // WriteAddress writes address to the database. func (db DataBase) WriteAddress(ctx context.Context, addr string) error { - _, err := db.inner.ExecContext(ctx, `INSERT INTO addresses(public_key) VALUES(?)`, addr) + _, err := db.inner.ExecContext(ctx, `INSERT INTO addresses(public_key) VALUES($1)`, addr) if err != nil { return errors.Join(ErrInsertFailed, err) } @@ -17,7 +17,7 @@ func (db DataBase) WriteAddress(ctx context.Context, addr string) error { // CheckAddressExists checks if address exists in the database. func (db DataBase) CheckAddressExists(ctx context.Context, addr string) (bool, error) { var exists bool - err := db.inner.QueryRowContext(ctx, `SELECT EXISTS(SELECT 1 FROM addresses WHERE public_key = ?)`, addr).Scan(&exists) + err := db.inner.QueryRowContext(ctx, `SELECT EXISTS(SELECT 1 FROM addresses WHERE public_key = $1)`, addr).Scan(&exists) if err != nil { return false, errors.Join(ErrSelectFailed, err) } diff --git a/repopostgre/block.go b/repopostgre/block.go index a215b25..9b68743 100644 --- a/repopostgre/block.go +++ b/repopostgre/block.go @@ -5,18 +5,33 @@ import ( "errors" "github.com/bartossh/Computantis/block" + "github.com/lib/pq" ) // LastBlock returns last block from the database. func (db DataBase) LastBlock(ctx context.Context) (block.Block, error) { - res, err := db.inner.QueryContext(ctx, "SELECT * FROM blocks ORDER BY index DESC LIMIT 1") + rows, err := db.inner.QueryContext(ctx, "SELECT * FROM blocks ORDER BY index DESC LIMIT 1") if err != nil { return block.Block{}, errors.Join(ErrSelectFailed, err) } + defer rows.Close() var b block.Block - if err := res.Scan(&b.ID, &b.Index, &b.Timestamp, &b.Nonce, &b.Difficulty, &b.Hash, &b.PrevHash, &b.TrxHashes); err != nil { - return block.Block{}, errors.Join(ErrScanFailed, err) + h := make([]byte, 0, 32) + prevH := make([]byte, 0, 32) + trxHashes := [][]byte{} + for rows.Next() { + if err := rows.Scan(&b.ID, &b.Index, &b.Timestamp, &b.Nonce, &b.Difficulty, &h, &prevH, pq.Array(&trxHashes)); err != nil { + return block.Block{}, errors.Join(ErrScanFailed, err) + } + } + + copy(b.Hash[:], h) + copy(b.PrevHash[:], prevH) + for _, hash := range trxHashes { + h := [32]byte{} + copy(h[:], hash) + b.TrxHashes = append(b.TrxHashes, h) } return b, nil @@ -24,14 +39,27 @@ func (db DataBase) LastBlock(ctx context.Context) (block.Block, error) { // ReadBlockByHash returns block with given hash. func (db DataBase) ReadBlockByHash(ctx context.Context, hash [32]byte) (block.Block, error) { - res, err := db.inner.QueryContext(ctx, "SELECT * FROM blocks WHERE hash = $1", hash) + rows, err := db.inner.QueryContext(ctx, "SELECT * FROM blocks WHERE hash = $1", hash) if err != nil { return block.Block{}, errors.Join(ErrSelectFailed, err) } + defer rows.Close() var b block.Block - if err := res.Scan(&b.ID, &b.Index, &b.Timestamp, &b.Nonce, &b.Difficulty, &b.Hash, &b.PrevHash, &b.TrxHashes); err != nil { - return block.Block{}, errors.Join(ErrScanFailed, err) + h := make([]byte, 0, 32) + prevH := make([]byte, 32) + trxHashes := [][]byte{} + for rows.Next() { + if err := rows.Scan(&b.ID, &b.Index, &b.Timestamp, &b.Nonce, &b.Difficulty, &h, &prevH, pq.Array(&trxHashes)); err != nil { + return block.Block{}, errors.Join(ErrScanFailed, err) + } + } + copy(b.Hash[:], h) + copy(b.PrevHash[:], prevH) + for _, hash := range trxHashes { + h := [32]byte{} + copy(h[:], hash) + b.TrxHashes = append(b.TrxHashes, h) } return b, nil @@ -39,10 +67,14 @@ func (db DataBase) ReadBlockByHash(ctx context.Context, hash [32]byte) (block.Bl // WriteBlock writes block to the database. func (db DataBase) WriteBlock(ctx context.Context, block block.Block) error { + trxHashes := make([][]byte, len(block.TrxHashes)) + for _, hash := range block.TrxHashes { + trxHashes = append(trxHashes, hash[:]) + } _, err := db.inner.ExecContext(ctx, `INSERT INTO blocks (index, timestamp, nonce, difficulty, hash, prev_hash, trx_hashes) VALUES ($1, $2, $3, $4, $5, $6, $7)`, - block.Index, block.Timestamp, block.Nonce, block.Difficulty, block.Hash, block.PrevHash, block.TrxHashes) + block.Index, block.Timestamp, block.Nonce, block.Difficulty, block.Hash[:], block.PrevHash[:], pq.Array(trxHashes)) if err != nil { return errors.Join(ErrInsertFailed, err) } diff --git a/repopostgre/migrations.go b/repopostgre/migrations.go new file mode 100644 index 0000000..1745555 --- /dev/null +++ b/repopostgre/migrations.go @@ -0,0 +1,9 @@ +package repopostgre + +import "context" + +// RunMigration satisfies the RepositoryProvider interface +// as PostgreSQL migrations are run on when database is created in docker-compose-postgresql.yml. +func (DataBase) RunMigration(_ context.Context) error { + return nil +} diff --git a/repopostgre/repopostgre.go b/repopostgre/repopostgre.go index dcd2d91..68ba499 100644 --- a/repopostgre/repopostgre.go +++ b/repopostgre/repopostgre.go @@ -6,8 +6,6 @@ import ( "database/sql" - "github.com/bartossh/Computantis/configuration" - _ "github.com/lib/pq" ) @@ -26,8 +24,8 @@ type DataBase struct { } // Connect creates new connection to the repository and returns pointer to the DataBase. -func Connect(ctx context.Context, cfg configuration.DBConfig) (*DataBase, error) { - db, err := sql.Open("postgres", fmt.Sprintf("%s/%s?sslmode=disable", cfg.ConnStr, cfg.DatabaseName)) +func Connect(ctx context.Context, conn, database string) (*DataBase, error) { + db, err := sql.Open("postgres", fmt.Sprintf("%s/%s?sslmode=disable", conn, database)) if err != nil { return nil, err } diff --git a/repopostgre/repopostgre_test.go b/repopostgre/repopostgre_test.go index 370c715..9f76192 100644 --- a/repopostgre/repopostgre_test.go +++ b/repopostgre/repopostgre_test.go @@ -1,3 +1,5 @@ +//go:build integration + package repopostgre import ( @@ -6,7 +8,7 @@ import ( "os" "testing" - "github.com/bartossh/Computantis/configuration" + "github.com/bartossh/Computantis/repohelper" "github.com/joho/godotenv" "github.com/stretchr/testify/assert" ) @@ -20,7 +22,7 @@ func TestConnection(t *testing.T) { passwd := os.Getenv("MONGO_DB_PASSWORD") dbName := os.Getenv("MONGO_DB_NAME") - cfg := configuration.DBConfig{ + cfg := repohelper.DBConfig{ ConnStr: fmt.Sprintf("postgres://%s:%s@localhost:5432", user, passwd), DatabaseName: dbName, Token: "19130b090d70afb384b6ebcb8572701a974e3a1090947bfc785b980841bfb054", diff --git a/repopostgre/search.go b/repopostgre/search.go index 271f759..76a7b51 100644 --- a/repopostgre/search.go +++ b/repopostgre/search.go @@ -8,7 +8,7 @@ func (db DataBase) FindAddress(ctx context.Context, search string, limit int) ([ limit = 1000 } var addresses []string - rows, err := db.inner.QueryContext(ctx, `SELECT public_key FROM addresses WHERE public_key LIKE ? LIMIT ?`, search, limit) + rows, err := db.inner.QueryContext(ctx, "SELECT public_key FROM addresses WHERE public_key LIKE $1 LIMIT $2", search, limit) if err != nil { return nil, err } @@ -34,7 +34,8 @@ func (db DataBase) WriteTransactionsInBlock(ctx context.Context, blockHash [32]b defer tx.Rollback() for _, trx := range trxHash { - if _, err := tx.ExecContext(ctx, `INSERT INTO transactionsInBlock (block_hash, transaction_hash) VALUES (?, ?)`, blockHash, trx); err != nil { + if _, err := tx.ExecContext(ctx, + "INSERT INTO transactionsInBlock (block_hash, transaction_hash) VALUES ($1, $2)", blockHash[:], trx); err != nil { return err } } @@ -44,9 +45,12 @@ func (db DataBase) WriteTransactionsInBlock(ctx context.Context, blockHash [32]b // FindTransactionInBlockHash finds Block hash in to which Transaction with given hash was added. func (db DataBase) FindTransactionInBlockHash(ctx context.Context, trxHash [32]byte) ([32]byte, error) { - var blockHash [32]byte - if err := db.inner.QueryRowContext(ctx, `SELECT block_hash FROM transactionsInBlock WHERE transaction_hash = ?`, trxHash).Scan(&blockHash); err != nil { + blockHash := make([]byte, 0, 32) + if err := db.inner.QueryRowContext(ctx, "SELECT block_hash FROM transactionsInBlock WHERE transaction_hash = $1", trxHash[:]). + Scan(&blockHash); err != nil { return [32]byte{}, err } - return blockHash, nil + var bh [32]byte + copy(bh[:], blockHash) + return bh, nil } diff --git a/repopostgre/token.go b/repopostgre/token.go index f3acb38..32dd205 100644 --- a/repopostgre/token.go +++ b/repopostgre/token.go @@ -14,8 +14,7 @@ import ( func (db DataBase) CheckToken(ctx context.Context, tkn string) (bool, error) { var t token.Token if err := db.inner.QueryRowContext(ctx, - `SELECT token, valid, expiration_date - FROM tokens WHERE token = ?`, tkn). + "SELECT token, valid, expiration_date FROM tokens WHERE token = $1", tkn). Scan(&t.Token, &t.Valid, &t.ExpirationDate); err != nil { if err == sql.ErrNoRows { return false, nil @@ -35,7 +34,7 @@ func (db DataBase) CheckToken(ctx context.Context, tkn string) (bool, error) { func (db DataBase) WriteToken(ctx context.Context, tkn string, expirationDate int64) error { if _, err := db.inner.ExecContext(ctx, `INSERT INTO tokens (token, valid, expiration_date) - VALUES (?, ?, ?)`, tkn, true, expirationDate); err != nil { + VALUES ($1, $2, $3)`, tkn, true, expirationDate); err != nil { return errors.Join(ErrInsertFailed, err) } return nil @@ -44,7 +43,7 @@ func (db DataBase) WriteToken(ctx context.Context, tkn string, expirationDate in // InvalidateToken invalidates token. func (db DataBase) InvalidateToken(ctx context.Context, token string) error { if _, err := db.inner.ExecContext(ctx, - `UPDATE tokens SET valid = ? WHERE token = ?`, false, token); err != nil { + "UPDATE tokens SET valid = false WHERE token = $1", token); err != nil { return errors.Join(ErrRemoveFailed, err) } return nil diff --git a/repopostgre/transaction.go b/repopostgre/transaction.go index c25213f..4854b63 100644 --- a/repopostgre/transaction.go +++ b/repopostgre/transaction.go @@ -14,7 +14,7 @@ func (db DataBase) WriteTemporaryTransaction(ctx context.Context, trx *transacti `INSERT INTO transactionsTemporary( created_at, hash, issuer_address, receiver_address, subject, data, issuer_signature, receiver_signature - ) VALUES(?, ?, ?, ?, ?, ?, ?, ?)`, + ) VALUES($1, $2, $3, $4, $5, $6, $7, $8)`, trx.CreatedAt, trx.Hash[:], trx.IssuerAddress, trx.ReceiverAddress, trx.Subject, trx.Data, trx.IssuerSignature, trx.ReceiverSignature) @@ -26,7 +26,7 @@ func (db DataBase) WriteTemporaryTransaction(ctx context.Context, trx *transacti // RemoveAwaitingTransaction removes transaction from the awaiting transaction storage. func (db DataBase) RemoveAwaitingTransaction(ctx context.Context, trxHash [32]byte) error { - _, err := db.inner.ExecContext(ctx, `DELETE FROM transactionsAwaitingReceiver WHERE hash = ?`, trxHash[:]) + _, err := db.inner.ExecContext(ctx, "DELETE FROM transactionsAwaitingReceiver WHERE hash = $1", trxHash[:]) if err != nil { errors.Join(ErrRemoveFailed, err) } @@ -42,31 +42,32 @@ func (db DataBase) WriteIssuerSignedTransactionForReceiver( _, err := db.inner.ExecContext( ctx, `INSERT INTO - transactionsTemporary( + transactionsAwaitingReceiver( created_at, hash, issuer_address, receiver_address, subject, data, issuer_signature, receiver_signature - ) VALUES(?, ?, ?, ?, ?, ?, ?, ?)`, + ) VALUES($1, $2, $3, $4, $5, $6, $7, $8)`, trx.CreatedAt, trx.Hash[:], trx.IssuerAddress, receiverAddr, trx.Subject, trx.Data, trx.IssuerSignature, trx.ReceiverSignature) if err != nil { - errors.Join(ErrInsertFailed, err) + return errors.Join(ErrInsertFailed, err) } return nil } // ReadAwaitingTransactionsByReceiver reads all transactions paired with given receiver address. func (db DataBase) ReadAwaitingTransactionsByReceiver(ctx context.Context, address string) ([]transaction.Transaction, error) { - rows, err := db.inner.QueryContext(ctx, `SELECT * FROM transactionsAwaitingReceiver WHERE receiver_address = ?`, address) + rows, err := db.inner.QueryContext(ctx, "SELECT * FROM transactionsAwaitingReceiver WHERE receiver_address = $1", address) if err != nil { return nil, errors.Join(ErrSelectFailed, err) } defer rows.Close() + var trxsAwaiting []transaction.Transaction for rows.Next() { var trx transaction.Transaction - var hash [32]byte + hash := make([]byte, 0, 32) err := rows.Scan( - &trx.CreatedAt, &hash, &trx.IssuerAddress, + &trx.ID, &trx.CreatedAt, &hash, &trx.IssuerAddress, &trx.ReceiverAddress, &trx.Subject, &trx.Data, &trx.IssuerSignature, &trx.ReceiverSignature) if err != nil { @@ -80,17 +81,18 @@ func (db DataBase) ReadAwaitingTransactionsByReceiver(ctx context.Context, addre // RemoveAwaitingTransaction removes transaction from the awaiting transaction storage. func (db DataBase) ReadAwaitingTransactionsByIssuer(ctx context.Context, address string) ([]transaction.Transaction, error) { - rows, err := db.inner.QueryContext(ctx, `SELECT * FROM transactionsAwaitingIssuer WHERE issuer_address = ?`, address) + rows, err := db.inner.QueryContext(ctx, "SELECT * FROM transactionsAwaitingReceiver WHERE issuer_address = $1", address) if err != nil { return nil, errors.Join(ErrSelectFailed, err) } defer rows.Close() + var trxsAwaiting []transaction.Transaction for rows.Next() { var trx transaction.Transaction - var hash [32]byte + hash := make([]byte, 0, 32) err := rows.Scan( - &trx.CreatedAt, &hash, &trx.IssuerAddress, + &trx.ID, &trx.CreatedAt, &hash, &trx.IssuerAddress, &trx.ReceiverAddress, &trx.Subject, &trx.Data, &trx.IssuerSignature, &trx.ReceiverSignature) if err != nil { @@ -104,11 +106,15 @@ func (db DataBase) ReadAwaitingTransactionsByIssuer(ctx context.Context, address // MoveTransactionsFromTemporaryToPermanent moves transactions from temporary storage to permanent storage. func (db DataBase) MoveTransactionsFromTemporaryToPermanent(ctx context.Context, hash [][32]byte) error { - _, err := db.inner.ExecContext(ctx, `INSERT INTO transactionsPermanent SELECT * FROM transactionsTemporary WHERE hash = ?`, hash) + hs := make([][]byte, 0, len(hash)) + for _, h := range hash { + hs = append(hs, h[:]) + } + _, err := db.inner.ExecContext(ctx, "INSERT INTO transactionsPermanent SELECT * FROM transactionsTemporary WHERE hash = $1", hs) if err != nil { return errors.Join(ErrMoveFailed, err) } - _, err = db.inner.ExecContext(ctx, `DELETE FROM transactionsTemporary WHERE hash = ?`, hash) + _, err = db.inner.ExecContext(ctx, "DELETE FROM transactionsTemporary WHERE hash = $1", hs) if err != nil { return errors.Join(ErrRemoveFailed, err) } @@ -117,17 +123,18 @@ func (db DataBase) MoveTransactionsFromTemporaryToPermanent(ctx context.Context, // ReadTemporaryTransactions reads all transactions from the temporary storage. func (db DataBase) ReadTemporaryTransactions(ctx context.Context) ([]transaction.Transaction, error) { - rows, err := db.inner.QueryContext(ctx, `SELECT * FROM transactionsTemporary`) + rows, err := db.inner.QueryContext(ctx, "SELECT * FROM transactionsTemporary") if err != nil { return nil, errors.Join(ErrSelectFailed, err) } defer rows.Close() + var trxsAwaiting []transaction.Transaction for rows.Next() { var trx transaction.Transaction - var hash [32]byte + hash := make([]byte, 0, 32) err := rows.Scan( - &trx.CreatedAt, &hash, &trx.IssuerAddress, + &trx.ID, &trx.CreatedAt, &hash, &trx.IssuerAddress, &trx.ReceiverAddress, &trx.Subject, &trx.Data, &trx.IssuerSignature, &trx.ReceiverSignature) if err != nil { diff --git a/repopostgre/validator.go b/repopostgre/validator.go index d62a815..32eecac 100644 --- a/repopostgre/validator.go +++ b/repopostgre/validator.go @@ -21,15 +21,16 @@ func (db DataBase) WriteValidatorStatus(ctx context.Context, vs *validator.Statu // ReadLastNValidatorStatuses reads last validator statuses from the database. func (db DataBase) ReadLastNValidatorStatuses(ctx context.Context, last int64) ([]validator.Status, error) { - res, err := db.inner.QueryContext(ctx, "SELECT * FROM validator_status ORDER BY index DESC LIMIT $1", last) + rows, err := db.inner.QueryContext(ctx, "SELECT * FROM validator_status ORDER BY index DESC LIMIT $1", last) if err != nil { return nil, errors.Join(ErrSelectFailed, err) } + defer rows.Close() var results []validator.Status - for res.Next() { + for rows.Next() { var vs validator.Status - if err := res.Scan(&vs.Index, &vs.Valid, &vs.CreatedAt); err != nil { + if err := rows.Scan(&vs.ID, &vs.Index, &vs.Valid, &vs.CreatedAt); err != nil { return nil, errors.Join(ErrScanFailed, err) } results = append(results, vs) diff --git a/server_settings_example_mongo.yaml b/server_settings_example_mongo.yaml new file mode 100644 index 0000000..2f744a3 --- /dev/null +++ b/server_settings_example_mongo.yaml @@ -0,0 +1,21 @@ +bookkeeper: + difficulty: 1 + block_write_timestamp: 300 + block_transactions_size: 25 +server: + port: 8080 + data_size_bytes: 15000000 +database: + conn_str: "mongodb://computantis:computantis@localhost:27017/?authSource=admin&authMechanism=SCRAM-SHA-256&readPreference=primary&&ssl=false&directConnection=true" + database_name: "computantis" + token: "a6858eLd1GHvoGXrq6nNhEiHrEmkRN3tXu5dYqCjiMUL9sRfUz1iBns0kEnPizzrLfj2TZGU2Wel52fJ6YDNiVrdtvf2kZm4" + token_expiration: 604800 +dataprovider: + longevity: 300 +validator: + websocket: "ws://localhost:8080/ws" + port: 9090 + toke: "some token from the provider" +file_operator: + wallet_path: "path/to/your/wallet" + wallet_passwd: "passwd to your wallet" \ No newline at end of file diff --git a/server_settings_example.yaml b/server_settings_example_postgresql.yaml similarity index 89% rename from server_settings_example.yaml rename to server_settings_example_postgresql.yaml index 37f0103..e7fa7e3 100644 --- a/server_settings_example.yaml +++ b/server_settings_example_postgresql.yaml @@ -6,7 +6,7 @@ server: port: 8080 data_size_bytes: 15000000 database: - conn_str: "mongodb://user:psswd@localhost:27017" + conn_str: "postgres://computantis:computantis@localhost:5432" database_name: "computantis" token: "a6858eLd1GHvoGXrq6nNhEiHrEmkRN3tXu5dYqCjiMUL9sRfUz1iBns0kEnPizzrLfj2TZGU2Wel52fJ6YDNiVrdtvf2kZm4" token_expiration: 604800 diff --git a/stress/stress_test.go b/stress/stress_test.go index 46c9ea0..f2825a3 100644 --- a/stress/stress_test.go +++ b/stress/stress_test.go @@ -24,13 +24,13 @@ func TestFullClientApiCycle(t *testing.T) { issuer := client.NewClient("http://localhost:8080", 5*time.Second, wallet.Helper{}, fileoperations.Helper{}, wallet.New) err := issuer.ValidateApiVersion() assert.Nil(t, err) - err = issuer.NewWallet("wpg6d0grqJjyRicC8oI0/w6IGivm5ypFNTO/wwPGW9A=") + err = issuer.NewWallet("G8OH7lHu5qfWVumWom0ySN29lakog8nhzSPEwROMjvhdI6VgZ6GoPcdJmoIo7sF3lxQNJMOTKxpYBr6zF992WN86uB7xTEJZ") assert.Nil(t, err) receiver := client.NewClient("http://localhost:8080", 5*time.Second, wallet.Helper{}, fileoperations.Helper{}, wallet.New) err = receiver.ValidateApiVersion() assert.Nil(t, err) - err = receiver.NewWallet("GWFuhvyFnmMg1/vhPCfoa9ct1pAMC1pWwlRg4kt0D/w=") + err = receiver.NewWallet("jykkeD6Tr6xikkYwC805kVoFThm8VGEHStTFk1lIU6RgEf7p3vjFpPQFI3VP9SYeARjYh2jecMSYsmgddjZZcy32iySHijJQ") assert.Nil(t, err) now := time.Now()