core: implement EIP-3651, warm coinbase (#25819)
Implements EIP-3651, "Warm Coinbase", for Shanghai hardfork. Specification: https://eips.ethereum.org/EIPS/eip-3651.
This commit is contained in:
parent
64dccf7aa4
commit
ec2ec2d08e
@ -4187,3 +4187,111 @@ func TestTransientStorageReset(t *testing.T) {
|
|||||||
t.Fatalf("Unexpected dirty storage slot")
|
t.Fatalf("Unexpected dirty storage slot")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestEIP3651(t *testing.T) {
|
||||||
|
var (
|
||||||
|
aa = common.HexToAddress("0x000000000000000000000000000000000000aaaa")
|
||||||
|
bb = common.HexToAddress("0x000000000000000000000000000000000000bbbb")
|
||||||
|
engine = ethash.NewFaker()
|
||||||
|
|
||||||
|
// A sender who makes transactions, has some funds
|
||||||
|
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
|
key2, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
|
||||||
|
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
||||||
|
addr2 = crypto.PubkeyToAddress(key2.PublicKey)
|
||||||
|
funds = new(big.Int).Mul(common.Big1, big.NewInt(params.Ether))
|
||||||
|
gspec = &Genesis{
|
||||||
|
Config: params.AllEthashProtocolChanges,
|
||||||
|
Alloc: GenesisAlloc{
|
||||||
|
addr1: {Balance: funds},
|
||||||
|
addr2: {Balance: funds},
|
||||||
|
// The address 0xAAAA sloads 0x00 and 0x01
|
||||||
|
aa: {
|
||||||
|
Code: []byte{
|
||||||
|
byte(vm.PC),
|
||||||
|
byte(vm.PC),
|
||||||
|
byte(vm.SLOAD),
|
||||||
|
byte(vm.SLOAD),
|
||||||
|
},
|
||||||
|
Nonce: 0,
|
||||||
|
Balance: big.NewInt(0),
|
||||||
|
},
|
||||||
|
// The address 0xBBBB calls 0xAAAA
|
||||||
|
bb: {
|
||||||
|
Code: []byte{
|
||||||
|
byte(vm.PUSH1), 0, // out size
|
||||||
|
byte(vm.DUP1), // out offset
|
||||||
|
byte(vm.DUP1), // out insize
|
||||||
|
byte(vm.DUP1), // in offset
|
||||||
|
byte(vm.PUSH2), // address
|
||||||
|
byte(0xaa),
|
||||||
|
byte(0xaa),
|
||||||
|
byte(vm.GAS), // gas
|
||||||
|
byte(vm.DELEGATECALL),
|
||||||
|
},
|
||||||
|
Nonce: 0,
|
||||||
|
Balance: big.NewInt(0),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
gspec.Config.BerlinBlock = common.Big0
|
||||||
|
gspec.Config.LondonBlock = common.Big0
|
||||||
|
gspec.Config.ShanghaiBlock = common.Big0
|
||||||
|
signer := types.LatestSigner(gspec.Config)
|
||||||
|
|
||||||
|
_, blocks, _ := GenerateChainWithGenesis(gspec, engine, 1, func(i int, b *BlockGen) {
|
||||||
|
b.SetCoinbase(aa)
|
||||||
|
// One transaction to Coinbase
|
||||||
|
txdata := &types.DynamicFeeTx{
|
||||||
|
ChainID: gspec.Config.ChainID,
|
||||||
|
Nonce: 0,
|
||||||
|
To: &bb,
|
||||||
|
Gas: 500000,
|
||||||
|
GasFeeCap: newGwei(5),
|
||||||
|
GasTipCap: big.NewInt(2),
|
||||||
|
AccessList: nil,
|
||||||
|
Data: []byte{},
|
||||||
|
}
|
||||||
|
tx := types.NewTx(txdata)
|
||||||
|
tx, _ = types.SignTx(tx, signer, key1)
|
||||||
|
|
||||||
|
b.AddTx(tx)
|
||||||
|
})
|
||||||
|
chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vm.Config{Tracer: logger.NewMarkdownLogger(&logger.Config{}, os.Stderr)}, nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create tester chain: %v", err)
|
||||||
|
}
|
||||||
|
if n, err := chain.InsertChain(blocks); err != nil {
|
||||||
|
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
block := chain.GetBlockByNumber(1)
|
||||||
|
|
||||||
|
// 1+2: Ensure EIP-1559 access lists are accounted for via gas usage.
|
||||||
|
innerGas := vm.GasQuickStep*2 + params.ColdSloadCostEIP2929*2
|
||||||
|
expectedGas := params.TxGas + 5*vm.GasFastestStep + vm.GasQuickStep + 100 + innerGas // 100 because 0xaaaa is in access list
|
||||||
|
if block.GasUsed() != expectedGas {
|
||||||
|
t.Fatalf("incorrect amount of gas spent: expected %d, got %d", expectedGas, block.GasUsed())
|
||||||
|
}
|
||||||
|
|
||||||
|
state, _ := chain.State()
|
||||||
|
|
||||||
|
// 3: Ensure that miner received only the tx's tip.
|
||||||
|
actual := state.GetBalance(block.Coinbase())
|
||||||
|
expected := new(big.Int).Add(
|
||||||
|
new(big.Int).SetUint64(block.GasUsed()*block.Transactions()[0].GasTipCap().Uint64()),
|
||||||
|
ethash.ConstantinopleBlockReward,
|
||||||
|
)
|
||||||
|
if actual.Cmp(expected) != 0 {
|
||||||
|
t.Fatalf("miner balance incorrect: expected %d, got %d", expected, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4: Ensure the tx sender paid for the gasUsed * (tip + block baseFee).
|
||||||
|
actual = new(big.Int).Sub(funds, state.GetBalance(addr1))
|
||||||
|
expected = new(big.Int).SetUint64(block.GasUsed() * (block.Transactions()[0].GasTipCap().Uint64() + block.BaseFee().Uint64()))
|
||||||
|
if actual.Cmp(expected) != 0 {
|
||||||
|
t.Fatalf("sender balance incorrect: expected %d, got %d", expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1067,26 +1067,32 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, error) {
|
|||||||
// - Add the contents of the optional tx access list (2930)
|
// - Add the contents of the optional tx access list (2930)
|
||||||
//
|
//
|
||||||
// Potential EIPs:
|
// Potential EIPs:
|
||||||
// - Reset transient storage(1153)
|
// - Reset access list (Berlin)
|
||||||
func (s *StateDB) Prepare(rules params.Rules, sender common.Address, dst *common.Address, precompiles []common.Address, list types.AccessList) {
|
// - Add coinbase to access list (EIP-3651)
|
||||||
|
// - Reset transient storage (EIP-1153)
|
||||||
|
func (s *StateDB) Prepare(rules params.Rules, sender, coinbase common.Address, dst *common.Address, precompiles []common.Address, list types.AccessList) {
|
||||||
if rules.IsBerlin {
|
if rules.IsBerlin {
|
||||||
// Clear out any leftover from previous executions
|
// Clear out any leftover from previous executions
|
||||||
s.accessList = newAccessList()
|
al := newAccessList()
|
||||||
|
s.accessList = al
|
||||||
|
|
||||||
s.AddAddressToAccessList(sender)
|
al.AddAddress(sender)
|
||||||
if dst != nil {
|
if dst != nil {
|
||||||
s.AddAddressToAccessList(*dst)
|
al.AddAddress(*dst)
|
||||||
// If it's a create-tx, the destination will be added inside evm.create
|
// If it's a create-tx, the destination will be added inside evm.create
|
||||||
}
|
}
|
||||||
for _, addr := range precompiles {
|
for _, addr := range precompiles {
|
||||||
s.AddAddressToAccessList(addr)
|
al.AddAddress(addr)
|
||||||
}
|
}
|
||||||
for _, el := range list {
|
for _, el := range list {
|
||||||
s.AddAddressToAccessList(el.Address)
|
al.AddAddress(el.Address)
|
||||||
for _, key := range el.StorageKeys {
|
for _, key := range el.StorageKeys {
|
||||||
s.AddSlotToAccessList(el.Address, key)
|
al.AddSlot(el.Address, key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if rules.IsShanghai { // EIP-3651: warm coinbase
|
||||||
|
al.AddAddress(coinbase)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Reset transient storage at the beginning of transaction execution
|
// Reset transient storage at the beginning of transaction execution
|
||||||
s.transientStorage = newTransientStorage()
|
s.transientStorage = newTransientStorage()
|
||||||
|
@ -322,7 +322,7 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
|
|||||||
// Execute the preparatory steps for state transition which includes:
|
// Execute the preparatory steps for state transition which includes:
|
||||||
// - prepare accessList(post-berlin)
|
// - prepare accessList(post-berlin)
|
||||||
// - reset transient storage(eip 1153)
|
// - reset transient storage(eip 1153)
|
||||||
st.state.Prepare(rules, msg.From(), msg.To(), vm.ActivePrecompiles(rules), msg.AccessList())
|
st.state.Prepare(rules, msg.From(), st.evm.Context.Coinbase, msg.To(), vm.ActivePrecompiles(rules), msg.AccessList())
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ret []byte
|
ret []byte
|
||||||
|
@ -69,7 +69,7 @@ type StateDB interface {
|
|||||||
// AddSlotToAccessList adds the given (address,slot) to the access list. This operation is safe to perform
|
// AddSlotToAccessList adds the given (address,slot) to the access list. This operation is safe to perform
|
||||||
// even if the feature/fork is not active yet
|
// even if the feature/fork is not active yet
|
||||||
AddSlotToAccessList(addr common.Address, slot common.Hash)
|
AddSlotToAccessList(addr common.Address, slot common.Hash)
|
||||||
Prepare(rules params.Rules, sender common.Address, dest *common.Address, precompiles []common.Address, txAccesses types.AccessList)
|
Prepare(rules params.Rules, sender, coinbase common.Address, dest *common.Address, precompiles []common.Address, txAccesses types.AccessList)
|
||||||
|
|
||||||
RevertToSnapshot(int)
|
RevertToSnapshot(int)
|
||||||
Snapshot() int
|
Snapshot() int
|
||||||
|
@ -122,7 +122,7 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) {
|
|||||||
// Execute the preparatory steps for state transition which includes:
|
// Execute the preparatory steps for state transition which includes:
|
||||||
// - prepare accessList(post-berlin)
|
// - prepare accessList(post-berlin)
|
||||||
// - reset transient storage(eip 1153)
|
// - reset transient storage(eip 1153)
|
||||||
cfg.State.Prepare(rules, cfg.Origin, &address, vm.ActivePrecompiles(rules), nil)
|
cfg.State.Prepare(rules, cfg.Origin, cfg.Coinbase, &address, vm.ActivePrecompiles(rules), nil)
|
||||||
|
|
||||||
cfg.State.CreateAccount(address)
|
cfg.State.CreateAccount(address)
|
||||||
// set the receiver's (the executing contract) code for execution.
|
// set the receiver's (the executing contract) code for execution.
|
||||||
@ -156,7 +156,7 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) {
|
|||||||
// Execute the preparatory steps for state transition which includes:
|
// Execute the preparatory steps for state transition which includes:
|
||||||
// - prepare accessList(post-berlin)
|
// - prepare accessList(post-berlin)
|
||||||
// - reset transient storage(eip 1153)
|
// - reset transient storage(eip 1153)
|
||||||
cfg.State.Prepare(rules, cfg.Origin, nil, vm.ActivePrecompiles(rules), nil)
|
cfg.State.Prepare(rules, cfg.Origin, cfg.Coinbase, nil, vm.ActivePrecompiles(rules), nil)
|
||||||
|
|
||||||
// Call the code with the given configuration.
|
// Call the code with the given configuration.
|
||||||
code, address, leftOverGas, err := vmenv.Create(
|
code, address, leftOverGas, err := vmenv.Create(
|
||||||
@ -185,7 +185,7 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, er
|
|||||||
// Execute the preparatory steps for state transition which includes:
|
// Execute the preparatory steps for state transition which includes:
|
||||||
// - prepare accessList(post-berlin)
|
// - prepare accessList(post-berlin)
|
||||||
// - reset transient storage(eip 1153)
|
// - reset transient storage(eip 1153)
|
||||||
statedb.Prepare(rules, cfg.Origin, &address, vm.ActivePrecompiles(rules), nil)
|
statedb.Prepare(rules, cfg.Origin, cfg.Coinbase, &address, vm.ActivePrecompiles(rules), nil)
|
||||||
|
|
||||||
// Call the code with the given configuration.
|
// Call the code with the given configuration.
|
||||||
ret, leftOverGas, err := vmenv.Call(
|
ret, leftOverGas, err := vmenv.Call(
|
||||||
|
@ -241,7 +241,7 @@ func runBenchmark(b *testing.B, t *StateTest) {
|
|||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for n := 0; n < b.N; n++ {
|
for n := 0; n < b.N; n++ {
|
||||||
snapshot := statedb.Snapshot()
|
snapshot := statedb.Snapshot()
|
||||||
statedb.Prepare(rules, msg.From(), msg.To(), vm.ActivePrecompiles(rules), msg.AccessList())
|
statedb.Prepare(rules, msg.From(), context.Coinbase, msg.To(), vm.ActivePrecompiles(rules), msg.AccessList())
|
||||||
b.StartTimer()
|
b.StartTimer()
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user