chaingen

package
v8.0.12 Latest Latest
Warning

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

Go to latest
Published: Jun 27, 2022 License: ISC Imports: 13 Imported by: 0

README

chaingen

Build Status ISC License GoDoc

Package chaingen provides facilities for generating a full chain of blocks.

Overview

Many consensus-related tests require a full chain of valid blocks with several pieces of contextual information such as versions and votes. Generating such a chain is not a trivial task due to things such as the fact that tickets must be purchased (at the correct ticket price), the appropriate winning votes must be cast (which implies keeping track of all live tickets and implementing the lottery selection algorithm), and all of the state-specific header fields such as the pool size and the proof-of-work and proof-of-stake difficulties must be set properly.

In order to simplify this complex process, this package provides a generator that keeps track of all of the necessary state and generates and solves blocks accordingly while allowing the caller to manipulate the blocks via munge functions.

Examples

Installation

$ go get -u github.com/Decred-Next/dcrnd/blockchain/chaingen

License

Package chaingen is licensed under the copyfree ISC License.

Documentation

Overview

Package chaingen provides facilities for generating a full chain of blocks.

Overview

Many consensus-related tests require a full chain of valid blocks with several pieces of contextual information such as versions and votes. Generating such a chain is not a trivial task due to things such as the fact that tickets must be purchased (at the correct ticket price), the appropriate winning votes must be cast (which implies keeping track of all live tickets and implementing the lottery selection algorithm), and all of the state-specific header fields such as the pool size and the proof-of-work and proof-of-stake difficulties must be set properly.

In order to simplify this complex process, this package provides a generator that keeps track of all of the necessary state and generates and solves blocks accordingly while allowing the caller to manipulate the blocks via munge functions.

Example (BasicUsage)

This example demonstrates creating a new generator instance and using it to generate the required first block and enough blocks to have mature coinbase outputs to work with along with asserting the generator state along the way.

params := chaincfg.RegNetParams()
g, err := chaingen.MakeGenerator(params)
if err != nil {
	fmt.Println(err)
	return
}

// Shorter versions of useful params for convenience.
coinbaseMaturity := params.CoinbaseMaturity

// ---------------------------------------------------------------------
// First block.
// ---------------------------------------------------------------------

// Add the required first block.
//
//   genesis -> bfb
g.CreateBlockOne("bfb", 0)
g.AssertTipHeight(1)
fmt.Println(g.TipName())

// ---------------------------------------------------------------------
// Generate enough blocks to have mature coinbase outputs to work with.
//
//   genesis -> bp -> bm0 -> bm1 -> ... -> bm#
// ---------------------------------------------------------------------

for i := uint16(0); i < coinbaseMaturity; i++ {
	blockName := fmt.Sprintf("bm%d", i)
	g.NextBlock(blockName, nil, nil)
	g.SaveTipCoinbaseOuts()
	fmt.Println(g.TipName())
}
g.AssertTipHeight(uint32(coinbaseMaturity) + 1)
Output:

bfb
bm0
bm1
bm2
bm3
bm4
bm5
bm6
bm7
bm8
bm9
bm10
bm11
bm12
bm13
bm14
bm15

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func IsSolved

func IsSolved(header *wire.BlockHeader) bool

IsSolved returns whether or not the header hashes to a value that is less than or equal to the target difficulty as specified by its bits field.

func PurchaseCommitmentScript

func PurchaseCommitmentScript(addr dcrutil.Address, amount, voteFeeLimit, revocationFeeLimit dcrutil.Amount) []byte

PurchaseCommitmentScript returns a standard provably-pruneable OP_RETURN commitment script suitable for use in a ticket purchase tx (sstx) using the provided target address, amount, and fee limits.

func ReplaceBlockVersion

func ReplaceBlockVersion(newVersion int32) func(*wire.MsgBlock)

ReplaceBlockVersion returns a function that itself takes a block and modifies it by replacing the stake version of the header.

