mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-20 10:52:25 +00:00
core: refactor block header validation tests to use test tables
This commit is contained in:
parent
2a0e1bb32b
commit
67e279e815
1 changed files with 165 additions and 120 deletions
|
|
@ -28,34 +28,29 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const height = 8
|
||||||
|
|
||||||
// Tests that simple header verification works, for both good and bad blocks.
|
// Tests that simple header verification works, for both good and bad blocks.
|
||||||
func TestHeaderVerification(t *testing.T) {
|
func TestHeaderVerification(t *testing.T) {
|
||||||
// Create a simple chain to verify
|
db, blocks, _, _ := setupDatabaseAndBlocks(t)
|
||||||
var (
|
|
||||||
testdb = ethdb.NewMemDatabase()
|
// Run the header checker for blocks one-by-one, checking for both valid and invalid nonces.
|
||||||
gspec = &Genesis{Config: params.TestChainConfig}
|
chain, _ := NewBlockChain(db, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil)
|
||||||
genesis = gspec.MustCommit(testdb)
|
|
||||||
blocks, _ = GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), testdb, 8, nil)
|
|
||||||
)
|
|
||||||
headers := make([]*types.Header, len(blocks))
|
|
||||||
for i, block := range blocks {
|
|
||||||
headers[i] = block.Header()
|
|
||||||
}
|
|
||||||
// Run the header checker for blocks one-by-one, checking for both valid and invalid nonces
|
|
||||||
chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil)
|
|
||||||
defer chain.Stop()
|
defer chain.Stop()
|
||||||
|
|
||||||
for i := 0; i < len(blocks); i++ {
|
for i, block := range blocks {
|
||||||
for j, valid := range []bool{true, false} {
|
for j, valid := range []bool{true, false} {
|
||||||
|
header := block.Header()
|
||||||
var results <-chan error
|
var results <-chan error
|
||||||
|
|
||||||
|
var engine *ethash.Ethash
|
||||||
if valid {
|
if valid {
|
||||||
engine := ethash.NewFaker()
|
engine = ethash.NewFaker()
|
||||||
_, results = engine.VerifyHeaders(chain, []*types.Header{headers[i]}, []bool{true})
|
|
||||||
} else {
|
} else {
|
||||||
engine := ethash.NewFakeFailer(headers[i].Number.Uint64())
|
engine = ethash.NewFakeFailer(header.Number.Uint64())
|
||||||
_, results = engine.VerifyHeaders(chain, []*types.Header{headers[i]}, []bool{true})
|
|
||||||
}
|
}
|
||||||
|
_, results = engine.VerifyHeaders(chain, []*types.Header{header}, []bool{true})
|
||||||
|
|
||||||
// Wait for the verification result
|
// Wait for the verification result
|
||||||
select {
|
select {
|
||||||
case result := <-results:
|
case result := <-results:
|
||||||
|
|
@ -72,64 +67,90 @@ func TestHeaderVerification(t *testing.T) {
|
||||||
case <-time.After(25 * time.Millisecond):
|
case <-time.After(25 * time.Millisecond):
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
chain.InsertChain(blocks[i : i+1])
|
chain.InsertChain([]*types.Block{block})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tests that concurrent header verification works, for both good and bad blocks.
|
func TestConcurrentHeaderVerification(t *testing.T) {
|
||||||
func TestHeaderConcurrentVerification2(t *testing.T) { testHeaderConcurrentVerification(t, 2) }
|
tests := []struct {
|
||||||
func TestHeaderConcurrentVerification8(t *testing.T) { testHeaderConcurrentVerification(t, 8) }
|
name string
|
||||||
func TestHeaderConcurrentVerification32(t *testing.T) { testHeaderConcurrentVerification(t, 32) }
|
threads int
|
||||||
|
valid bool
|
||||||
func testHeaderConcurrentVerification(t *testing.T, threads int) {
|
}{
|
||||||
// Create a simple chain to verify
|
{
|
||||||
var (
|
"2ThreadsSucceeds",
|
||||||
testdb = ethdb.NewMemDatabase()
|
2,
|
||||||
gspec = &Genesis{Config: params.TestChainConfig}
|
true,
|
||||||
genesis = gspec.MustCommit(testdb)
|
},
|
||||||
blocks, _ = GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), testdb, 8, nil)
|
{
|
||||||
)
|
"2ThreadsFails",
|
||||||
headers := make([]*types.Header, len(blocks))
|
2,
|
||||||
seals := make([]bool, len(blocks))
|
false,
|
||||||
|
},
|
||||||
for i, block := range blocks {
|
{
|
||||||
headers[i] = block.Header()
|
"8ThreadsSucceeds",
|
||||||
seals[i] = true
|
8,
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"8ThreadsFails",
|
||||||
|
8,
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"32ThreadsSucceeds",
|
||||||
|
32,
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"32ThreadsFails",
|
||||||
|
32,
|
||||||
|
false,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, tc := range tests {
|
||||||
|
// Tests cannot be run in parallel due to modifying runtime.GOMAXPROCS.
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
db, _, headers, seals := setupDatabaseAndBlocks(t)
|
||||||
|
|
||||||
// Set the number of threads to verify on
|
// Set the number of threads to verify on
|
||||||
old := runtime.GOMAXPROCS(threads)
|
old := runtime.GOMAXPROCS(tc.threads)
|
||||||
defer runtime.GOMAXPROCS(old)
|
defer runtime.GOMAXPROCS(old)
|
||||||
|
|
||||||
// Run the header checker for the entire block chain at once both for a valid and
|
// Run the header checker for the entire block chain at once both for a valid and
|
||||||
// also an invalid chain (enough if one arbitrary block is invalid).
|
// also an invalid chain (enough if one arbitrary block is invalid).
|
||||||
for i, valid := range []bool{true, false} {
|
|
||||||
var results <-chan error
|
var results <-chan error
|
||||||
|
|
||||||
if valid {
|
var chain *BlockChain
|
||||||
chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil)
|
var err error
|
||||||
_, results = chain.engine.VerifyHeaders(chain, headers, seals)
|
if tc.valid {
|
||||||
chain.Stop()
|
chain, err = NewBlockChain(db, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil)
|
||||||
} else {
|
} else {
|
||||||
chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFakeFailer(uint64(len(headers)-1)), vm.Config{}, nil)
|
chain, err = NewBlockChain(db, nil, params.TestChainConfig, ethash.NewFakeFailer(uint64(height-1)), vm.Config{}, nil)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error creating blockchain: %v", err)
|
||||||
|
}
|
||||||
_, results = chain.engine.VerifyHeaders(chain, headers, seals)
|
_, results = chain.engine.VerifyHeaders(chain, headers, seals)
|
||||||
chain.Stop()
|
chain.Stop()
|
||||||
}
|
|
||||||
// Wait for all the verification results
|
// Wait for all the verification results
|
||||||
checks := make(map[int]error)
|
checks := make(map[int]error)
|
||||||
for j := 0; j < len(blocks); j++ {
|
for i := range headers {
|
||||||
select {
|
select {
|
||||||
case result := <-results:
|
case result := <-results:
|
||||||
checks[j] = result
|
checks[i] = result
|
||||||
|
|
||||||
case <-time.After(time.Second):
|
case <-time.After(time.Second):
|
||||||
t.Fatalf("test %d.%d: verification timeout", i, j)
|
t.Fatalf("Verification timed out after receiving %d results", i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Check nonce check validity
|
// Check nonce check validity
|
||||||
for j := 0; j < len(blocks); j++ {
|
for i := range headers {
|
||||||
want := valid || (j < len(blocks)-2) // We chose the last-but-one nonce in the chain to fail
|
want := tc.valid || (i < len(headers)-2) // We chose the last-but-one nonce in the chain to fail
|
||||||
if (checks[j] == nil) != want {
|
if (checks[i] == nil) != want {
|
||||||
t.Errorf("test %d.%d: validity mismatch: have %v, want %v", i, j, checks[j], want)
|
t.Errorf("Validity mismatch for result %d: got %v, want %v", i, checks[i], want)
|
||||||
}
|
}
|
||||||
if !want {
|
if !want {
|
||||||
// A few blocks after the first error may pass verification due to concurrent
|
// A few blocks after the first error may pass verification due to concurrent
|
||||||
|
|
@ -141,39 +162,45 @@ func testHeaderConcurrentVerification(t *testing.T, threads int) {
|
||||||
// Make sure no more data is returned
|
// Make sure no more data is returned
|
||||||
select {
|
select {
|
||||||
case result := <-results:
|
case result := <-results:
|
||||||
t.Fatalf("test %d: unexpected result returned: %v", i, result)
|
t.Fatalf("Received unexpected result: %v", result)
|
||||||
case <-time.After(25 * time.Millisecond):
|
case <-time.After(25 * time.Millisecond):
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tests that aborting a header validation indeed prevents further checks from being
|
// Tests that aborting a header validation indeed prevents further checks from being
|
||||||
// run, as well as checks that no left-over goroutines are leaked.
|
// run, as well as checks that no left-over goroutines are leaked.
|
||||||
func TestHeaderConcurrentAbortion2(t *testing.T) { testHeaderConcurrentAbortion(t, 2) }
|
func TestConcurrentHeaderVerificationAbortion(t *testing.T) {
|
||||||
func TestHeaderConcurrentAbortion8(t *testing.T) { testHeaderConcurrentAbortion(t, 8) }
|
tests := []struct {
|
||||||
func TestHeaderConcurrentAbortion32(t *testing.T) { testHeaderConcurrentAbortion(t, 32) }
|
name string
|
||||||
|
threads int
|
||||||
func testHeaderConcurrentAbortion(t *testing.T, threads int) {
|
}{
|
||||||
// Create a simple chain to verify
|
{
|
||||||
var (
|
"2Threads",
|
||||||
testdb = ethdb.NewMemDatabase()
|
2,
|
||||||
gspec = &Genesis{Config: params.TestChainConfig}
|
},
|
||||||
genesis = gspec.MustCommit(testdb)
|
{
|
||||||
blocks, _ = GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), testdb, 1024, nil)
|
"8Threads",
|
||||||
)
|
8,
|
||||||
headers := make([]*types.Header, len(blocks))
|
},
|
||||||
seals := make([]bool, len(blocks))
|
{
|
||||||
|
"32Threads",
|
||||||
for i, block := range blocks {
|
32,
|
||||||
headers[i] = block.Header()
|
},
|
||||||
seals[i] = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, tc := range tests {
|
||||||
|
// Tests cannot be run in parallel due to modifying runtime.GOMAXPROCS.
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
db, _, headers, seals := setupDatabaseAndBlocks(t)
|
||||||
|
|
||||||
// Set the number of threads to verify on
|
// Set the number of threads to verify on
|
||||||
old := runtime.GOMAXPROCS(threads)
|
old := runtime.GOMAXPROCS(tc.threads)
|
||||||
defer runtime.GOMAXPROCS(old)
|
defer runtime.GOMAXPROCS(old)
|
||||||
|
|
||||||
// Start the verifications and immediately abort
|
// Start the verifications and immediately abort
|
||||||
chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFakeDelayer(time.Millisecond), vm.Config{}, nil)
|
chain, _ := NewBlockChain(db, nil, params.TestChainConfig, ethash.NewFakeDelayer(time.Millisecond), vm.Config{}, nil)
|
||||||
defer chain.Stop()
|
defer chain.Stop()
|
||||||
|
|
||||||
abort, results := chain.engine.VerifyHeaders(chain, headers, seals)
|
abort, results := chain.engine.VerifyHeaders(chain, headers, seals)
|
||||||
|
|
@ -185,7 +212,7 @@ func testHeaderConcurrentAbortion(t *testing.T, threads int) {
|
||||||
select {
|
select {
|
||||||
case result := <-results:
|
case result := <-results:
|
||||||
if result != nil {
|
if result != nil {
|
||||||
t.Errorf("header %d: validation failed: %v", verified, result)
|
t.Errorf("Header %d validation failed: %v", verified, result)
|
||||||
}
|
}
|
||||||
verified++
|
verified++
|
||||||
case <-time.After(50 * time.Millisecond):
|
case <-time.After(50 * time.Millisecond):
|
||||||
|
|
@ -193,7 +220,25 @@ func testHeaderConcurrentAbortion(t *testing.T, threads int) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Check that abortion was honored by not processing too many POWs
|
// Check that abortion was honored by not processing too many POWs
|
||||||
if verified > 2*threads {
|
if verified > 2*tc.threads {
|
||||||
t.Errorf("verification count too large: have %d, want below %d", verified, 2*threads)
|
t.Errorf("Verification count too large: got %d, want below %d", verified, 2*tc.threads)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setupDatabaseAndBlocks(t *testing.T) (ethdb.Database, []*types.Block, []*types.Header, []bool) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
db := ethdb.NewMemDatabase()
|
||||||
|
gspec := &Genesis{Config: params.TestChainConfig}
|
||||||
|
genesis := gspec.MustCommit(db)
|
||||||
|
blocks, _ := GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, height, nil)
|
||||||
|
headers := make([]*types.Header, 0)
|
||||||
|
seals := make([]bool, 0)
|
||||||
|
for _, block := range blocks {
|
||||||
|
headers = append(headers, block.Header())
|
||||||
|
seals = append(seals, true)
|
||||||
|
}
|
||||||
|
return db, blocks, headers, seals
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue