Computantis

module
v0.0.0-...-1b5cee2 Latest Latest
Warning

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

Go to latest
Published: May 15, 2023 License: GPL-3.0

README

Computantis

CodeQL pages-build-deployment

The Computantis is a set of services that keeps track of transactions between wallets. Transactions are not transferring any tokens between wallets but it might be the case if someone wants to use it this way. Just this set of services isn’t designed to track token exchange. Instead, transactions are entities holding data that the transaction issuer and transaction receiver agreed upon. Each wallet has its own independent history of transactions. There is a set of strict rules allowing for transactions to happen:

The central server is private to the corporation, government or agency. It is trusted by the above entity and participants. This solution isn’t proposing the distributed system of transactions as this is not the case. It is ensuring that the transaction is issued and signed and received and signed to confirm its validity. Blockchain keeps transactions history immutable so the validators can be sure that no one will corrupt the transactions. Transaction to be valid needs to:

  • Have a valid issuer signature.
  • Have a valid receiver signature.
  • Have a valid data digest.
  • Have a valid issuer public address.
  • Have a valid receiver public address.
  • Be part of a blockchain.

The full cycle of the transaction and block forging happens as follows:

  1. The Issuer creates the transaction and signs it with the issuer's private key, attaching the issuer's public key to the transaction.
  2. The Issuer sends the transaction to the central server.
  3. The central server validates The Issuer address, signature, data digest and expiration date. If the transaction is valid then it is kept in awaiting transactions repository for the receiver to sign.
  4. Receiver asks for awaiting transactions for the receiver's signature in the process of proving his public address and signing data received from the server by the receiver's private key.
  5. If the signature is valid then all awaiting transactions are transferred to the receiver.
  6. The receiver can sign transactions that the receiver agrees on, and sends them back to the central server.
  7. The central server validates the address, signature, data digest and expiration date then appends the transaction to be added to the next forged block. The transaction is moved from the awaiting transactions repository to the temporary repository (just in case of any unexpected crashes or attacks on the central server).
  8. The central servers follow sets of rules from the configuration yaml file, which describes how often the block is forged, how many transactions it can hold and what is the difficulty for hashing the block.
  9. When the block is forged and added to the blockchain then transactions that are part of the block are moved from the temporary transactions repository to the permanent transactions repository.
  10. Information about the new blocks is sent to all validators. Validators cannot reject blocks or rewrite the blockchain. The validator serves the purpose of tracking the central node blockchain to ensure data are not corrupted, the central node isn’t hacked, stores the blocks in its own repository, and serves as an information node for the external clients. If the blockchain is corrupted then the validator raises an alert when noticing corrupted data. It is good practice to have many validator nodes held by independent entities.

Execute the server

  1. Run database docker compose up.
  2. Build the server go build -o path/to/bin/central cmd/central/main.go.
  3. Create server_settings.yaml according to server_settings_example.yaml file in path/to/bin/ folder.
  4. Run ./path/to/bin/central.

Run for development

  1. Run database with docker compose up -d.
  2. Create server_settings.yaml according to server_settings_example.yaml in the repo root folder.
  3. Run make run or go run cmd/central/main.go.

Stress test

Directory stress/ contains central node REST API performance tests. Bottleneck is on I/O calls, mostly database writes. Single PostgreSQL database instance run in docker 1CPU and 2GB RAM allows for full cycle processing of 750 transactions per second. This is rough estimate and I would soon provide more precise benchmarks.

Package provides webassembly package that expose client API to the front-end applications.

To use client API allowing for creating a wallet and communication with Central Server REST API copy wasm/bin/wallet.wasm and wasm/js/wasm_exec.js to you fronted project and execute as in example below.

<html>  
    <head>
        <meta charset="utf-8"/>
        <script src="wasm_exec.js"></script>
        <script>
            const go = new Go();
            WebAssembly.instantiateStreaming(fetch("wallet.wasm"), go.importObject).then((result) => {
                go.run(result.instance);
            });
        </script>
    </head>
    <body></body>
</html>  

Computantis architecture

The schema below shows how pieces of the computation service are connected together. This is one of the simple examples with high availability where central nodes, db shards and validators may be scaled and orchestrated with k8s or another tool. Because the central node service, validator service as well as wallet API are compiled to a single lightweight binary running on a variety of architectures we can make the whole system lightweight. Container size can be kept very minimal without the need for interpreters or virtual engines which is securing containers, minimizing the cold start and the container size to a few megabytes. Low RAM and CPU consumption of a single node allows to run service on IoT devices.

The task of the whole system is simple, validate transactions, act as a message hub and seal the history of transactions within the immutable blockchain, but be performant with that task.

Use case scenario

The example below describes how Computantis can seal the transactions happening in distributed IoT services. The data are only encrypted when flowing between services. When in the system it might be the case that data are corrupted or when service is compromised then transactions may be faked. The Computantis is on its way to discovering probable scenarios of sealing the data with cryptographic signatures and immutable history. It is one of the scenarios when such an approach may be taken into consideration.

Go Documentation

address

import "github.com/bartossh/Computantis/address"

Index

type Address

Address holds information about unique PublicKey.

type Address struct {
    ID        any    `json:"-"          bson:"_id,omitempty" db:"id"`
    PublicKey string `json:"public_key" bson:"public_key"    db:"public_key"`
}

aeswrapper

import "github.com/bartossh/Computantis/aeswrapper"

Index

Variables

var (
    ErrInvalidKeyLength   = errors.New("invalid key length, must be longer then 32 bytes")
    ErrCipherFailure      = errors.New("cipher creation failure")
    ErrGCMFailure         = errors.New("gcm creation failure")
    ErrRandomNonceFailure = errors.New("random nonce creation failure")
    ErrOpenDataFailure    = errors.New("open data failure, cannot decrypt data")
)

type Helper

Helper wraps eas encryption and decryption. Uses Galois Counter Mode (GCM) for encryption and decryption.

type Helper struct{}
func New
func New() Helper

Creates a new Helper.

func (Helper) Decrypt
func (h Helper) Decrypt(key, data []byte) ([]byte, error)

Decrypt decrypts data with key. Key must be at least 32 bytes long.

func (Helper) Encrypt
func (h Helper) Encrypt(key, data []byte) ([]byte, error)

Encrypt encrypts data with key. Key must be at least 32 bytes long.

block

import "github.com/bartossh/Computantis/block"

Index

type Block

Block holds block information. Block is a part of a blockchain assuring immutability of the data. Block mining difficulty may change if needed and is a part of a hash digest. Block ensures that transactions hashes are valid and match the transactions stored in the repository.

type Block struct {
    ID         any        `json:"-"          bson:"_id"        db:"id"`
    Index      uint64     `json:"index"      bson:"index"      db:"index"`
    Timestamp  uint64     `json:"timestamp"  bson:"timestamp"  db:"timestamp"`
    Nonce      uint64     `json:"nonce"      bson:"nonce"      db:"nonce"`
    Difficulty uint64     `json:"difficulty" bson:"difficulty" db:"difficulty"`
    Hash       [32]byte   `json:"hash"       bson:"hash"       db:"hash"`
    PrevHash   [32]byte   `json:"prev_hash"  bson:"prev_hash"  db:"prev_hash"`
    TrxHashes  [][32]byte `json:"trx_hashes" bson:"trx_hashes" db:"trx_hashes"`
}
func New
func New(difficulty, next uint64, prevHash [32]byte, trxHashes [][32]byte) Block

New creates a new Block hashing it with given difficulty. Higher difficulty requires more computations to happen to find possible target hash. Difficulty is stored inside the Block and is a part of a hashed data. Transactions hashes are prehashed before calculating the Block hash with merkle tree.

