Skip to content

Commit

Permalink
fullblockstest: adding the tests for the integration of processBlocks…
Browse files Browse the repository at this point in the history
… and processHeaderBlocks

Adding the tests that would check if a block with validated header give the expected reponse when block data is added to that node.
  • Loading branch information
samay-kothari committed Aug 10, 2022
1 parent 18f1b26 commit 975eaf6
Show file tree
Hide file tree
Showing 3 changed files with 174 additions and 4 deletions.
73 changes: 73 additions & 0 deletions blockchain/fullblocktests/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"testing"

"github.com/utreexo/utreexod/blockchain"
"github.com/utreexo/utreexod/btcutil"
)

type chaingenHarness struct {
Expand Down Expand Up @@ -130,3 +131,75 @@ func (g *chaingenHarness) RejectHeader(blockName string, code blockchain.ErrorCo
"marked as known invalid", blockName, blockHash)
}
}

// testAcceptedBlock attempts to process the block in the provided test
// instance and ensures that it was accepted according to the flags
// specified in the test.
func (g *chaingenHarness) AcceptBlock(item AcceptedBlock) {
g.t.Helper()

blockHeight := item.Height
block := btcutil.NewBlock(item.Block)
block.SetHeight(blockHeight)
g.t.Logf("Testing block %s (hash %s, height %d)",
item.Name, block.Hash(), blockHeight)

isMainChain, isOrphan, err := g.chain.ProcessBlock(block,
blockchain.BFNone)
if err != nil {
g.t.Fatalf("block %q (hash %s, height %d) should "+
"have been accepted: %v", item.Name,
block.Hash(), blockHeight, err)
}

// Ensure the main chain and orphan flags match the values
// specified in the test.
if isMainChain != item.IsMainChain {
g.t.Fatalf("block %q (hash %s, height %d) unexpected main "+
"chain flag -- got %v, want %v", item.Name,
block.Hash(), blockHeight, isMainChain,
item.IsMainChain)
}
if isOrphan != item.IsOrphan {
g.t.Fatalf("block %q (hash %s, height %d) unexpected "+
"orphan flag -- got %v, want %v", item.Name,
block.Hash(), blockHeight, isOrphan,
item.IsOrphan)
}
}

// testRejectedBlock attempts to process the block in the provided test
// instance and ensures that it was rejected with the reject code
// specified in the test.
func (g *chaingenHarness) RejectBlock(item RejectedBlock) {
g.t.Helper()

blockHeight := item.Height
block := btcutil.NewBlock(item.Block)
block.SetHeight(blockHeight)
g.t.Logf("Testing block %s (hash %s, height %d)",
item.Name, block.Hash(), blockHeight)

_, _, err := g.chain.ProcessBlock(block, blockchain.BFNone)
if err == nil {
g.t.Fatalf("block %q (hash %s, height %d) should not "+
"have been accepted", item.Name, block.Hash(),
blockHeight)
}

// Ensure the error code is of the expected type and the reject
// code matches the value specified in the test instance.
rerr, ok := err.(blockchain.RuleError)
if !ok {
g.t.Fatalf("block %q (hash %s, height %d) returned "+
"unexpected error type -- got %T, want "+
"blockchain.RuleError", item.Name, block.Hash(),
blockHeight, err)
}
if rerr.ErrorCode != item.RejectCode {
g.t.Fatalf("block %q (hash %s, height %d) does not have "+
"expected reject code -- got %v, want %v",
item.Name, block.Hash(), blockHeight,
rerr.ErrorCode, item.RejectCode)
}
}
101 changes: 97 additions & 4 deletions blockchain/fullblocktests/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -2214,7 +2214,15 @@ func GenerateHeaders() (generator *testGenerator, tests [][]TestInstance) {
blockHeight := g.blockHeights[blockName]
return RejectedHeader{blockName, blockHeader, blockHeight, code}
}

