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 ¶
- func IsSolved(header *wire.BlockHeader) bool
- func PurchaseCommitmentScript(addr dcrutil.Address, amount, voteFeeLimit, revocationFeeLimit dcrutil.Amount) []byte
- func ReplaceBlockVersion(newVersion int32) func(*wire.MsgBlock)
- func ReplaceStakeVersion(newVersion uint32) func(*wire.MsgBlock)
- func ReplaceVoteVersions(newVersion uint32) func(*wire.MsgBlock)
- func ReplaceVotes(voteBits uint16, newVersion uint32) func(*wire.MsgBlock)
- func UniqueOpReturnScript() []byte
- func VoteCommitmentScript(hash chainhash.Hash, height uint32) []byte
- type Generator
- func (g *Generator) AssertBlockRevocationTx(b *wire.MsgBlock, txIndex uint32)
- func (g *Generator) AssertBlockVersion(expected int32)
- func (g *Generator) AssertPoolSize(expected uint32)
- func (g *Generator) AssertScriptSigOpsCount(script []byte, expected int)
- func (g *Generator) AssertStakeVersion(expected uint32)
- func (g *Generator) AssertTipBlockHash(expected chainhash.Hash)
- func (g *Generator) AssertTipBlockMerkleRoot(expected chainhash.Hash)
- func (g *Generator) AssertTipBlockNumTxns(expected int)
- func (g *Generator) AssertTipBlockSigOpsCount(expected int)
- func (g *Generator) AssertTipBlockSize(expected int)
- func (g *Generator) AssertTipBlockStakeRoot(expected chainhash.Hash)
- func (g *Generator) AssertTipBlockTxOutOpReturn(txIndex, txOutIndex uint32)
- func (g *Generator) AssertTipDisapprovesPrevious()
- func (g *Generator) AssertTipHeight(expected uint32)
- func (g *Generator) AssertTipNumRevocations(expected uint8)
- func (g *Generator) BlockByHash(hash *chainhash.Hash) *wire.MsgBlock
- func (g *Generator) BlockByName(blockName string) *wire.MsgBlock
- func (g *Generator) CalcNextReqStakeDifficulty(prevBlock *wire.MsgBlock) int64
- func (g *Generator) CalcNextRequiredDifficulty() uint32
- func (g *Generator) CalcNextRequiredStakeDifficulty() int64
- func (g *Generator) CreateBlockOne(blockName string, additionalAmount dcrutil.Amount, ...) *wire.MsgBlock
- func (g *Generator) CreateCoinbaseTx(blockHeight uint32, numVotes uint16) *wire.MsgTx
- func (g *Generator) CreateRevocationTx(ticketTx *wire.MsgTx, ticketBlockHeight, ticketBlockIndex uint32) *wire.MsgTx
- func (g *Generator) CreateSpendTx(spend *SpendableOut, fee dcrutil.Amount) *wire.MsgTx
- func (g *Generator) CreateSpendTxForTx(tx *wire.MsgTx, blockHeight, txIndex uint32, fee dcrutil.Amount) *wire.MsgTx
- func (g *Generator) CreateTicketPurchaseTx(spend *SpendableOut, ticketPrice, fee dcrutil.Amount) *wire.MsgTx
- func (g *Generator) CreateVoteTx(voteBlock *wire.MsgBlock, ticketTx *wire.MsgTx, ...) *wire.MsgTx
- func (g *Generator) NextBlock(blockName string, spend *SpendableOut, ticketSpends []SpendableOut, ...) *wire.MsgBlock
- func (g *Generator) NumSpendableCoinbaseOuts() int
- func (g *Generator) OldestCoinbaseOuts() []SpendableOut
- func (g *Generator) P2shOpTrueAddr() dcrutil.Address
- func (g *Generator) Params() *chaincfg.Params
- func (g *Generator) ReplaceVoteBitsN(voteNum int, voteBits uint16) func(*wire.MsgBlock)
- func (g *Generator) ReplaceWithNVotes(numVotes uint16) func(*wire.MsgBlock)
- func (g *Generator) SaveSpendableCoinbaseOuts()
- func (g *Generator) SaveTipCoinbaseOuts()
- func (g *Generator) SetTip(blockName string)
- func (g *Generator) Tip() *wire.MsgBlock
- func (g *Generator) TipName() string
- func (g *Generator) UpdateBlockState(oldBlockName string, oldBlockHash chainhash.Hash, newBlockName string, ...)
- type SpendableOut
- func MakeSpendableOut(block *wire.MsgBlock, txIndex, txOutIndex uint32) SpendableOut
- func MakeSpendableOutForSTx(tx *wire.MsgTx, blockHeight, txIndex, txOutIndex uint32) SpendableOut
- func MakeSpendableOutForTx(tx *wire.MsgTx, blockHeight, txIndex, txOutIndex uint32) SpendableOut
- func MakeSpendableStakeOut(block *wire.MsgBlock, txIndex, txOutIndex uint32) SpendableOut
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
AssertBlockVersion panics if the current tip block associated with the generator does not have the specified block version in the header.
func (*Generator) AssertPoolSize ¶
AssertPoolSize panics if the current tip block associated with the generator does not indicate the specified pool size.
func (*Generator) AssertScriptSigOpsCount ¶
AssertScriptSigOpsCount panics if the provided script does not have the specified number of signature operations.
func (*Generator) AssertStakeVersion ¶
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 ¶
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 ¶
AssertTipBlockSigOpsCount panics if the current tip block associated with the generator does not have the specified number of signature operations.
func (*Generator) AssertTipBlockSize ¶
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 ¶
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 ¶
AssertTipHeight panics if the current tip block associated with the generator does not have the specified height.
func (*Generator) AssertTipNumRevocations ¶
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 ¶
BlockByName returns the block associated with the provided block name. It will panic if the specified block name does not exist.
func (*Generator) CalcNextReqStakeDifficulty ¶
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 ¶
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:
- Use the proof-of-work limit for all blocks before the first retarget window
- Use the previous block's difficulty if the next block is not at a retarget interval
- 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
- 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 ¶
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:
- Use the minimum value for any blocks before any tickets could have possibly been purchased due to coinbase maturity requirements
- Return 0 if the current tip block stake difficulty is 0. This is a safety check against a condition that should never actually happen.
- Use the previous block's difficulty if the next block is not at a retarget interval
- 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
- 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
- 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
- Calculate the tickets per window retarget difficulty based on the exponential weighted average and ensure it is limited to the max retarget adjustment factor
- 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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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.