...

Package vfs

import "github.com/securesharelabs/vstore/vfs"
Overview
Index

Overview ▾

Package vfs implements a verifiable store for CometBFT nodes.

vfs defines key components for the establishment of a secure data communication protocol which is based on digital signatures with ed25519 elliptic-curve.

Structures

  • IdentitySecretProvider: Creates AES-256 secrets used to encrypt the database.
  • SecretProvider: Creates AES-256 secrets used to encrypt private keys.
  • Signable: Interfaces that describes data to be signed using an ed25519 private key.
  • SignedTransaction: Describes a signed data object that is timestamped.
  • State: Consists of a blockchain height, a number of transactions and merkle roots.
  • VStoreApplication: A CometBFT ABCI application to run on top of CometBFT nodes.

Examples

vstore --home=/tmp/.vfs-home --socket=unix://vfs.sock
vstore version
vstore info --home=/tmp/.vfs-home
vstore factory --home /tmp/.vfs-home --data "Message here" --commit
vstore query --home /tmp/.vfs-home --hash TRANSACTION_HASH_HEX

Index ▾

Constants
func ComputeHash(p *SignedTransaction) []byte
func Decrypt(secret []byte, ciphertext []byte) ([]byte, error)
func Encrypt(secret []byte, data []byte) ([]byte, error)
func GenerateSecret(pw, salt []byte) ([]byte, []byte, error)
func MustGenerateIdentity(idFile string, pw []byte) (string, string)
func MustGenerateSecret(pw, salt []byte) ([]byte, []byte)
func NewIdentity(file string, pw []byte) *identityFile
func PubKeyToProto(pubKey crypto.PubKey) cmtp2p.PublicKey
func SignData(priv ed25519.PrivKey, tx Signable) []byte
type IdentitySecretProvider
type SecretProvider
type Signable
type SignedTransaction
    func FromBytes(bz []byte) (*SignedTransaction, error)
    func FromProto(pb *vfsp2p.Transaction) (*SignedTransaction, error)
    func NewSignedTransactionFromBytes(tx []byte) (*SignedTransaction, error)
    func (p SignedTransaction) Bytes() []byte
    func (p SignedTransaction) PublicKey() string
    func (p SignedTransaction) ToProto() *vfsp2p.Transaction
    func (p SignedTransaction) Verify() bool
type State
    func (s State) Hash() []byte
    func (s State) SortedMerkleRoots() [][]byte
type TransactionBody
    func (p TransactionBody) Bytes() []byte
    func (p TransactionBody) Sign(priv ed25519.PrivKey) ([]byte, error)
type VStoreApplication
    func NewInMemoryVStoreApplication(id_file string, password []byte) *VStoreApplication
    func NewVStoreApplication(db cmtdb.DB, id_file string, password []byte) *VStoreApplication
    func (app *VStoreApplication) CheckTx(_ context.Context, check *abci.RequestCheckTx) (*abci.ResponseCheckTx, error)
    func (app *VStoreApplication) Commit(_ context.Context, commit *abci.RequestCommit) (*abci.ResponseCommit, error)
    func (app *VStoreApplication) FinalizeBlock(ctx context.Context, req *abci.RequestFinalizeBlock) (*abci.ResponseFinalizeBlock, error)
    func (app *VStoreApplication) Info(_ context.Context, info *abci.RequestInfo) (*abci.ResponseInfo, error)
    func (app *VStoreApplication) InitChain(_ context.Context, chain *abci.RequestInitChain) (*abci.ResponseInitChain, error)
    func (app *VStoreApplication) PrepareProposal(ctx context.Context, proposal *abci.RequestPrepareProposal) (*abci.ResponsePrepareProposal, error)
    func (app *VStoreApplication) ProcessProposal(ctx context.Context, proposal *abci.RequestProcessProposal) (*abci.ResponseProcessProposal, error)
    func (app *VStoreApplication) Query(_ context.Context, req *abci.RequestQuery) (*abci.ResponseQuery, error)

Package files

crypto.go doc.go opcode.go state.go tx.go tx_body.go vfs.go

Constants

Return codes for vfs application

const (
    CodeTypeOK                    uint32 = 0
    CodeTypeEmptyDataError        uint32 = 1
    CodeTypeInvalidFormatError    uint32 = 2
    CodeTypeInvalidSignatureError uint32 = 3
)
const (
    AppVersion        uint64 = 1
    QueryType_Default string = "hash"
    QueryType_Height  string = "height"
    QueryType_PubKey  string = "pubkey"
)

func ComputeHash

func ComputeHash(p *SignedTransaction) []byte

ComputeHash computes the SHA256 hash of a signed transaction The transaction hash consists of a SHA256 of the signer public key, followed by the data and the attached timestamp bytes.

func Decrypt

func Decrypt(secret []byte, ciphertext []byte) ([]byte, error)

Decrypt decrypts a ciphertext using the secret with the AES block cipher algo.

func Encrypt

func Encrypt(secret []byte, data []byte) ([]byte, error)

Encrypt encrypts a plaintext using the secret with the AES block cipher algo.