acceptBlock := func(blockName string, block *wire.MsgBlock, isMainChain, isOrphan bool) TestInstance {
blockHeight := g.blockHeights[blockName]
return AcceptedBlock{blockName, block, blockHeight, isMainChain,
isOrphan}
}
rejectBlock := func(blockName string, block *wire.MsgBlock, code blockchain.ErrorCode) TestInstance {
blockHeight := g.blockHeights[blockName]
return RejectedBlock{blockName, block, blockHeight, code}
}
// Define some convenience helper functions to populate the tests slice
// with test instances that have the described characteristics.
//
Expand All @@ -2224,6 +2232,13 @@ func GenerateHeaders() (generator *testGenerator, tests [][]TestInstance) {
//
// rejected creates and appends a single rejectHeader test instance for
// the current tip.
//
// acceptedBlock creates and appends a single acceptBlock test instance for
// the current tip which expects the block to be accepted to the main
// chain.
//
// rejectedBlock creates and appends a single rejectBlock test instance for
// the current tip.
accepted := func() {
tests = append(tests, []TestInstance{
acceptHeader(g.tipName, g.tip),
Expand All @@ -2234,6 +2249,16 @@ func GenerateHeaders() (generator *testGenerator, tests [][]TestInstance) {
rejectHeader(g.tipName, g.tip, code),
})
}
acceptedBlock := func() {
tests = append(tests, []TestInstance{
acceptBlock(g.tipName, g.tip, true, false),
})
}
rejectedBlock := func(code blockchain.ErrorCode) {
tests = append(tests, []TestInstance{
rejectBlock(g.tipName, g.tip, code),
})
}
// ---------------------------------------------------------------------
// Generate enough blocks to have mature coinbase outputs to work with.
//
Expand Down Expand Up @@ -2317,11 +2342,79 @@ func GenerateHeaders() (generator *testGenerator, tests [][]TestInstance) {
g.updateBlockState("b3", origHash, "b3", b3)
}
rejected(blockchain.ErrUnexpectedDifficulty)
// Adding a block with valid header
// Adding a block with valid header but invalid spend
//
// ... -> b0() -> b4(1)
// ... -> b0() -> b4(2)
g.setTip("b0")
g.nextBlock("b4", outs[1])
g.nextBlock("b4", outs[2])
accepted()
// Adding a block with valid header and valid spend, but invalid parent
//
// ... -> b0() -> b5(1)
g.nextBlock("b5", outs[1])
accepted()
// Adding a block with valid header and valid spend and valid parent
//
// ... -> b0() -> b6(1)
g.setTip("b0")
g.nextBlock("b6", outs[1])
// Accepting/Rejecting the blocks for the headers that were
// accepted/rejected
testInstances = make([]TestInstance, 0)
for i := uint16(0); i < coinbaseMaturity; i++ {
blockName := fmt.Sprintf("bm%d", i)
g.setTip(blockName)
testInstances = append(testInstances, acceptBlock(g.tipName,
g.tip, true, false))
}
tests = append(tests, testInstances)
// Accepting the block b0
//
// ... -> b0
g.setTip("b0")
acceptedBlock()
// Rejecting the block data for b1 because of high hash
// ... -> b0()
// \-> b1(1)
g.setTip("b1")
rejectedBlock(blockchain.ErrHighHash)
// Acccept the block as orphan
//
// -> b1a(1)
g.setTip("b1a")
tests = append(tests, []TestInstance{
acceptBlock(g.tipName, g.tip, false, true),
})
// Reject the block with invalid proof of work
//
// ... -> b0()
// \-> b2(1)
g.setTip("b2")
rejectedBlock(blockchain.ErrUnexpectedDifficulty)
// Reject the block with invalid negative proof of work
//
// ... -> b0()
// \-> b3(1)
g.setTip("b3")
rejectedBlock(blockchain.ErrUnexpectedDifficulty)
// Rejecting the block with valid header but invalid spend
//
// ... -> b0()
// \-> b4(2)
g.setTip("b4")
rejectedBlock(blockchain.ErrImmatureSpend)
// Since the block is rejected, so rejecting the header also
// which was earlier accepted.
rejected(blockchain.ErrKnownInvalidBlock)
// Rejecting a block with valid header and valid spend, but invalid parent
//
// -> b4(2) -> b5(1)
g.setTip("b5")
rejectedBlock(blockchain.ErrInvalidAncestorBlock)
// Accepting the block
//
// ... -> b0() -> b6(1)
g.setTip("b6")
acceptedBlock()
return &g, tests
}
4 changes: 4 additions & 0 deletions blockchain/fullblocktests/process_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ func TestProcessLogic(t *testing.T) {
harness.AcceptHeader(item.Name)
case RejectedHeader:
harness.RejectHeader(item.Name, item.RejectCode)
case AcceptedBlock:
harness.AcceptBlock(item)
case RejectedBlock:
harness.RejectBlock(item)
default:
t.Fatalf("test #%d, item #%d is not one of "+
"the supported test instance types -- "+
Expand Down

0 comments on commit 975eaf6

Please sign in to comment.