func ReplaceStakeVersion

func ReplaceStakeVersion(newVersion uint32) func(*wire.MsgBlock)

ReplaceStakeVersion returns a function that itself takes a block and modifies it by replacing the stake version of the header.

func ReplaceVoteVersions

func ReplaceVoteVersions(newVersion uint32) func(*wire.MsgBlock)

ReplaceVoteVersions returns a function that itself takes a block and modifies it by replacing the voter version of the stake transactions.

NOTE: This must only be used as a munger to the 'NextBlock' function or it will lead to an invalid live ticket pool.

func ReplaceVotes

func ReplaceVotes(voteBits uint16, newVersion uint32) func(*wire.MsgBlock)

ReplaceVotes returns a function that itself takes a block and modifies it by replacing the voter version and bits of the stake transactions.

NOTE: This must only be used as a munger to the 'NextBlock' function or it will lead to an invalid live ticket pool.

func UniqueOpReturnScript

func UniqueOpReturnScript() []byte

UniqueOpReturnScript returns a standard provably-pruneable OP_RETURN script with a random uint64 encoded as the data.

func VoteCommitmentScript

func VoteCommitmentScript(hash chainhash.Hash, height uint32) []byte

VoteCommitmentScript returns a standard provably-pruneable OP_RETURN script suitable for use in a vote tx (ssgen) given the block hash and height to vote on.

Types

type Generator

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

Generator houses state used to ease the process of generating test blocks that build from one another along with housing other useful things such as available spendable outputs and generic payment scripts used throughout the tests.

func MakeGenerator

func MakeGenerator(params *chaincfg.Params) (Generator, error)

MakeGenerator returns a generator instance initialized with the genesis block as the tip as well as a cached generic pay-to-script-hash script for OP_TRUE.

func (*Generator) AssertBlockRevocationTx

func (g *Generator) AssertBlockRevocationTx(b *wire.MsgBlock, txIndex uint32)

AssertBlockRevocationTx panics if the current tip block associated with the generator does not have a revocation at the specified transaction index provided.

func (*Generator) AssertBlockVersion

func (g *Generator) AssertBlockVersion(expected int32)

AssertBlockVersion panics if the current tip block associated with the generator does not have the specified block version in the header.

func (*Generator) AssertPoolSize

func (g *Generator) AssertPoolSize(expected uint32)

AssertPoolSize panics if the current tip block associated with the generator does not indicate the specified pool size.

func (*Generator) AssertScriptSigOpsCount

func (g *Generator) AssertScriptSigOpsCount(script []byte, expected int)

AssertScriptSigOpsCount panics if the provided script does not have the specified number of signature operations.

func (*Generator) AssertStakeVersion

func (g *Generator) AssertStakeVersion(expected uint32)

AssertStakeVersion panics if the current tip block associated with the generator does not have the specified stake version in the header.

func (*Generator) AssertTipBlockHash

func (g *Generator) AssertTipBlockHash(expected chainhash.Hash)

AssertTipBlockHash panics if the current tip block associated with the generator does not match the specified hash.

func (*Generator) AssertTipBlockMerkleRoot

func (g *Generator) AssertTipBlockMerkleRoot(expected chainhash.Hash)

AssertTipBlockMerkleRoot panics if the merkle root in header of the current tip block associated with the generator does not match the specified hash.

func (*Generator) AssertTipBlockNumTxns

func (g *Generator) AssertTipBlockNumTxns(expected int)

AssertTipBlockNumTxns panics if the number of transactions in the current tip block associated with the generator does not match the specified value.

func (*Generator) AssertTipBlockSigOpsCount

func (g *Generator) AssertTipBlockSigOpsCount(expected int)

AssertTipBlockSigOpsCount panics if the current tip block associated with the generator does not have the specified number of signature operations.

func (*Generator) AssertTipBlockSize

