txhelper

package module
v0.0.0-...-357b404 Latest Latest
Warning

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

Go to latest
Published: Jan 6, 2024 License: MIT Imports: 24 Imported by: 0

README

TxHelper - Blockchain Transaction Layer Simulator

Blockchain transaction models differently impact blockchain consensus. TxHelper provides the functionalities required to simulate these different transaction models for consensus benchmarks easily. Currently, txHelper supports 6 different transaction models with two digital signatures. It also allows changing application-specific data, such as the average payload size and the average number of inputs/outputs per transaction to simulate different applications.

Run tests: go test

Run benchmarks: go test -v -bench=. -run=BenchmarkExeContext_VerifyStoredAllTransactionPeers

What is a blockchain transaction?

Blockchains are live databases with applications. In some cases, the application is a decentralized bank that transfers money between users securely while data blockchains' application may be a decentralized file system. A transaction is a technical term used to represent an individual application update, e.g., money transferring or updating a file(s). A generic transaction contains inputs, outputs, and a transaction header. An input is a piece of application data that already exists, and an output is the application data that will replace those inputs. For example, an input could be an identifier to a blockchain file, and the output could be the updated version of that file. Another example would be that an input user is transferring some coins to an output user. In both cases, the transaction header contains cryptographic data (digital signatures and zero-knowledge proofs) to prove that the application update is correct and authenticated by input owners. However, in some cases, the applications may want accountability, which means the output owners must authorize the transaction, e.g., owners of files or accountable-auditable cash systems where users are only allowed to pay if the receivers authorize them.

Transaction Models

TxHelper supports the following transaction 6 models.

Transaction Model Transaction Type Transaction Header Currently supported Signatures
Classic UTXO (1) Non-Zero-History Signatures of input owners Schnorr, Aggregated BLS
Classic Accounts (2) Non-Zero-History Signature of input owner Schnoor, Aggregated BLS
Classic Accountable UTXO (3) Non-Zero-History Signatures of input + output owners Schnorr, Aggregated BLS
Classic Accountable Accounts (4) Non-Zero-History Signatures of input + output owners Schnorr, Aggregated BLS
Origami UTXO (5) Zero-History Activity-proof, excess and a difference-signature Schnorr, BLS
Origami Accounts (6) Zero-History Activity-proof and signatures of all output owners Schnorr, BLS
Application Simulation

TxHelper simulates a generic application where you can set the average payload (average size of an output/input), and the average number of inputs and outputs per transaction. For example, the payload of a cryptocurrency is 8 bytes plus average contract size (for plain-text coins) or roughly 800 bytes (for confidential coins), while the payload could be higher as 2KB for file systems.

Tradeoffs of Models

The most suitable transaction model for an application depends on the application-specific requirements since each transaction model has some tradeoffs. The simplest example is the difference between the UTXO model and the account model. In the account model, each public key has only one state, and a transaction takes that state and modify into a new state. Hence, the account-based transactions, inputs are the identifiers to the accounts, and outputs contain the new state. Unless an output belongs to a new account, the output does not need to mention the public key, which can be recovered from reading the blockchain via the input identifier. However, this efficiency comes at the cost of additional database queries to make sure that the new public keys were not used before.

In UTXO (Unspent Transaction Outputs) model, each output has a public key associated with them, and multiple outputs may belong to the same public key. Hence, verifiers do not need to run database queries on unique public keys, but there is a tradeoff on transaction size since all new public keys must be stated in the transaction.

In summary, if the users prefer to create a new public key frequently, then the UTXO model is a better option. However, if the users tend to keep the public keys for the long term, then an account model is a better option.

TxHelper helps to identify what happens to blockchain performance with different application requirements and transaction models, so the best transaction model can be selected.

Tradeoffs of Signatures

Digital signatures (or some unforgeable proofs) are required to verify that the owners authorized the transaction. Hence, digital signature has a significant impact on blockchain performance. TxHelper currently supports two digital signatures, Schnorr signatures and aggregated BLS signatures.