func (*Block) Validate
func (b *Block) Validate(trxHashes [][32]byte) bool

Validate validates the Block. Validations goes in the same order like Block hashing algorithm, just the proof of work part is not required as Nonce is already known.

blockchain

import "github.com/bartossh/Computantis/blockchain"

Index

Variables

var (
    ErrBlockNotFound        = errors.New("block not found")
    ErrInvalidBlockPrevHash = errors.New("block prev hash is invalid")
    ErrInvalidBlockHash     = errors.New("block hash is invalid")
    ErrInvalidBlockIndex    = errors.New("block index is invalid")
)

func GenesisBlock

func GenesisBlock(ctx context.Context, rw BlockReadWriter) error

GenesisBlock creates a genesis block. It is a first block in the blockchain. The genesis block is created only if there is no other block in the repository. Otherwise returning an error.

type BlockReadWriter

BlockReadWriter provides read and write access to the blockchain repository.

type BlockReadWriter interface {
    BlockReader
    BlockWriter
}

type BlockReader

BlockReader provides read access to the blockchain repository.

type BlockReader interface {
    LastBlock(ctx context.Context) (block.Block, error)
    ReadBlockByHash(ctx context.Context, hash [32]byte) (block.Block, error)
}

type BlockWriter

BlockWriter provides write access to the blockchain repository.

type BlockWriter interface {
    WriteBlock(ctx context.Context, block block.Block) error
}

type Blockchain

Blockchain keeps track of the blocks creating immutable chain of data. Blockchain is stored in repository as separate blocks that relates to each other based on the hash of the previous block.

type Blockchain struct {
    // contains filtered or unexported fields
}
func New
func New(ctx context.Context, rw BlockReadWriter) (*Blockchain, error)

New creates a new Blockchain that has access to the blockchain stored in the repository. The access to the repository is injected via BlockReadWriter interface. You can use any implementation of repository that implements BlockReadWriter interface and ensures unique indexing for Block Hash, PrevHash and Index.

func (*Blockchain) LastBlockHashIndex
func (c *Blockchain) LastBlockHashIndex(ctx context.Context) ([32]byte, uint64, error)

LastBlockHashIndex returns last block hash and index.

func (*Blockchain) ReadBlocksFromIndex
func (c *Blockchain) ReadBlocksFromIndex(ctx context.Context, idx uint64) ([]block.Block, error)

ReadBlocksFromIndex reads all blocks from given index till the current block in consecutive order.

func (*Blockchain) ReadLastNBlocks
func (c *Blockchain) ReadLastNBlocks(ctx context.Context, n int) ([]block.Block, error)

ReadLastNBlocks reads the last n blocks in reverse consecutive order.

func (*Blockchain) WriteBlock
func (c *Blockchain) WriteBlock(ctx context.Context, block block.Block) error

WriteBlock writes block in to the blockchain repository.

bookkeeping

import "github.com/bartossh/Computantis/bookkeeping"

Index

Variables

var (
    ErrTrxExistsInTheLadger            = errors.New("transaction is already in the ledger")
    ErrTrxExistsInTheBlockchain        = errors.New("transaction is already in the blockchain")
    ErrAddressNotExists                = errors.New("address does not exist in the addresses repository")
    ErrBlockTxsCorrupted               = errors.New("all transaction failed, block corrupted")
    ErrDifficultyNotInRange            = errors.New("invalid difficulty, difficulty can by in range [1 : 255]")
    ErrBlockWriteTimestampNoInRange    = errors.New("block write timestamp is not in range of [one second : four hours]")
    ErrBlockTransactionsSizeNotInRange = errors.New("block transactions size is not in range of [1 : 60000]")
)
var (
    ErrSynchronizerWatchFailure   = errors.New("synchronizer failure")
    ErrSynchronizerReleaseFailure = errors.New("synchronizer release failure")
    ErrSynchronizerStopped        = errors.New("synchronizer stopped")
)

type AddressChecker

AddressChecker provides address existence check method. If you use other repository than addresses repository, you can implement this interface but address should be uniquely indexed in your repository implementation.

type AddressChecker interface {
    CheckAddressExists(ctx context.Context, address string) (bool, error)
}

type BlockFindWriter

BlockFindWriter provides block find and write method.

type BlockFindWriter interface {
    FindTransactionInBlockHash(ctx context.Context, trxHash [32]byte) ([32]byte, error)
}

type BlockReadWriter

BlockReadWriter provides block read and write methods.

type BlockReadWriter interface {
    BlockReader
    BlockWriter
}

type BlockReader

BlockReader provides block read methods.

type BlockReader interface {
    LastBlockHashIndex(ctx context.Context) ([32]byte, uint64, error)
}

type BlockSubscription

BlockSubscription provides block publishing method. It uses reactive package. It you are using your own implementation of reactive package take care of Publish method to be non-blocking.

type BlockSubscription interface {
    Publish(block.Block)
}

type BlockWriter

BlockWriter provides block write methods.

type BlockWriter interface {
    WriteBlock(ctx context.Context, block block.Block) error
}

type Config

Config is a configuration of the Ledger.

type Config struct {
    Difficulty            uint64 `json:"difficulty"              bson:"difficulty"              yaml:"difficulty"`
    BlockWriteTimestamp   uint64 `json:"block_write_timestamp"   bson:"block_write_timestamp"   yaml:"block_write_timestamp"`
    BlockTransactionsSize int    `json:"block_transactions_size" bson:"block_transactions_size" yaml:"block_transactions_size"`
}
func (Config) Validate
func (c Config) Validate() error

Validate validates the Ledger configuration.

type DataBaseProvider

DataBaseProvider abstracts all the methods that are expected from repository.

type DataBaseProvider interface {
    Synchronizer
    TrxWriteReadMover
    NodeRegister
}

type Ledger

Ledger is a collection of ledger functionality to perform bookkeeping. It performs all the actions on the transactions and blockchain. Ladger seals all the transaction actions in the blockchain.

type Ledger struct {
    // contains filtered or unexported fields
}
func New
func New(config Config, bc BlockReadWriter, db DataBaseProvider, ac AddressChecker, vr SignatureVerifier, tf BlockFindWriter, log logger.Logger, blcSub BlockSubscription, sub Subscriber) (*Ledger, error)

New creates new Ledger if config is valid or returns error otherwise.

func (*Ledger) Run
func (l *Ledger) Run(ctx context.Context)

Run runs the Ladger engine that writes blocks to the blockchain repository. Run starts a goroutine and can be stopped by cancelling the context. It is non-blocking and concurrent safe.

func (*Ledger) VerifySignature
func (l *Ledger) VerifySignature(message, signature []byte, hash [32]byte, address string) error

VerifySignature verifies signature of the message.

func (*Ledger) WriteCandidateTransaction
func (l *Ledger) WriteCandidateTransaction(ctx context.Context, trx *transaction.Transaction) error

WriteCandidateTransaction validates and writes a transaction to the repository. Transaction is not yet a part of the blockchain at this point. Ladger will perform all the necessary checks and validations before writing it to the repository. The candidate needs to be signed by the receiver later in the process to be placed as a candidate in the blockchain.

func (*Ledger) WriteIssuerSignedTransactionForReceiver
func (l *Ledger) WriteIssuerSignedTransactionForReceiver(ctx context.Context, trx *transaction.Transaction) error

WriteIssuerSignedTransactionForReceiver validates issuer signature and writes a transaction to the repository for receiver.

type NodeRegister

NodeRegister abstracts node registration operations.

