core: add test for sidechain imports of long chains, where hash lookup is not on canon chain

This commit is contained in:
Martin Holst Swende 2019-11-19 10:07:54 +01:00
parent 7aa5ec37d2
commit 54e16f8679
No known key found for this signature in database
GPG key ID: 683B438C05A5DDF0
2 changed files with 97 additions and 1 deletions

View file

@ -2363,3 +2363,95 @@ func TestDeleteCreateRevert(t *testing.T) {
t.Fatalf("block %d: failed to insert into chain: %v", n, err) t.Fatalf("block %d: failed to insert into chain: %v", n, err)
} }
} }
// TestImportlongSidechainWithBlockhash200 imports a canonical chain of 500 blocks,
// and then finds out about a longer sidechain which forked off at block 200 (300 blocks ago).
// It will then import the sidechain, and execute the transactions.
// Now, at block 450, there is a transaction which executes BLOCKHASH(205), and stores
// the result in slot(0).
// This test is meant to ensure that the hashbuffer-based GetAncestorHash correctly
// handles non-canon lookups
func TestImportlongSidechainWithBlockhash200(t *testing.T) {
var (
aa = common.HexToAddress("0x000000000000000000000000000000000000aaaa")
engine = ethash.NewFaker()
db = rawdb.NewMemoryDatabase()
// A sender who makes transactions, has some funds
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
address = crypto.PubkeyToAddress(key.PublicKey)
funds = big.NewInt(1000000000)
gspec = &Genesis{
Config: params.TestChainConfig,
Alloc: GenesisAlloc{
address: {Balance: funds},
// The address 0xAAAAA stores BLOCKHASH in slot zero
aa: {
// Push
Code: []byte{
byte(vm.PUSH1), byte(205), // [205]
byte(vm.BLOCKHASH), // [ bh(205) ]
byte(vm.PUSH1), byte(0), // [bh(205), 0] ; location
byte(vm.SSTORE), // []
},
Nonce: 1,
Balance: big.NewInt(0),
},
},
}
genesis = gspec.MustCommit(db)
)
blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 500, func(i int, b *BlockGen) {
b.SetCoinbase(common.Address{1})
})
// Import the canonical chain
diskdb := rawdb.NewMemoryDatabase()
gspec.MustCommit(diskdb)
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil)
if err != nil {
t.Fatalf("failed to create initial chain: %v", err)
}
if n, err := chain.InsertChain(blocks); err != nil {
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
}
// Ok, the canon chain is done. Now generate the competing chain. In order to be able to
// do so, we need to do it in two steps, so we can call b.AddTxWithChain, and have the
// sidechain blocks be in the chain already
forkBlockNum := 200
// Add 200 sideblocks
sideblocks1, _ := GenerateChain(params.TestChainConfig, blocks[forkBlockNum], engine, db, 248, func(i int, b *BlockGen) {
b.SetCoinbase(common.Address{2})
})
if n, err := chain.InsertChain(sideblocks1); err != nil {
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
}
// Add another 150 blocks
sideblocks2, _ := GenerateChain(params.TestChainConfig, sideblocks1[len(sideblocks1)-1], engine, db, 150, func(i int, b *BlockGen) {
b.SetCoinbase(common.Address{2})
if i == 0 {
tx, _ := types.SignTx(types.NewTransaction(0, aa,
big.NewInt(0), 50000, big.NewInt(1), nil), types.HomesteadSigner{}, key)
b.AddTxWithChain(chain, tx)
}
})
if n, err := chain.InsertChain(sideblocks2); err != nil {
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
}
if got, exp := chain.CurrentBlock().Hash(), sideblocks2[len(sideblocks2)-1].Hash(); got != exp {
t.Fatalf("error, exp %x got %x", got, exp)
}
statedb, err := chain.State()
if err != nil {
t.Fatalf("failed to get state: %v", err)
}
val := statedb.GetState(aa, common.Hash{})
// Check canon
if got, exp := val, chain.GetCanonicalHash(205); got != exp {
t.Fatalf("wrong hash, got %x exp %x", got, exp)
}
// Sanity check against the sideblocks
if got, exp := val, sideblocks1[3].Hash(); got != exp {
t.Fatalf("wrong hash, got %x exp %x", got, exp)
}
}

View file

@ -546,7 +546,11 @@ func (hc *HeaderChain) GetAncestorHash(ref *types.Header, target uint64) common.
return common.Hash{} return common.Hash{}
} }
var ( var (
maxNonCanonLookups = uint64(100) // The maxNonCanonLookups _must_ be above 256, since this method is used
// when importing sidechains. If we are importing a very long sidechain,
// then we _might_ have to look back up to 256 blocks in order to execute
// the BLOCKHASH opcode, regardless if it's canon or not
maxNonCanonLookups = uint64(260)
) )
if hashHistoryTail, _ := hc.hashHistory.Oldest(); hashHistoryTail <= target { if hashHistoryTail, _ := hc.hashHistory.Oldest(); hashHistoryTail <= target {
// Iterate the chain until we hit the target or we hit a ancestor // Iterate the chain until we hit the target or we hit a ancestor