func (g *Generator) AssertTipBlockSize(expected int)

AssertTipBlockSize panics if the current tip block associated with the generator does not have the specified size when serialized.

func (*Generator) AssertTipBlockStakeRoot

func (g *Generator) AssertTipBlockStakeRoot(expected chainhash.Hash)

AssertTipBlockStakeRoot panics if the stake root in header of the current tip block associated with the generator does not match the specified hash.

func (*Generator) AssertTipBlockTxOutOpReturn

func (g *Generator) AssertTipBlockTxOutOpReturn(txIndex, txOutIndex uint32)

AssertTipBlockTxOutOpReturn panics if the current tip block associated with the generator does not have an OP_RETURN script for the transaction output at the provided tx index and output index.

func (*Generator) AssertTipDisapprovesPrevious

func (g *Generator) AssertTipDisapprovesPrevious()

AssertTipDisapprovesPrevious panics if the current tip block associated with the generator does not disapprove the previous block.

func (*Generator) AssertTipHeight

func (g *Generator) AssertTipHeight(expected uint32)

AssertTipHeight panics if the current tip block associated with the generator does not have the specified height.

func (*Generator) AssertTipNumRevocations

func (g *Generator) AssertTipNumRevocations(expected uint8)

AssertTipNumRevocations panics if the number of revocations in header of the current tip block associated with the generator does not match the specified value.

func (*Generator) BlockByHash

func (g *Generator) BlockByHash(hash *chainhash.Hash) *wire.MsgBlock

BlockByHash returns the block associated with the provided block hash. It will panic if the specified block hash does not exist.

func (*Generator) BlockByName

func (g *Generator) BlockByName(blockName string) *wire.MsgBlock

BlockByName returns the block associated with the provided block name. It will panic if the specified block name does not exist.

func (*Generator) CalcNextReqStakeDifficulty

func (g *Generator) CalcNextReqStakeDifficulty(prevBlock *wire.MsgBlock) int64

CalcNextReqStakeDifficulty returns the required stake difficulty (aka ticket price) for the block after the provided block the generator is associated with.

See the documentation of CalcNextRequiredStakeDifficulty for more details.

func (*Generator) CalcNextRequiredDifficulty

func (g *Generator) CalcNextRequiredDifficulty() uint32

CalcNextRequiredDifficulty returns the required proof-of-work difficulty for the block after the current tip block the generator is associated with.

An overview of the algorithm is as follows:

  1. Use the proof-of-work limit for all blocks before the first retarget window
  2. Use the previous block's difficulty if the next block is not at a retarget interval
  3. Calculate the ideal retarget difficulty for each window based on the actual timespan of the window versus the target timespan and exponentially weight each difficulty such that the most recent window has the highest weight
  4. Calculate the final retarget difficulty based on the exponential weighted average and ensure it is limited to the max retarget adjustment factor

func (*Generator) CalcNextRequiredStakeDifficulty

func (g *Generator) CalcNextRequiredStakeDifficulty() int64

CalcNextRequiredStakeDifficulty returns the required stake difficulty (aka ticket price) for the block after the current tip block the generator is associated with.

An overview of the algorithm is as follows:

  1. Use the minimum value for any blocks before any tickets could have possibly been purchased due to coinbase maturity requirements
  2. Return 0 if the current tip block stake difficulty is 0. This is a safety check against a condition that should never actually happen.
  3. Use the previous block's difficulty if the next block is not at a retarget interval
  4. Calculate the ideal retarget difficulty for each window based on the actual pool size in the window versus the target pool size skewed by a constant factor to weight the ticket pool size instead of the tickets per block and exponentially weight each difficulty such that the most recent window has the highest weight
  5. Calculate the pool size retarget difficulty based on the exponential weighted average and ensure it is limited to the max retarget adjustment factor -- This is the first metric used to calculate the final difficulty
  6. Calculate the ideal retarget difficulty for each window based on the actual new tickets in the window versus the target new tickets per window and exponentially weight each difficulty such that the most recent window has the highest weight
  7. Calculate the tickets per window retarget difficulty based on the exponential weighted average and ensure it is limited to the max retarget adjustment factor
  8. Calculate the final difficulty by averaging the pool size retarget difficulty from #5 and the tickets per window retarget difficulty from #7 using scaled multiplication and ensure it is limited to the max retarget adjustment factor