type NodeRegister interface {
    CountRegistered(ctx context.Context) (int, error)
}

type SignatureVerifier

SignatureVerifier provides signature verification method.

type SignatureVerifier interface {
    Verify(message, signature []byte, hash [32]byte, address string) error
}

type Subscriber

type Subscriber interface {
    SubscribeToLockBlockchainNotification(ctx context.Context, c chan<- bool, node string)
}

type Synchronizer

Synchronizer abstracts blockchain synchronization operations.

type Synchronizer interface {
    AddToBlockchainLockQueue(ctx context.Context, nodeID string) error
    RemoveFromBlockchainLocks(ctx context.Context, nodeID string) error
    CheckIsOnTopOfBlockchainsLocks(ctx context.Context, nodeID string) (bool, error)
}

type TrxWriteReadMover

TrxWriteReadMover provides transactions write, read and move methods. It allows to access temporary, permanent and awaiting transactions.

type TrxWriteReadMover interface {
    WriteIssuerSignedTransactionForReceiver(ctx context.Context, trx *transaction.Transaction) error
    MoveTransactionsFromTemporaryToPermanent(ctx context.Context, blockHash [32]byte, hashes [][32]byte) error
    MoveTransactionsFromAwaitingToTemporary(ctx context.Context, trxHash [32]byte) error
    ReadAwaitingTransactionsByReceiver(ctx context.Context, address string) ([]transaction.Transaction, error)
    ReadAwaitingTransactionsByIssuer(ctx context.Context, address string) ([]transaction.Transaction, error)
    ReadTemporaryTransactions(ctx context.Context) ([]transaction.Transaction, error)
}

client

import "github.com/bartossh/Computantis/client"

Index

Variables

var (
    ErrApiVersionMismatch            = fmt.Errorf("api version mismatch")
    ErrApiHeaderMismatch             = fmt.Errorf("api header mismatch")
    ErrStatusCodeMismatch            = fmt.Errorf("status code mismatch")
    ErrContentTypeMismatch           = fmt.Errorf("content type mismatch")
    ErrWalletChecksumMismatch        = fmt.Errorf("wallet checksum mismatch")
    ErrWalletVersionMismatch         = fmt.Errorf("wallet version mismatch")
    ErrServerReturnsInconsistentData = fmt.Errorf("server returns inconsistent data")
    ErrRejectedByServer              = fmt.Errorf("rejected by server")
    ErrWalletNotReady                = fmt.Errorf("wallet not ready, read wallet first")
    ErrSigningFailed                 = fmt.Errorf("signing failed")
)

type Client

Client is a rest client for the API. It provides methods to communicate with the API server and is designed to serve as a easy way of building client applications that uses the REST API of the central node.

type Client struct {
    // contains filtered or unexported fields
}
func NewClient
func NewClient(apiRoot string, timeout time.Duration, fw transaction.Verifier, wrs WalletReadSaver, walletCreator NewSignValidatorCreator) *Client

NewClient creates a new rest client.

func (*Client) Address
func (c *Client) Address() (string, error)

Address reads the wallet address. Address is a string representation of wallet public key.

func (*Client) ConfirmTransaction
func (c *Client) ConfirmTransaction(trx *transaction.Transaction) error

ConfirmTransaction confirms transaction by signing it with the wallet and then sending it to the API server.

func (*Client) DataToSign
func (c *Client) DataToSign(address string) (server.DataToSignResponse, error)

DataToSign returns data to sign for the given address. Data to sign are randomly generated bytes by the server and stored in pair with the address. Signing this data is a proof that the signing public address is the owner of the wallet a making request.

func (*Client) FlushWalletFromMemory
func (c *Client) FlushWalletFromMemory()

FlushWalletFromMemory flushes the wallet from the memory. Do it after you have saved the wallet to the file. It is recommended to use this just before logging out from the UI or closing the front end app that.

func (*Client) GenerateToken
func (c *Client) GenerateToken(t time.Time) (token.Token, error)

GenerateToken generates a token for the given time in the central node repository. It is only permitted to generate a token if wallet has admin permissions in the central node.

func (*Client) NewWallet
func (c *Client) NewWallet(token string) error

NewWallet creates a new wallet and sends a request to the API server to validate the wallet.

func (*Client) PostWebhookBlock
func (c *Client) PostWebhookBlock(url string, token string, block *block.Block) error

PostWebhookBlock posts validator.WebHookNewBlockMessage to given url.

func (*Client) ProposeTransaction
func (c *Client) ProposeTransaction(receiverAddr string, subject string, data []byte) error

ProposeTransaction sends a Transaction proposal to the API server for provided receiver address. Subject describes how to read the data from the transaction. For example, if the subject is "json", then the data can by decoded to map[sting]any, when subject "pdf" than it should be decoded by proper pdf decoder, when "csv" then it should be decoded by proper csv decoder. Client is not responsible for decoding the data, it is only responsible for sending the data to the API server.

func (*Client) ReadIssuedTransactions
func (c *Client) ReadIssuedTransactions() ([]transaction.Transaction, error)

ReadIssuedTransactions reads all issued transactions belonging to current wallet from the API server.

func (*Client) ReadWaitingTransactions
func (c *Client) ReadWaitingTransactions() ([]transaction.Transaction, error)

ReadWaitingTransactions reads all waiting transactions belonging to current wallet from the API server.

func (*Client) ReadWalletFromFile
func (c *Client) ReadWalletFromFile() error

ReadWalletFromFile reads the wallet from the file in the path.

func (*Client) SaveWalletToFile
func (c *Client) SaveWalletToFile() error

SaveWalletToFile saves the wallet to the file in the path.

func (*Client) Sign
func (c *Client) Sign(d []byte) (digest [32]byte, signature []byte, err error)

Sign signs the given data with the wallet and returns digest and signature or error otherwise. This process creates a proof for the API server that requesting client is the owner of the wallet.

func (*Client) ValidateApiVersion
func (c *Client) ValidateApiVersion() error

ValidateApiVersion makes a call to the API server and validates client and server API versions and header correctness. If API version not much it is returning an error as accessing the API server with different API version may lead to unexpected results.

type NewSignValidatorCreator

NewWalletCreator is a function that creates a new SignValidator.

type NewSignValidatorCreator func() (wallet.Wallet, error)

type WalletReadSaver

WalletReadSaver allows to read and save the wallet.

type WalletReadSaver interface {
    ReadWallet() (wallet.Wallet, error)
    SaveWallet(w wallet.Wallet) error
}

configuration

import "github.com/bartossh/Computantis/configuration"

Index

type Configuration

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      repository.DBConfig   `yaml:"database"`
    DataProvider  dataprovider.Config   `yaml:"data_provider"`
    Validator     validator.Config      `yaml:"validator"`
    FileOperator  fileoperations.Config `yaml:"file_operator"`
    SignerService signerservice.Config  `yaml:"signer_service"`
}
func Read
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.

dataprovider

import "github.com/bartossh/Computantis/dataprovider"

Index

type Cache

Cache is a simple in-memory cache for storing generated data.

type Cache struct {
    // contains filtered or unexported fields
}
func New
func New(ctx context.Context, cfg Config) *Cache

New creates new Cache and runs the cleaner.

func (*Cache) ProvideData
func (c *Cache) ProvideData(address string) []byte

ProvideData generates data and stores it referring to given address.

func (*Cache) ValidateData
func (c *Cache) ValidateData(address string, data []byte) bool

ValidateData checks if data is stored for given address and is not expired.

type Config

Config holds configuration for Cache.

type Config struct {
    Longevity uint64 `yaml:"longevity"` // Data longevity in seconds.
}

fileoperations