Signature Public Key Size Signature Size Public Aggregation Verification Time
Schnorr (1) 32 64 No O(number of public keys)
Aggregated BLS (2) 128 32 Yes O(number of public keys)

Even though BLS signatures are shorter and can be aggregated, they typically take more time for verification. Once the other hand, Schnorr public keys are shorter and take less time to verify but cannot publicly aggregate signatures. Hence, the choice of the signature depends on how frequently the new public keys are created and whether the transaction model is UTXO or account-based.

Tradeoffs of Zero-History and Non-Zero-History

In blockchains, the consensus proofs show the accepted transactions. In non-zero-history blockchains, verifiers cannot verify the consensus proofs without the history of the blockchain, e.g., spent UXTO or an old account state. Hence, a self-verifiable blockchain is catastrophically large, and the startup time can be days. However, in zero-history blockchains, the consensus proof can be verified without the history, and they are immutable (there is only one valid zero-history blockchain per consensus proof set) due to a special proof called activity proof (32 bytes per transaction). Therefore, these blockchains significantly reduce the size and decrease the startup time of new peers.

However, zero-history blockchains only perform better than non-zero-history blockchains if there is an application history. For example, if there are only new accounts but no account is being modified, then zero-history blockchains will be larger than non-zero-history blockchains due to activity proofs and no history to delete. However, in real applications, zero-history blockchains are significantly shorter than non-zero-history blockchains.

Setting up TxHelper

In a blockchain network, clients create and send transactions to peers (miners). First, we need to create two TxHelper contexts for clients and peers. For example,

ctxClient := NewContext(clientId, 1, txModel, SigType, averageSize, totalUsers, averageInputMax, averageOutputMax, distributionType)

ctxPeer := NewContext(peerId, 2, txModel, SigType, averageSize, totalUsers, averageInputMax, averageOutputMax, distributionType)

Note that these peer contexts should be included with your consensus peers/nodes.

Here, clientId and peerId is used to identify each context separately. For clients, uType = 1 and for peers, uType = 2. TxHelper supports two signatures, Schnorr signatures (sigType = 1) and BLS signatures (sigType = 2) on elliptic curves. Note that both client(s) and peer(s) must have the same variables. We will explain other variables in the next section.

Random Transaction Generation

Once we have the contexts, we can create a sequence of random transactions to create new UTXO/accounts and update existing UTXO and accounts.

var tx *Transaction
tx = ctxClient.RandomTransaction()

Here, TxHelper randomly chooses the input size from [0, averageInputMax] and the output size from [0, averageOutputMax]. If there are not enough inputs, the input size will be updated, e.g., the first transaction will always have zero inputs. Each transaction output will contain an average size a number of random bytes as the simulated payload of the application. Note that we can limit the total unique public keys in the system via totalUsers, except for Origami UTXO. If current accounts or unique public keys have exceeded, transactions will not create any new accounts or users with new public keys. In account-based transactions, the input size will be equal to the output size in that case.

Saving and Verification

Once the transaction is created, we can get bytes of the transaction to send the peers. Also, peers can convert bytes into a transaction after receiving them (note that TxHelper does not provide network functionalities). For example,

txBytes = ctxClient.ToBytes(tx)  // to send

var tx1 Transaction
ctxPeer.FromBytes(txBytes, &tx1) // after receiving

Then peers verify the transactions and add them to the blockchain. In consensus testing, the peers propose blocks with verified transactions. Once the block passes the consensus phase, all transactions will be inserted into the blockchain.

val, err = ctxPeer.VerifyIncomingTransaction(&tx1) // Peer verify the transactions before sending
if val == true {
    ctxPeer.UpdateAppDataPeer(i, &tx1)
    ctxPeer.InsertTxHeader(i, &tx1)
}

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type AppData

