Skip to content

Commit

Permalink
all: implement EIP-2718 + EIP-2930
Browse files Browse the repository at this point in the history
  • Loading branch information
lightclient committed Oct 26, 2020
1 parent 1a55e20 commit 142d206
Show file tree
Hide file tree
Showing 27 changed files with 931 additions and 242 deletions.
17 changes: 9 additions & 8 deletions accounts/abi/bind/backends/simulated.go
Original file line number Diff line number Diff line change
Expand Up @@ -705,14 +705,15 @@ type callMsg struct {
ethereum.CallMsg
}

func (m callMsg) From() common.Address { return m.CallMsg.From }
func (m callMsg) Nonce() uint64 { return 0 }
func (m callMsg) CheckNonce() bool { return false }
func (m callMsg) To() *common.Address { return m.CallMsg.To }
func (m callMsg) GasPrice() *big.Int { return m.CallMsg.GasPrice }
func (m callMsg) Gas() uint64 { return m.CallMsg.Gas }
func (m callMsg) Value() *big.Int { return m.CallMsg.Value }
func (m callMsg) Data() []byte { return m.CallMsg.Data }
func (m callMsg) From() common.Address { return m.CallMsg.From }
func (m callMsg) Nonce() uint64 { return 0 }
func (m callMsg) CheckNonce() bool { return false }
func (m callMsg) To() *common.Address { return m.CallMsg.To }
func (m callMsg) GasPrice() *big.Int { return m.CallMsg.GasPrice }
func (m callMsg) Gas() uint64 { return m.CallMsg.Gas }
func (m callMsg) Value() *big.Int { return m.CallMsg.Value }
func (m callMsg) Data() []byte { return m.CallMsg.Data }
func (m callMsg) AccessList() *types.AccessList { return m.CallMsg.AccessList }

// filterBackend implements filters.Backend to support filtering for logs without
// taking bloom-bits acceleration structures into account.
Expand Down
2 changes: 1 addition & 1 deletion core/bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ func genValueTx(nbytes int) func(int, *BlockGen) {
return func(i int, gen *BlockGen) {
toaddr := common.Address{}
data := make([]byte, nbytes)
gas, _ := IntrinsicGas(data, false, false, false)
gas, _ := IntrinsicGas(data, nil, false, false, false, false)
tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(benchRootAddr), toaddr, big.NewInt(1), gas, nil, data), types.HomesteadSigner{}, benchRootKey)
gen.AddTx(tx)
}
Expand Down
70 changes: 70 additions & 0 deletions core/blockchain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3029,3 +3029,73 @@ func TestInitThenFailCreateContract(t *testing.T) {
}
}
}

func TestEIP2718Transition(t *testing.T) {
var (
aa = common.HexToAddress("0x000000000000000000000000000000000000aaaa")

// Generate a canonical chain to act as the main dataset
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.YoloV2ChainConfig,
Alloc: GenesisAlloc{
address: {Balance: funds},
// The address 0xAAAAA selfdestructs if called
aa: {
// Code needs to just selfdestruct
Code: []byte{
byte(vm.PC),
byte(vm.PC),
byte(vm.SLOAD),
byte(vm.SLOAD),
},
Nonce: 0,
Balance: big.NewInt(0),
},
},
}
genesis = gspec.MustCommit(db)
)

blocks, _ := GenerateChain(gspec.Config, genesis, engine, db, 1, func(i int, b *BlockGen) {
b.SetCoinbase(common.Address{1})

// One transaction to AAAA
accesses := types.AccessList{types.AccessTuple{
Address: &aa,
StorageKeys: []*common.Hash{
{0},
},
}}

tx := types.NewAccessListTransaction(gspec.Config.ChainID, 0, aa, big.NewInt(0), 30000, big.NewInt(1), nil, &accesses)
tx, _ = types.SignTx(tx, types.NewEIP2718Signer(gspec.Config.ChainID), key)

b.AddTx(tx)
})
// Import the canonical chain
diskdb := rawdb.NewMemoryDatabase()
gspec.MustCommit(diskdb)

chain, err := NewBlockChain(diskdb, nil, gspec.Config, engine, vm.Config{}, 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)