import "github.com/bartossh/Computantis/fileoperations"

Index

type Config

Config holds configuration of the file operator Helper.

type Config struct {
    WalletPath   string `yaml:"wallet_path"`   // wallet path to the wallet file
    WalletPasswd string `yaml:"wallet_passwd"` // wallet password to the wallet file in hex format
}

type Helper

Helper holds all file operation methods.

type Helper struct {
    // contains filtered or unexported fields
}
func New
func New(cfg Config, s Sealer) Helper

New creates new Helper.

func (Helper) ReadWallet
func (h Helper) ReadWallet() (wallet.Wallet, error)

RereadWallet reads wallet from the file.

func (Helper) SaveWallet
func (h Helper) SaveWallet(w wallet.Wallet) error

SaveWallet saves wallet to the file.

type Sealer

type Sealer interface {
    Encrypt(key, data []byte) ([]byte, error)
    Decrypt(key, data []byte) ([]byte, error)
}

logger

import "github.com/bartossh/Computantis/logger"

Index

type Log

Log is log marshaled and written in to the io.Writer of the helper implementing Logger abstraction.

type Log struct {
    ID        any       `json:"_id"        bson:"_id"        db:"id"`
    Level     string    `jon:"level"       bson:"level"      db:"level"`
    Msg       string    `json:"msg"        bson:"msg"        db:"msg"`
    CreatedAt time.Time `json:"created_at" bson:"created_at" db:"created_at"`
}

type Logger

Logger provides logging methods for debug, info, warning, error and fatal.

type Logger interface {
    Debug(msg string)
    Info(msg string)
    Warn(msg string)
    Error(msg string)
    Fatal(msg string)
}

logging

import "github.com/bartossh/Computantis/logging"

Index

type Helper

Helper helps with writing logs to io.Writers. Helper implements logger.Logger interface. Writing is done concurrently with out blocking the current thread.

type Helper struct {
    // contains filtered or unexported fields
}
func New
func New(callOnWriteLogErr, callOnFatal func(error), writers ...io.Writer) Helper

New creates new Helper.

func (Helper) Debug
func (h Helper) Debug(msg string)

Debug writes debug log.

func (Helper) Error
func (h Helper) Error(msg string)

Error writes error log.

func (Helper) Fatal
func (h Helper) Fatal(msg string)

Fatal writes fatal log.

func (Helper) Info
func (h Helper) Info(msg string)

Info writes info log.

func (Helper) Warn
func (h Helper) Warn(msg string)

Warn writes warning log.

reactive

import "github.com/bartossh/Computantis/reactive"

Index

type Observable

Observable creates a container for subscribers. This works in single producer multiple consumer pattern.

type Observable[T any] struct {
    // contains filtered or unexported fields
}
func New
func New[T any](size int) *Observable[T]

New creates Observable container that holds channels for all subscribers. size is the buffer size of each channel.

func (*Observable[T]) Publish
func (o *Observable[T]) Publish(v T)

Publish publishes value to all subscribers.

func (*Observable[T]) Subscribe
func (o *Observable[T]) Subscribe() *subscriber[T]

Subscribe subscribes to the container.

repository

import "github.com/bartossh/Computantis/repository"

Index

Variables

var (
    ErrInsertFailed                            = fmt.Errorf("insert failed")
    ErrRemoveFailed                            = fmt.Errorf("remove failed")
    ErrSelectFailed                            = fmt.Errorf("select failed")
    ErrMoveFailed                              = fmt.Errorf("move failed")
    ErrScanFailed                              = fmt.Errorf("scan failed")
    ErrUnmarshalFailed                         = fmt.Errorf("unmarshal failed")
    ErrCommitFailed                            = fmt.Errorf("transaction commit failed")
    ErrTrxBeginFailed                          = fmt.Errorf("transaction begin failed")
    ErrAddingToLockQueueBlockChainFailed       = fmt.Errorf("adding to lock blockchain failed")
    ErrRemovingFromLockQueueBlockChainFailed   = fmt.Errorf("removing from lock blockchain failed")
    ErrListenFailed                            = fmt.Errorf("listen failed")
    ErrCheckingIsOnTopOfBlockchainsLocksFailed = fmt.Errorf("checking is on top of blockchains locks failed")
    ErrNodeRegisterFailed                      = fmt.Errorf("node register failed")
    ErrNodeUnregisterFailed                    = fmt.Errorf("node unregister failed")
    ErrNodeLookupFailed                        = fmt.Errorf("node lookup failed")
    ErrNodeRegisteredAddressesQueryFailed      = fmt.Errorf("node registered addresses query failed")
)

type DBConfig

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.
    IsSSL        bool   `yaml:"is_ssl"`        // IsSSL is the flag that indicates if the connection should be encrypted.
}

type DataBase

Database provides database access for read, write and delete of repository entities.

type DataBase struct {
    // contains filtered or unexported fields
}
func Connect
func Connect(ctx context.Context, cfg DBConfig) (*DataBase, error)

Connect creates new connection to the repository and returns pointer to the DataBase.

func (DataBase) AddToBlockchainLockQueue
func (db DataBase) AddToBlockchainLockQueue(ctx context.Context, nodeID string) error

AddToBlockchainLockQueue adds blockchain lock to queue.

func (DataBase) CheckAddressExists
func (db DataBase) CheckAddressExists(ctx context.Context, addr string) (bool, error)

CheckAddressExists checks if address exists in the database.

func (DataBase) CheckIsOnTopOfBlockchainsLocks
func (db DataBase) CheckIsOnTopOfBlockchainsLocks(ctx context.Context, nodeID string) (bool, error)

CheckIsOnTopOfBlockchainsLocks checks if node is on top of blockchain locks queue.

func (DataBase) CheckToken
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) CountRegistered
func (db DataBase) CountRegistered(ctx context.Context) (int, error)

CountRegistered counts registered nodes in the database.

func (DataBase) Disconnect
func (db DataBase) Disconnect(ctx context.Context) error

Disconnect disconnects user from database

func (DataBase) FindAddress
func (db DataBase) FindAddress(ctx context.Context, search string, limit int) ([]string, error)

FindAddress finds address in the database.

func (DataBase) FindTransactionInBlockHash
func (db DataBase) FindTransactionInBlockHash(ctx context.Context, trxHash [32]byte) ([32]byte, error)

FindTransactionInBlockHash returns block hash in to which transaction with given hash was added. If transaction is not yet added to any block, empty hash is returned.

func (DataBase) InvalidateToken
func (db DataBase) InvalidateToken(ctx context.Context, token string) error

InvalidateToken invalidates token.

func (DataBase) IsAddressAdmin
func (db DataBase) IsAddressAdmin(ctx context.Context, addr string) (bool, error)

IsAddressAdmin checks if address has access level admin.

func (DataBase) IsAddressStandard
func (db DataBase) IsAddressStandard(ctx context.Context, addr string) (bool, error)

IsAddressStandard checks if address has access level standard.

func (DataBase) IsAddressSuspended
func (db DataBase) IsAddressSuspended(ctx context.Context, addr string) (bool, error)

IsAddressAdmin checks if address has access level suspended.

func (DataBase) IsAddressTrusted
func (db DataBase) IsAddressTrusted(ctx context.Context, addr string) (bool, error)

IsAddressTrusted checks if address has access level trusted.

func (DataBase) LastBlock
func (db DataBase) LastBlock(ctx context.Context) (block.Block, error)

LastBlock returns last block from the database.

func (DataBase) MoveTransactionsFromAwaitingToTemporary
func (db DataBase) MoveTransactionsFromAwaitingToTemporary(ctx context.Context, trxHash [32]byte) error