type AppData struct {
	Inputs  []InputData  `json:"i"` // inputs as a byte array
	Outputs []OutputData `json:"o"` // outputs as a byte array
}

type ExeContext

type ExeContext struct {
	AverageInputMax  uint8 // average number of inputs will be in [0, AverageInputMax]
	AverageOutputMax uint8 // average number of outputs  will be in [0, AverageOutputMax]

	PublicKeyReuse int // (UTXO models) when a new output is created whether to reuse the input public key or
	// not will be decided from this such that probability of reuse = 1/publicKeyReuse
	TotalUsers     int // (ACC models) total number of users represented if this is a client
	TotalTx        int // total number of transactions if this is a peer
	TotalBlock     int // total number of blocks  if this is a peer
	TotalTempUsers int // maximum number of temp users

	TempUsers map[[sha256.Size]byte]TempUser
	TempPKs   map[[128]byte]int
	TempTxH   map[int][]byte // only used for origami accounts

	CurrentUsers           int
	CurrentUsersWithTemp   int
	CurrentOutputs         int // in Origami, CurrentUsers = CurrentOutputs
	CurrentOutputsWithTemp int // in Origami, CurrentUsers = CurrentOutputs
	DeletedOutputs         int //
	// contains filtered or unexported fields
}

func NewContext

func NewContext(exeId int, uType int, txType int, sigType int32, averageSize uint16, totalUsers int,
	averageInputMax uint8, averageOutputMax uint8, distributionType int, enableIndexing bool, publicKeyReuse int) ExeContext

func (*ExeContext) CreateTxHeader

func (ctx *ExeContext) CreateTxHeader(txh *TxHeader, data *AppData)

CreateTxHeader creates a transaction header

func (*ExeContext) FixedTransaction

func (ctx *ExeContext) FixedTransaction(inSize uint8, outSize uint8) *Transaction

func (*ExeContext) FromBytes

func (ctx *ExeContext) FromBytes(arr []byte, tx *Transaction) bool

func (*ExeContext) GetTxHeaderIdentifier

func (ctx *ExeContext) GetTxHeaderIdentifier(tx *Transaction, txBytes []byte) (bool, []byte, *string)

GetTxHeaderIdentifier outputs an identifier a special hash to be included into the tx block hash computation for classics: hash is the hash of the entire transaction for origami: hash is the hash of (activity, all account pks)

func (*ExeContext) InsertTxHeader

func (ctx *ExeContext) InsertTxHeader(txn int, tx *Transaction) (bool, *string)

InsertTxHeader adds a transaction header, which was verified before.

func (*ExeContext) ModDiv

func (ctx *ExeContext) ModDiv(a []byte, b []byte) (c []byte)

ModDiv h0 = (h0 * h1^{-1}) % q

func (*ExeContext) ModMul

func (ctx *ExeContext) ModMul(a []byte, b []byte) (c []byte)

ModMul h0 = (h0 * h1) % q

func (*ExeContext) PrepareAppDataClient

func (ctx *ExeContext) PrepareAppDataClient(data *AppData) (bool, error)

PrepareAppDataClient get user details for inputs using the header

func (*ExeContext) PrepareAppDataPeer

func (ctx *ExeContext) PrepareAppDataPeer(data *AppData) (bool, error)

PrepareAppDataPeer get output details for inputs using the header

func (*ExeContext) PrepareAppDataPeerWithTemps

func (ctx *ExeContext) PrepareAppDataPeerWithTemps(data *AppData) (bool, error)

PrepareAppDataPeerWithTemps get output details for inputs using the header. Note that it also checks temporary users

func (*ExeContext) PrintDetails

func (ctx *ExeContext) PrintDetails()

func (*ExeContext) RandomAppData

func (ctx *ExeContext) RandomAppData(data *AppData, inSize uint8, outSize uint8, averageSize uint16)

RandomAppData creates an application data change for randomly chosen users

func (*ExeContext) RandomTransaction

func (ctx *ExeContext) RandomTransaction() *Transaction