func GenerateSecret

func GenerateSecret(pw, salt []byte) ([]byte, []byte, error)

GenerateSecret generates a 32-bytes secret by creating a SHA-256 hash of a salted password using a random salt of 8-bytes. If a non-empty salt is provided, it is expected to be of 8-bytes length. It returns the 32-bytes secret and an 8-bytes salt.

func MustGenerateIdentity

func MustGenerateIdentity(idFile string, pw []byte) (string, string)

MustGenerateIdentity generates a new ed25519 private key and saves it to the provided idFile file. A password pw is used to encrypt the private key. 8 bytes are added in front of the ciphertext which consist of a random salt. The created identity file contains a base64-encoded AES ciphertext prefixed with a random salt of 8 bytes. This function will panic if any errors occur.

func MustGenerateSecret

func MustGenerateSecret(pw, salt []byte) ([]byte, []byte)

MustGenerateSecret generates a 32-bytes secret with salt or panics. This function will panic if any errors occur.

func NewIdentity

func NewIdentity(file string, pw []byte) *identityFile

NewIdentity creates a new identityFile instance

func PubKeyToProto

func PubKeyToProto(pubKey crypto.PubKey) cmtp2p.PublicKey

func SignData

func SignData(priv ed25519.PrivKey, tx Signable) []byte

SignData signs a transaction using a private key.

type IdentitySecretProvider

IdentitySecretProvider describes a provider that returns an AES-256 secret which is used to encrypt the database.

type IdentitySecretProvider interface {
    // Secret returns the 32-bytes secret used for encryption of data.
    // This is the secret used to encrypt the contents of the database.
    Secret() ([]byte, error)

    // PrivKey returns a ed25519 private key instance.
    PrivKey() (ed25519.PrivKey, error)

    // PubKey returns a ed25519 public key from the private key.
    PubKey() (crypto.PubKey, error)
}

type SecretProvider

SecretProvider describes a provider that returns an AES-256 secret which is used to encrypt an ed25519 identity (private key).

type SecretProvider interface {
    // Bytes returns the raw bytes of a secret provider which include the random
    // salt (8 bytes) used for encryption and the private key (64 bytes)
    // Note: This function does not decrypt the AES encrypted private key.
    Bytes() ([]byte, error)

    // Open returns the bytes of the private key (64-bytes)
    // This method shall decrypt the AES encrypted private key.
    Open() ([]byte, error)

    // Secret returns the 32-bytes secret used for encryption of the private key.
    // This is the secret used to encrypt the contents of an identity file.
    Secret() ([]byte, error)

    // Identity returns a ed25519 identity which is used to encrypt the database.
    Identity() IdentitySecretProvider
}

type Signable

Signable describes data that can be signed using a private key.

type Signable interface {
    Sign(ed25519.PrivKey) ([]byte, error)
    Bytes() []byte
}

type SignedTransaction

SignedTransaction describes a signed data object that includes an owner public key, a SHA-256 hash, a size, a signature and a timestamp.

type SignedTransaction struct {
    Signer    ed25519.PubKey
    Hash      []byte
    Signature []byte
    Size      int
    Time      time.Time
    Data      TransactionBody
}

func FromBytes

func FromBytes(bz []byte) (*SignedTransaction, error)

FromBytes takes a bytes slice and returns the SignedTransaction

func FromProto

func FromProto(pb *vfsp2p.Transaction) (*SignedTransaction, error)

FromProto takes a transaction proto message and returns the SignedTransaction.

func NewSignedTransactionFromBytes

func NewSignedTransactionFromBytes(tx []byte) (*SignedTransaction, error)

NewSignedTransaction expects a signed data payload which contains an owner public key (32 bytes), a signature (64 bytes), a timestamp and at least 1 byte of arbitrary data. TODO: TBI verification of timestamp (too far in future, etc.) TODO: TBI when to verify signatures (careful with CheckTx)

func (SignedTransaction) Bytes

func (p SignedTransaction) Bytes() []byte

Bytes returns a byte slice built from the size-prefixed data and the signature.

func (SignedTransaction) PublicKey

func (p SignedTransaction) PublicKey() string

PublicKey returns the uppercase hexadecimal representation of the signer public key.

func (SignedTransaction) ToProto

func (p SignedTransaction) ToProto() *vfsp2p.Transaction

ToProto returns a protobuf transaction object.

func (SignedTransaction) Verify

func (p SignedTransaction) Verify() bool

Verify returns a boolean that determines the validity of a signature.

type State

State describes the vstore application state which consists of a latest blockchain height, a total number of transactions and cryptographic commitments about transaction data (merkle roots).

type State struct {

    // NumTransactions is essentially the total number of transactions processed.
    // This is used for the appHash in combination with the merkle roots
    NumTransactions int64 `json:"num_transactions"`
    Height          int64 `json:"height"`

    // MerkleRoots contains the cryptographic commitments for transactions that
    // have previously been processed.
    // This is used for the appHash.
    MerkleRoots map[string][]byte `json:"merkle_roots"`
    // contains filtered or unexported fields
}

func (State) Hash

