This converts the internal mining package to use the stdaddr package instead of dcrutil.Address as well as to support script versions. All callers that use the mining package in the repository are updated accordingly to maintain code that continues to build and work properly as well as pass all tests. In order to achieve this, it introduces temporary address conversion code that will be removed in future commits. This is part of a series of commits to convert all packages in the repository to make use of the new stdaddr package.
196 lines
6.4 KiB
Go
196 lines
6.4 KiB
Go
// Copyright (c) 2020-2021 The Decred developers
|
|
// Use of this source code is governed by an ISC
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package mining
|
|
|
|
import (
|
|
"errors"
|
|
"testing"
|
|
|
|
"github.com/decred/dcrd/blockchain/v4"
|
|
"github.com/decred/dcrd/chaincfg/chainhash"
|
|
"github.com/decred/dcrd/chaincfg/v3"
|
|
"github.com/decred/dcrd/dcrutil/v4"
|
|
"github.com/decred/dcrd/txscript/v4"
|
|
"github.com/decred/dcrd/txscript/v4/stdaddr"
|
|
"github.com/decred/dcrd/wire"
|
|
)
|
|
|
|
// TestNewBlockTemplateBasicErrorScenarios tests various basic error scenarios
|
|
// that can occur during new block template generation.
|
|
func TestNewBlockTemplateBasicErrorScenarios(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
// Create a new mining harness instance.
|
|
harness, _, err := newMiningHarness(chaincfg.MainNetParams())
|
|
if err != nil {
|
|
t.Fatalf("error creating mining harness: %v", err)
|
|
}
|
|
|
|
// Create a test address for use in template generation.
|
|
address, err := stdaddr.DecodeAddress("Dsi8CRt85xYyempXs7ZPL1rBxvDdAGZmgsg",
|
|
harness.chainParams)
|
|
if err != nil {
|
|
t.Fatalf("error decoding address: %v", err)
|
|
}
|
|
|
|
// Test error retrieving standard verify flags.
|
|
standardVerifyFlags := harness.policy.StandardVerifyFlags
|
|
var errFlags = errors.New("error retrieving standard verify flags")
|
|
harness.policy.StandardVerifyFlags = func() (txscript.ScriptFlags, error) {
|
|
return 0, errFlags
|
|
}
|
|
_, err = harness.generator.NewBlockTemplate(address)
|
|
if !errors.Is(err, errFlags) {
|
|
t.Fatalf("unexpected error retrieving standard verify flags -- got %v, "+
|
|
"want %v", err, errFlags)
|
|
}
|
|
harness.policy.StandardVerifyFlags = standardVerifyFlags
|
|
|
|
// Test error retrieving treasury agenda.
|
|
var errTreasuryAgenda = errors.New("error retrieving treasury agenda")
|
|
harness.chain.isTreasuryAgendaActiveErr = errTreasuryAgenda
|
|
_, err = harness.generator.NewBlockTemplate(address)
|
|
if !errors.Is(err, errTreasuryAgenda) {
|
|
t.Fatalf("unexpected error retrieving treasury agenda -- got %v, want %v",
|
|
err, errTreasuryAgenda)
|
|
}
|
|
harness.chain.isTreasuryAgendaActiveErr = nil
|
|
}
|
|
|
|
// TestNewBlockTemplate tests the generation of a new block template containing
|
|
// regular and vote transactions.
|
|
func TestNewBlockTemplate(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
// Create a new mining harness instance.
|
|
harness, spendableOuts, err := newMiningHarness(chaincfg.MainNetParams())
|
|
if err != nil {
|
|
t.Fatalf("error creating mining harness: %v", err)
|
|
}
|
|
|
|
// Create a test address for use in template generation.
|
|
address, err := stdaddr.DecodeAddress("Dsi8CRt85xYyempXs7ZPL1rBxvDdAGZmgsg",
|
|
harness.chainParams)
|
|
if err != nil {
|
|
t.Fatalf("error decoding address: %v", err)
|
|
}
|
|
|
|
// Define a munger to apply transaction fees.
|
|
applyTxFee := func(fee int64) func(*wire.MsgTx) {
|
|
return func(tx *wire.MsgTx) {
|
|
tx.TxOut[0].Value -= fee
|
|
}
|
|
}
|
|
|
|
// Create additional transactions from the first spendable output provided by
|
|
// the harness.
|
|
numTxs := 6
|
|
txs := make([]*dcrutil.Tx, numTxs)
|
|
baseTx, err := harness.CreateSignedTx(spendableOuts, uint32(numTxs))
|
|
if err != nil {
|
|
t.Fatalf("unable to create transaction: %v", err)
|
|
}
|
|
harness.AddFakeUTXO(baseTx, harness.chain.bestState.Height, 1,
|
|
harness.chain.isTreasuryAgendaActive)
|
|
for i := 0; i < numTxs; i++ {
|
|
tx, err := harness.CreateSignedTx([]spendableOutput{
|
|
txOutToSpendableOut(baseTx, uint32(i), wire.TxTreeRegular)}, 1,
|
|
applyTxFee(5000))
|
|
if err != nil {
|
|
t.Fatalf("unable to create transaction: %v", err)
|
|
}
|
|
txs[i] = tx
|
|
}
|
|
|
|
// Create ticket purchase transactions spending the outputs of the prior
|
|
// regular transactions.
|
|
numVotes := 5
|
|
tickets := make([]*dcrutil.Tx, numVotes)
|
|
ticketHashes := make([]chainhash.Hash, numVotes)
|
|
for i := 0; i < numVotes; i++ {
|
|
ticket, err := harness.CreateTicketPurchase(txs[i], 40000)
|
|
if err != nil {
|
|
t.Fatalf("unable to create ticket purchase transaction: %v", err)
|
|
}
|
|
tickets[i] = ticket
|
|
ticketHashes[i] = ticket.MsgTx().TxHash()
|
|
}
|
|
|
|
// Add the ticket outputs as utxos to fake their existence. Use one after
|
|
// the stake enabled height for the height of the fake utxos to ensure they
|
|
// are mature for the votes cast at stake validation height below.
|
|
harness.chain.bestState = blockchain.BestState{
|
|
Height: harness.chainParams.StakeEnabledHeight + 1,
|
|
}
|
|
for i, ticket := range tickets {
|
|
harness.AddFakeUTXO(ticket, harness.chain.bestState.Height, uint32(i+1),
|
|
harness.chain.isTreasuryAgendaActive)
|
|
}
|
|
|
|
// Create votes on a block at stake validation height using the previously
|
|
// created tickets.
|
|
hash := chainhash.Hash{0x5c, 0xa1, 0xab, 0x1e}
|
|
harness.chain.tipGeneration = []chainhash.Hash{hash}
|
|
harness.chain.bestState = blockchain.BestState{
|
|
Hash: hash,
|
|
Height: harness.chainParams.StakeValidationHeight,
|
|
NextWinningTickets: ticketHashes,
|
|
}
|
|
votes := make([]*dcrutil.Tx, numVotes)
|
|
for i, ticket := range tickets {
|
|
vote, err := harness.CreateVote(ticket)
|
|
if err != nil {
|
|
t.Fatalf("unable to create vote: %v", err)
|
|
}
|
|
votes[i] = vote
|
|
}
|
|
|
|
// Add vote transactions to the tx source.
|
|
for _, vote := range votes {
|
|
_, err = harness.AddTransactionToTxSource(vote)
|
|
if err != nil {
|
|
t.Fatalf("unable to add transaction to the tx source: %v", err)
|
|
}
|
|
}
|
|
|
|
// Add remaining regular transactions to the tx source.
|
|
for i := numVotes; i < numTxs; i++ {
|
|
_, err = harness.AddTransactionToTxSource(txs[i])
|
|
if err != nil {
|
|
t.Fatalf("unable to add transaction to the tx source: %v", err)
|
|
}
|
|
}
|
|
|
|
// Generate a new block template.
|
|
blockTemplate, err := harness.generator.NewBlockTemplate(address)
|
|
if err != nil {
|
|
t.Fatalf("unexpected err generating block template: %v", err)
|
|
}
|
|
|
|
// Validate the number of transactions in the generated block template.
|
|
gotTx := len(blockTemplate.Block.Transactions)
|
|
wantTx := numTxs - numVotes + 1 // + 1 for coinbase.
|
|
if gotTx != wantTx {
|
|
t.Fatalf("unexpected number of transactions in template -- got %v, want %v",
|
|
gotTx, wantTx)
|
|
}
|
|
|
|
// Validate the number of stake transactions in the generated block template.
|
|
gotStx := len(blockTemplate.Block.STransactions)
|
|
wantStx := numVotes + 1 // + 1 for stakebase.
|
|
if gotStx != wantStx {
|
|
t.Fatalf("unexpected number of stake transactions in template -- got %v, "+
|
|
"want %v", gotStx, wantStx)
|
|
}
|
|
|
|
// Validate that the block is sane. These checks are context free.
|
|
block := dcrutil.NewBlock(blockTemplate.Block)
|
|
err = blockchain.CheckBlockSanity(block, harness.generator.cfg.TimeSource,
|
|
harness.chainParams)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error when checking block sanity: %v", err)
|
|
}
|
|
}
|