NOTE: In order to simplify the test code, this implementation does not use big integers so it will NOT match the actual consensus code for really big numbers. However, the parameters on simnet and the pool sizes used in these tests are low enough that this is not an issue for the tests. Anyone looking at this code should NOT use it for mainnet calculations as is since it will not always yield the correct results.

func (*Generator) CreateBlockOne

func (g *Generator) CreateBlockOne(blockName string, additionalAmount dcrutil.Amount, mungers ...func(*wire.MsgBlock)) *wire.MsgBlock

CreateBlockOne generates the first block of the chain with the required payouts. The additional amount parameter can be used to create a block that is otherwise a completely valid block one except it adds the extra amount to each payout and thus create a block that violates consensus.

func (*Generator) CreateCoinbaseTx

func (g *Generator) CreateCoinbaseTx(blockHeight uint32, numVotes uint16) *wire.MsgTx

CreateCoinbaseTx returns a coinbase transaction paying an appropriate subsidy based on the passed block height and number of votes to the dev org and proof-of-work miner.

See the addCoinbaseTxOutputs documentation for a breakdown of the outputs the transaction contains.

func (*Generator) CreateRevocationTx

func (g *Generator) CreateRevocationTx(ticketTx *wire.MsgTx, ticketBlockHeight, ticketBlockIndex uint32) *wire.MsgTx

CreateRevocationTx returns a new transaction (ssrtx) refunding the ticket price for a ticket which either missed its vote or expired.

The transaction consists of the following inputs: - The outpoint of the ticket that was missed or expired.

The transaction consists of the following outputs: - The payouts according to the ticket commitments.

func (*Generator) CreateSpendTx

func (g *Generator) CreateSpendTx(spend *SpendableOut, fee dcrutil.Amount) *wire.MsgTx

CreateSpendTx creates a transaction that spends from the provided spendable output and includes an additional unique OP_RETURN output to ensure the transaction ends up with a unique hash. The public key script is a simple OP_TRUE p2sh script which avoids the need to track addresses and signature scripts in the tests. The signature script is the opTrueRedeemScript.

func (*Generator) CreateSpendTxForTx

func (g *Generator) CreateSpendTxForTx(tx *wire.MsgTx, blockHeight, txIndex uint32, fee dcrutil.Amount) *wire.MsgTx

CreateSpendTxForTx creates a transaction that spends from the first output of the provided transaction and includes an additional unique OP_RETURN output to ensure the transaction ends up with a unique hash. The public key script is a simple OP_TRUE p2sh script which avoids the need to track addresses and signature scripts in the tests. This signature script the opTrueRedeemScript.

func (*Generator) CreateTicketPurchaseTx

func (g *Generator) CreateTicketPurchaseTx(spend *SpendableOut, ticketPrice, fee dcrutil.Amount) *wire.MsgTx

CreateTicketPurchaseTx creates a new transaction that spends the provided output to purchase a stake submission ticket (sstx) at the given ticket price. Both the ticket and the change will go to a p2sh script that is composed with a single OP_TRUE.

The transaction consists of the following outputs: - First output is an OP_SSTX followed by the OP_TRUE p2sh script hash - Second output is an OP_RETURN followed by the commitment script - Third output is an OP_SSTXCHANGE followed by the OP_TRUE p2sh script hash

func (*Generator) CreateVoteTx

func (g *Generator) CreateVoteTx(voteBlock *wire.MsgBlock, ticketTx *wire.MsgTx, ticketBlockHeight, ticketBlockIndex uint32) *wire.MsgTx