MoveTransactionsFromAwaitingToTemporary moves awaiting transaction marking it as temporary.

func (DataBase) MoveTransactionsFromTemporaryToPermanent
func (db DataBase) MoveTransactionsFromTemporaryToPermanent(ctx context.Context, blockHash [32]byte, hashes [][32]byte) error

MoveTransactionsFromTemporaryToPermanent moves transactions by marking transactions with matching hash to be permanent and sets block hash field to referenced block hash.

func (DataBase) Ping
func (db DataBase) Ping(ctx context.Context) error

Ping checks if the connection to the database is still alive.

func (DataBase) ReadAwaitingTransactionsByIssuer
func (db DataBase) ReadAwaitingTransactionsByIssuer(ctx context.Context, address string) ([]transaction.Transaction, error)

ReadAwaitingTransactionsByIssuer reads all transactions paired with given issuer address.

func (DataBase) ReadAwaitingTransactionsByReceiver
func (db DataBase) ReadAwaitingTransactionsByReceiver(ctx context.Context, address string) ([]transaction.Transaction, error)

ReadAwaitingTransactionsByReceiver reads all transactions paired with given receiver address.

func (DataBase) ReadBlockByHash
func (db DataBase) ReadBlockByHash(ctx context.Context, hash [32]byte) (block.Block, error)

ReadBlockByHash returns block with given hash.

func (DataBase) ReadLastNValidatorStatuses
func (db DataBase) ReadLastNValidatorStatuses(ctx context.Context, last int64) ([]validator.Status, error)

ReadLastNValidatorStatuses reads last validator statuses from the database.

func (DataBase) ReadRegisteredNodesAddresses
func (db DataBase) ReadRegisteredNodesAddresses(ctx context.Context) ([]string, error)

ReadAddresses reads registered nodes addresses from the database.

func (DataBase) ReadTemporaryTransactions
func (db DataBase) ReadTemporaryTransactions(ctx context.Context) ([]transaction.Transaction, error)

ReadTemporaryTransactions reads all transactions that are marked as temporary.

func (DataBase) RegisterNode
func (db DataBase) RegisterNode(ctx context.Context, n, ws string) error

RegisterNode registers node in the database.

func (DataBase) RemoveFromBlockchainLocks
func (db DataBase) RemoveFromBlockchainLocks(ctx context.Context, nodeID string) error

RemoveFromBlockchainLocks removes blockchain lock from queue.

func (DataBase) RunMigration
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) UnregisterNode
func (db DataBase) UnregisterNode(ctx context.Context, n string) error

UnregisterNode unregister node from the database.

func (DataBase) Write
func (db DataBase) Write(p []byte) (n int, err error)

Write writes log to the database. p is a marshaled logger.Log.

func (DataBase) WriteAddress
func (db DataBase) WriteAddress(ctx context.Context, addr string) error

WriteAddress writes address to the database.

func (DataBase) WriteBlock
func (db DataBase) WriteBlock(ctx context.Context, block block.Block) error

WriteBlock writes block to the database.

func (DataBase) WriteIssuerSignedTransactionForReceiver
func (db DataBase) WriteIssuerSignedTransactionForReceiver(ctx context.Context, trx *transaction.Transaction) error

WriteIssuerSignedTransactionForReceiver writes transaction to the storage marking it as awaiting.

func (DataBase) WriteToken
func (db DataBase) WriteToken(ctx context.Context, tkn string, expirationDate int64) error

WriteToken writes unique token to the database.

func (DataBase) WriteValidatorStatus
func (db DataBase) WriteValidatorStatus(ctx context.Context, vs *validator.Status) error

WriteValidatorStatus writes validator status to the database.

type Listener

Listener wraps listener for notifications from database. Provides methods for listening and closing.

type Listener struct {
    // contains filtered or unexported fields
}
func Listen
func Listen(conn string, report func(ev pq.ListenerEventType, err error)) (Listener, error)

Listen creates Listener for notifications from database.

func Subscribe
func Subscribe(ctx context.Context, cfg DBConfig) (Listener, error)

Subscribe subscribes to the database events.

func (Listener) Close
func (l Listener) Close()

Close closes listener.

func (Listener) SubscribeToLockBlockchainNotification
func (l Listener) SubscribeToLockBlockchainNotification(ctx context.Context, c chan<- bool, node string)

SubscribeToLockBlockchainNotification listens for blockchain lock. To stop subscription, close channel.

serializer

import "github.com/bartossh/Computantis/serializer"

Index

func Base58Decode

func Base58Decode(input []byte) ([]byte, error)

Base58Decode decodes base58 string to byte array.

func Base58Encode

func Base58Encode(input []byte) []byte

Base58Encode encodes byte array to base58 string.

server

import "github.com/bartossh/Computantis/server"

Index

Constants

const (
    ApiVersion = "1.0.0"
    Header     = "Computantis-Central"
)
const (
    AliveURL              = "/alive"                         // URL to check if server is alive and version.
    SearchAddressURL      = searchGroupURL + addressURL      // URL to search for address.
    SearchBlockURL        = searchGroupURL + blockURL        // URL to search for block that contains transaction hash.
    ProposeTransactionURL = transactionGroupURL + proposeURL // URL to propose transaction signed by the issuer.
    ConfirmTransactionURL = transactionGroupURL + confirmURL // URL to confirm transaction signed by the receiver.
    AwaitedTransactionURL = transactionGroupURL + awaitedURL // URL to get awaited transactions for the receiver.
    IssuedTransactionURL  = transactionGroupURL + issuedURL  // URL to get issued transactions for the issuer.
    DataToValidateURL     = validatorGroupURL + dataURL      // URL to get data to validate address by signing rew message.
    CreateAddressURL      = addressGroupURL + createURL      // URL to create new address.
    GenerateTokenURL      = tokenGroupURL + generateURL      // URL to generate new token.
    WsURL                 = "/ws"                            // URL to connect to websocket.
)
const (
    CommandEcho           = "echo"
    CommandSocketList     = "socketlist"
    CommandNewBlock       = "command_new_block"
    CommandNewTransaction = "command_new_transaction"
)

Variables

var (
    ErrWrongPortSpecified = errors.New("port must be between 1 and 65535")
    ErrWrongMessageSize   = errors.New("message size must be between 1024 and 15000000")
)

func Run

func Run(ctx context.Context, c Config, repo Repository, bookkeeping Bookkeeper, pv RandomDataProvideValidator, log logger.Logger, rx ReactiveSubscriberProvider) error

Run initializes routing and runs the server. To stop the server cancel the context. It blocks until the context is canceled.

type AddressReaderWriterModifier

AddressReaderWriterModifier abstracts address operations.

type AddressReaderWriterModifier interface {
    FindAddress(ctx context.Context, search string, limit int) ([]string, error)
    CheckAddressExists(ctx context.Context, address string) (bool, error)
    WriteAddress(ctx context.Context, address string) error
    IsAddressSuspended(ctx context.Context, addr string) (bool, error)
    IsAddressStandard(ctx context.Context, addr string) (bool, error)
    IsAddressTrusted(ctx context.Context, addr string) (bool, error)
    IsAddressAdmin(ctx context.Context, addr string) (bool, error)
}

type AliveResponse

AliveResponse is a response for alive and version check.

type AliveResponse struct {
    Alive      bool   `json:"alive"`
    APIVersion string `json:"api_version"`
    APIHeader  string `json:"api_header"`
}

type AwaitedIssuedTransactionRequest

AwaitedIssuedTransactionRequest is a request to get awaited or issued transactions for given address. Request contains of Address for which Transactions are requested, Data in binary format, Hash of Data and Signature of the Data to prove that entity doing the request is an Address owner.

