Upcoming changes constitute breaking public API changes to the chaincfg
module, therefore, this follows the process for introducing major API
breaks which consists of:
- Bump the major version in the go.mod of the affected module if not
already done since the last release tag
- Add a replacement to the go.mod in the main module if not already done
since the last release tag
- Update all imports in the repo to use the new major version as
necessary
- Make necessary modifications to allow all other modules to use the
new version in the same commit
- Repeat the process for any other modules the require a new major as a
result of consuming the new major(s)
151 lines
5.3 KiB
Go
151 lines
5.3 KiB
Go
// Copyright (c) 2019-2020 The Decred developers
|
|
// Use of this source code is governed by an ISC
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package blockchain
|
|
|
|
import (
|
|
"fmt"
|
|
"testing"
|
|
|
|
"github.com/decred/dcrd/blockchain/v3/chaingen"
|
|
"github.com/decred/dcrd/chaincfg/chainhash"
|
|
"github.com/decred/dcrd/chaincfg/v3"
|
|
"github.com/decred/dcrd/wire"
|
|
)
|
|
|
|
// TestProcessOrder ensures processing-specific logic such as orphan handling,
|
|
// duplicate block handling, and out-of-order reorgs to invalid blocks works as
|
|
// expected.
|
|
func TestProcessOrder(t *testing.T) {
|
|
// Create a test harness initialized with the genesis block as the tip.
|
|
params := chaincfg.RegNetParams()
|
|
g, teardownFunc := newChaingenHarness(t, params, "processordertest")
|
|
defer teardownFunc()
|
|
|
|
// Shorter versions of useful params for convenience.
|
|
coinbaseMaturity := params.CoinbaseMaturity
|
|
stakeValidationHeight := params.StakeValidationHeight
|
|
|
|
// ---------------------------------------------------------------------
|
|
// Generate and accept enough blocks to reach stake validation height.
|
|
// ---------------------------------------------------------------------
|
|
|
|
g.AdvanceToStakeValidationHeight()
|
|
|
|
// ---------------------------------------------------------------------
|
|
// Generate enough blocks to have a known distance to the first mature
|
|
// coinbase outputs for all tests that follow. These blocks continue
|
|
// to purchase tickets to avoid running out of votes.
|
|
//
|
|
// ... -> bsv# -> bbm0 -> bbm1 -> ... -> bbm#
|
|
// ---------------------------------------------------------------------
|
|
|
|
for i := uint16(0); i < coinbaseMaturity; i++ {
|
|
outs := g.OldestCoinbaseOuts()
|
|
blockName := fmt.Sprintf("bbm%d", i)
|
|
g.NextBlock(blockName, nil, outs[1:])
|
|
g.SaveTipCoinbaseOuts()
|
|
g.AcceptTipBlock()
|
|
}
|
|
g.AssertTipHeight(uint32(stakeValidationHeight) + uint32(coinbaseMaturity))
|
|
|
|
// Collect spendable outputs into two different slices. The outs slice
|
|
// is intended to be used for regular transactions that spend from the
|
|
// output, while the ticketOuts slice is intended to be used for stake
|
|
// ticket purchases.
|
|
var outs []*chaingen.SpendableOut
|
|
var ticketOuts [][]chaingen.SpendableOut
|
|
for i := uint16(0); i < coinbaseMaturity; i++ {
|
|
coinbaseOuts := g.OldestCoinbaseOuts()
|
|
outs = append(outs, &coinbaseOuts[0])
|
|
ticketOuts = append(ticketOuts, coinbaseOuts[1:])
|
|
}
|
|
|
|
// Ensure duplicate blocks are rejected.
|
|
//
|
|
// ... -> b1(0)
|
|
// \-> b1(0)
|
|
g.NextBlock("b1", outs[0], ticketOuts[0])
|
|
g.AcceptTipBlock()
|
|
g.RejectTipBlock(ErrDuplicateBlock)
|
|
|
|
// ---------------------------------------------------------------------
|
|
// Orphan tests.
|
|
// ---------------------------------------------------------------------
|
|
|
|
// Create valid orphan block with zero prev hash.
|
|
//
|
|
// No previous block
|
|
// \-> borphan0(1)
|
|
g.SetTip("b1")
|
|
g.NextBlock("borphan0", outs[1], ticketOuts[1], func(b *wire.MsgBlock) {
|
|
b.Header.PrevBlock = chainhash.Hash{}
|
|
})
|
|
g.RejectTipBlock(ErrMissingParent)
|
|
|
|
// Create valid orphan block.
|
|
//
|
|
// ... -> b1(0)
|
|
// \-> borphanbase(1) -> borphan1(2)
|
|
g.SetTip("b1")
|
|
g.NextBlock("borphanbase", outs[1], ticketOuts[1])
|
|
g.NextBlock("borphan1", outs[2], ticketOuts[2])
|
|
g.RejectTipBlock(ErrMissingParent)
|
|
|
|
// Ensure duplicate orphan blocks are rejected.
|
|
g.RejectTipBlock(ErrMissingParent)
|
|
|
|
// ---------------------------------------------------------------------
|
|
// Out-of-order forked reorg to invalid block tests.
|
|
//
|
|
// NOTE: These tests have been modified to be processed in order despite
|
|
// the comments for now due to the removal of orphan processing. They
|
|
// are otherwise left intact because future commits will allow headers
|
|
// to be processed independently from the block data which will allow
|
|
// out of order processing of data again so long as the headers are
|
|
// processed in order.
|
|
// ---------------------------------------------------------------------
|
|
|
|
// Create a fork that ends with block that generates too much proof-of-work
|
|
// coinbase, but with a valid fork first.
|
|
//
|
|
// ... -> b1(0) -> b2(1)
|
|
// \-> bpw1(1) -> bpw2(2) -> bpw3(3)
|
|
// (bpw1 added last)
|
|
g.SetTip("b1")
|
|
g.NextBlock("b2", outs[1], ticketOuts[1])
|
|
g.AcceptTipBlock()
|
|
g.ExpectTip("b2")
|
|
|
|
g.SetTip("b1")
|
|
g.NextBlock("bpw1", outs[1], ticketOuts[1])
|
|
g.AcceptedToSideChainWithExpectedTip("b2")
|
|
g.NextBlock("bpw2", outs[2], ticketOuts[2])
|
|
g.NextBlock("bpw3", outs[3], ticketOuts[3], func(b *wire.MsgBlock) {
|
|
// Increase the first proof-of-work coinbase subsidy.
|
|
b.Transactions[0].TxOut[2].Value++
|
|
})
|
|
g.AcceptBlock("bpw2")
|
|
g.RejectBlock("bpw3", ErrBadCoinbaseValue)
|
|
g.ExpectTip("bpw2")
|
|
|
|
// Create a fork that ends with block that generates too much dev-org
|
|
// coinbase, but with a valid fork first.
|
|
//
|
|
// ... -> b1(0) -> bpw1(1) -> bpw2(2)
|
|
// \-> bdc1(2) -> bdc2(3) -> bdc3(4)
|
|
// (bdc1 added last)
|
|
g.SetTip("bpw1")
|
|
g.NextBlock("bdc1", outs[2], ticketOuts[2])
|
|
g.AcceptedToSideChainWithExpectedTip("bpw2")
|
|
g.NextBlock("bdc2", outs[3], ticketOuts[3])
|
|
g.NextBlock("bdc3", outs[4], ticketOuts[4], func(b *wire.MsgBlock) {
|
|
// Increase the proof-of-work dev subsidy by the provided amount.
|
|
b.Transactions[0].TxOut[0].Value++
|
|
})
|
|
g.AcceptBlock("bdc2")
|
|
g.RejectBlock("bdc3", ErrNoTax)
|
|
g.ExpectTip("bdc2")
|
|
}
|