CreateVoteTx returns a new transaction (ssgen) paying an appropriate subsidy for the given block height (and the number of votes per block) as well as the original commitments.

The transaction consists of the following outputs:

  • First output is an OP_RETURN followed by the block hash and height
  • Second output is an OP_RETURN followed by the vote bits
  • Third and subsequent outputs are the payouts according to the ticket commitments and the appropriate proportion of the vote subsidy.

func (*Generator) NextBlock

func (g *Generator) NextBlock(blockName string, spend *SpendableOut, ticketSpends []SpendableOut, mungers ...func(*wire.MsgBlock)) *wire.MsgBlock

NextBlock builds a new block that extends the current tip associated with the generator and updates the generator's tip to the newly generated block.

The block will include the following: - A coinbase with the following outputs:

  • One that pays the required 10% subsidy to the dev org
  • One that contains a standard coinbase OP_RETURN script
  • Six that pay the required 60% subsidy to an OP_TRUE p2sh script

- When a spendable output is provided:

  • A transaction that spends from the provided output the following outputs:
  • One that pays the inputs amount minus 1 atom to an OP_TRUE p2sh script

- Once the coinbase maturity has been reached:

  • A ticket purchase transaction (sstx) for each provided ticket spendable output with the following outputs:
  • One OP_SSTX output that grants voting rights to an OP_TRUE p2sh script
  • One OP_RETURN output that contains the required commitment and pays the subsidy to an OP_TRUE p2sh script
  • One OP_SSTXCHANGE output that sends change to an OP_TRUE p2sh script

- Once the stake validation height has been reached:

  • 5 vote transactions (ssgen) as required according to the live ticket pool and vote selection rules with the following outputs:
  • One OP_RETURN followed by the block hash and height being voted on
  • One OP_RETURN followed by the vote bits
  • One or more OP_SSGEN outputs with the payouts according to the original ticket commitments
  • Revocation transactions (ssrtx) as required according to any missed votes with the following outputs:
  • One or more OP_SSRTX outputs with the payouts according to the original ticket commitments

Additionally, if one or more munge functions are specified, they will be invoked with the block prior to solving it. This provides callers with the opportunity to modify the block which is especially useful for testing.

In order to simply the logic in the munge functions, the following rules are applied after all munge functions have been invoked:

  • All votes will have their commitments updated if the previous hash or height was manually changed after stake validation height has been reached
  • The merkle root will be recalculated unless it was manually changed
  • The stake root will be recalculated unless it was manually changed
  • The size of the block will be recalculated unless it was manually changed
  • The block will be solved unless the nonce was changed

func (*Generator) NumSpendableCoinbaseOuts

func (g *Generator) NumSpendableCoinbaseOuts() int

NumSpendableCoinbaseOuts returns the number of proof-of-work outputs that were previously saved to the generated but have not yet been collected.

func (*Generator) OldestCoinbaseOuts

func (g *Generator) OldestCoinbaseOuts() []SpendableOut

OldestCoinbaseOuts removes the oldest set of coinbase proof-of-work outputs that was previously saved to the generator and returns the set as a slice.

func (*Generator) P2shOpTrueAddr

func (g *Generator) P2shOpTrueAddr() dcrutil.Address

P2shOpTrueAddr returns the generator p2sh script that is composed with a single OP_TRUE.

func (*Generator) Params

func (g *Generator) Params() *chaincfg.Params

Params returns the chain params associated with the generator instance.

func (*Generator) ReplaceVoteBitsN

func (g *Generator) ReplaceVoteBitsN(voteNum int, voteBits uint16) func(*wire.MsgBlock)

ReplaceVoteBitsN returns a function that itself takes a block and modifies it by replacing the vote bits of the vote located at the provided index. It will panic if the stake transaction at the provided index is not already a vote.