type AwaitedIssuedTransactionRequest struct {
    Address   string   `json:"address"`
    Data      []byte   `json:"data"`
    Hash      [32]byte `json:"hash"`
    Signature []byte   `json:"signature"`
}

type AwaitedTransactionResponse

AwaitedTransactionResponse is a response for awaited transactions request.

type AwaitedTransactionResponse struct {
    Success             bool                      `json:"success"`
    AwaitedTransactions []transaction.Transaction `json:"awaited_transactions"`
}

type Bookkeeper

Bookkeeper abstracts methods of the bookkeeping of a blockchain.

type Bookkeeper interface {
    Verifier
    Run(ctx context.Context)
    WriteCandidateTransaction(ctx context.Context, tx *transaction.Transaction) error
    WriteIssuerSignedTransactionForReceiver(ctx context.Context, trx *transaction.Transaction) error
}

type Config

Config contains configuration of the server.

type Config struct {
    Port             int    `yaml:"port"`              // Port to listen on.
    DataSizeBytes    int    `yaml:"data_size_bytes"`   // Size of the data to be stored in the transaction.
    WebsocketAddress string `yaml:"websocket_address"` // Address of the websocket server.
}

type CreateAddressRequest

CreateAddressRequest is a request to create an address.

type CreateAddressRequest struct {
    Address   string   `json:"address"`
    Token     string   `json:"token"`
    Data      []byte   `json:"data"`
    Hash      [32]byte `json:"hash"`
    Signature []byte   `json:"signature"`
}

type CreateAddressResponse

Response for address creation request. If Success is true, Address contains created address in base58 format.

type CreateAddressResponse struct {
    Success bool   `json:"success"`
    Address string `json:"address"`
}

type DataToSignRequest

DataToSignRequest is a request to get data to sign for proving identity.

type DataToSignRequest struct {
    Address string `json:"address"`
}

type DataToSignResponse

DataToSignRequest is a response containing data to sign for proving identity.

type DataToSignResponse struct {
    Data []byte `json:"message"`
}

type GenerateTokenRequest

GenerateTokenRequest is a request for token generation.

type GenerateTokenRequest struct {
    Address    string   `json:"address"`
    Expiration int64    `json:"expiration"`
    Data       []byte   `json:"data"`
    Hash       [32]byte `json:"hash"`
    Signature  []byte   `json:"signature"`
}

type GenerateTokenResponse

GenerateTokenResponse is a response containing generated token.

type GenerateTokenResponse = token.Token

type IssuedTransactionResponse

AwaitedTransactionResponse is a response for issued transactions request.

type IssuedTransactionResponse struct {
    Success            bool                      `json:"success"`
    IssuedTransactions []transaction.Transaction `json:"issued_transactions"`
}

type Message

Message is the message that is used to exchange information between the server and the client.

type Message struct {
    Command     string                  `json:"command"`               // Command is the command that refers to the action handler in websocket protocol.
    Error       string                  `json:"error,omitempty"`       // Error is the error message that is sent to the client.
    Block       block.Block             `json:"block,omitempty"`       // Block is the block that is sent to the client.
    Transaction transaction.Transaction `json:"transaction,omitempty"` // Transaction is the transaction validated by the central server and will be added to the next block.
    Sockets     []string                `json:"sockets,omitempty"`     // sockets is the list of central nodes web-sockets addresses.
}

type RandomDataProvideValidator

RandomDataProvideValidator provides random binary data for signing to prove identity and the validator of data being valid and not expired.

type RandomDataProvideValidator interface {
    ProvideData(address string) []byte
    ValidateData(address string, data []byte) bool
}

type ReactiveSubscriberProvider

ReactiveSubscriberProvider provides reactive subscription to the blockchain. It allows to listen for the new blocks created by the Ladger.

type ReactiveSubscriberProvider interface {
    Cancel()
    Channel() <-chan block.Block
}

type Register

Register abstracts node registration operations.

type Register interface {
    RegisterNode(ctx context.Context, n, ws string) error
    UnregisterNode(ctx context.Context, n string) error
    ReadRegisteredNodesAddresses(ctx context.Context) ([]string, error)
    CountRegistered(ctx context.Context) (int, error)
}

type Repository

Repository is the interface that wraps the basic CRUD and Search methods. Repository should be properly indexed to allow for transaction and block hash. as well as address public keys to be and unique and the hash lookup should be fast. Repository holds the blocks and transaction that are part of the blockchain.

type Repository interface {
    Register
    AddressReaderWriterModifier
    TokenWriteInvalidateChecker
    FindTransactionInBlockHash(ctx context.Context, trxHash [32]byte) ([32]byte, error)
    ReadAwaitingTransactionsByIssuer(ctx context.Context, address string) ([]transaction.Transaction, error)
    ReadAwaitingTransactionsByReceiver(ctx context.Context, address string) ([]transaction.Transaction, error)
}

type SearchAddressRequest

SearchAddressRequest is a request to search for address.

type SearchAddressRequest struct {
    Address string `json:"address"`
}

type SearchAddressResponse

SearchAddressResponse is a response for address search.

type SearchAddressResponse struct {
    Addresses []string `json:"addresses"`
}

type SearchBlockRequest

SearchBlockRequest is a request to search for block.

type SearchBlockRequest struct {
    Address    string   `json:"address"`
    RawTrxHash [32]byte `json:"raw_trx_hash"`
}

type SearchBlockResponse

SearchBlockResponse is a response for block search.

type SearchBlockResponse struct {
    RawBlockHash [32]byte `json:"raw_block_hash"`
}

type TokenWriteInvalidateChecker

TokenWriteInvalidateChecker abstracts token operations.

type TokenWriteInvalidateChecker interface {
    WriteToken(ctx context.Context, tkn string, expirationDate int64) error
    CheckToken(ctx context.Context, token string) (bool, error)
    InvalidateToken(ctx context.Context, token string) error
}

type TransactionConfirmProposeResponse

TransactionConfirmProposeResponse is a response for transaction propose.

type TransactionConfirmProposeResponse struct {
    Success bool     `json:"success"`
    TrxHash [32]byte `json:"trx_hash"`
}

type TransactionProposeRequest

TransactionProposeRequest is a request to propose a transaction.

type TransactionProposeRequest struct {
    ReceiverAddr string                  `json:"receiver_addr"`
    Transaction  transaction.Transaction `json:"transaction"`
}

type Verifier

Verifier provides methods to verify the signature of the message.

type Verifier interface {
    VerifySignature(message, signature []byte, hash [32]byte, address string) error
}

signerservice

import "github.com/bartossh/Computantis/signerservice"

Index

Constants

const (
    Alive                   = "/alive"                 // alive URL allows to check if server is alive and if sign service is of the same version.
    IssueTransaction        = "/transactions/issue"    // issue URL allows to issue transaction signed by the issuer.
    ConfirmTransaction      = "/transaction/sign"      // sign URL allows to sign transaction received by the receiver.
    GetIssuedTransactions   = "/transactions/issued"   // issued URL allows to get issued transactions for the issuer.
    GetReceivedTransactions = "/transactions/received" // received URL allows to get received transactions for the receiver.
    CreateWallet            = "/wallet/create"         // create URL allows to create new wallet.
    ReadWalletPublicAddress = "/wallet/address"        // address URL allows to read public address of the wallet.
    GetOneDayToken          = "token/day"              // token/day URL allows to get one day token.
    GetOneWeekToken         = "token/week"             // token/week URL allows to get one week token.
)

func Run

