diff --git a/core/state_processor.go b/core/state_processor.go index 0bcdcc4260..486e12ab40 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -81,7 +81,12 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg ProcessBeaconBlockRoot(*beaconRoot, vmenv, statedb) } if p.config.IsPrague(block.Number(), block.Time()) { - ProcessParentBlockHash(statedb, block.NumberU64()-1, block.ParentHash()) + parent := p.bc.GetBlockByHash(block.ParentHash()) + if !p.config.IsPrague(parent.Number(), parent.Time()) { + InsertBlockHashHistoryAtEip2935Fork(statedb, block.NumberU64()-1, block.ParentHash(), p.bc) + } else { + ProcessParentBlockHash(statedb, block.NumberU64()-1, block.ParentHash()) + } } // Iterate over and process the individual transactions for i, tx := range block.Transactions() { @@ -194,6 +199,17 @@ func ProcessBeaconBlockRoot(beaconRoot common.Hash, vmenv *vm.EVM, statedb *stat statedb.Finalise(true) } +// InsertBlockHashHistoryAtEip2935Fork inserts the block hashes for the 256 ancestors +// of the fork block. +func InsertBlockHashHistoryAtEip2935Fork(statedb *state.StateDB, prevNumber uint64, prevHash common.Hash, chain consensus.ChainHeaderReader) { + ancestor := chain.GetHeader(prevHash, prevNumber) + for i := prevNumber; i > 0 && i >= prevNumber-256; i-- { + ProcessParentBlockHash(statedb, i, ancestor.Hash()) + ancestor = chain.GetHeader(ancestor.ParentHash, ancestor.Number.Uint64()-1) + } +} + +// ProcessParentBlockHash inserts the parent block hash into the history storage contract. func ProcessParentBlockHash(statedb *state.StateDB, prevNumber uint64, prevHash common.Hash) { var key common.Hash binary.BigEndian.PutUint64(key[24:], prevNumber) diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 5a2b0fc4ab..bf21b8363d 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -449,22 +449,9 @@ func opBlockhash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ( } evm := interpreter.evm - bnum := evm.Context.BlockNumber.Uint64() - // if Prague is active, check if we are past the 256th block so that - // reading from the contract can be activated (EIP 2935). - if interpreter.evm.chainRules.IsPrague && bnum > 256 { - if getBlockHashFromContract(bnum-256, evm.StateDB) != (common.Hash{}) { - // EIP-2935 case: get the block number from the fork, as we are 256 blocks - // after the fork activation. - - num.SetBytes(getBlockHashFromContract(num64, evm.StateDB).Bytes()) - return nil, nil - } - - // if the 256th ancestor didn't have its hash stored in the - // history contract, then we are within 256 blocks of the - // fork activation, and the former behavior should be retained. - // Fall through the legacy use case. + if evm.chainRules.IsPrague { + num.SetBytes(getBlockHashFromContract(num64, evm.StateDB).Bytes()) + return nil, nil } var upper, lower uint64 diff --git a/core/vm/instructions_test.go b/core/vm/instructions_test.go index 5f481a7fbe..bcbf08b4a4 100644 --- a/core/vm/instructions_test.go +++ b/core/vm/instructions_test.go @@ -827,27 +827,29 @@ func TestBlockHashEip2935(t *testing.T) { TerminalTotalDifficulty: big.NewInt(0), TerminalTotalDifficultyPassed: true, } - env = NewEVM(blockContext, TxContext{}, statedb, chainConfig, Config{}) - stack = newstack() - pc = uint64(0) - evmInterpreter = env.interpreter - callBlockHash10 = func(t *testing.T, name string) { - stack.push(uint256.NewInt(10)) - opBlockhash(&pc, evmInterpreter, &ScopeContext{nil, stack, nil}) - if len(stack.data) != 1 { - t.Errorf("Expected one item on stack got %d: ", len(stack.data)) - } - actual := stack.pop() - expected, overflow := uint256.FromBig(new(big.Int).SetBytes(expect.Bytes())) - if overflow { - t.Errorf("invalid overflow") - } - if actual.Cmp(expected) != 0 { - t.Errorf("%s: expected %x, got %x", name, expected, actual) + env = NewEVM(blockContext, TxContext{}, statedb, chainConfig, Config{}) + stack = newstack() + pc = uint64(0) + evmInterpreter = env.interpreter + callBlockHashN = func(n uint64) func(t *testing.T, name string) uint256.Int { + return func(t *testing.T, name string) uint256.Int { + stack.push(uint256.NewInt(n)) + opBlockhash(&pc, evmInterpreter, &ScopeContext{nil, stack, nil}) + if len(stack.data) != 1 { + t.Errorf("Expected one item on stack got %d: ", len(stack.data)) + } + return stack.pop() } } + callBlockHash10 = callBlockHashN(10) + callBlockHash1024 = callBlockHashN(1024) ) + expected, overflow := uint256.FromBig(new(big.Int).SetBytes(expect.Bytes())) + if overflow { + t.Errorf("invalid overflow") + } + // insert 500 block hashes, starting from genesis. for i := uint64(0); i < 500; i++ { var blockNumber common.Hash @@ -857,12 +859,24 @@ func TestBlockHashEip2935(t *testing.T) { // simulate a call to BLOCKHASH at block 100, i.e. less than 256 // blocks after a genesis with eip 2935 activated. - callBlockHash10(t, "legacy") + actual := callBlockHash10(t, "legacy") + if actual.Cmp(expected) != 0 { + t.Errorf("%s: expected %x, got %x", "legacy", expected, actual) + } // now do the same thing much later, after all blocks have // been inserted. - env.Context.BlockNumber.SetInt64(500) - callBlockHash10(t, "contract") + env.Context.BlockNumber.SetInt64(501) + actual = callBlockHash10(t, "contract") + if actual.Cmp(expected) != 0 { + t.Errorf("%s: expected %x, got %x", "contract", expected, actual) + } + + // still at block 501, but now request the hash of a higher block + actual = callBlockHash1024(t, "contract with non-existent block hash") + if !actual.IsZero() { + t.Errorf("%s: expected 0, got %x", "contract with non-existent block hash", actual) + } } func TestOpMCopy(t *testing.T) {