func (s State) Hash() []byte

Hash returns the hash of the application state. This is computed as the merkle root of all the committed transaction hashes using a deterministic merkle root slices as produced with MerkleRoots(). The produced hash can be used to verify the integrity of the State. This function is used as the "AppHash"

func (State) SortedMerkleRoots

func (s State) SortedMerkleRoots() [][]byte

MerkleRoots returns a slice of merkle roots that is *deterministic* due to keys always being sorted lexicographically.

type TransactionBody

TransactionBody represents *unsigned* data.

type TransactionBody []byte

func (TransactionBody) Bytes

func (p TransactionBody) Bytes() []byte

Bytes returns a byte representation of unsigned data. Bytes implements Signable

func (TransactionBody) Sign

func (p TransactionBody) Sign(priv ed25519.PrivKey) ([]byte, error)

Sign creates a digital signature of the bytes using the private key implementation for ed25519. Only ed25519 compatibility is added for now because of being able to batch verify ed25519 signatures. Sign implements Signable

type VStoreApplication

VStoreApplication describes the vStore ABCI application.

type VStoreApplication struct {
    abci.BaseApplication
    // contains filtered or unexported fields
}

func NewInMemoryVStoreApplication

func NewInMemoryVStoreApplication(
    id_file string,
    password []byte,
) *VStoreApplication

NewInMemoryApplication creates a new application from an in memory database. NOTE: the data will not be persisted.

func NewVStoreApplication

func NewVStoreApplication(
    db cmtdb.DB,
    id_file string,
    password []byte,
) *VStoreApplication

NewVStoreApplication creates a vfs application using a DB to load the State and an ed25519 identity to encrypt/decrypt database entities.

func (*VStoreApplication) CheckTx

func (app *VStoreApplication) CheckTx(
    _ context.Context,
    check *abci.RequestCheckTx,
) (*abci.ResponseCheckTx, error)

CheckTx handles inbound transactions or in the case of re-CheckTx assesses old transaction validity after a state transition happened. It is preferable to keep the checks as stateless and as quick as possible. For the vfs application, we check that each transaction has a valid tx format: - Must not be empty - Must contain at least the owner pubkey (32 bytes) and a signature (64 bytes) - Must contain at least 1 byte of arbitrary data CheckTx implements abci.Application

func (*VStoreApplication) Commit

func (app *VStoreApplication) Commit(
    _ context.Context,
    commit *abci.RequestCommit,
) (*abci.ResponseCommit, error)

Commit is called after FinalizeBlock and after the CometBFT state updates. The vfs application persists the staged data (from FinalizeBlock) in database in a modified key-value store where the key is the tx hash, and where values describe marshalled protobuf instances of vfsp2p.Transaction. Commit implements abci.Application

func (*VStoreApplication) FinalizeBlock

func (app *VStoreApplication) FinalizeBlock(
    ctx context.Context,
    req *abci.RequestFinalizeBlock,
) (*abci.ResponseFinalizeBlock, error)

FinalizeBlock executes the block against the application state. Transactions are processed one-by-one and are cached in memory. They will be persisted when Commit is called. ConsensusParams are never changed. FinalizeBlock implements abci.Application

func (*VStoreApplication) Info

func (app *VStoreApplication) Info(
    _ context.Context,
    info *abci.RequestInfo,
) (*abci.ResponseInfo, error)

Info returns information about the State of the application. This is used everytime a CometBFT instance begins and forwards its version to the application. Based on this information, CometBFT will ensure synchronicity with the application by potentially replaying some blocks. If the application returns a 0 LastBlockHeight, CometBFT will call InitChain. Info implements abci.Application

func (*VStoreApplication) InitChain

func (app *VStoreApplication) InitChain(
    _ context.Context,
    chain *abci.RequestInitChain,
) (*abci.ResponseInitChain, error)

InitChain returns the application hash in case the application starts with values pre-populated. This method is called whenever a new instance of the application is started, i.e. when LastBlockHeight is 0. InitChain implements abci.Application

func (*VStoreApplication) PrepareProposal

func (app *VStoreApplication) PrepareProposal(
    ctx context.Context,
    proposal *abci.RequestPrepareProposal,
) (*abci.ResponsePrepareProposal, error)

PrepareProposal is called only when the node is a proposer. CometBFT stages a set of transactions for the application. NOTE: we assume that CometBFT won't provide too many transactions for 1 block. PrepareProposal implements abci.Application

func (*VStoreApplication) ProcessProposal

func (app *VStoreApplication) ProcessProposal(
    ctx context.Context,
    proposal *abci.RequestProcessProposal,
) (*abci.ResponseProcessProposal, error)

ProcessProposal is called whenever a node receives a complete proposal and allows the application to validate the proposal. Only validators from the validator set will have this method called. ProcessProposal implements abci.Application

func (*VStoreApplication) Query

func (app *VStoreApplication) Query(
    _ context.Context,
    req *abci.RequestQuery,
) (*abci.ResponseQuery, error)

Query returns an associated value or nil if missing. Expects a transaction hash in the request's Data field. Query implements abci.Application