func Run(ctx context.Context, cfg Config, log logger.Logger, timeout time.Duration, fw transaction.Verifier, wrs client.WalletReadSaver, walletCreator client.NewSignValidatorCreator) error

Run runs the service application that exposes the API for creating, validating and signing transactions. This blocks until the context is canceled.

type Config

Config is the configuration for the server

type Config struct {
    Port               string `yaml:"port"`
    CentralNodeAddress string `yaml:"central_node_address"`
}

type ConfirmTransactionRequest

ValidateTransactionRequest is a request to validate transaction.

type ConfirmTransactionRequest struct {
    Transaction transaction.Transaction `json:"transaction"`
}

type ConfirmTransactionResponse

ConfirmTransactionResponse is response to validate transaction.

type ConfirmTransactionResponse struct {
    Ok  bool   `json:"ok"`
    Err string `json:"err"`
}

type CreateWalletRequest

CreateWalletRequest is a request to create wallet.

type CreateWalletRequest struct {
    Token string `json:"token"`
}

type CreateWalletResponse

CreateWalletResponse is response to create wallet.

type CreateWalletResponse struct {
    Ok  bool   `json:"ok"`
    Err string `json:"err"`
}

type IssueTransactionRequest

IssueTransactionRequest is a request message that contains data and subject of the transaction to be issued.

type IssueTransactionRequest struct {
    ReceiverAddress string `json:"receiver_address"`
    Subject         string `json:"subject"`
    Data            []byte `json:"data"`
}

type IssueTransactionResponse

IssueTransactionResponse is response to issued transaction.

type IssueTransactionResponse struct {
    Ok  bool   `json:"ok"`
    Err string `json:"err"`
}

type IssuedTransactionResponse

IssuedTransactionResponse is a response of issued transactions.

type IssuedTransactionResponse struct {
    Ok           bool                      `json:"ok"`
    Err          string                    `json:"err"`
    Transactions []transaction.Transaction `json:"transactions"`
}

type ReadWalletPublicAddressResponse

ReadWalletPublicAddressResponse is a response to read wallet public address.

type ReadWalletPublicAddressResponse struct {
    Ok      bool   `json:"ok"`
    Err     string `json:"err"`
    Address string `json:"address"`
}

type ReceivedTransactionResponse

ReceivedTransactionResponse is a response of issued transactions.

type ReceivedTransactionResponse struct {
    Ok           bool                      `json:"ok"`
    Err          string                    `json:"err"`
    Transactions []transaction.Transaction `json:"transactions"`
}

stdoutwriter

import "github.com/bartossh/Computantis/stdoutwriter"

Index

type Logger

type Logger struct{}
func (Logger) Write
func (l Logger) Write(p []byte) (n int, err error)

stress

import "github.com/bartossh/Computantis/stress"

Stress is a package that provides a simple way to stress test your code on the full cycle of transaction processing.

Index

token

import "github.com/bartossh/Computantis/token"

Index

type Token

Token holds information about unique token. Token is a way of proving to the REST API of the central server that the request is valid and comes from the client that is allowed to use the API.

type Token struct {
    ID             any    `json:"-"               bson:"_id,omitempty"   db:"id"`
    Token          string `json:"token"           bson:"token"           db:"token"`
    Valid          bool   `json:"valid"           bson:"valid"           db:"valid"`
    ExpirationDate int64  `json:"expiration_date" bson:"expiration_date" db:"expiration_date"`
}
func New
func New(expiration int64) (Token, error)

New creates new token.

transaction

import "github.com/bartossh/Computantis/transaction"

Index

Constants

const ExpirationTimeInDays = 7 // transaction validity expiration time in days. TODO: move to config

Variables

var (
    ErrTransactionHasAFutureTime        = errors.New("transaction has a future time")
    ErrExpiredTransaction               = errors.New("transaction has expired")
    ErrTransactionHashIsInvalid         = errors.New("transaction hash is invalid")
    ErrSignatureNotValidOrDataCorrupted = errors.New("signature not valid or data are corrupted")
    ErrSubjectIsEmpty                   = errors.New("subject cannot be empty")
    ErrAddressIsInvalid                 = errors.New("address is invalid")
)

type Signer

Signer provides signing and address methods.

type Signer interface {
    Sign(message []byte) (digest [32]byte, signature []byte)
    Address() string
}

type Transaction

Transaction contains transaction information, subject type, subject data, signatures and public keys. Transaction is valid for a week from being issued. Subject represents an information how to read the Data and / or how to decode them. Data is not validated by the computantis server, Ladger ior block. What is stored in Data is not important for the whole Computantis system. It is only important that the data are signed by the issuer and the receiver and both parties agreed on them.

type Transaction struct {
    ID                any       `json:"-"                  bson:"_id"                db:"id"`
    CreatedAt         time.Time `json:"created_at"         bson:"created_at"         db:"created_at"`
    Hash              [32]byte  `json:"hash"               bson:"hash"               db:"hash"`
    IssuerAddress     string    `json:"issuer_address"     bson:"issuer_address"     db:"issuer_address"`
    ReceiverAddress   string    `json:"receiver_address"   bson:"receiver_address"   db:"receiver_address"`
    Subject           string    `json:"subject"            bson:"subject"            db:"subject"`
    Data              []byte    `json:"data"               bson:"data"               db:"data"`
    IssuerSignature   []byte    `json:"issuer_signature"   bson:"issuer_signature"   db:"issuer_signature"`
    ReceiverSignature []byte    `json:"receiver_signature" bson:"receiver_signature" db:"receiver_signature"`
}
func New
func New(subject string, data []byte, receiverAddress string, issuer Signer) (Transaction, error)

New creates new transaction signed by the issuer.

func (*Transaction) GeMessage
func (t *Transaction) GeMessage() []byte

GeMessage returns message used for signature validation.

func (*Transaction) Sign
func (t *Transaction) Sign(receiver Signer, v Verifier) ([32]byte, error)

Sign verifies issuer signature and signs Transaction by the receiver.

type TransactionAwaitingReceiverSignature

TransactionAwaitingReceiverSignature represents transaction awaiting receiver signature. It is as well the entity of all issued transactions that has not been signed by receiver yet.

type TransactionAwaitingReceiverSignature struct {
    ID              any         `json:"-"                bson:"_id,omitempty"    db:"id"`
    ReceiverAddress string      `json:"receiver_address" bson:"receiver_address" db:"receiver_address"`
    IssuerAddress   string      `json:"issuer_address"   bson:"issuer_address"   db:"issuer_address"`
    Transaction     Transaction `json:"transaction"      bson:"transaction"      db:"-"`
    TransactionHash [32]byte    `json:"transaction_hash" bson:"transaction_hash" db:"hash"`
}

type TransactionInBlock

TransactionInBlock stores relation between Transaction and Block to which Transaction was added. It is stored for fast lookup only to allow to find Block hash in which Transaction was added.

type TransactionInBlock struct {
    ID              any      `json:"-" bson:"_id,omitempty"    db:"id"`
    BlockHash       [32]byte `json:"-" bson:"block_hash"       db:"block_hash"`
    TransactionHash [32]byte `json:"-" bson:"transaction_hash" db:"transaction_hash"`
}

type Verifier

Verifier provides signature verification method.

type Verifier interface {
    Verify(message, signature []byte, hash [32]byte, issuer string) error
}

validator

import "github.com/bartossh/Computantis/validator"

Index

Constants

const (
    Header = "Computantis-Validator"
)

Variables

var (
    ErrProofBlockIsInvalid    = fmt.Errorf("block proof is invalid")
    ErrBlockIndexIsInvalid    = fmt.Errorf("block index is invalid")
    ErrBlockPrevHashIsInvalid = fmt.Errorf("block previous hash is invalid")
)