RandomTransaction outputs a transaction according to the sigType and txModel txModel:1 - classicUTXO txModel:2 - classicACC txModel:3 - classicAUTXO txModel:4 - classicAACC txModel:5 - origamiUTXO txModel:6 - origamiACC

func (*ExeContext) ToBytes

func (ctx *ExeContext) ToBytes(tx *Transaction) []byte

func (*ExeContext) UpdateAppDataClient

func (ctx *ExeContext) UpdateAppDataClient(data *AppData) (bool, error)

UpdateAppDataClient update user details for new app data changes

func (*ExeContext) UpdateAppDataPeer

func (ctx *ExeContext) UpdateAppDataPeer(txNum int, tx *Transaction) (bool, *string)

UpdateAppDataPeer update output details for new app data changes

func (*ExeContext) UpdateAppDataPeerToTemp

func (ctx *ExeContext) UpdateAppDataPeerToTemp(txNum int, tx *Transaction) (bool, *string)

UpdateAppDataPeerToTemp update output details for new app data changes

func (*ExeContext) VerifyIncomingTransaction

func (ctx *ExeContext) VerifyIncomingTransaction(tx *Transaction) (bool, *string)

VerifyIncomingTransaction verifies a raw transaction

func (*ExeContext) VerifyIncomingTransactionWithTemp

func (ctx *ExeContext) VerifyIncomingTransactionWithTemp(tx *Transaction) (bool, *string)

VerifyIncomingTransactionWithTemp verifies a raw transaction including temps

func (*ExeContext) VerifyStoredAllTransaction

func (ctx *ExeContext) VerifyStoredAllTransaction() (bool, *string)

VerifyStoredAllTransaction verifies all stored transactions

func (*ExeContext) VerifyTxHeader

func (ctx *ExeContext) VerifyTxHeader(txh *TxHeader, data *AppData) (bool, *string)

VerifyTxHeader verifies a transaction header

type Generator

type Generator interface {
	NewKey(random cipher.Stream) kyber.Scalar
}

type InputData

type InputData struct {
	Header []byte `json:"h"` // identifier like a hash
	// contains filtered or unexported fields
}

type OutputData

type OutputData struct {
	Pk   []byte `json:"p"` // public key
	N    uint8  `json:"n"`
	Data []byte `json:"d"` // new application data
	// contains filtered or unexported fields
}

type Pubkey

type Pubkey struct {
	// contains filtered or unexported fields
}

type SigKeyPair

type SigKeyPair struct {
	Pk kyber.Point  `json:"p"`
	Sk kyber.Scalar `json:"s"`
}

type Signature

type Signature []byte

type SignatureContext

type SignatureContext struct {
	SigType int32 // signature module

	SkSize  int32
	PkSize  int32
	SigSize int32
	// contains filtered or unexported fields
}

func NewSigContext

func NewSigContext(sigType int32) *SignatureContext

NewSigContext assigns ctx objects 1 - Schnorr 2 - BLS

type Suite

type Suite interface {
	kyber.Group
	kyber.Random
}

type TempUser

type TempUser struct {
	// contains filtered or unexported fields
}

type Transaction

type Transaction struct {
	N    int32    `json:"n"` // transaction index
	Txh  TxHeader `json:"t"` // transaction header
	Data AppData  `json:"d"` // application Data
}

type TxHeader

type TxHeader struct {
	Kyber []Signature `json:"k"` // signatures
	// contains filtered or unexported fields
}

type User

type User struct {
	H      []byte `json:"H"`      // hash
	N      uint8  `json:"N"`      // number of outputs created by the user
	Keys   []byte `json:"Keys"`   // pk with/out sk
	Data   []byte `json:"Data"`   // most recent application Data
	UDelta []byte `json:"UDelta"` // could be empty
	Txns   []int  `json:"txns"`   // for origami-header identifier
	// contains filtered or unexported fields
}

Jump to

Keyboard shortcuts

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