expected := params.TxGas + params.TxAccessListAddress + params.TxAccessListStorageKey + vm.GasQuickStep*2 + vm.WarmStorageReadCostEIP2929 + vm.ColdSloadCostEIP2929
if block.GasUsed() != expected {
t.Fatalf("incorrect amount of gas spent: expected %d, got %d", expected, block.GasUsed())

}
}
2 changes: 2 additions & 0 deletions core/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,6 @@ var (
// ErrIntrinsicGas is returned if the transaction is specified to use less gas
// than required to start the invocation.
ErrIntrinsicGas = errors.New("intrinsic gas too low")

ErrTxTypeInvalid = errors.New("invalid tx type")
)
19 changes: 18 additions & 1 deletion core/state_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,10 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
// for the transaction, gas used and an error if the transaction failed,
// indicating the block was invalid.
func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, cfg vm.Config) (*types.Receipt, error) {
if !config.IsYoloV2(header.Number) && tx.Type() != types.LegacyTxId {
return nil, ErrTxTypeInvalid
}

msg, err := tx.AsMessage(types.MakeSigner(config, header.Number))
if err != nil {
return nil, err
Expand All @@ -105,6 +109,14 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo
for _, addr := range vmenv.ActivePrecompiles() {
statedb.AddAddressToAccessList(addr)
}
if msg.AccessList() != nil {
for _, el := range *msg.AccessList() {
statedb.AddAddressToAccessList(*el.Address)
for _, key := range el.StorageKeys {
statedb.AddSlotToAccessList(*el.Address, *key)
}
}
}
}

// Apply the transaction to the current state (included in the env)
Expand All @@ -123,7 +135,12 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo

// Create a new receipt for the transaction, storing the intermediate root and gas used by the tx
// based on the eip phase, we're passing whether the root touch-delete accounts.
receipt := types.NewReceipt(root, result.Failed(), *usedGas)
var receipt *types.Receipt
if config.IsYoloV2(header.Number) {
receipt = types.NewEIP2718Receipt(tx.Type(), root, result.Failed(), *usedGas)
} else {
receipt = types.NewReceipt(root, result.Failed(), *usedGas)
}
receipt.TxHash = tx.Hash()
receipt.GasUsed = result.UsedGas
// if the transaction created a contract, store the creation address in the receipt.
Expand Down
31 changes: 21 additions & 10 deletions core/state_transition.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"math/big"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/params"
)
Expand Down Expand Up @@ -50,6 +51,7 @@ type StateTransition struct {
initialGas uint64
value *big.Int
data []byte
accessList *types.AccessList
state vm.StateDB
evm *vm.EVM
}
Expand All @@ -66,6 +68,7 @@ type Message interface {
Nonce() uint64
CheckNonce() bool
Data() []byte
AccessList() *types.AccessList
}

// ExecutionResult includes all output after executing given evm
Expand Down Expand Up @@ -104,10 +107,10 @@ func (result *ExecutionResult) Revert() []byte {
}

// IntrinsicGas computes the 'intrinsic gas' for a message with the given data.
func IntrinsicGas(data []byte, contractCreation, isHomestead bool, isEIP2028 bool) (uint64, error) {
func IntrinsicGas(data []byte, accessList *types.AccessList, isContractCreation bool, isHomestead, isEIP2028, isEIP2718 bool) (uint64, error) {
// Set the starting gas for the raw transaction
var gas uint64
if contractCreation && isHomestead {
if isContractCreation && isHomestead {
gas = params.TxGasContractCreation
} else {
gas = params.TxGas
Expand Down Expand Up @@ -137,19 +140,26 @@ func IntrinsicGas(data []byte, contractCreation, isHomestead bool, isEIP2028 boo
}
gas += z * params.TxDataZeroGas
}

if isEIP2718 && accessList != nil {
gas += uint64(accessList.Addresses()) * params.TxAccessListAddress
gas += uint64(accessList.StorageKeys()) * params.TxAccessListStorageKey
}

return gas, nil
}

// NewStateTransition initialises and returns a new state transition object.
func NewStateTransition(evm *vm.EVM, msg Message, gp *GasPool) *StateTransition {
return &StateTransition{
gp: gp,
evm: evm,
msg: msg,
gasPrice: msg.GasPrice(),
value: msg.Value(),
data: msg.Data(),
state: evm.StateDB,
gp: gp,
evm: evm,
msg: msg,
gasPrice: msg.GasPrice(),
value: msg.Value(),
data: msg.Data(),
state: evm.StateDB,
accessList: msg.AccessList(),
}
}

Expand Down Expand Up @@ -232,10 +242,11 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
sender := vm.AccountRef(msg.From())
homestead := st.evm.ChainConfig().IsHomestead(st.evm.BlockNumber)
istanbul := st.evm.ChainConfig().IsIstanbul(st.evm.BlockNumber)
yoloV2 := st.evm.ChainConfig().IsYoloV2(st.evm.BlockNumber)
contractCreation := msg.To() == nil

// Check clauses 4-5, subtract intrinsic gas if everything is correct
gas, err := IntrinsicGas(st.data, contractCreation, homestead, istanbul)
gas, err := IntrinsicGas(st.data, st.accessList, contractCreation, homestead, istanbul, yoloV2)
if err != nil {
return nil, err
}
Expand Down
4 changes: 3 additions & 1 deletion core/tx_pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ type TxPool struct {
mu sync.RWMutex

istanbul bool // Fork indicator whether we are in the istanbul stage.
yoloV2 bool // Fork indicator whether we are in the YoloV2 stage.

currentState *state.StateDB // Current state in the blockchain head
pendingNonces *txNoncer // Pending state tracking virtual nonces
Expand Down Expand Up @@ -550,7 +551,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
return ErrInsufficientFunds
}
// Ensure the transaction has more gas than the basic tx fee.
intrGas, err := IntrinsicGas(tx.Data(), tx.To() == nil, true, pool.istanbul)
intrGas, err := IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, true, pool.istanbul, pool.yoloV2)
if err != nil {
return err
}
Expand Down Expand Up @@ -1180,6 +1181,7 @@ func (pool *TxPool) reset(oldHead, newHead *types.Header) {
// Update all fork indicator by next pending block number.
next := new(big.Int).Add(newHead.Number, big.NewInt(1))
pool.istanbul = pool.chainconfig.IsIstanbul(next)
pool.yoloV2 = pool.chainconfig.IsYoloV2(next)
}

// promoteExecutables moves transactions that have become processable from the
Expand Down
Loading

0 comments on commit 142d206

Please sign in to comment.