NOTE: This must only be used as a munger to the 'NextBlock' function or it will lead to an invalid live ticket pool.

func (*Generator) ReplaceWithNVotes

func (g *Generator) ReplaceWithNVotes(numVotes uint16) func(*wire.MsgBlock)

ReplaceWithNVotes returns a function that itself takes a block and modifies it by replacing the votes in the stake tree with specified number of votes.

NOTE: This must only be used as a munger to the 'NextBlock' function or it will lead to an invalid live ticket pool. To help safeguard against improper usage, it will panic if called with a block that does not connect to the current tip block.

func (*Generator) SaveSpendableCoinbaseOuts

func (g *Generator) SaveSpendableCoinbaseOuts()

SaveSpendableCoinbaseOuts adds all proof-of-work coinbase outputs starting from the block after the last block that had its coinbase outputs collected and ending at the current tip. This is useful to batch the collection of the outputs once the tests reach a stable point so they don't have to manually add them for the right tests which will ultimately end up being the best chain.

func (*Generator) SaveTipCoinbaseOuts

func (g *Generator) SaveTipCoinbaseOuts()

SaveTipCoinbaseOuts adds the proof-of-work outputs of the coinbase tx in the current tip block to the list of spendable outputs.

func (*Generator) SetTip

func (g *Generator) SetTip(blockName string)

SetTip changes the tip of the instance to the block with the provided name. This is useful since the tip is used for things such as generating subsequent blocks.

func (*Generator) Tip

func (g *Generator) Tip() *wire.MsgBlock

Tip returns the current tip block of the generator instance.

func (*Generator) TipName

func (g *Generator) TipName() string

TipName returns the name of the current tip block of the generator instance.

func (*Generator) UpdateBlockState

func (g *Generator) UpdateBlockState(oldBlockName string, oldBlockHash chainhash.Hash, newBlockName string, newBlock *wire.MsgBlock)

UpdateBlockState manually updates the generator state to remove all internal map references to a block via its old hash and insert new ones for the new block hash. This is useful if the test code has to manually change a block after 'NextBlock' has returned.

type SpendableOut

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

SpendableOut represents a transaction output that is spendable along with additional metadata such as the block its in and how much it pays.

func MakeSpendableOut

func MakeSpendableOut(block *wire.MsgBlock, txIndex, txOutIndex uint32) SpendableOut

MakeSpendableOut returns a spendable output for the given block, transaction index within the block, and transaction output index within the transaction.

func MakeSpendableOutForSTx

func MakeSpendableOutForSTx(tx *wire.MsgTx, blockHeight, txIndex, txOutIndex uint32) SpendableOut

MakeSpendableOutForSTx returns a spendable output for a stake transaction.

func MakeSpendableOutForTx

func MakeSpendableOutForTx(tx *wire.MsgTx, blockHeight, txIndex, txOutIndex uint32) SpendableOut

MakeSpendableOutForTx returns a spendable output for a regular transaction.

func MakeSpendableStakeOut

func MakeSpendableStakeOut(block *wire.MsgBlock, txIndex, txOutIndex uint32) SpendableOut

MakeSpendableStakeOut returns a spendable stake output for the given block, transaction index within the block, and transaction output index within the transaction.

func (*SpendableOut) Amount

func (s *SpendableOut) Amount() dcrutil.Amount

Amount returns the amount associated with the spendable output.

func (*SpendableOut) BlockHeight

func (s *SpendableOut) BlockHeight() uint32

BlockHeight returns the block height of the block the spendable output is in.

func (*SpendableOut) BlockIndex

func (s *SpendableOut) BlockIndex() uint32

BlockIndex returns the offset into the block the spendable output is in.

func (*SpendableOut) PrevOut

func (s *SpendableOut) PrevOut() wire.OutPoint

PrevOut returns the outpoint associated with the spendable output.

Jump to

Keyboard shortcuts

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