func Run

func Run(ctx context.Context, cfg Config, srw StatusReadWriter, log logger.Logger, ver Verifier, wh WebhookCreateRemovePoster, wallet *wallet.Wallet) error

Run initializes routing and runs the validator. To stop the validator cancel the context. Validator connects to the central server via websocket and listens for new blocks. It will block until the context is canceled.

type Config

Config contains configuration of the validator.

type Config struct {
    Token     string `yaml:"token"`     // token is used to authenticate validator in the central server
    Websocket string `yaml:"websocket"` // websocket address of the central server
    Port      int    `yaml:"port"`      // port on which validator will listen for http requests
}

type CreateRemoveUpdateHookRequest

CreateRemoveUpdateHookRequest is the request sent to create, remove or update the webhook.

type CreateRemoveUpdateHookRequest struct {
    URL       string `json:"address"`        // URL is a url  of the webhook.
    Hook      string `json:"hook"`           // Hook is a type of the webhook. It describes on what event the webhook is triggered.
    Address   string `json:"wallet_address"` // Address is the address of the wallet that is used to sign the webhook.
    Token     string `json:"token"`          // Token is the token added to the webhook to verify that the message comes from the valid source.
    Data      []byte `json:"data"`           // Data is the data is a subject of the signature. It is signed by the wallet address.
    Digest    []byte `json:"digest"`         // Digest is the digest of the data. It is used to verify that the data is not changed.
    Signature []byte `json:"signature"`      // Signature is the signature of the data. It is used to verify that the data is not changed.
}

type Status

Status is a status of each received block by the validator. It keeps track of invalid blocks in case of blockchain corruption.

type Status struct {
    ID        any         `json:"-"          bson:"_id,omitempty" db:"id"`
    Index     int64       `json:"index"      bson:"index"         db:"index"`
    Block     block.Block `json:"block"      bson:"block"         db:"-"`
    Valid     bool        `json:"valid"      bson:"valid"         db:"valid"`
    CreatedAt time.Time   `json:"created_at" bson:"created_at"    db:"created_at"`
}

type StatusReadWriter

StatusReadWriter provides methods to bulk read and single write validator status.

type StatusReadWriter interface {
    WriteValidatorStatus(ctx context.Context, vs *Status) error
    ReadLastNValidatorStatuses(ctx context.Context, last int64) ([]Status, error)
}

type Verifier

Verifier provides methods to verify the signature of the message.

type Verifier interface {
    Verify(message, signature []byte, hash [32]byte, address string) error
}

type WebHookNewBlockMessage

WebHookNewBlockMessage is the message sent to the webhook url that was created.

type WebHookNewBlockMessage struct {
    Token string      `json:"token"` // Token given to the webhook by the webhooks creator to validate the message source.
    Block block.Block `json:"block"` // Block is the block that was mined.
    Valid bool        `json:"valid"` // Valid is the flag that indicates if the block is valid.
}

type WebhookCreateRemovePoster

WebhookCreateRemovePoster provides methods to create, remove webhooks and post messages to webhooks.

type WebhookCreateRemovePoster interface {
    CreateWebhook(trigger string, h webhooks.Hook) error
    RemoveWebhook(trigger string, h webhooks.Hook) error
    PostWebhookBlock(blc *block.Block)
}

wallet

import "github.com/bartossh/Computantis/wallet"

Index

type Helper

Helper provides wallet helper functionalities without knowing about wallet private and public keys.

type Helper struct{}
func NewVerifier
func NewVerifier() Helper

NewVerifier creates new wallet Helper verifier.

func (Helper) AddressToPubKey
func (h Helper) AddressToPubKey(address string) (ed25519.PublicKey, error)

AddressToPubKey creates ED25519 public key from address, or returns error otherwise.

func (Helper) Verify
func (h Helper) Verify(message, signature []byte, hash [32]byte, address string) error

Verify verifies if message is signed by given key and hash is equal.

type Wallet

Wallet holds public and private key of the wallet owner.

type Wallet struct {
    Private ed25519.PrivateKey `json:"private" bson:"private"`
    Public  ed25519.PublicKey  `json:"public" bson:"public"`
}
func DecodeGOBWallet
func DecodeGOBWallet(data []byte) (Wallet, error)

DecodeGOBWallet tries to decode Wallet from gob representation or returns error otherwise.

func New
func New() (Wallet, error)

New tries to creates a new Wallet or returns error otherwise.

func (*Wallet) Address
func (w *Wallet) Address() string

Address creates address from the public key that contains wallet version and checksum.

func (*Wallet) ChecksumLength
func (w *Wallet) ChecksumLength() int

ChecksumLength returns checksum length.

func (*Wallet) EncodeGOB
func (w *Wallet) EncodeGOB() ([]byte, error)

EncodeGOB tries to encodes Wallet in to the gob representation or returns error otherwise.

func (*Wallet) Sign
func (w *Wallet) Sign(message []byte) (digest [32]byte, signature []byte)

Sign signs the message with Ed25519 signature. Returns digest hash sha256 and signature.

func (*Wallet) Verify
func (w *Wallet) Verify(message, signature []byte, hash [32]byte) bool

Verify verifies message ED25519 signature and hash. Uses hashing sha256.

func (*Wallet) Version
func (w *Wallet) Version() byte

Version returns wallet version.

webhooks

import "github.com/bartossh/Computantis/webhooks"

Index

Constants

const (
    TriggerNewBlock = "trigger_new_block" // TriggerNewBlock is the trigger for new block. It is triggered when a new block is forged.
)

Variables

var (
    ErrorHookNotImplemented = errors.New("hook not implemented")
)

type Hook

Hook is the hook that is used to trigger the webhook.

type Hook struct {
    URL   string `json:"address"` // URL is a url  of the webhook.
    Token string `json:"token"`   // Token is the token added to the webhook to verify that the message comes from the valid source.
}

type HookRequestHTTPPoster

HookRequestHTTPPoster provides PostWebhookBlock method that allows to post new forged block to the webhook url over HTTP protocol.

type HookRequestHTTPPoster interface {
    PostWebhookBlock(url string, token string, block *block.Block) error
}

type Service

Service provide webhook service that is used to create, remove and update webhooks.

type Service struct {
    // contains filtered or unexported fields
}
func New
func New(client HookRequestHTTPPoster, l logger.Logger) *Service

New creates new instance of the webhook service.

func (*Service) CreateWebhook
func (s *Service) CreateWebhook(trigger string, h Hook) error

CreateWebhook creates new webhook or or updates existing one for given trigger.

func (*Service) PostWebhookBlock
func (s *Service) PostWebhookBlock(blc *block.Block)

PostWebhookBlock posts block to all webhooks that are subscribed to the new block trigger.

func (*Service) RemoveWebhook
func (s *Service) RemoveWebhook(trigger string, h Hook) error

RemoveWebhook removes webhook for given trigger and Hook URL.

central

import "github.com/bartossh/Computantis/cmd/central"

Index

validator

import "github.com/bartossh/Computantis/cmd/validator"

Index

wallet

import "github.com/bartossh/Computantis/cmd/wallet"

Index

Generated by gomarkdoc

Directories

Path Synopsis
cmd
Stress is a package that provides a simple way to stress test your code on the full cycle of transaction processing.
Stress is a package that provides a simple way to stress test your code on the full cycle of transaction processing.
your front-end html should look like this: ```html <html> <head>
your front-end html should look like this: ```html <html> <head>

Jump to

Keyboard shortcuts

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