From d9916d1acbd226a6b3b40b9ae8c005b63a9f9672 Mon Sep 17 00:00:00 2001 From: Guillaume Ballet Date: Tue, 14 Apr 2020 11:10:14 +0200 Subject: [PATCH 01/43] eth2: introduce RPC apis, catalyst command line option and extra instructions Co-Authored-By: Mikhail Kalinin --- cmd/geth/consolecmd_test.go | 2 +- cmd/geth/main.go | 1 + cmd/geth/usage.go | 1 + cmd/utils/flags.go | 8 + core/blockchain.go | 44 +++- core/chain_makers.go | 2 +- core/state_processor.go | 2 +- core/tx_pool.go | 2 + core/vm/evm.go | 7 + core/vm/jump_table.go | 18 ++ core/vm/opcodes.go | 342 ++++++++++++++++-------------- eth/backend.go | 10 + eth/eth2.go | 320 ++++++++++++++++++++++++++++ eth/eth2_test.go | 290 +++++++++++++++++++++++++ eth/ethconfig/config.go | 3 + eth/handler.go | 4 + eth/handler_eth.go | 1 + eth/handler_eth_test.go | 1 + eth/protocols/eth/handler.go | 3 + eth/protocols/eth/handler_test.go | 3 + eth/protocols/eth/handlers.go | 8 + miner/worker.go | 2 +- 22 files changed, 904 insertions(+), 170 deletions(-) create mode 100644 eth/eth2.go create mode 100644 eth/eth2_test.go diff --git a/cmd/geth/consolecmd_test.go b/cmd/geth/consolecmd_test.go index c3f41b187c4..38657eda8fd 100644 --- a/cmd/geth/consolecmd_test.go +++ b/cmd/geth/consolecmd_test.go @@ -31,7 +31,7 @@ import ( ) const ( - ipcAPIs = "admin:1.0 debug:1.0 eth:1.0 ethash:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0" + ipcAPIs = "admin:1.0 debug:1.0 eth2:1.0 eth:1.0 ethash:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0" httpAPIs = "eth:1.0 net:1.0 rpc:1.0 web3:1.0" ) diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 12a5ae9bfcb..78e65161daf 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -151,6 +151,7 @@ var ( utils.EVMInterpreterFlag, utils.MinerNotifyFullFlag, configFileFlag, + utils.CatalystFlag, } rpcFlags = []cli.Flag{ diff --git a/cmd/geth/usage.go b/cmd/geth/usage.go index d7f8fd7ab91..980794db735 100644 --- a/cmd/geth/usage.go +++ b/cmd/geth/usage.go @@ -235,6 +235,7 @@ var AppHelpFlagGroups = []flags.FlagGroup{ utils.SnapshotFlag, utils.BloomFilterSizeFlag, cli.HelpFlag, + utils.CatalystFlag, }, }, } diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index d631b8e332e..90c55aa4f96 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -755,6 +755,11 @@ var ( Usage: "External EVM configuration (default = built-in interpreter)", Value: "", } + // Catalyst + CatalystFlag = cli.BoolFlag{ + Name: "catalyst", + Usage: "Set geth into catalyst mode", + } ) // MakeDataDir retrieves the currently requested data directory, terminating @@ -1670,6 +1675,9 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { SetDNSDiscoveryDefaults(cfg, params.MainnetGenesisHash) } } + if ctx.GlobalIsSet(CatalystFlag.Name) { + cfg.Catalyst = true + } } // SetDNSDiscoveryDefaults configures DNS discovery with the given URL if diff --git a/core/blockchain.go b/core/blockchain.go index d65ce4f0484..19576f8b33f 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -1482,7 +1482,7 @@ func (bc *BlockChain) writeKnownBlock(block *types.Block) error { current := bc.CurrentBlock() if block.ParentHash() != current.Hash() { - if err := bc.reorg(current, block); err != nil { + if err := bc.Reorg(current, block); err != nil { return err } } @@ -1606,7 +1606,7 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types. if reorg { // Reorganise the chain if the parent is not the head block if block.ParentHash() != currentBlock.Hash() { - if err := bc.reorg(currentBlock, block); err != nil { + if err := bc.Reorg(currentBlock, block); err != nil { return NonStatTy, err } } @@ -1693,6 +1693,44 @@ func (bc *BlockChain) InsertChain(chain types.Blocks) (int, error) { return n, err } +// InsertChainWithoutSealVerification works exactly the same +// except for seal verification, seal verification is omitted +func (bc *BlockChain) InsertChainWithoutSealVerification(chain types.Blocks) (int, error) { + // Sanity check that we have something meaningful to import + if len(chain) == 0 { + return 0, nil + } + + bc.blockProcFeed.Send(true) + defer bc.blockProcFeed.Send(false) + + // Remove already known canon-blocks + var ( + block, prev *types.Block + ) + // Do a sanity check that the provided chain is actually ordered and linked + for i := 1; i < len(chain); i++ { + block = chain[i] + prev = chain[i-1] + if block.NumberU64() != prev.NumberU64()+1 || block.ParentHash() != prev.Hash() { + // Chain broke ancestry, log a message (programming error) and skip insertion + log.Error("Non contiguous block insert", "number", block.Number(), "hash", block.Hash(), + "parent", block.ParentHash(), "prevnumber", prev.Number(), "prevhash", prev.Hash()) + + return 0, fmt.Errorf("non contiguous insert: item %d is #%d [%x…], item %d is #%d [%x…] (parent [%x…])", i-1, prev.NumberU64(), + prev.Hash().Bytes()[:4], i, block.NumberU64(), block.Hash().Bytes()[:4], block.ParentHash().Bytes()[:4]) + } + } + // Pre-checks passed, start the full block imports + bc.wg.Add(1) + bc.chainmu.Lock() + n, err := bc.insertChain(chain, false) + bc.chainmu.Unlock() + bc.wg.Done() + + return n, err +} + // insertChain is the internal implementation of InsertChain, which assumes that // 1) chains are contiguous, and 2) The chain mutex is held. // @@ -2125,7 +2163,7 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator) (i // reorg takes two blocks, an old chain and a new chain and will reconstruct the // blocks and inserts them to be part of the new canonical chain and accumulates // potential missing transactions and post an event about them. -func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error { +func (bc *BlockChain) Reorg(oldBlock, newBlock *types.Block) error { var ( newChain types.Blocks oldChain types.Blocks diff --git a/core/chain_makers.go b/core/chain_makers.go index e058e5a78e5..ced491a0252 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -103,7 +103,7 @@ func (b *BlockGen) AddTxWithChain(bc *BlockChain, tx *types.Transaction) { b.SetCoinbase(common.Address{}) } b.statedb.Prepare(tx.Hash(), common.Hash{}, len(b.txs)) - receipt, err := ApplyTransaction(b.config, bc, &b.header.Coinbase, b.gasPool, b.statedb, b.header, tx, &b.header.GasUsed, vm.Config{}) + receipt, err := ApplyTransaction(b.config, bc, &b.header.Coinbase, b.gasPool, b.statedb, b.header, tx, &b.header.GasUsed, vm.Config{}, nil) if err != nil { panic(err) } diff --git a/core/state_processor.go b/core/state_processor.go index 40a953f0d4f..c996df2fc2f 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -138,7 +138,7 @@ func applyTransaction(msg types.Message, config *params.ChainConfig, bc ChainCon // and uses the input parameters for its environment. It returns the receipt // 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) { +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, bcctx *vm.BeaconChainContext) (*types.Receipt, error) { msg, err := tx.AsMessage(types.MakeSigner(config, header.Number)) if err != nil { return nil, err diff --git a/core/tx_pool.go b/core/tx_pool.go index 5db1d3df329..d82ea120084 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -153,6 +153,8 @@ type TxPoolConfig struct { GlobalQueue uint64 // Maximum number of non-executable transaction slots for all accounts Lifetime time.Duration // Maximum amount of time non-executable transaction are queued + + ProcessTxs bool // Whether transaction processing is enabled upon node's launch } // DefaultTxPoolConfig contains the default configurations for the transaction diff --git a/core/vm/evm.go b/core/vm/evm.go index 3f16f33b2d8..7b1027b5a0e 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -76,6 +76,11 @@ func run(evm *EVM, contract *Contract, input []byte, readOnly bool) ([]byte, err return nil, errors.New("no compatible interpreter") } +type BeaconChainContext struct { + BeaconRoots []common.Hash // Provides information for BEACONSTATEROOT + RandaoMix common.Hash // Provides information for RANDAOMIX +} + // BlockContext provides the EVM with auxiliary information. Once provided // it shouldn't be modified. type BlockContext struct { @@ -93,6 +98,8 @@ type BlockContext struct { BlockNumber *big.Int // Provides information for NUMBER Time *big.Int // Provides information for TIME Difficulty *big.Int // Provides information for DIFFICULTY + + BeaconCtx *BeaconChainContext // Provides eth2-specific information } // TxContext provides the EVM with information about a transaction. diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index bb1800ea919..947419a1c89 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -62,6 +62,24 @@ var ( // JumpTable contains the EVM opcodes supported at a given fork. type JumpTable [256]*operation +//nolint:deadcode +func newCatalystInstructionSet() JumpTable { + instructionSet := newBerlinInstructionSet() + instructionSet[BEACONSTATEROOT] = &operation{ + execute: opBeaconStateRoot, + constantGas: GasExtStep, + minStack: minStack(1, 1), + maxStack: maxStack(1, 1), + } + instructionSet[RANDAOMIX] = &operation{ + execute: opRandaoMix, + constantGas: GasExtStep, + minStack: minStack(0, 1), + maxStack: maxStack(0, 1), + } + return instructionSet +} + // newBerlinInstructionSet returns the frontier, homestead, byzantium, // contantinople, istanbul, petersburg and berlin instructions. func newBerlinInstructionSet() JumpTable { diff --git a/core/vm/opcodes.go b/core/vm/opcodes.go index b0adf37d0c2..6ff06b0e424 100644 --- a/core/vm/opcodes.go +++ b/core/vm/opcodes.go @@ -101,24 +101,29 @@ const ( NUMBER DIFFICULTY GASLIMIT - CHAINID OpCode = 0x46 - SELFBALANCE OpCode = 0x47 + CHAINID OpCode = 0x46 + SELFBALANCE OpCode = 0x47 + BEACONSTATEROOT OpCode = 0x48 + RANDAOMIX OpCode = 0x49 ) // 0x50 range - 'storage' and execution. const ( - POP OpCode = 0x50 - MLOAD OpCode = 0x51 - MSTORE OpCode = 0x52 - MSTORE8 OpCode = 0x53 - SLOAD OpCode = 0x54 - SSTORE OpCode = 0x55 - JUMP OpCode = 0x56 - JUMPI OpCode = 0x57 - PC OpCode = 0x58 - MSIZE OpCode = 0x59 - GAS OpCode = 0x5a - JUMPDEST OpCode = 0x5b + POP OpCode = 0x50 + MLOAD OpCode = 0x51 + MSTORE OpCode = 0x52 + MSTORE8 OpCode = 0x53 + SLOAD OpCode = 0x54 + SSTORE OpCode = 0x55 + JUMP OpCode = 0x56 + JUMPI OpCode = 0x57 + PC OpCode = 0x58 + MSIZE OpCode = 0x59 + GAS OpCode = 0x5a + JUMPDEST OpCode = 0x5b + BEGINSUB OpCode = 0x5c + RETURNSUB OpCode = 0x5d + JUMPSUB OpCode = 0x5e ) // 0x60 range. @@ -272,14 +277,16 @@ var opCodeToString = map[OpCode]string{ EXTCODEHASH: "EXTCODEHASH", // 0x40 range - block operations. - BLOCKHASH: "BLOCKHASH", - COINBASE: "COINBASE", - TIMESTAMP: "TIMESTAMP", - NUMBER: "NUMBER", - DIFFICULTY: "DIFFICULTY", - GASLIMIT: "GASLIMIT", - CHAINID: "CHAINID", - SELFBALANCE: "SELFBALANCE", + BLOCKHASH: "BLOCKHASH", + COINBASE: "COINBASE", + TIMESTAMP: "TIMESTAMP", + NUMBER: "NUMBER", + DIFFICULTY: "DIFFICULTY", + GASLIMIT: "GASLIMIT", + CHAINID: "CHAINID", + SELFBALANCE: "SELFBALANCE", + BEACONSTATEROOT: "BEACONSTATEROOT", + RANDAOMIX: "RANDAOMIX", // 0x50 range - 'storage' and execution. POP: "POP", @@ -297,6 +304,10 @@ var opCodeToString = map[OpCode]string{ GAS: "GAS", JUMPDEST: "JUMPDEST", + BEGINSUB: "BEGINSUB", + JUMPSUB: "JUMPSUB", + RETURNSUB: "RETURNSUB", + // 0x60 range - push. PUSH1: "PUSH1", PUSH2: "PUSH2", @@ -396,147 +407,152 @@ func (op OpCode) String() string { } var stringToOp = map[string]OpCode{ - "STOP": STOP, - "ADD": ADD, - "MUL": MUL, - "SUB": SUB, - "DIV": DIV, - "SDIV": SDIV, - "MOD": MOD, - "SMOD": SMOD, - "EXP": EXP, - "NOT": NOT, - "LT": LT, - "GT": GT, - "SLT": SLT, - "SGT": SGT, - "EQ": EQ, - "ISZERO": ISZERO, - "SIGNEXTEND": SIGNEXTEND, - "AND": AND, - "OR": OR, - "XOR": XOR, - "BYTE": BYTE, - "SHL": SHL, - "SHR": SHR, - "SAR": SAR, - "ADDMOD": ADDMOD, - "MULMOD": MULMOD, - "SHA3": SHA3, - "ADDRESS": ADDRESS, - "BALANCE": BALANCE, - "ORIGIN": ORIGIN, - "CALLER": CALLER, - "CALLVALUE": CALLVALUE, - "CALLDATALOAD": CALLDATALOAD, - "CALLDATASIZE": CALLDATASIZE, - "CALLDATACOPY": CALLDATACOPY, - "CHAINID": CHAINID, - "DELEGATECALL": DELEGATECALL, - "STATICCALL": STATICCALL, - "CODESIZE": CODESIZE, - "CODECOPY": CODECOPY, - "GASPRICE": GASPRICE, - "EXTCODESIZE": EXTCODESIZE, - "EXTCODECOPY": EXTCODECOPY, - "RETURNDATASIZE": RETURNDATASIZE, - "RETURNDATACOPY": RETURNDATACOPY, - "EXTCODEHASH": EXTCODEHASH, - "BLOCKHASH": BLOCKHASH, - "COINBASE": COINBASE, - "TIMESTAMP": TIMESTAMP, - "NUMBER": NUMBER, - "DIFFICULTY": DIFFICULTY, - "GASLIMIT": GASLIMIT, - "SELFBALANCE": SELFBALANCE, - "POP": POP, - "MLOAD": MLOAD, - "MSTORE": MSTORE, - "MSTORE8": MSTORE8, - "SLOAD": SLOAD, - "SSTORE": SSTORE, - "JUMP": JUMP, - "JUMPI": JUMPI, - "PC": PC, - "MSIZE": MSIZE, - "GAS": GAS, - "JUMPDEST": JUMPDEST, - "PUSH1": PUSH1, - "PUSH2": PUSH2, - "PUSH3": PUSH3, - "PUSH4": PUSH4, - "PUSH5": PUSH5, - "PUSH6": PUSH6, - "PUSH7": PUSH7, - "PUSH8": PUSH8, - "PUSH9": PUSH9, - "PUSH10": PUSH10, - "PUSH11": PUSH11, - "PUSH12": PUSH12, - "PUSH13": PUSH13, - "PUSH14": PUSH14, - "PUSH15": PUSH15, - "PUSH16": PUSH16, - "PUSH17": PUSH17, - "PUSH18": PUSH18, - "PUSH19": PUSH19, - "PUSH20": PUSH20, - "PUSH21": PUSH21, - "PUSH22": PUSH22, - "PUSH23": PUSH23, - "PUSH24": PUSH24, - "PUSH25": PUSH25, - "PUSH26": PUSH26, - "PUSH27": PUSH27, - "PUSH28": PUSH28, - "PUSH29": PUSH29, - "PUSH30": PUSH30, - "PUSH31": PUSH31, - "PUSH32": PUSH32, - "DUP1": DUP1, - "DUP2": DUP2, - "DUP3": DUP3, - "DUP4": DUP4, - "DUP5": DUP5, - "DUP6": DUP6, - "DUP7": DUP7, - "DUP8": DUP8, - "DUP9": DUP9, - "DUP10": DUP10, - "DUP11": DUP11, - "DUP12": DUP12, - "DUP13": DUP13, - "DUP14": DUP14, - "DUP15": DUP15, - "DUP16": DUP16, - "SWAP1": SWAP1, - "SWAP2": SWAP2, - "SWAP3": SWAP3, - "SWAP4": SWAP4, - "SWAP5": SWAP5, - "SWAP6": SWAP6, - "SWAP7": SWAP7, - "SWAP8": SWAP8, - "SWAP9": SWAP9, - "SWAP10": SWAP10, - "SWAP11": SWAP11, - "SWAP12": SWAP12, - "SWAP13": SWAP13, - "SWAP14": SWAP14, - "SWAP15": SWAP15, - "SWAP16": SWAP16, - "LOG0": LOG0, - "LOG1": LOG1, - "LOG2": LOG2, - "LOG3": LOG3, - "LOG4": LOG4, - "CREATE": CREATE, - "CREATE2": CREATE2, - "CALL": CALL, - "RETURN": RETURN, - "CALLCODE": CALLCODE, - "REVERT": REVERT, - "SELFDESTRUCT": SELFDESTRUCT, + "STOP": STOP, + "ADD": ADD, + "MUL": MUL, + "SUB": SUB, + "DIV": DIV, + "SDIV": SDIV, + "MOD": MOD, + "SMOD": SMOD, + "EXP": EXP, + "NOT": NOT, + "LT": LT, + "GT": GT, + "SLT": SLT, + "SGT": SGT, + "EQ": EQ, + "ISZERO": ISZERO, + "SIGNEXTEND": SIGNEXTEND, + "AND": AND, + "OR": OR, + "XOR": XOR, + "BYTE": BYTE, + "SHL": SHL, + "SHR": SHR, + "SAR": SAR, + "ADDMOD": ADDMOD, + "MULMOD": MULMOD, + "SHA3": SHA3, + "ADDRESS": ADDRESS, + "BALANCE": BALANCE, + "ORIGIN": ORIGIN, + "CALLER": CALLER, + "CALLVALUE": CALLVALUE, + "CALLDATALOAD": CALLDATALOAD, + "CALLDATASIZE": CALLDATASIZE, + "CALLDATACOPY": CALLDATACOPY, + "CHAINID": CHAINID, + "DELEGATECALL": DELEGATECALL, + "STATICCALL": STATICCALL, + "CODESIZE": CODESIZE, + "CODECOPY": CODECOPY, + "GASPRICE": GASPRICE, + "EXTCODESIZE": EXTCODESIZE, + "EXTCODECOPY": EXTCODECOPY, + "RETURNDATASIZE": RETURNDATASIZE, + "RETURNDATACOPY": RETURNDATACOPY, + "EXTCODEHASH": EXTCODEHASH, + "BLOCKHASH": BLOCKHASH, + "COINBASE": COINBASE, + "TIMESTAMP": TIMESTAMP, + "NUMBER": NUMBER, + "DIFFICULTY": DIFFICULTY, + "GASLIMIT": GASLIMIT, + "SELFBALANCE": SELFBALANCE, + "BEACONSTATEROOT": BEACONSTATEROOT, + "RANDAOMIX": RANDAOMIX, + "POP": POP, + "MLOAD": MLOAD, + "MSTORE": MSTORE, + "MSTORE8": MSTORE8, + "SLOAD": SLOAD, + "SSTORE": SSTORE, + "JUMP": JUMP, + "JUMPI": JUMPI, + "PC": PC, + "MSIZE": MSIZE, + "GAS": GAS, + "JUMPDEST": JUMPDEST, + "BEGINSUB": BEGINSUB, + "RETURNSUB": RETURNSUB, + "JUMPSUB": JUMPSUB, + "PUSH1": PUSH1, + "PUSH2": PUSH2, + "PUSH3": PUSH3, + "PUSH4": PUSH4, + "PUSH5": PUSH5, + "PUSH6": PUSH6, + "PUSH7": PUSH7, + "PUSH8": PUSH8, + "PUSH9": PUSH9, + "PUSH10": PUSH10, + "PUSH11": PUSH11, + "PUSH12": PUSH12, + "PUSH13": PUSH13, + "PUSH14": PUSH14, + "PUSH15": PUSH15, + "PUSH16": PUSH16, + "PUSH17": PUSH17, + "PUSH18": PUSH18, + "PUSH19": PUSH19, + "PUSH20": PUSH20, + "PUSH21": PUSH21, + "PUSH22": PUSH22, + "PUSH23": PUSH23, + "PUSH24": PUSH24, + "PUSH25": PUSH25, + "PUSH26": PUSH26, + "PUSH27": PUSH27, + "PUSH28": PUSH28, + "PUSH29": PUSH29, + "PUSH30": PUSH30, + "PUSH31": PUSH31, + "PUSH32": PUSH32, + "DUP1": DUP1, + "DUP2": DUP2, + "DUP3": DUP3, + "DUP4": DUP4, + "DUP5": DUP5, + "DUP6": DUP6, + "DUP7": DUP7, + "DUP8": DUP8, + "DUP9": DUP9, + "DUP10": DUP10, + "DUP11": DUP11, + "DUP12": DUP12, + "DUP13": DUP13, + "DUP14": DUP14, + "DUP15": DUP15, + "DUP16": DUP16, + "SWAP1": SWAP1, + "SWAP2": SWAP2, + "SWAP3": SWAP3, + "SWAP4": SWAP4, + "SWAP5": SWAP5, + "SWAP6": SWAP6, + "SWAP7": SWAP7, + "SWAP8": SWAP8, + "SWAP9": SWAP9, + "SWAP10": SWAP10, + "SWAP11": SWAP11, + "SWAP12": SWAP12, + "SWAP13": SWAP13, + "SWAP14": SWAP14, + "SWAP15": SWAP15, + "SWAP16": SWAP16, + "LOG0": LOG0, + "LOG1": LOG1, + "LOG2": LOG2, + "LOG3": LOG3, + "LOG4": LOG4, + "CREATE": CREATE, + "CREATE2": CREATE2, + "CALL": CALL, + "RETURN": RETURN, + "CALLCODE": CALLCODE, + "REVERT": REVERT, + "SELFDESTRUCT": SELFDESTRUCT, } // StringToOp finds the opcode whose name is stored in `str`. diff --git a/eth/backend.go b/eth/backend.go index 9cf8b856630..704cfaa4e5a 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -220,9 +220,14 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { EventMux: eth.eventMux, Checkpoint: checkpoint, Whitelist: config.Whitelist, + Catalyst: config.Catalyst, }); err != nil { return nil, err } + if config.Catalyst { + // Activate transaction processing by default in catalyst mode + eth.handler.acceptTxs = 1 + } eth.miner = miner.New(eth, &config.Miner, chainConfig, eth.EventMux(), eth.engine, eth.isLocalBlock) eth.miner.SetExtra(makeExtraData(config.Miner.ExtraData)) @@ -337,6 +342,11 @@ func (s *Ethereum) APIs() []rpc.API { Version: "1.0", Service: s.netRPCService, Public: true, + }, { + Namespace: "eth2", + Version: "1.0", + Service: NewEth2API(s), + Public: true, }, }...) } diff --git a/eth/eth2.go b/eth/eth2.go new file mode 100644 index 00000000000..6927c51fc00 --- /dev/null +++ b/eth/eth2.go @@ -0,0 +1,320 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package eth + +import ( + "fmt" + "math/big" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/log" + chainParams "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/trie" +) + +type Eth2API struct { + eth *Ethereum + env *eth2bpenv + head common.Hash +} + +// NewEth2API creates a new API definition for the eth2 prototype. +func NewEth2API(eth *Ethereum) *Eth2API { + return &Eth2API{eth: eth} +} + +type eth2bpenv struct { + state *state.StateDB + tcount int + gasPool *core.GasPool + + header *types.Header + txs []*types.Transaction + receipts []*types.Receipt +} + +func (api *Eth2API) commitTransaction(tx *types.Transaction, coinbase common.Address, bcParentRoots []common.Hash, randao common.Hash) error { + //snap := eth2rpc.current.state.Snapshot() + + chain := api.eth.BlockChain() + receipt, err := core.ApplyTransaction(chain.Config(), chain, &coinbase, api.env.gasPool, api.env.state, api.env.header, tx, &api.env.header.GasUsed, *chain.GetVMConfig(), &vm.BeaconChainContext{BeaconRoots: bcParentRoots, RandaoMix: randao}) + if err != nil { + //w.current.state.RevertToSnapshot(snap) + return err + } + api.env.txs = append(api.env.txs, tx) + api.env.receipts = append(api.env.receipts, receipt) + + return nil +} + +func (api *Eth2API) makeEnv(parent *types.Block, header *types.Header) error { + state, err := api.eth.BlockChain().StateAt(parent.Root()) + if err != nil { + return err + } + api.env = ð2bpenv{ + state: state, + header: header, + gasPool: new(core.GasPool).AddGas(header.GasLimit), + } + return nil +} + +// Structure described at https://hackmd.io/T9x2mMA4S7us8tJwEB3FDQ +type ProduceBlockParams struct { + ParentHash common.Hash `json:"parent_hash"` + RandaoMix common.Hash `json:"randao_mix"` + Slot uint64 `json:"slot"` + Timestamp uint64 `json:"timestamp"` + RecentBeaconBlockRoots []common.Hash `json:"recent_beacon_block_roots"` +} + +// Structure described at https://ethresear.ch/t/executable-beacon-chain/8271 +type ExecutableData struct { + Coinbase common.Address `json:"coinbase"` + StateRoot common.Hash `json:"state_root"` + GasLimit uint64 `json:"gas_limit"` + GasUsed uint64 `json:"gas_used"` + Transactions []*types.Transaction `json:"transactions"` + ReceiptRoot common.Hash `json:"receipt_root"` + LogsBloom []byte `json:"logs_bloom"` + BlockHash common.Hash `json:"block_hash"` + ParentHash common.Hash `json:"parent_hash"` + Difficulty *big.Int `json:"difficulty"` +} + +// ProduceBlock creates a new block, inserts it into the chain, and returns the "execution +// data" required for eth2 clients to process the new block. +func (api *Eth2API) ProduceBlock(params ProduceBlockParams) (*ExecutableData, error) { + log.Info("Produce block", "parentHash", params.ParentHash) + + bc := api.eth.BlockChain() + parent := bc.GetBlockByHash(params.ParentHash) + pool := api.eth.TxPool() + + if parent.Time() >= params.Timestamp { + return nil, fmt.Errorf("child timestamp lower than parent's: %d >= %d", parent.Time(), params.Timestamp) + } + // this will ensure we're not going off too far in the future + if now := uint64(time.Now().Unix()); params.Timestamp > now+1 { + wait := time.Duration(params.Timestamp-now) * time.Second + log.Info("Producing block too far in the future", "wait", common.PrettyDuration(wait)) + time.Sleep(wait) + } + + pending, err := pool.Pending() + if err != nil { + return nil, err + } + + coinbase, err := api.eth.Etherbase() + if err != nil { + return nil, err + } + num := parent.Number() + header := &types.Header{ + ParentHash: parent.Hash(), + Number: num.Add(num, common.Big1), + Coinbase: coinbase, + GasLimit: parent.GasLimit(), // Keep the gas limit constant in this prototype + Extra: []byte{}, + Time: params.Timestamp, + } + err = api.eth.Engine().Prepare(bc, header) + if err != nil { + return nil, err + } + + err = api.makeEnv(parent, header) + if err != nil { + return nil, err + } + signer := types.NewEIP155Signer(bc.Config().ChainID) + txs := types.NewTransactionsByPriceAndNonce(signer, pending) + + var transactions []*types.Transaction + + for { + if api.env.gasPool.Gas() < chainParams.TxGas { + log.Trace("Not enough gas for further transactions", "have", api.env.gasPool, "want", chainParams.TxGas) + break + } + + tx := txs.Peek() + if tx == nil { + break + } + + from, _ := types.Sender(signer, tx) + // XXX replay protection check is missing + + // Execute the transaction + api.env.state.Prepare(tx.Hash(), common.Hash{}, api.env.tcount) + err := api.commitTransaction(tx, coinbase, params.RecentBeaconBlockRoots, params.RandaoMix) + switch err { + case core.ErrGasLimitReached: + // Pop the current out-of-gas transaction without shifting in the next from the account + log.Trace("Gas limit exceeded for current block", "sender", from) + txs.Pop() + + case core.ErrNonceTooLow: + // New head notification data race between the transaction pool and miner, shift + log.Trace("Skipping transaction with low nonce", "sender", from, "nonce", tx.Nonce()) + txs.Shift() + + case core.ErrNonceTooHigh: + // Reorg notification data race between the transaction pool and miner, skip account = + log.Trace("Skipping account with high nonce", "sender", from, "nonce", tx.Nonce()) + txs.Pop() + + case nil: + // Everything ok, collect the logs and shift in the next transaction from the same account + api.env.tcount++ + txs.Shift() + transactions = append(transactions, tx) + + default: + // Strange error, discard the transaction and get the next in line (note, the + // nonce-too-high clause will prevent us from executing in vain). + log.Debug("Transaction failed, account skipped", "hash", tx.Hash(), "err", err) + txs.Shift() + } + } + + block, err := api.eth.Engine().FinalizeAndAssemble(bc, header, api.env.state, transactions, nil /* uncles */, api.env.receipts) + if err != nil { + return nil, err + } + + var logs []*types.Log + var receipts = make(types.Receipts, len(api.env.receipts)) + hash := block.Hash() + for i, receipt := range api.env.receipts { + // add block location fields + receipt.BlockHash = hash + receipt.BlockNumber = block.Number() + receipt.TransactionIndex = uint(i) + + receipts[i] = new(types.Receipt) + *receipts[i] = *receipt + // Update the block hash in all logs since it is now available and not when the + // receipt/log of individual transactions were created. + for _, log := range receipt.Logs { + log.BlockHash = hash + } + logs = append(logs, receipt.Logs...) + } + + block.Header().ReceiptHash = types.DeriveSha(receipts, new(trie.Trie)) + + return &ExecutableData{ + Coinbase: block.Coinbase(), + StateRoot: block.Root(), + GasLimit: block.GasLimit(), + GasUsed: block.GasUsed(), + Transactions: []*types.Transaction(block.Transactions()), + ReceiptRoot: block.ReceiptHash(), + LogsBloom: block.Bloom().Bytes(), + BlockHash: block.Hash(), + Difficulty: block.Difficulty(), + ParentHash: block.ParentHash(), + }, nil +} + +// Structure described at https://hackmd.io/T9x2mMA4S7us8tJwEB3FDQ +type InsertBlockParams struct { + RandaoMix common.Hash `json:"randao_mix"` + Slot uint64 `json:"slot"` + Timestamp uint64 `json:"timestamp"` + RecentBeaconBlockRoots []common.Hash `json:"recent_beacon_block_roots"` + ExecutableData ExecutableData `json:"executable_data"` +} + +var zeroNonce [8]byte + +func insertBlockParamsToBlock(params InsertBlockParams, number *big.Int) *types.Block { + header := &types.Header{ + ParentHash: params.ExecutableData.ParentHash, + UncleHash: types.EmptyUncleHash, + Coinbase: params.ExecutableData.Coinbase, + Root: params.ExecutableData.StateRoot, + TxHash: types.DeriveSha(types.Transactions(params.ExecutableData.Transactions), trie.NewStackTrie(nil)), + ReceiptHash: params.ExecutableData.ReceiptRoot, + Bloom: types.BytesToBloom(params.ExecutableData.LogsBloom), + Difficulty: params.ExecutableData.Difficulty, + Number: number, + GasLimit: params.ExecutableData.GasLimit, + GasUsed: params.ExecutableData.GasUsed, + Time: params.Timestamp, + Extra: nil, + MixDigest: common.Hash{}, + Nonce: zeroNonce, + } + block := types.NewBlockWithHeader(header).WithBody(params.ExecutableData.Transactions, nil /* uncles */) + + return block +} + +// InsertBlock creates an Eth1 block, inserts it in the chain, and either returns true, +// or false + an error. This is a bit redundant for go, but simplifies things on the +// eth2 side. +func (api *Eth2API) InsertBlock(params InsertBlockParams) (bool, error) { + // compute block number as parent.number + 1 + parent := api.eth.BlockChain().GetBlockByHash(params.ExecutableData.ParentHash) + if parent == nil { + return false, fmt.Errorf("could not find parent %x", params.ExecutableData.ParentHash) + } + number := big.NewInt(0) + number.Add(parent.Number(), big.NewInt(1)) + + block := insertBlockParamsToBlock(params, number) + _, err := api.eth.BlockChain().InsertChainWithoutSealVerification(types.Blocks([]*types.Block{block})) + + return (err == nil), err +} + +// Used in tests to add a the list of transactions from a block to the tx pool. +func (api *Eth2API) addBlockTxs(block *types.Block) error { + for _, tx := range block.Transactions() { + api.eth.txPool.AddLocal(tx) + } + + return nil +} + +//func (api *Eth2API) SetHead(newHead common.Hash) error { +//oldBlock := api.eth.BlockChain().CurrentBlock() + +//if oldBlock.Hash() == newHead { +//return nil +//} + +//newBlock := api.eth.BlockChain().GetBlockByHash(newHead) + +//err := api.eth.BlockChain().Reorg(oldBlock, newBlock) +//if err != nil { +//return err +//} +//api.head = newHead +//return nil +//} diff --git a/eth/eth2_test.go b/eth/eth2_test.go new file mode 100644 index 00000000000..72599bc0c3e --- /dev/null +++ b/eth/eth2_test.go @@ -0,0 +1,290 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package eth + +import ( + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/ethash" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/node" + "github.com/ethereum/go-ethereum/params" +) + +var ( + testBalance = big.NewInt(2e10) +) + +func generateTestChain() (*core.Genesis, []*types.Block) { + db := rawdb.NewMemoryDatabase() + config := params.AllEthashProtocolChanges + genesis := &core.Genesis{ + Config: config, + Alloc: core.GenesisAlloc{testAddr: {Balance: testBalance}}, + ExtraData: []byte("test genesis"), + Timestamp: 9000, + } + generate := func(i int, g *core.BlockGen) { + g.OffsetTime(5) + g.SetExtra([]byte("test")) + } + gblock := genesis.ToBlock(db) + engine := ethash.NewFaker() + blocks, _ := core.GenerateChain(config, gblock, engine, db, 10, generate) + blocks = append([]*types.Block{gblock}, blocks...) + return genesis, blocks +} + +func generateTestChainWithFork(n int, fork int) (*core.Genesis, []*types.Block, []*types.Block) { + if fork >= n { + fork = n - 1 + } + db := rawdb.NewMemoryDatabase() + config := params.AllEthashProtocolChanges + genesis := &core.Genesis{ + Config: config, + Alloc: core.GenesisAlloc{testAddr: {Balance: testBalance}}, + ExtraData: []byte("test genesis"), + Timestamp: 9000, + } + generate := func(i int, g *core.BlockGen) { + g.OffsetTime(5) + g.SetExtra([]byte("test")) + } + generateFork := func(i int, g *core.BlockGen) { + g.OffsetTime(5) + g.SetExtra([]byte("testF")) + } + gblock := genesis.ToBlock(db) + engine := ethash.NewFaker() + blocks, _ := core.GenerateChain(config, gblock, engine, db, n, generate) + blocks = append([]*types.Block{gblock}, blocks...) + forkedBlocks, _ := core.GenerateChain(config, blocks[fork], engine, db, n-fork, generateFork) + return genesis, blocks, forkedBlocks +} + +func TestEth2ProduceBlock(t *testing.T) { + genesis, blocks := generateTestChain() + + n, err := node.New(&node.Config{}) + if err != nil { + t.Fatalf("could not get node: %v", err) + } + ethservice, err := New(n, &Config{Genesis: genesis, Ethash: ethash.Config{PowMode: ethash.ModeFake}}) + if err != nil { + t.Fatalf("can't create new ethereum service: %v", err) + } + if err := n.Start(); err != nil { + t.Fatalf("can't start test node: %v", err) + } + if _, err := ethservice.BlockChain().InsertChain(blocks[1:9]); err != nil { + t.Fatalf("can't import test blocks: %v", err) + } + ethservice.SetEtherbase(testAddr) + + api := NewEth2API(ethservice) + signer := types.NewEIP155Signer(ethservice.BlockChain().Config().ChainID) + tx, err := types.SignTx(types.NewTransaction(0, blocks[8].Coinbase(), big.NewInt(1000), params.TxGas, nil, nil), signer, testKey) + if err != nil { + t.Fatalf("error signing transaction, err=%v", err) + } + ethservice.txPool.AddLocal(tx) + blockParams := ProduceBlockParams{ + ParentHash: blocks[8].ParentHash(), + Slot: blocks[8].NumberU64(), + Timestamp: blocks[8].Time(), + } + execData, err := api.ProduceBlock(blockParams) + + if err != nil { + t.Fatalf("error producing block, err=%v", err) + } + + if len(execData.Transactions) != 1 { + t.Fatalf("invalid number of transactions %d != 1", len(execData.Transactions)) + } +} + +func TestEth2ProduceBlockWithAnotherBlocksTxs(t *testing.T) { + genesis, blocks := generateTestChain() + + n, err := node.New(&node.Config{}) + if err != nil { + t.Fatalf("could not get node: %v", err) + } + ethservice, err := New(n, &Config{Genesis: genesis, Ethash: ethash.Config{PowMode: ethash.ModeFake}}) + if err != nil { + t.Fatalf("can't create new ethereum service: %v", err) + } + if err := n.Start(); err != nil { + t.Fatalf("can't start test node: %v", err) + } + if _, err := ethservice.BlockChain().InsertChain(blocks[1:9]); err != nil { + t.Fatalf("can't import test blocks: %v", err) + } + ethservice.SetEtherbase(testAddr) + + api := NewEth2API(ethservice) + + // Put the 10th block's tx in the pool and produce a new block + api.addBlockTxs(blocks[9]) + blockParams := ProduceBlockParams{ + ParentHash: blocks[9].ParentHash(), + Slot: blocks[9].NumberU64(), + Timestamp: blocks[9].Time(), + } + execData, err := api.ProduceBlock(blockParams) + if err != nil { + t.Fatalf("error producing block, err=%v", err) + } + + if len(execData.Transactions) != blocks[9].Transactions().Len() { + t.Fatalf("invalid number of transactions %d != 1", len(execData.Transactions)) + } +} + +func TestEth2InsertBlock(t *testing.T) { + genesis, blocks, forkedBlocks := generateTestChainWithFork(10, 4) + + n, err := node.New(&node.Config{}) + if err != nil { + t.Fatalf("could not get node: %v", err) + } + ethservice, err := New(n, &Config{Genesis: genesis, Ethash: ethash.Config{PowMode: ethash.ModeFake}}) + if err != nil { + t.Fatalf("can't create new ethereum service: %v", err) + } + if err := n.Start(); err != nil { + t.Fatalf("can't start test node: %v", err) + } + if _, err := ethservice.BlockChain().InsertChain(blocks[1:5]); err != nil { + t.Fatalf("can't import test blocks: %v", err) + } + + api := NewEth2API(ethservice) + for i := 5; i < 10; i++ { + p := InsertBlockParams{ + Slot: blocks[i].NumberU64(), + Timestamp: blocks[i].Time(), + ExecutableData: ExecutableData{ + ParentHash: ethservice.BlockChain().CurrentBlock().Hash(), + Coinbase: blocks[i].Coinbase(), + StateRoot: blocks[i].Root(), + Difficulty: blocks[i].Difficulty(), + GasLimit: blocks[i].GasLimit(), + GasUsed: blocks[i].GasUsed(), + Transactions: []*types.Transaction(blocks[i].Transactions()), + ReceiptRoot: blocks[i].ReceiptHash(), + LogsBloom: blocks[i].Bloom().Bytes(), + BlockHash: blocks[i].Hash(), + }, + } + success, err := api.InsertBlock(p) + if err != nil || !success { + t.Fatalf("Failed to insert block: %v", err) + } + } + + // Introduce the fork point + lastBlockNum := blocks[4].Number() + lastBlock := blocks[4] + for i := 0; i < 4; i++ { + lastBlockNum.Add(lastBlockNum, big.NewInt(1)) + p := InsertBlockParams{ + Slot: lastBlockNum.Uint64(), + Timestamp: forkedBlocks[i].Time(), + ExecutableData: ExecutableData{ + ParentHash: lastBlock.Hash(), + Coinbase: forkedBlocks[i].Coinbase(), + StateRoot: forkedBlocks[i].Root(), + Difficulty: forkedBlocks[i].Difficulty(), + GasLimit: forkedBlocks[i].GasLimit(), + GasUsed: forkedBlocks[i].GasUsed(), + Transactions: []*types.Transaction(blocks[i].Transactions()), + ReceiptRoot: forkedBlocks[i].ReceiptHash(), + LogsBloom: forkedBlocks[i].Bloom().Bytes(), + BlockHash: forkedBlocks[i].Hash(), + }, + } + success, err := api.InsertBlock(p) + if err != nil || !success { + t.Fatalf("Failed to insert forked block #%d: %v", i, err) + } + lastBlock = insertBlockParamsToBlock(p, lastBlockNum) + } + + exp := common.HexToHash("526db89301fc787799ef8c272fe512898b97ad96d0b69caee19dc5393b092110") + if ethservice.BlockChain().CurrentBlock().Hash() != exp { + t.Fatalf("Wrong head after inserting fork %x != %x", exp, ethservice.BlockChain().CurrentBlock().Hash()) + } +} + +//func TestEth2SetHead(t *testing.T) { +//genesis, blocks, forkedBlocks := generateTestChainWithFork(10, 5) + +//n, err := node.New(&node.Config{}) +//if err != nil { +//t.Fatalf("could not get node: %v", err) +//} +//ethservice, err := New(n, &Config{Genesis: genesis, Ethash: ethash.Config{PowMode: ethash.ModeFake}}) +//if err != nil { +//t.Fatalf("can't create new ethereum service: %v", err) +//} +//if err := n.Start(); err != nil { +//t.Fatalf("can't start test node: %v", err) +//} +//if _, err := ethservice.BlockChain().InsertChain(blocks[1:5]); err != nil { +//t.Fatalf("can't import test blocks: %v", err) +//} + +//api := NewEth2API(ethservice) +//for i := 5; i < 10; i++ { +//var blockRLP bytes.Buffer +//rlp.Encode(&blockRLP, blocks[i]) +//err := api.InsertBlock(blockRLP.Bytes()) +//if err != nil { +//t.Fatalf("Failed to insert block: %v", err) +//} +//} +//api.head = blocks[9].Hash() + +//if ethservice.BlockChain().CurrentBlock().Hash() != blocks[9].Hash() { +//t.Fatalf("Wrong head") +//} + +//for i := 0; i < 3; i++ { +//var blockRLP bytes.Buffer +//rlp.Encode(&blockRLP, forkedBlocks[i]) +//err := api.InsertBlock(blockRLP.Bytes()) +//if err != nil { +//t.Fatalf("Failed to insert block: %v", err) +//} +//} + +//api.SetHead(forkedBlocks[2].Hash()) + +//if ethservice.BlockChain().CurrentBlock().Hash() == forkedBlocks[2].Hash() { +//t.Fatalf("Wrong head after inserting fork %x != %x", blocks[9].Hash(), ethservice.BlockChain().CurrentBlock().Hash()) +//} +//if api.head != forkedBlocks[2].Hash() { +//t.Fatalf("Registered wrong head") +//} +//} diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index 0c6eb0bdd7d..6b21b5b0613 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -201,6 +201,9 @@ type Config struct { // Berlin block override (TODO: remove after the fork) OverrideBerlin *big.Int `toml:",omitempty"` + + // Specify if catalyst mode is enabled + Catalyst bool `toml:",omitempty"` } // CreateConsensusEngine creates a consensus engine for the given chain configuration. diff --git a/eth/handler.go b/eth/handler.go index 11c8565de10..1a3951515e4 100644 --- a/eth/handler.go +++ b/eth/handler.go @@ -85,6 +85,7 @@ type handlerConfig struct { EventMux *event.TypeMux // Legacy event mux, deprecate for `feed` Checkpoint *params.TrustedCheckpoint // Hard coded checkpoint for sync challenges Whitelist map[uint64]common.Hash // Hard coded whitelist for sync challenged + Catalyst bool // True iff this is catalyst } type handler struct { @@ -123,6 +124,8 @@ type handler struct { chainSync *chainSyncer wg sync.WaitGroup peerWG sync.WaitGroup + + IsCatalyst bool } // newHandler returns a handler for all Ethereum chain management protocol. @@ -142,6 +145,7 @@ func newHandler(config *handlerConfig) (*handler, error) { whitelist: config.Whitelist, txsyncCh: make(chan *txsync), quitSync: make(chan struct{}), + IsCatalyst: config.Catalyst, } if config.Sync == downloader.FullSync { // The database seems empty as the current block is the genesis. Yet the fast diff --git a/eth/handler_eth.go b/eth/handler_eth.go index 3ff9f2245be..1fe8fb5aba3 100644 --- a/eth/handler_eth.go +++ b/eth/handler_eth.go @@ -39,6 +39,7 @@ type ethHandler handler func (h *ethHandler) Chain() *core.BlockChain { return h.chain } func (h *ethHandler) StateBloom() *trie.SyncBloom { return h.stateBloom } func (h *ethHandler) TxPool() eth.TxPool { return h.txpool } +func (h *ethHandler) Catalyst() bool { return h.IsCatalyst } // RunPeer is invoked when a peer joins on the `eth` protocol. func (h *ethHandler) RunPeer(peer *eth.Peer, hand eth.Handler) error { diff --git a/eth/handler_eth_test.go b/eth/handler_eth_test.go index 5f5d4e9e82d..a29e8a4d303 100644 --- a/eth/handler_eth_test.go +++ b/eth/handler_eth_test.go @@ -54,6 +54,7 @@ func (h *testEthHandler) TxPool() eth.TxPool { panic("no backi func (h *testEthHandler) AcceptTxs() bool { return true } func (h *testEthHandler) RunPeer(*eth.Peer, eth.Handler) error { panic("not used in tests") } func (h *testEthHandler) PeerInfo(enode.ID) interface{} { panic("not used in tests") } +func (h *testEthHandler) Catalyst() bool { return false } func (h *testEthHandler) Handle(peer *eth.Peer, packet eth.Packet) error { switch packet := packet.(type) { diff --git a/eth/protocols/eth/handler.go b/eth/protocols/eth/handler.go index 0dc3de98982..5815c9459e9 100644 --- a/eth/protocols/eth/handler.go +++ b/eth/protocols/eth/handler.go @@ -92,6 +92,9 @@ type Backend interface { // the remote peer. Only packets not consumed by the protocol handler will // be forwarded to the backend. Handle(peer *Peer, packet Packet) error + + // Catalist returns true if the backend in running in catalyst mode + Catalyst() bool } // TxPool defines the methods needed by the protocol handler to serve transactions. diff --git a/eth/protocols/eth/handler_test.go b/eth/protocols/eth/handler_test.go index 30beae931b6..7daca0bb60c 100644 --- a/eth/protocols/eth/handler_test.go +++ b/eth/protocols/eth/handler_test.go @@ -108,6 +108,9 @@ func (b *testBackend) AcceptTxs() bool { func (b *testBackend) Handle(*Peer, Packet) error { panic("data processing tests should be done in the handler package") } +func (b *testBackend) Catalyst() bool { + return false +} // Tests that block headers can be retrieved from a remote chain based on user queries. func TestGetBlockHeaders64(t *testing.T) { testGetBlockHeaders(t, 64) } diff --git a/eth/protocols/eth/handlers.go b/eth/protocols/eth/handlers.go index 8433fa343a7..8be01613ed3 100644 --- a/eth/protocols/eth/handlers.go +++ b/eth/protocols/eth/handlers.go @@ -273,6 +273,10 @@ func answerGetReceiptsQuery(backend Backend, query GetReceiptsPacket, peer *Peer } func handleNewBlockhashes(backend Backend, msg Decoder, peer *Peer) error { + if backend.Catalyst() { + return nil + } + // A batch of new block announcements just arrived ann := new(NewBlockHashesPacket) if err := msg.Decode(ann); err != nil { @@ -287,6 +291,10 @@ func handleNewBlockhashes(backend Backend, msg Decoder, peer *Peer) error { } func handleNewBlock(backend Backend, msg Decoder, peer *Peer) error { + if backend.Catalyst() { + return nil + } + // Retrieve and decode the propagated block ann := new(NewBlockPacket) if err := msg.Decode(ann); err != nil { diff --git a/miner/worker.go b/miner/worker.go index 2cee6af0c32..d484690ede7 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -736,7 +736,7 @@ func (w *worker) updateSnapshot() { func (w *worker) commitTransaction(tx *types.Transaction, coinbase common.Address) ([]*types.Log, error) { snap := w.current.state.Snapshot() - receipt, err := core.ApplyTransaction(w.chainConfig, w.chain, &coinbase, w.current.gasPool, w.current.state, w.current.header, tx, &w.current.header.GasUsed, *w.chain.GetVMConfig()) + receipt, err := core.ApplyTransaction(w.chainConfig, w.chain, &coinbase, w.current.gasPool, w.current.state, w.current.header, tx, &w.current.header.GasUsed, *w.chain.GetVMConfig(), nil) if err != nil { w.current.state.RevertToSnapshot(snap) return nil, err From fad2587aff666b452dcfe8d7868cc740c83f2972 Mon Sep 17 00:00:00 2001 From: Guillaume Ballet Date: Thu, 8 Apr 2021 16:29:18 +0200 Subject: [PATCH 02/43] Enable catalyst with option --catalyst --- core/vm/interpreter.go | 2 ++ core/vm/jump_table.go | 1 + params/config.go | 19 +++++++++++++------ 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 3b67ad6dea4..9c56199f2eb 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -98,6 +98,8 @@ func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter { if cfg.JumpTable[STOP] == nil { var jt JumpTable switch { + case evm.chainRules.IsCatalyst: + jt = catalystInstructionSet case evm.chainRules.IsBerlin: jt = berlinInstructionSet case evm.chainRules.IsIstanbul: diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index 947419a1c89..8e794833961 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -57,6 +57,7 @@ var ( constantinopleInstructionSet = newConstantinopleInstructionSet() istanbulInstructionSet = newIstanbulInstructionSet() berlinInstructionSet = newBerlinInstructionSet() + catalystInstructionSet = newCatalystInstructionSet() ) // JumpTable contains the EVM opcodes supported at a given fork. diff --git a/params/config.go b/params/config.go index 143e2e2a369..f6b19f28b6a 100644 --- a/params/config.go +++ b/params/config.go @@ -244,16 +244,16 @@ var ( // // This configuration is intentionally not using keyed fields to force anyone // adding flags to the config to also have to set these fields. - AllEthashProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, new(EthashConfig), nil} + AllEthashProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, new(EthashConfig), nil} // AllCliqueProtocolChanges contains every protocol change (EIPs) introduced // and accepted by the Ethereum core developers into the Clique consensus. // // This configuration is intentionally not using keyed fields to force anyone // adding flags to the config to also have to set these fields. - AllCliqueProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, &CliqueConfig{Period: 0, Epoch: 30000}} + AllCliqueProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, nil, &CliqueConfig{Period: 0, Epoch: 30000}} - TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, new(EthashConfig), nil} + TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, new(EthashConfig), nil} TestRules = TestChainConfig.Rules(new(big.Int)) ) @@ -326,8 +326,9 @@ type ChainConfig struct { MuirGlacierBlock *big.Int `json:"muirGlacierBlock,omitempty"` // Eip-2384 (bomb delay) switch block (nil = no fork, 0 = already activated) BerlinBlock *big.Int `json:"berlinBlock,omitempty"` // Berlin switch block (nil = no fork, 0 = already on berlin) - YoloV3Block *big.Int `json:"yoloV3Block,omitempty"` // YOLO v3: Gas repricings TODO @holiman add EIP references - EWASMBlock *big.Int `json:"ewasmBlock,omitempty"` // EWASM switch block (nil = no fork, 0 = already activated) + YoloV3Block *big.Int `json:"yoloV3Block,omitempty"` // YOLO v3: Gas repricings TODO @holiman add EIP references + EWASMBlock *big.Int `json:"ewasmBlock,omitempty"` // EWASM switch block (nil = no fork, 0 = already activated) + CatalystBlock *big.Int `json:"catalystBlock,omitempty"` // Catalyst switch block (nil = no fork, 0 = already on catalyst) // Various consensus engines Ethash *EthashConfig `json:"ethash,omitempty"` @@ -440,6 +441,11 @@ func (c *ChainConfig) IsBerlin(num *big.Int) bool { return isForked(c.BerlinBlock, num) || isForked(c.YoloV3Block, num) } +// IsCatalyst returns whether num is either equal to the Merge fork block or greater. +func (c *ChainConfig) IsCatalyst(num *big.Int) bool { + return isForked(c.CatalystBlock, num) +} + // IsEWASM returns whether num represents a block number after the EWASM fork func (c *ChainConfig) IsEWASM(num *big.Int) bool { return isForked(c.EWASMBlock, num) @@ -623,7 +629,7 @@ type Rules struct { ChainID *big.Int IsHomestead, IsEIP150, IsEIP155, IsEIP158 bool IsByzantium, IsConstantinople, IsPetersburg, IsIstanbul bool - IsBerlin bool + IsBerlin, IsCatalyst bool } // Rules ensures c's ChainID is not nil. @@ -643,5 +649,6 @@ func (c *ChainConfig) Rules(num *big.Int) Rules { IsPetersburg: c.IsPetersburg(num), IsIstanbul: c.IsIstanbul(num), IsBerlin: c.IsBerlin(num), + IsCatalyst: c.IsCatalyst(num), } } From 9a35a224cd2cc98c6d8097b071237e56b024ebdb Mon Sep 17 00:00:00 2001 From: Guillaume Ballet Date: Thu, 8 Apr 2021 16:47:36 +0200 Subject: [PATCH 03/43] Fix rebase and merge errors --- core/vm/instructions.go | 27 +++++++++++++++++++++++++++ core/vm/opcodes.go | 34 ++++++++++++---------------------- eth/handler.go | 3 +++ 3 files changed, 42 insertions(+), 22 deletions(-) diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 3277674ee82..7cb281b8edb 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -480,6 +480,33 @@ func opGasLimit(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([ return nil, nil } +func opBeaconStateRoot(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + num := scope.Stack.peek() + num64, overflow := num.Uint64WithOverflow() + if overflow { + num.Clear() + return nil, nil + } + var upper, lower uint64 + upper = interpreter.evm.Context.BlockNumber.Uint64() + if upper < 257 { + lower = 0 + } else { + lower = upper - 256 + } + if num64 >= lower && num64 < upper { + num.SetBytes(interpreter.evm.Context.BeaconCtx.BeaconRoots[num64-lower].Bytes()) + } else { + num.Clear() + } + return nil, nil +} + +func opRandaoMix(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + scope.Stack.push(new(uint256.Int).SetBytes(interpreter.evm.Context.BeaconCtx.RandaoMix.Bytes())) + return nil, nil +} + func opPop(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { scope.Stack.pop() return nil, nil diff --git a/core/vm/opcodes.go b/core/vm/opcodes.go index 6ff06b0e424..436b55522c1 100644 --- a/core/vm/opcodes.go +++ b/core/vm/opcodes.go @@ -109,21 +109,18 @@ const ( // 0x50 range - 'storage' and execution. const ( - POP OpCode = 0x50 - MLOAD OpCode = 0x51 - MSTORE OpCode = 0x52 - MSTORE8 OpCode = 0x53 - SLOAD OpCode = 0x54 - SSTORE OpCode = 0x55 - JUMP OpCode = 0x56 - JUMPI OpCode = 0x57 - PC OpCode = 0x58 - MSIZE OpCode = 0x59 - GAS OpCode = 0x5a - JUMPDEST OpCode = 0x5b - BEGINSUB OpCode = 0x5c - RETURNSUB OpCode = 0x5d - JUMPSUB OpCode = 0x5e + POP OpCode = 0x50 + MLOAD OpCode = 0x51 + MSTORE OpCode = 0x52 + MSTORE8 OpCode = 0x53 + SLOAD OpCode = 0x54 + SSTORE OpCode = 0x55 + JUMP OpCode = 0x56 + JUMPI OpCode = 0x57 + PC OpCode = 0x58 + MSIZE OpCode = 0x59 + GAS OpCode = 0x5a + JUMPDEST OpCode = 0x5b ) // 0x60 range. @@ -304,10 +301,6 @@ var opCodeToString = map[OpCode]string{ GAS: "GAS", JUMPDEST: "JUMPDEST", - BEGINSUB: "BEGINSUB", - JUMPSUB: "JUMPSUB", - RETURNSUB: "RETURNSUB", - // 0x60 range - push. PUSH1: "PUSH1", PUSH2: "PUSH2", @@ -474,9 +467,6 @@ var stringToOp = map[string]OpCode{ "MSIZE": MSIZE, "GAS": GAS, "JUMPDEST": JUMPDEST, - "BEGINSUB": BEGINSUB, - "RETURNSUB": RETURNSUB, - "JUMPSUB": JUMPSUB, "PUSH1": PUSH1, "PUSH2": PUSH2, "PUSH3": PUSH3, diff --git a/eth/handler.go b/eth/handler.go index 1a3951515e4..094db818900 100644 --- a/eth/handler.go +++ b/eth/handler.go @@ -147,6 +147,9 @@ func newHandler(config *handlerConfig) (*handler, error) { quitSync: make(chan struct{}), IsCatalyst: config.Catalyst, } + if h.IsCatalyst { + h.acceptTxs = 1 + } if config.Sync == downloader.FullSync { // The database seems empty as the current block is the genesis. Yet the fast // block is ahead, so fast sync was enabled for this node at a certain point. From ccfb7371531906f06edcccb3d20c6e94226e7b3d Mon Sep 17 00:00:00 2001 From: Guillaume Ballet Date: Fri, 9 Apr 2021 14:19:11 +0200 Subject: [PATCH 04/43] remove the code that isn't needed in rayonism --- core/blockchain.go | 6 +- core/chain_makers.go | 2 +- core/state_processor.go | 2 +- core/types/transaction.go | 2 + core/vm/evm.go | 7 - core/vm/instructions.go | 27 ---- core/vm/interpreter.go | 2 - core/vm/jump_table.go | 19 --- core/vm/opcodes.go | 308 +++++++++++++++++++------------------- eth/eth2.go | 38 +++-- miner/worker.go | 2 +- 11 files changed, 182 insertions(+), 233 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index 19576f8b33f..f30a674f787 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -1482,7 +1482,7 @@ func (bc *BlockChain) writeKnownBlock(block *types.Block) error { current := bc.CurrentBlock() if block.ParentHash() != current.Hash() { - if err := bc.Reorg(current, block); err != nil { + if err := bc.reorg(current, block); err != nil { return err } } @@ -1606,7 +1606,7 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types. if reorg { // Reorganise the chain if the parent is not the head block if block.ParentHash() != currentBlock.Hash() { - if err := bc.Reorg(currentBlock, block); err != nil { + if err := bc.reorg(currentBlock, block); err != nil { return NonStatTy, err } } @@ -2163,7 +2163,7 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator) (i // reorg takes two blocks, an old chain and a new chain and will reconstruct the // blocks and inserts them to be part of the new canonical chain and accumulates // potential missing transactions and post an event about them. -func (bc *BlockChain) Reorg(oldBlock, newBlock *types.Block) error { +func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error { var ( newChain types.Blocks oldChain types.Blocks diff --git a/core/chain_makers.go b/core/chain_makers.go index ced491a0252..e058e5a78e5 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -103,7 +103,7 @@ func (b *BlockGen) AddTxWithChain(bc *BlockChain, tx *types.Transaction) { b.SetCoinbase(common.Address{}) } b.statedb.Prepare(tx.Hash(), common.Hash{}, len(b.txs)) - receipt, err := ApplyTransaction(b.config, bc, &b.header.Coinbase, b.gasPool, b.statedb, b.header, tx, &b.header.GasUsed, vm.Config{}, nil) + receipt, err := ApplyTransaction(b.config, bc, &b.header.Coinbase, b.gasPool, b.statedb, b.header, tx, &b.header.GasUsed, vm.Config{}) if err != nil { panic(err) } diff --git a/core/state_processor.go b/core/state_processor.go index c996df2fc2f..40a953f0d4f 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -138,7 +138,7 @@ func applyTransaction(msg types.Message, config *params.ChainConfig, bc ChainCon // and uses the input parameters for its environment. It returns the receipt // 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, bcctx *vm.BeaconChainContext) (*types.Receipt, error) { +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) { msg, err := tx.AsMessage(types.MakeSigner(config, header.Number)) if err != nil { return nil, err diff --git a/core/types/transaction.go b/core/types/transaction.go index a35e07a5a31..3fee4fbfbee 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -20,6 +20,7 @@ import ( "bytes" "container/heap" "errors" + "fmt" "io" "math/big" "sync/atomic" @@ -429,6 +430,7 @@ type TransactionsByPriceAndNonce struct { // Note, the input map is reowned so the caller should not interact any more with // if after providing it to the constructor. func NewTransactionsByPriceAndNonce(signer Signer, txs map[common.Address]Transactions) *TransactionsByPriceAndNonce { + fmt.Println("tx found", len(txs)) // Initialize a price and received time based heap with the head transactions heads := make(TxByPriceAndTime, 0, len(txs)) for from, accTxs := range txs { diff --git a/core/vm/evm.go b/core/vm/evm.go index 7b1027b5a0e..3f16f33b2d8 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -76,11 +76,6 @@ func run(evm *EVM, contract *Contract, input []byte, readOnly bool) ([]byte, err return nil, errors.New("no compatible interpreter") } -type BeaconChainContext struct { - BeaconRoots []common.Hash // Provides information for BEACONSTATEROOT - RandaoMix common.Hash // Provides information for RANDAOMIX -} - // BlockContext provides the EVM with auxiliary information. Once provided // it shouldn't be modified. type BlockContext struct { @@ -98,8 +93,6 @@ type BlockContext struct { BlockNumber *big.Int // Provides information for NUMBER Time *big.Int // Provides information for TIME Difficulty *big.Int // Provides information for DIFFICULTY - - BeaconCtx *BeaconChainContext // Provides eth2-specific information } // TxContext provides the EVM with information about a transaction. diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 7cb281b8edb..3277674ee82 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -480,33 +480,6 @@ func opGasLimit(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([ return nil, nil } -func opBeaconStateRoot(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - num := scope.Stack.peek() - num64, overflow := num.Uint64WithOverflow() - if overflow { - num.Clear() - return nil, nil - } - var upper, lower uint64 - upper = interpreter.evm.Context.BlockNumber.Uint64() - if upper < 257 { - lower = 0 - } else { - lower = upper - 256 - } - if num64 >= lower && num64 < upper { - num.SetBytes(interpreter.evm.Context.BeaconCtx.BeaconRoots[num64-lower].Bytes()) - } else { - num.Clear() - } - return nil, nil -} - -func opRandaoMix(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.push(new(uint256.Int).SetBytes(interpreter.evm.Context.BeaconCtx.RandaoMix.Bytes())) - return nil, nil -} - func opPop(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { scope.Stack.pop() return nil, nil diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 9c56199f2eb..3b67ad6dea4 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -98,8 +98,6 @@ func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter { if cfg.JumpTable[STOP] == nil { var jt JumpTable switch { - case evm.chainRules.IsCatalyst: - jt = catalystInstructionSet case evm.chainRules.IsBerlin: jt = berlinInstructionSet case evm.chainRules.IsIstanbul: diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index 8e794833961..bb1800ea919 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -57,30 +57,11 @@ var ( constantinopleInstructionSet = newConstantinopleInstructionSet() istanbulInstructionSet = newIstanbulInstructionSet() berlinInstructionSet = newBerlinInstructionSet() - catalystInstructionSet = newCatalystInstructionSet() ) // JumpTable contains the EVM opcodes supported at a given fork. type JumpTable [256]*operation -//nolint:deadcode -func newCatalystInstructionSet() JumpTable { - instructionSet := newBerlinInstructionSet() - instructionSet[BEACONSTATEROOT] = &operation{ - execute: opBeaconStateRoot, - constantGas: GasExtStep, - minStack: minStack(1, 1), - maxStack: maxStack(1, 1), - } - instructionSet[RANDAOMIX] = &operation{ - execute: opRandaoMix, - constantGas: GasExtStep, - minStack: minStack(0, 1), - maxStack: maxStack(0, 1), - } - return instructionSet -} - // newBerlinInstructionSet returns the frontier, homestead, byzantium, // contantinople, istanbul, petersburg and berlin instructions. func newBerlinInstructionSet() JumpTable { diff --git a/core/vm/opcodes.go b/core/vm/opcodes.go index 436b55522c1..b0adf37d0c2 100644 --- a/core/vm/opcodes.go +++ b/core/vm/opcodes.go @@ -101,10 +101,8 @@ const ( NUMBER DIFFICULTY GASLIMIT - CHAINID OpCode = 0x46 - SELFBALANCE OpCode = 0x47 - BEACONSTATEROOT OpCode = 0x48 - RANDAOMIX OpCode = 0x49 + CHAINID OpCode = 0x46 + SELFBALANCE OpCode = 0x47 ) // 0x50 range - 'storage' and execution. @@ -274,16 +272,14 @@ var opCodeToString = map[OpCode]string{ EXTCODEHASH: "EXTCODEHASH", // 0x40 range - block operations. - BLOCKHASH: "BLOCKHASH", - COINBASE: "COINBASE", - TIMESTAMP: "TIMESTAMP", - NUMBER: "NUMBER", - DIFFICULTY: "DIFFICULTY", - GASLIMIT: "GASLIMIT", - CHAINID: "CHAINID", - SELFBALANCE: "SELFBALANCE", - BEACONSTATEROOT: "BEACONSTATEROOT", - RANDAOMIX: "RANDAOMIX", + BLOCKHASH: "BLOCKHASH", + COINBASE: "COINBASE", + TIMESTAMP: "TIMESTAMP", + NUMBER: "NUMBER", + DIFFICULTY: "DIFFICULTY", + GASLIMIT: "GASLIMIT", + CHAINID: "CHAINID", + SELFBALANCE: "SELFBALANCE", // 0x50 range - 'storage' and execution. POP: "POP", @@ -400,149 +396,147 @@ func (op OpCode) String() string { } var stringToOp = map[string]OpCode{ - "STOP": STOP, - "ADD": ADD, - "MUL": MUL, - "SUB": SUB, - "DIV": DIV, - "SDIV": SDIV, - "MOD": MOD, - "SMOD": SMOD, - "EXP": EXP, - "NOT": NOT, - "LT": LT, - "GT": GT, - "SLT": SLT, - "SGT": SGT, - "EQ": EQ, - "ISZERO": ISZERO, - "SIGNEXTEND": SIGNEXTEND, - "AND": AND, - "OR": OR, - "XOR": XOR, - "BYTE": BYTE, - "SHL": SHL, - "SHR": SHR, - "SAR": SAR, - "ADDMOD": ADDMOD, - "MULMOD": MULMOD, - "SHA3": SHA3, - "ADDRESS": ADDRESS, - "BALANCE": BALANCE, - "ORIGIN": ORIGIN, - "CALLER": CALLER, - "CALLVALUE": CALLVALUE, - "CALLDATALOAD": CALLDATALOAD, - "CALLDATASIZE": CALLDATASIZE, - "CALLDATACOPY": CALLDATACOPY, - "CHAINID": CHAINID, - "DELEGATECALL": DELEGATECALL, - "STATICCALL": STATICCALL, - "CODESIZE": CODESIZE, - "CODECOPY": CODECOPY, - "GASPRICE": GASPRICE, - "EXTCODESIZE": EXTCODESIZE, - "EXTCODECOPY": EXTCODECOPY, - "RETURNDATASIZE": RETURNDATASIZE, - "RETURNDATACOPY": RETURNDATACOPY, - "EXTCODEHASH": EXTCODEHASH, - "BLOCKHASH": BLOCKHASH, - "COINBASE": COINBASE, - "TIMESTAMP": TIMESTAMP, - "NUMBER": NUMBER, - "DIFFICULTY": DIFFICULTY, - "GASLIMIT": GASLIMIT, - "SELFBALANCE": SELFBALANCE, - "BEACONSTATEROOT": BEACONSTATEROOT, - "RANDAOMIX": RANDAOMIX, - "POP": POP, - "MLOAD": MLOAD, - "MSTORE": MSTORE, - "MSTORE8": MSTORE8, - "SLOAD": SLOAD, - "SSTORE": SSTORE, - "JUMP": JUMP, - "JUMPI": JUMPI, - "PC": PC, - "MSIZE": MSIZE, - "GAS": GAS, - "JUMPDEST": JUMPDEST, - "PUSH1": PUSH1, - "PUSH2": PUSH2, - "PUSH3": PUSH3, - "PUSH4": PUSH4, - "PUSH5": PUSH5, - "PUSH6": PUSH6, - "PUSH7": PUSH7, - "PUSH8": PUSH8, - "PUSH9": PUSH9, - "PUSH10": PUSH10, - "PUSH11": PUSH11, - "PUSH12": PUSH12, - "PUSH13": PUSH13, - "PUSH14": PUSH14, - "PUSH15": PUSH15, - "PUSH16": PUSH16, - "PUSH17": PUSH17, - "PUSH18": PUSH18, - "PUSH19": PUSH19, - "PUSH20": PUSH20, - "PUSH21": PUSH21, - "PUSH22": PUSH22, - "PUSH23": PUSH23, - "PUSH24": PUSH24, - "PUSH25": PUSH25, - "PUSH26": PUSH26, - "PUSH27": PUSH27, - "PUSH28": PUSH28, - "PUSH29": PUSH29, - "PUSH30": PUSH30, - "PUSH31": PUSH31, - "PUSH32": PUSH32, - "DUP1": DUP1, - "DUP2": DUP2, - "DUP3": DUP3, - "DUP4": DUP4, - "DUP5": DUP5, - "DUP6": DUP6, - "DUP7": DUP7, - "DUP8": DUP8, - "DUP9": DUP9, - "DUP10": DUP10, - "DUP11": DUP11, - "DUP12": DUP12, - "DUP13": DUP13, - "DUP14": DUP14, - "DUP15": DUP15, - "DUP16": DUP16, - "SWAP1": SWAP1, - "SWAP2": SWAP2, - "SWAP3": SWAP3, - "SWAP4": SWAP4, - "SWAP5": SWAP5, - "SWAP6": SWAP6, - "SWAP7": SWAP7, - "SWAP8": SWAP8, - "SWAP9": SWAP9, - "SWAP10": SWAP10, - "SWAP11": SWAP11, - "SWAP12": SWAP12, - "SWAP13": SWAP13, - "SWAP14": SWAP14, - "SWAP15": SWAP15, - "SWAP16": SWAP16, - "LOG0": LOG0, - "LOG1": LOG1, - "LOG2": LOG2, - "LOG3": LOG3, - "LOG4": LOG4, - "CREATE": CREATE, - "CREATE2": CREATE2, - "CALL": CALL, - "RETURN": RETURN, - "CALLCODE": CALLCODE, - "REVERT": REVERT, - "SELFDESTRUCT": SELFDESTRUCT, + "STOP": STOP, + "ADD": ADD, + "MUL": MUL, + "SUB": SUB, + "DIV": DIV, + "SDIV": SDIV, + "MOD": MOD, + "SMOD": SMOD, + "EXP": EXP, + "NOT": NOT, + "LT": LT, + "GT": GT, + "SLT": SLT, + "SGT": SGT, + "EQ": EQ, + "ISZERO": ISZERO, + "SIGNEXTEND": SIGNEXTEND, + "AND": AND, + "OR": OR, + "XOR": XOR, + "BYTE": BYTE, + "SHL": SHL, + "SHR": SHR, + "SAR": SAR, + "ADDMOD": ADDMOD, + "MULMOD": MULMOD, + "SHA3": SHA3, + "ADDRESS": ADDRESS, + "BALANCE": BALANCE, + "ORIGIN": ORIGIN, + "CALLER": CALLER, + "CALLVALUE": CALLVALUE, + "CALLDATALOAD": CALLDATALOAD, + "CALLDATASIZE": CALLDATASIZE, + "CALLDATACOPY": CALLDATACOPY, + "CHAINID": CHAINID, + "DELEGATECALL": DELEGATECALL, + "STATICCALL": STATICCALL, + "CODESIZE": CODESIZE, + "CODECOPY": CODECOPY, + "GASPRICE": GASPRICE, + "EXTCODESIZE": EXTCODESIZE, + "EXTCODECOPY": EXTCODECOPY, + "RETURNDATASIZE": RETURNDATASIZE, + "RETURNDATACOPY": RETURNDATACOPY, + "EXTCODEHASH": EXTCODEHASH, + "BLOCKHASH": BLOCKHASH, + "COINBASE": COINBASE, + "TIMESTAMP": TIMESTAMP, + "NUMBER": NUMBER, + "DIFFICULTY": DIFFICULTY, + "GASLIMIT": GASLIMIT, + "SELFBALANCE": SELFBALANCE, + "POP": POP, + "MLOAD": MLOAD, + "MSTORE": MSTORE, + "MSTORE8": MSTORE8, + "SLOAD": SLOAD, + "SSTORE": SSTORE, + "JUMP": JUMP, + "JUMPI": JUMPI, + "PC": PC, + "MSIZE": MSIZE, + "GAS": GAS, + "JUMPDEST": JUMPDEST, + "PUSH1": PUSH1, + "PUSH2": PUSH2, + "PUSH3": PUSH3, + "PUSH4": PUSH4, + "PUSH5": PUSH5, + "PUSH6": PUSH6, + "PUSH7": PUSH7, + "PUSH8": PUSH8, + "PUSH9": PUSH9, + "PUSH10": PUSH10, + "PUSH11": PUSH11, + "PUSH12": PUSH12, + "PUSH13": PUSH13, + "PUSH14": PUSH14, + "PUSH15": PUSH15, + "PUSH16": PUSH16, + "PUSH17": PUSH17, + "PUSH18": PUSH18, + "PUSH19": PUSH19, + "PUSH20": PUSH20, + "PUSH21": PUSH21, + "PUSH22": PUSH22, + "PUSH23": PUSH23, + "PUSH24": PUSH24, + "PUSH25": PUSH25, + "PUSH26": PUSH26, + "PUSH27": PUSH27, + "PUSH28": PUSH28, + "PUSH29": PUSH29, + "PUSH30": PUSH30, + "PUSH31": PUSH31, + "PUSH32": PUSH32, + "DUP1": DUP1, + "DUP2": DUP2, + "DUP3": DUP3, + "DUP4": DUP4, + "DUP5": DUP5, + "DUP6": DUP6, + "DUP7": DUP7, + "DUP8": DUP8, + "DUP9": DUP9, + "DUP10": DUP10, + "DUP11": DUP11, + "DUP12": DUP12, + "DUP13": DUP13, + "DUP14": DUP14, + "DUP15": DUP15, + "DUP16": DUP16, + "SWAP1": SWAP1, + "SWAP2": SWAP2, + "SWAP3": SWAP3, + "SWAP4": SWAP4, + "SWAP5": SWAP5, + "SWAP6": SWAP6, + "SWAP7": SWAP7, + "SWAP8": SWAP8, + "SWAP9": SWAP9, + "SWAP10": SWAP10, + "SWAP11": SWAP11, + "SWAP12": SWAP12, + "SWAP13": SWAP13, + "SWAP14": SWAP14, + "SWAP15": SWAP15, + "SWAP16": SWAP16, + "LOG0": LOG0, + "LOG1": LOG1, + "LOG2": LOG2, + "LOG3": LOG3, + "LOG4": LOG4, + "CREATE": CREATE, + "CREATE2": CREATE2, + "CALL": CALL, + "RETURN": RETURN, + "CALLCODE": CALLCODE, + "REVERT": REVERT, + "SELFDESTRUCT": SELFDESTRUCT, } // StringToOp finds the opcode whose name is stored in `str`. diff --git a/eth/eth2.go b/eth/eth2.go index 6927c51fc00..67a2a97e01b 100644 --- a/eth/eth2.go +++ b/eth/eth2.go @@ -25,7 +25,6 @@ import ( "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/log" chainParams "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/trie" @@ -56,7 +55,7 @@ func (api *Eth2API) commitTransaction(tx *types.Transaction, coinbase common.Add //snap := eth2rpc.current.state.Snapshot() chain := api.eth.BlockChain() - receipt, err := core.ApplyTransaction(chain.Config(), chain, &coinbase, api.env.gasPool, api.env.state, api.env.header, tx, &api.env.header.GasUsed, *chain.GetVMConfig(), &vm.BeaconChainContext{BeaconRoots: bcParentRoots, RandaoMix: randao}) + receipt, err := core.ApplyTransaction(chain.Config(), chain, &coinbase, api.env.gasPool, api.env.state, api.env.header, tx, &api.env.header.GasUsed, *chain.GetVMConfig()) if err != nil { //w.current.state.RevertToSnapshot(snap) return err @@ -162,6 +161,7 @@ func (api *Eth2API) ProduceBlock(params ProduceBlockParams) (*ExecutableData, er tx := txs.Peek() if tx == nil { + fmt.Println("no tx") break } @@ -302,19 +302,27 @@ func (api *Eth2API) addBlockTxs(block *types.Block) error { return nil } -//func (api *Eth2API) SetHead(newHead common.Hash) error { -//oldBlock := api.eth.BlockChain().CurrentBlock() +// FinalizeBlock is called to mark a block as synchronized, so +// that data that is no longer needed can be removed. +func (api *Eth2API) FinalizeBlock(blockHash common.Hash) error { + // Stubbed for now, it's not critical + return nil +} + +// SetHead is called to perform a force choice. +func (api *Eth2API) SetHead(newHead common.Hash) error { + //oldBlock := api.eth.BlockChain().CurrentBlock() -//if oldBlock.Hash() == newHead { -//return nil -//} + //if oldBlock.Hash() == newHead { + //return nil + //} -//newBlock := api.eth.BlockChain().GetBlockByHash(newHead) + //newBlock := api.eth.BlockChain().GetBlockByHash(newHead) -//err := api.eth.BlockChain().Reorg(oldBlock, newBlock) -//if err != nil { -//return err -//} -//api.head = newHead -//return nil -//} + //err := api.eth.BlockChain().Reorg(oldBlock, newBlock) + //if err != nil { + //return err + //} + //api.head = newHead + return nil +} diff --git a/miner/worker.go b/miner/worker.go index d484690ede7..2cee6af0c32 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -736,7 +736,7 @@ func (w *worker) updateSnapshot() { func (w *worker) commitTransaction(tx *types.Transaction, coinbase common.Address) ([]*types.Log, error) { snap := w.current.state.Snapshot() - receipt, err := core.ApplyTransaction(w.chainConfig, w.chain, &coinbase, w.current.gasPool, w.current.state, w.current.header, tx, &w.current.header.GasUsed, *w.chain.GetVMConfig(), nil) + receipt, err := core.ApplyTransaction(w.chainConfig, w.chain, &coinbase, w.current.gasPool, w.current.state, w.current.header, tx, &w.current.header.GasUsed, *w.chain.GetVMConfig()) if err != nil { w.current.state.RevertToSnapshot(snap) return nil, err From 26ee92a4c6fb5d3ea5b34244953853fb9cb6dddd Mon Sep 17 00:00:00 2001 From: Guillaume Ballet Date: Fri, 9 Apr 2021 14:43:53 +0200 Subject: [PATCH 05/43] Fix unit tests --- core/types/transaction.go | 2 -- eth/eth2.go | 25 +++++++++++-------------- eth/eth2_test.go | 28 +++++++++++++--------------- 3 files changed, 24 insertions(+), 31 deletions(-) diff --git a/core/types/transaction.go b/core/types/transaction.go index 3fee4fbfbee..a35e07a5a31 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -20,7 +20,6 @@ import ( "bytes" "container/heap" "errors" - "fmt" "io" "math/big" "sync/atomic" @@ -430,7 +429,6 @@ type TransactionsByPriceAndNonce struct { // Note, the input map is reowned so the caller should not interact any more with // if after providing it to the constructor. func NewTransactionsByPriceAndNonce(signer Signer, txs map[common.Address]Transactions) *TransactionsByPriceAndNonce { - fmt.Println("tx found", len(txs)) // Initialize a price and received time based heap with the head transactions heads := make(TxByPriceAndTime, 0, len(txs)) for from, accTxs := range txs { diff --git a/eth/eth2.go b/eth/eth2.go index 67a2a97e01b..8c6867541dc 100644 --- a/eth/eth2.go +++ b/eth/eth2.go @@ -51,7 +51,7 @@ type eth2bpenv struct { receipts []*types.Receipt } -func (api *Eth2API) commitTransaction(tx *types.Transaction, coinbase common.Address, bcParentRoots []common.Hash, randao common.Hash) error { +func (api *Eth2API) commitTransaction(tx *types.Transaction, coinbase common.Address) error { //snap := eth2rpc.current.state.Snapshot() chain := api.eth.BlockChain() @@ -80,12 +80,9 @@ func (api *Eth2API) makeEnv(parent *types.Block, header *types.Header) error { } // Structure described at https://hackmd.io/T9x2mMA4S7us8tJwEB3FDQ -type ProduceBlockParams struct { - ParentHash common.Hash `json:"parent_hash"` - RandaoMix common.Hash `json:"randao_mix"` - Slot uint64 `json:"slot"` - Timestamp uint64 `json:"timestamp"` - RecentBeaconBlockRoots []common.Hash `json:"recent_beacon_block_roots"` +type AssembleBlockParams struct { + ParentHash common.Hash `json:"parent_hash"` + Timestamp uint64 `json:"timestamp"` } // Structure described at https://ethresear.ch/t/executable-beacon-chain/8271 @@ -102,9 +99,9 @@ type ExecutableData struct { Difficulty *big.Int `json:"difficulty"` } -// ProduceBlock creates a new block, inserts it into the chain, and returns the "execution +// AssembleBlock creates a new block, inserts it into the chain, and returns the "execution // data" required for eth2 clients to process the new block. -func (api *Eth2API) ProduceBlock(params ProduceBlockParams) (*ExecutableData, error) { +func (api *Eth2API) AssembleBlock(params AssembleBlockParams) (*ExecutableData, error) { log.Info("Produce block", "parentHash", params.ParentHash) bc := api.eth.BlockChain() @@ -170,7 +167,7 @@ func (api *Eth2API) ProduceBlock(params ProduceBlockParams) (*ExecutableData, er // Execute the transaction api.env.state.Prepare(tx.Hash(), common.Hash{}, api.env.tcount) - err := api.commitTransaction(tx, coinbase, params.RecentBeaconBlockRoots, params.RandaoMix) + err := api.commitTransaction(tx, coinbase) switch err { case core.ErrGasLimitReached: // Pop the current out-of-gas transaction without shifting in the next from the account @@ -242,7 +239,7 @@ func (api *Eth2API) ProduceBlock(params ProduceBlockParams) (*ExecutableData, er } // Structure described at https://hackmd.io/T9x2mMA4S7us8tJwEB3FDQ -type InsertBlockParams struct { +type NewBlockParams struct { RandaoMix common.Hash `json:"randao_mix"` Slot uint64 `json:"slot"` Timestamp uint64 `json:"timestamp"` @@ -252,7 +249,7 @@ type InsertBlockParams struct { var zeroNonce [8]byte -func insertBlockParamsToBlock(params InsertBlockParams, number *big.Int) *types.Block { +func insertBlockParamsToBlock(params NewBlockParams, number *big.Int) *types.Block { header := &types.Header{ ParentHash: params.ExecutableData.ParentHash, UncleHash: types.EmptyUncleHash, @@ -275,10 +272,10 @@ func insertBlockParamsToBlock(params InsertBlockParams, number *big.Int) *types. return block } -// InsertBlock creates an Eth1 block, inserts it in the chain, and either returns true, +// NewBlock creates an Eth1 block, inserts it in the chain, and either returns true, // or false + an error. This is a bit redundant for go, but simplifies things on the // eth2 side. -func (api *Eth2API) InsertBlock(params InsertBlockParams) (bool, error) { +func (api *Eth2API) NewBlock(params NewBlockParams) (bool, error) { // compute block number as parent.number + 1 parent := api.eth.BlockChain().GetBlockByHash(params.ExecutableData.ParentHash) if parent == nil { diff --git a/eth/eth2_test.go b/eth/eth2_test.go index 72599bc0c3e..1c6847bbca7 100644 --- a/eth/eth2_test.go +++ b/eth/eth2_test.go @@ -81,7 +81,7 @@ func generateTestChainWithFork(n int, fork int) (*core.Genesis, []*types.Block, return genesis, blocks, forkedBlocks } -func TestEth2ProduceBlock(t *testing.T) { +func TestEth2AssembleBlock(t *testing.T) { genesis, blocks := generateTestChain() n, err := node.New(&node.Config{}) @@ -107,12 +107,11 @@ func TestEth2ProduceBlock(t *testing.T) { t.Fatalf("error signing transaction, err=%v", err) } ethservice.txPool.AddLocal(tx) - blockParams := ProduceBlockParams{ + blockParams := AssembleBlockParams{ ParentHash: blocks[8].ParentHash(), - Slot: blocks[8].NumberU64(), Timestamp: blocks[8].Time(), } - execData, err := api.ProduceBlock(blockParams) + execData, err := api.AssembleBlock(blockParams) if err != nil { t.Fatalf("error producing block, err=%v", err) @@ -123,7 +122,7 @@ func TestEth2ProduceBlock(t *testing.T) { } } -func TestEth2ProduceBlockWithAnotherBlocksTxs(t *testing.T) { +func TestEth2AssembleBlockWithAnotherBlocksTxs(t *testing.T) { genesis, blocks := generateTestChain() n, err := node.New(&node.Config{}) @@ -146,12 +145,11 @@ func TestEth2ProduceBlockWithAnotherBlocksTxs(t *testing.T) { // Put the 10th block's tx in the pool and produce a new block api.addBlockTxs(blocks[9]) - blockParams := ProduceBlockParams{ + blockParams := AssembleBlockParams{ ParentHash: blocks[9].ParentHash(), - Slot: blocks[9].NumberU64(), Timestamp: blocks[9].Time(), } - execData, err := api.ProduceBlock(blockParams) + execData, err := api.AssembleBlock(blockParams) if err != nil { t.Fatalf("error producing block, err=%v", err) } @@ -161,7 +159,7 @@ func TestEth2ProduceBlockWithAnotherBlocksTxs(t *testing.T) { } } -func TestEth2InsertBlock(t *testing.T) { +func TestEth2NewBlock(t *testing.T) { genesis, blocks, forkedBlocks := generateTestChainWithFork(10, 4) n, err := node.New(&node.Config{}) @@ -181,7 +179,7 @@ func TestEth2InsertBlock(t *testing.T) { api := NewEth2API(ethservice) for i := 5; i < 10; i++ { - p := InsertBlockParams{ + p := NewBlockParams{ Slot: blocks[i].NumberU64(), Timestamp: blocks[i].Time(), ExecutableData: ExecutableData{ @@ -197,7 +195,7 @@ func TestEth2InsertBlock(t *testing.T) { BlockHash: blocks[i].Hash(), }, } - success, err := api.InsertBlock(p) + success, err := api.NewBlock(p) if err != nil || !success { t.Fatalf("Failed to insert block: %v", err) } @@ -208,7 +206,7 @@ func TestEth2InsertBlock(t *testing.T) { lastBlock := blocks[4] for i := 0; i < 4; i++ { lastBlockNum.Add(lastBlockNum, big.NewInt(1)) - p := InsertBlockParams{ + p := NewBlockParams{ Slot: lastBlockNum.Uint64(), Timestamp: forkedBlocks[i].Time(), ExecutableData: ExecutableData{ @@ -224,7 +222,7 @@ func TestEth2InsertBlock(t *testing.T) { BlockHash: forkedBlocks[i].Hash(), }, } - success, err := api.InsertBlock(p) + success, err := api.NewBlock(p) if err != nil || !success { t.Fatalf("Failed to insert forked block #%d: %v", i, err) } @@ -259,7 +257,7 @@ func TestEth2InsertBlock(t *testing.T) { //for i := 5; i < 10; i++ { //var blockRLP bytes.Buffer //rlp.Encode(&blockRLP, blocks[i]) -//err := api.InsertBlock(blockRLP.Bytes()) +//err := api.NewBlock(blockRLP.Bytes()) //if err != nil { //t.Fatalf("Failed to insert block: %v", err) //} @@ -273,7 +271,7 @@ func TestEth2InsertBlock(t *testing.T) { //for i := 0; i < 3; i++ { //var blockRLP bytes.Buffer //rlp.Encode(&blockRLP, forkedBlocks[i]) -//err := api.InsertBlock(blockRLP.Bytes()) +//err := api.NewBlock(blockRLP.Bytes()) //if err != nil { //t.Fatalf("Failed to insert block: %v", err) //} From dfdff92f907f02e790a776a9dcb4df3426a2fe8b Mon Sep 17 00:00:00 2001 From: Guillaume Ballet Date: Wed, 14 Apr 2021 20:15:53 +0200 Subject: [PATCH 06/43] integrate review feedback --- eth/backend.go | 2 +- eth/eth2.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/eth/backend.go b/eth/backend.go index 704cfaa4e5a..8c89a0929a5 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -343,7 +343,7 @@ func (s *Ethereum) APIs() []rpc.API { Service: s.netRPCService, Public: true, }, { - Namespace: "eth2", + Namespace: "consensus", Version: "1.0", Service: NewEth2API(s), Public: true, diff --git a/eth/eth2.go b/eth/eth2.go index 8c6867541dc..7f39c7f6e93 100644 --- a/eth/eth2.go +++ b/eth/eth2.go @@ -145,7 +145,7 @@ func (api *Eth2API) AssembleBlock(params AssembleBlockParams) (*ExecutableData, if err != nil { return nil, err } - signer := types.NewEIP155Signer(bc.Config().ChainID) + signer := types.LatestSigner(bc.Config()) txs := types.NewTransactionsByPriceAndNonce(signer, pending) var transactions []*types.Transaction From dae9d1ae5ad7dc30f5c0dcd4a157a0252ea71e23 Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Thu, 15 Apr 2021 18:23:55 +0200 Subject: [PATCH 07/43] Change difficulty calculations to accept 1 for each block --- consensus/ethash/consensus.go | 2 ++ eth/eth2_test.go | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/consensus/ethash/consensus.go b/consensus/ethash/consensus.go index 011a5688efb..e23bd824af4 100644 --- a/consensus/ethash/consensus.go +++ b/consensus/ethash/consensus.go @@ -315,6 +315,8 @@ func (ethash *Ethash) CalcDifficulty(chain consensus.ChainHeaderReader, time uin func CalcDifficulty(config *params.ChainConfig, time uint64, parent *types.Header) *big.Int { next := new(big.Int).Add(parent.Number, big1) switch { + case config.IsCatalyst(next): + return big.NewInt(1) case config.IsMuirGlacier(next): return calcDifficultyEip2384(time, parent) case config.IsConstantinople(next): diff --git a/eth/eth2_test.go b/eth/eth2_test.go index 1c6847bbca7..e2c77474666 100644 --- a/eth/eth2_test.go +++ b/eth/eth2_test.go @@ -58,7 +58,7 @@ func generateTestChainWithFork(n int, fork int) (*core.Genesis, []*types.Block, fork = n - 1 } db := rawdb.NewMemoryDatabase() - config := params.AllEthashProtocolChanges + config := ¶ms.ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, big.NewInt(0), new(params.EthashConfig), nil} genesis := &core.Genesis{ Config: config, Alloc: core.GenesisAlloc{testAddr: {Balance: testBalance}}, From de593d093a87b53f3733bf79de3564438a76a011 Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Thu, 15 Apr 2021 18:25:29 +0200 Subject: [PATCH 08/43] Bring API up to spec --- eth/eth2.go | 79 +++++++++++++++++++++++------------------------- eth/eth2_test.go | 56 ++++++++++++++-------------------- 2 files changed, 61 insertions(+), 74 deletions(-) diff --git a/eth/eth2.go b/eth/eth2.go index 7f39c7f6e93..651def87138 100644 --- a/eth/eth2.go +++ b/eth/eth2.go @@ -85,18 +85,19 @@ type AssembleBlockParams struct { Timestamp uint64 `json:"timestamp"` } -// Structure described at https://ethresear.ch/t/executable-beacon-chain/8271 +// Structure described at https://notes.ethereum.org/@n0ble/rayonism-the-merge-spec#Parameters1 type ExecutableData struct { - Coinbase common.Address `json:"coinbase"` - StateRoot common.Hash `json:"state_root"` - GasLimit uint64 `json:"gas_limit"` - GasUsed uint64 `json:"gas_used"` + BlockHash common.Hash `json:"blockHash"` + ParentHash common.Hash `json:"parentHash"` + Miner common.Address `json:"miner"` + StateRoot common.Hash `json:"stateRoot"` + Number uint64 `json:"number"` + GasLimit uint64 `json:"gasLimit"` + GasUsed uint64 `json:"gasUsed"` + Timestamp uint64 `json:"timestamp"` + ReceiptRoot common.Hash `json:"receiptsRoot"` + LogsBloom []byte `json:"logsBloom"` Transactions []*types.Transaction `json:"transactions"` - ReceiptRoot common.Hash `json:"receipt_root"` - LogsBloom []byte `json:"logs_bloom"` - BlockHash common.Hash `json:"block_hash"` - ParentHash common.Hash `json:"parent_hash"` - Difficulty *big.Int `json:"difficulty"` } // AssembleBlock creates a new block, inserts it into the chain, and returns the "execution @@ -225,69 +226,65 @@ func (api *Eth2API) AssembleBlock(params AssembleBlockParams) (*ExecutableData, block.Header().ReceiptHash = types.DeriveSha(receipts, new(trie.Trie)) return &ExecutableData{ - Coinbase: block.Coinbase(), + BlockHash: block.Hash(), + ParentHash: block.ParentHash(), + Miner: block.Coinbase(), StateRoot: block.Root(), + Number: block.NumberU64(), GasLimit: block.GasLimit(), GasUsed: block.GasUsed(), - Transactions: []*types.Transaction(block.Transactions()), + Timestamp: block.Time(), ReceiptRoot: block.ReceiptHash(), LogsBloom: block.Bloom().Bytes(), - BlockHash: block.Hash(), - Difficulty: block.Difficulty(), - ParentHash: block.ParentHash(), + Transactions: []*types.Transaction(block.Transactions()), }, nil } -// Structure described at https://hackmd.io/T9x2mMA4S7us8tJwEB3FDQ -type NewBlockParams struct { - RandaoMix common.Hash `json:"randao_mix"` - Slot uint64 `json:"slot"` - Timestamp uint64 `json:"timestamp"` - RecentBeaconBlockRoots []common.Hash `json:"recent_beacon_block_roots"` - ExecutableData ExecutableData `json:"executable_data"` -} - var zeroNonce [8]byte -func insertBlockParamsToBlock(params NewBlockParams, number *big.Int) *types.Block { +func insertBlockParamsToBlock(params ExecutableData, number *big.Int, parentTime uint64) *types.Block { header := &types.Header{ - ParentHash: params.ExecutableData.ParentHash, + ParentHash: params.ParentHash, UncleHash: types.EmptyUncleHash, - Coinbase: params.ExecutableData.Coinbase, - Root: params.ExecutableData.StateRoot, - TxHash: types.DeriveSha(types.Transactions(params.ExecutableData.Transactions), trie.NewStackTrie(nil)), - ReceiptHash: params.ExecutableData.ReceiptRoot, - Bloom: types.BytesToBloom(params.ExecutableData.LogsBloom), - Difficulty: params.ExecutableData.Difficulty, + Coinbase: params.Miner, + Root: params.StateRoot, + TxHash: types.DeriveSha(types.Transactions(params.Transactions), trie.NewStackTrie(nil)), + ReceiptHash: params.ReceiptRoot, + Bloom: types.BytesToBloom(params.LogsBloom), + Difficulty: big.NewInt(1), Number: number, - GasLimit: params.ExecutableData.GasLimit, - GasUsed: params.ExecutableData.GasUsed, - Time: params.Timestamp, + GasLimit: params.GasLimit, + GasUsed: params.GasUsed, + Time: parentTime + 1, Extra: nil, MixDigest: common.Hash{}, Nonce: zeroNonce, } - block := types.NewBlockWithHeader(header).WithBody(params.ExecutableData.Transactions, nil /* uncles */) + block := types.NewBlockWithHeader(header).WithBody(params.Transactions, nil /* uncles */) return block } +type NewBlockReturn struct { + Valid bool `json:"valid"` +} + // NewBlock creates an Eth1 block, inserts it in the chain, and either returns true, // or false + an error. This is a bit redundant for go, but simplifies things on the // eth2 side. -func (api *Eth2API) NewBlock(params NewBlockParams) (bool, error) { +func (api *Eth2API) NewBlock(params ExecutableData) (*NewBlockReturn, error) { // compute block number as parent.number + 1 - parent := api.eth.BlockChain().GetBlockByHash(params.ExecutableData.ParentHash) + parent := api.eth.BlockChain().GetBlockByHash(params.ParentHash) if parent == nil { - return false, fmt.Errorf("could not find parent %x", params.ExecutableData.ParentHash) + return &NewBlockReturn{false}, fmt.Errorf("could not find parent %x", params.ParentHash) } number := big.NewInt(0) number.Add(parent.Number(), big.NewInt(1)) - block := insertBlockParamsToBlock(params, number) + block := insertBlockParamsToBlock(params, number, parent.Time()) _, err := api.eth.BlockChain().InsertChainWithoutSealVerification(types.Blocks([]*types.Block{block})) - return (err == nil), err + return &NewBlockReturn{err == nil}, err } // Used in tests to add a the list of transactions from a block to the tx pool. diff --git a/eth/eth2_test.go b/eth/eth2_test.go index e2c77474666..e988a88fe9e 100644 --- a/eth/eth2_test.go +++ b/eth/eth2_test.go @@ -179,24 +179,19 @@ func TestEth2NewBlock(t *testing.T) { api := NewEth2API(ethservice) for i := 5; i < 10; i++ { - p := NewBlockParams{ - Slot: blocks[i].NumberU64(), - Timestamp: blocks[i].Time(), - ExecutableData: ExecutableData{ - ParentHash: ethservice.BlockChain().CurrentBlock().Hash(), - Coinbase: blocks[i].Coinbase(), - StateRoot: blocks[i].Root(), - Difficulty: blocks[i].Difficulty(), - GasLimit: blocks[i].GasLimit(), - GasUsed: blocks[i].GasUsed(), - Transactions: []*types.Transaction(blocks[i].Transactions()), - ReceiptRoot: blocks[i].ReceiptHash(), - LogsBloom: blocks[i].Bloom().Bytes(), - BlockHash: blocks[i].Hash(), - }, + p := ExecutableData{ + ParentHash: ethservice.BlockChain().CurrentBlock().Hash(), + Miner: blocks[i].Coinbase(), + StateRoot: blocks[i].Root(), + GasLimit: blocks[i].GasLimit(), + GasUsed: blocks[i].GasUsed(), + Transactions: []*types.Transaction(blocks[i].Transactions()), + ReceiptRoot: blocks[i].ReceiptHash(), + LogsBloom: blocks[i].Bloom().Bytes(), + BlockHash: blocks[i].Hash(), } success, err := api.NewBlock(p) - if err != nil || !success { + if err != nil || !success.Valid { t.Fatalf("Failed to insert block: %v", err) } } @@ -206,27 +201,22 @@ func TestEth2NewBlock(t *testing.T) { lastBlock := blocks[4] for i := 0; i < 4; i++ { lastBlockNum.Add(lastBlockNum, big.NewInt(1)) - p := NewBlockParams{ - Slot: lastBlockNum.Uint64(), - Timestamp: forkedBlocks[i].Time(), - ExecutableData: ExecutableData{ - ParentHash: lastBlock.Hash(), - Coinbase: forkedBlocks[i].Coinbase(), - StateRoot: forkedBlocks[i].Root(), - Difficulty: forkedBlocks[i].Difficulty(), - GasLimit: forkedBlocks[i].GasLimit(), - GasUsed: forkedBlocks[i].GasUsed(), - Transactions: []*types.Transaction(blocks[i].Transactions()), - ReceiptRoot: forkedBlocks[i].ReceiptHash(), - LogsBloom: forkedBlocks[i].Bloom().Bytes(), - BlockHash: forkedBlocks[i].Hash(), - }, + p := ExecutableData{ + ParentHash: lastBlock.Hash(), + Miner: forkedBlocks[i].Coinbase(), + StateRoot: forkedBlocks[i].Root(), + GasLimit: forkedBlocks[i].GasLimit(), + GasUsed: forkedBlocks[i].GasUsed(), + Transactions: []*types.Transaction(blocks[i].Transactions()), + ReceiptRoot: forkedBlocks[i].ReceiptHash(), + LogsBloom: forkedBlocks[i].Bloom().Bytes(), + BlockHash: forkedBlocks[i].Hash(), } success, err := api.NewBlock(p) - if err != nil || !success { + if err != nil || !success.Valid { t.Fatalf("Failed to insert forked block #%d: %v", i, err) } - lastBlock = insertBlockParamsToBlock(p, lastBlockNum) + lastBlock = insertBlockParamsToBlock(p, lastBlockNum, forkedBlocks[i].Time()) } exp := common.HexToHash("526db89301fc787799ef8c272fe512898b97ad96d0b69caee19dc5393b092110") From ce8fff93cfcb59db615a76e816347a375c4c849b Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Thu, 15 Apr 2021 19:21:44 +0200 Subject: [PATCH 09/43] Fix unit tests --- eth/eth2.go | 6 +++--- eth/eth2_test.go | 8 ++++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/eth/eth2.go b/eth/eth2.go index 651def87138..3e07120f278 100644 --- a/eth/eth2.go +++ b/eth/eth2.go @@ -242,7 +242,7 @@ func (api *Eth2API) AssembleBlock(params AssembleBlockParams) (*ExecutableData, var zeroNonce [8]byte -func insertBlockParamsToBlock(params ExecutableData, number *big.Int, parentTime uint64) *types.Block { +func insertBlockParamsToBlock(params ExecutableData, number *big.Int) *types.Block { header := &types.Header{ ParentHash: params.ParentHash, UncleHash: types.EmptyUncleHash, @@ -255,7 +255,7 @@ func insertBlockParamsToBlock(params ExecutableData, number *big.Int, parentTime Number: number, GasLimit: params.GasLimit, GasUsed: params.GasUsed, - Time: parentTime + 1, + Time: params.Timestamp, Extra: nil, MixDigest: common.Hash{}, Nonce: zeroNonce, @@ -281,7 +281,7 @@ func (api *Eth2API) NewBlock(params ExecutableData) (*NewBlockReturn, error) { number := big.NewInt(0) number.Add(parent.Number(), big.NewInt(1)) - block := insertBlockParamsToBlock(params, number, parent.Time()) + block := insertBlockParamsToBlock(params, number) _, err := api.eth.BlockChain().InsertChainWithoutSealVerification(types.Blocks([]*types.Block{block})) return &NewBlockReturn{err == nil}, err diff --git a/eth/eth2_test.go b/eth/eth2_test.go index e988a88fe9e..78c7819c2a3 100644 --- a/eth/eth2_test.go +++ b/eth/eth2_test.go @@ -189,6 +189,7 @@ func TestEth2NewBlock(t *testing.T) { ReceiptRoot: blocks[i].ReceiptHash(), LogsBloom: blocks[i].Bloom().Bytes(), BlockHash: blocks[i].Hash(), + Timestamp: blocks[i].Time(), } success, err := api.NewBlock(p) if err != nil || !success.Valid { @@ -196,6 +197,8 @@ func TestEth2NewBlock(t *testing.T) { } } + exp := ethservice.BlockChain().CurrentBlock().Hash() + // Introduce the fork point lastBlockNum := blocks[4].Number() lastBlock := blocks[4] @@ -205,21 +208,22 @@ func TestEth2NewBlock(t *testing.T) { ParentHash: lastBlock.Hash(), Miner: forkedBlocks[i].Coinbase(), StateRoot: forkedBlocks[i].Root(), + Number: lastBlockNum.Uint64(), GasLimit: forkedBlocks[i].GasLimit(), GasUsed: forkedBlocks[i].GasUsed(), Transactions: []*types.Transaction(blocks[i].Transactions()), ReceiptRoot: forkedBlocks[i].ReceiptHash(), LogsBloom: forkedBlocks[i].Bloom().Bytes(), BlockHash: forkedBlocks[i].Hash(), + Timestamp: forkedBlocks[i].Time(), } success, err := api.NewBlock(p) if err != nil || !success.Valid { t.Fatalf("Failed to insert forked block #%d: %v", i, err) } - lastBlock = insertBlockParamsToBlock(p, lastBlockNum, forkedBlocks[i].Time()) + lastBlock = insertBlockParamsToBlock(p, lastBlockNum) } - exp := common.HexToHash("526db89301fc787799ef8c272fe512898b97ad96d0b69caee19dc5393b092110") if ethservice.BlockChain().CurrentBlock().Hash() != exp { t.Fatalf("Wrong head after inserting fork %x != %x", exp, ethservice.BlockChain().CurrentBlock().Hash()) } From 0602ca754bbddd9759c536fb09c8283b53dc1f1d Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Thu, 15 Apr 2021 19:37:11 +0200 Subject: [PATCH 10/43] Add a `success` field to the response in GinalizeBlock and SetHead --- eth/eth2.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/eth/eth2.go b/eth/eth2.go index 3e07120f278..c6b09a4872f 100644 --- a/eth/eth2.go +++ b/eth/eth2.go @@ -296,15 +296,19 @@ func (api *Eth2API) addBlockTxs(block *types.Block) error { return nil } +type GenericResponse struct { + Success bool `json:"success"` +} + // FinalizeBlock is called to mark a block as synchronized, so // that data that is no longer needed can be removed. -func (api *Eth2API) FinalizeBlock(blockHash common.Hash) error { +func (api *Eth2API) FinalizeBlock(blockHash common.Hash) (*GenericResponse, error) { // Stubbed for now, it's not critical - return nil + return &GenericResponse{false}, nil } // SetHead is called to perform a force choice. -func (api *Eth2API) SetHead(newHead common.Hash) error { +func (api *Eth2API) SetHead(newHead common.Hash) (*GenericResponse, error) { //oldBlock := api.eth.BlockChain().CurrentBlock() //if oldBlock.Hash() == newHead { @@ -318,5 +322,5 @@ func (api *Eth2API) SetHead(newHead common.Hash) error { //return err //} //api.head = newHead - return nil + return &GenericResponse{false}, nil } From 8e3adf9300769b1dc60a6b2fea9d5bfafd240fd9 Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Thu, 15 Apr 2021 19:42:31 +0200 Subject: [PATCH 11/43] quell linter warning --- eth/eth2_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/eth/eth2_test.go b/eth/eth2_test.go index 78c7819c2a3..0a1ce00a5e0 100644 --- a/eth/eth2_test.go +++ b/eth/eth2_test.go @@ -58,6 +58,7 @@ func generateTestChainWithFork(n int, fork int) (*core.Genesis, []*types.Block, fork = n - 1 } db := rawdb.NewMemoryDatabase() + //nolint:composites config := ¶ms.ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, big.NewInt(0), new(params.EthashConfig), nil} genesis := &core.Genesis{ Config: config, From 78e41ff121d2497ae0f921670306c69205a54c96 Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Fri, 16 Apr 2021 10:28:24 +0200 Subject: [PATCH 12/43] fix console test breakage because of the namespace change --- cmd/geth/consolecmd_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/geth/consolecmd_test.go b/cmd/geth/consolecmd_test.go index 38657eda8fd..48583d15f44 100644 --- a/cmd/geth/consolecmd_test.go +++ b/cmd/geth/consolecmd_test.go @@ -31,7 +31,7 @@ import ( ) const ( - ipcAPIs = "admin:1.0 debug:1.0 eth2:1.0 eth:1.0 ethash:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0" + ipcAPIs = "admin:1.0 consensus:1.0 debug:1.0 eth:1.0 ethash:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0" httpAPIs = "eth:1.0 net:1.0 rpc:1.0 web3:1.0" ) From 05f75c5f4abc642df6a7e8e001cb066cea20d171 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 16 Apr 2021 13:23:36 +0200 Subject: [PATCH 13/43] eth, eth/ethconfig: remove Catalyst config flag --- cmd/utils/flags.go | 3 --- eth/backend.go | 6 +----- eth/ethconfig/config.go | 3 --- eth/handler.go | 7 ------- eth/handler_eth.go | 1 - eth/handler_eth_test.go | 1 - eth/protocols/eth/handler.go | 3 --- eth/protocols/eth/handler_test.go | 3 --- eth/protocols/eth/handlers.go | 8 -------- 9 files changed, 1 insertion(+), 34 deletions(-) diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 90c55aa4f96..5f072b58ff2 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -1675,9 +1675,6 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { SetDNSDiscoveryDefaults(cfg, params.MainnetGenesisHash) } } - if ctx.GlobalIsSet(CatalystFlag.Name) { - cfg.Catalyst = true - } } // SetDNSDiscoveryDefaults configures DNS discovery with the given URL if diff --git a/eth/backend.go b/eth/backend.go index 8c89a0929a5..ca812d0d638 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -220,14 +220,10 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { EventMux: eth.eventMux, Checkpoint: checkpoint, Whitelist: config.Whitelist, - Catalyst: config.Catalyst, }); err != nil { return nil, err } - if config.Catalyst { - // Activate transaction processing by default in catalyst mode - eth.handler.acceptTxs = 1 - } + eth.miner = miner.New(eth, &config.Miner, chainConfig, eth.EventMux(), eth.engine, eth.isLocalBlock) eth.miner.SetExtra(makeExtraData(config.Miner.ExtraData)) diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index 6b21b5b0613..0c6eb0bdd7d 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -201,9 +201,6 @@ type Config struct { // Berlin block override (TODO: remove after the fork) OverrideBerlin *big.Int `toml:",omitempty"` - - // Specify if catalyst mode is enabled - Catalyst bool `toml:",omitempty"` } // CreateConsensusEngine creates a consensus engine for the given chain configuration. diff --git a/eth/handler.go b/eth/handler.go index 094db818900..11c8565de10 100644 --- a/eth/handler.go +++ b/eth/handler.go @@ -85,7 +85,6 @@ type handlerConfig struct { EventMux *event.TypeMux // Legacy event mux, deprecate for `feed` Checkpoint *params.TrustedCheckpoint // Hard coded checkpoint for sync challenges Whitelist map[uint64]common.Hash // Hard coded whitelist for sync challenged - Catalyst bool // True iff this is catalyst } type handler struct { @@ -124,8 +123,6 @@ type handler struct { chainSync *chainSyncer wg sync.WaitGroup peerWG sync.WaitGroup - - IsCatalyst bool } // newHandler returns a handler for all Ethereum chain management protocol. @@ -145,10 +142,6 @@ func newHandler(config *handlerConfig) (*handler, error) { whitelist: config.Whitelist, txsyncCh: make(chan *txsync), quitSync: make(chan struct{}), - IsCatalyst: config.Catalyst, - } - if h.IsCatalyst { - h.acceptTxs = 1 } if config.Sync == downloader.FullSync { // The database seems empty as the current block is the genesis. Yet the fast diff --git a/eth/handler_eth.go b/eth/handler_eth.go index 1fe8fb5aba3..3ff9f2245be 100644 --- a/eth/handler_eth.go +++ b/eth/handler_eth.go @@ -39,7 +39,6 @@ type ethHandler handler func (h *ethHandler) Chain() *core.BlockChain { return h.chain } func (h *ethHandler) StateBloom() *trie.SyncBloom { return h.stateBloom } func (h *ethHandler) TxPool() eth.TxPool { return h.txpool } -func (h *ethHandler) Catalyst() bool { return h.IsCatalyst } // RunPeer is invoked when a peer joins on the `eth` protocol. func (h *ethHandler) RunPeer(peer *eth.Peer, hand eth.Handler) error { diff --git a/eth/handler_eth_test.go b/eth/handler_eth_test.go index a29e8a4d303..5f5d4e9e82d 100644 --- a/eth/handler_eth_test.go +++ b/eth/handler_eth_test.go @@ -54,7 +54,6 @@ func (h *testEthHandler) TxPool() eth.TxPool { panic("no backi func (h *testEthHandler) AcceptTxs() bool { return true } func (h *testEthHandler) RunPeer(*eth.Peer, eth.Handler) error { panic("not used in tests") } func (h *testEthHandler) PeerInfo(enode.ID) interface{} { panic("not used in tests") } -func (h *testEthHandler) Catalyst() bool { return false } func (h *testEthHandler) Handle(peer *eth.Peer, packet eth.Packet) error { switch packet := packet.(type) { diff --git a/eth/protocols/eth/handler.go b/eth/protocols/eth/handler.go index 5815c9459e9..0dc3de98982 100644 --- a/eth/protocols/eth/handler.go +++ b/eth/protocols/eth/handler.go @@ -92,9 +92,6 @@ type Backend interface { // the remote peer. Only packets not consumed by the protocol handler will // be forwarded to the backend. Handle(peer *Peer, packet Packet) error - - // Catalist returns true if the backend in running in catalyst mode - Catalyst() bool } // TxPool defines the methods needed by the protocol handler to serve transactions. diff --git a/eth/protocols/eth/handler_test.go b/eth/protocols/eth/handler_test.go index 7daca0bb60c..30beae931b6 100644 --- a/eth/protocols/eth/handler_test.go +++ b/eth/protocols/eth/handler_test.go @@ -108,9 +108,6 @@ func (b *testBackend) AcceptTxs() bool { func (b *testBackend) Handle(*Peer, Packet) error { panic("data processing tests should be done in the handler package") } -func (b *testBackend) Catalyst() bool { - return false -} // Tests that block headers can be retrieved from a remote chain based on user queries. func TestGetBlockHeaders64(t *testing.T) { testGetBlockHeaders(t, 64) } diff --git a/eth/protocols/eth/handlers.go b/eth/protocols/eth/handlers.go index 8be01613ed3..8433fa343a7 100644 --- a/eth/protocols/eth/handlers.go +++ b/eth/protocols/eth/handlers.go @@ -273,10 +273,6 @@ func answerGetReceiptsQuery(backend Backend, query GetReceiptsPacket, peer *Peer } func handleNewBlockhashes(backend Backend, msg Decoder, peer *Peer) error { - if backend.Catalyst() { - return nil - } - // A batch of new block announcements just arrived ann := new(NewBlockHashesPacket) if err := msg.Decode(ann); err != nil { @@ -291,10 +287,6 @@ func handleNewBlockhashes(backend Backend, msg Decoder, peer *Peer) error { } func handleNewBlock(backend Backend, msg Decoder, peer *Peer) error { - if backend.Catalyst() { - return nil - } - // Retrieve and decode the propagated block ann := new(NewBlockPacket) if err := msg.Decode(ann); err != nil { From 1fdba04965919ca77cec9a3b086ab379a0eb6774 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 16 Apr 2021 13:43:43 +0200 Subject: [PATCH 14/43] eth/catalyst: move catalyst implementation to its own package --- eth/backend.go | 5 ---- eth/{eth2.go => catalyst/api.go} | 29 +++++++++++----------- eth/{eth2_test.go => catalyst/api_test.go} | 29 ++++++++++++++++------ eth/catalyst/catalyst.go | 25 +++++++++++++++++++ 4 files changed, 61 insertions(+), 27 deletions(-) rename eth/{eth2.go => catalyst/api.go} (91%) rename eth/{eth2_test.go => catalyst/api_test.go} (90%) create mode 100644 eth/catalyst/catalyst.go diff --git a/eth/backend.go b/eth/backend.go index ca812d0d638..4c7374612e5 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -338,11 +338,6 @@ func (s *Ethereum) APIs() []rpc.API { Version: "1.0", Service: s.netRPCService, Public: true, - }, { - Namespace: "consensus", - Version: "1.0", - Service: NewEth2API(s), - Public: true, }, }...) } diff --git a/eth/eth2.go b/eth/catalyst/api.go similarity index 91% rename from eth/eth2.go rename to eth/catalyst/api.go index c6b09a4872f..48d14ce32d4 100644 --- a/eth/eth2.go +++ b/eth/catalyst/api.go @@ -14,7 +14,8 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -package eth +// Package catalyst implements the temporary eth1/eth2 RPC integration. +package catalyst import ( "fmt" @@ -25,20 +26,20 @@ import ( "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/log" chainParams "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/trie" ) -type Eth2API struct { - eth *Ethereum +type consensusAPI struct { + eth *eth.Ethereum env *eth2bpenv head common.Hash } -// NewEth2API creates a new API definition for the eth2 prototype. -func NewEth2API(eth *Ethereum) *Eth2API { - return &Eth2API{eth: eth} +func newConsensusAPI(eth *eth.Ethereum) *consensusAPI { + return &consensusAPI{eth: eth} } type eth2bpenv struct { @@ -51,7 +52,7 @@ type eth2bpenv struct { receipts []*types.Receipt } -func (api *Eth2API) commitTransaction(tx *types.Transaction, coinbase common.Address) error { +func (api *consensusAPI) commitTransaction(tx *types.Transaction, coinbase common.Address) error { //snap := eth2rpc.current.state.Snapshot() chain := api.eth.BlockChain() @@ -66,7 +67,7 @@ func (api *Eth2API) commitTransaction(tx *types.Transaction, coinbase common.Add return nil } -func (api *Eth2API) makeEnv(parent *types.Block, header *types.Header) error { +func (api *consensusAPI) makeEnv(parent *types.Block, header *types.Header) error { state, err := api.eth.BlockChain().StateAt(parent.Root()) if err != nil { return err @@ -102,7 +103,7 @@ type ExecutableData struct { // AssembleBlock creates a new block, inserts it into the chain, and returns the "execution // data" required for eth2 clients to process the new block. -func (api *Eth2API) AssembleBlock(params AssembleBlockParams) (*ExecutableData, error) { +func (api *consensusAPI) AssembleBlock(params AssembleBlockParams) (*ExecutableData, error) { log.Info("Produce block", "parentHash", params.ParentHash) bc := api.eth.BlockChain() @@ -272,7 +273,7 @@ type NewBlockReturn struct { // NewBlock creates an Eth1 block, inserts it in the chain, and either returns true, // or false + an error. This is a bit redundant for go, but simplifies things on the // eth2 side. -func (api *Eth2API) NewBlock(params ExecutableData) (*NewBlockReturn, error) { +func (api *consensusAPI) NewBlock(params ExecutableData) (*NewBlockReturn, error) { // compute block number as parent.number + 1 parent := api.eth.BlockChain().GetBlockByHash(params.ParentHash) if parent == nil { @@ -288,9 +289,9 @@ func (api *Eth2API) NewBlock(params ExecutableData) (*NewBlockReturn, error) { } // Used in tests to add a the list of transactions from a block to the tx pool. -func (api *Eth2API) addBlockTxs(block *types.Block) error { +func (api *consensusAPI) addBlockTxs(block *types.Block) error { for _, tx := range block.Transactions() { - api.eth.txPool.AddLocal(tx) + api.eth.TxPool().AddLocal(tx) } return nil @@ -302,13 +303,13 @@ type GenericResponse struct { // FinalizeBlock is called to mark a block as synchronized, so // that data that is no longer needed can be removed. -func (api *Eth2API) FinalizeBlock(blockHash common.Hash) (*GenericResponse, error) { +func (api *consensusAPI) FinalizeBlock(blockHash common.Hash) (*GenericResponse, error) { // Stubbed for now, it's not critical return &GenericResponse{false}, nil } // SetHead is called to perform a force choice. -func (api *Eth2API) SetHead(newHead common.Hash) (*GenericResponse, error) { +func (api *consensusAPI) SetHead(newHead common.Hash) (*GenericResponse, error) { //oldBlock := api.eth.BlockChain().CurrentBlock() //if oldBlock.Hash() == newHead { diff --git a/eth/eth2_test.go b/eth/catalyst/api_test.go similarity index 90% rename from eth/eth2_test.go rename to eth/catalyst/api_test.go index 0a1ce00a5e0..21817603c61 100644 --- a/eth/eth2_test.go +++ b/eth/catalyst/api_test.go @@ -14,7 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -package eth +package catalyst import ( "math/big" @@ -25,11 +25,20 @@ import ( "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/params" ) var ( + // testKey is a private key to use for funding a tester account. + testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + + // testAddr is the Ethereum address of the tester account. + testAddr = crypto.PubkeyToAddress(testKey.PublicKey) + testBalance = big.NewInt(2e10) ) @@ -89,7 +98,8 @@ func TestEth2AssembleBlock(t *testing.T) { if err != nil { t.Fatalf("could not get node: %v", err) } - ethservice, err := New(n, &Config{Genesis: genesis, Ethash: ethash.Config{PowMode: ethash.ModeFake}}) + ethcfg := ðconfig.Config{Genesis: genesis, Ethash: ethash.Config{PowMode: ethash.ModeFake}} + ethservice, err := eth.New(n, ethcfg) if err != nil { t.Fatalf("can't create new ethereum service: %v", err) } @@ -101,13 +111,13 @@ func TestEth2AssembleBlock(t *testing.T) { } ethservice.SetEtherbase(testAddr) - api := NewEth2API(ethservice) + api := newConsensusAPI(ethservice) signer := types.NewEIP155Signer(ethservice.BlockChain().Config().ChainID) tx, err := types.SignTx(types.NewTransaction(0, blocks[8].Coinbase(), big.NewInt(1000), params.TxGas, nil, nil), signer, testKey) if err != nil { t.Fatalf("error signing transaction, err=%v", err) } - ethservice.txPool.AddLocal(tx) + ethservice.TxPool().AddLocal(tx) blockParams := AssembleBlockParams{ ParentHash: blocks[8].ParentHash(), Timestamp: blocks[8].Time(), @@ -130,7 +140,8 @@ func TestEth2AssembleBlockWithAnotherBlocksTxs(t *testing.T) { if err != nil { t.Fatalf("could not get node: %v", err) } - ethservice, err := New(n, &Config{Genesis: genesis, Ethash: ethash.Config{PowMode: ethash.ModeFake}}) + ethcfg := ðconfig.Config{Genesis: genesis, Ethash: ethash.Config{PowMode: ethash.ModeFake}} + ethservice, err := eth.New(n, ethcfg) if err != nil { t.Fatalf("can't create new ethereum service: %v", err) } @@ -142,7 +153,7 @@ func TestEth2AssembleBlockWithAnotherBlocksTxs(t *testing.T) { } ethservice.SetEtherbase(testAddr) - api := NewEth2API(ethservice) + api := newConsensusAPI(ethservice) // Put the 10th block's tx in the pool and produce a new block api.addBlockTxs(blocks[9]) @@ -167,7 +178,9 @@ func TestEth2NewBlock(t *testing.T) { if err != nil { t.Fatalf("could not get node: %v", err) } - ethservice, err := New(n, &Config{Genesis: genesis, Ethash: ethash.Config{PowMode: ethash.ModeFake}}) + + ethcfg := ðconfig.Config{Genesis: genesis, Ethash: ethash.Config{PowMode: ethash.ModeFake}} + ethservice, err := eth.New(n, ethcfg) if err != nil { t.Fatalf("can't create new ethereum service: %v", err) } @@ -178,7 +191,7 @@ func TestEth2NewBlock(t *testing.T) { t.Fatalf("can't import test blocks: %v", err) } - api := NewEth2API(ethservice) + api := newConsensusAPI(ethservice) for i := 5; i < 10; i++ { p := ExecutableData{ ParentHash: ethservice.BlockChain().CurrentBlock().Hash(), diff --git a/eth/catalyst/catalyst.go b/eth/catalyst/catalyst.go new file mode 100644 index 00000000000..7f1b4d03434 --- /dev/null +++ b/eth/catalyst/catalyst.go @@ -0,0 +1,25 @@ +package catalyst + +import ( + "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/node" + "github.com/ethereum/go-ethereum/rpc" +) + +type Service struct { + api *consensusAPI +} + +// New creates a catalyst service and registers it with the node. +func New(stack *node.Node, backend *eth.Ethereum) *Service { + c := &Service{api: newConsensusAPI(backend)} + stack.RegisterAPIs([]rpc.API{ + { + Namespace: "consensus", + Version: "1.0", + Service: c.api, + Public: true, + }, + }) + return c +} From d267c140490d92833d8a97e4e36a3128fc6fa3c4 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 16 Apr 2021 13:57:57 +0200 Subject: [PATCH 15/43] cmd/geth: register catalyst service when --catalyst given --- cmd/geth/config.go | 13 ++++++++++++- cmd/utils/flags.go | 8 +++++--- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/cmd/geth/config.go b/cmd/geth/config.go index 6fc75363c66..a8039dad41b 100644 --- a/cmd/geth/config.go +++ b/cmd/geth/config.go @@ -28,8 +28,10 @@ import ( "gopkg.in/urfave/cli.v1" "github.com/ethereum/go-ethereum/cmd/utils" + "github.com/ethereum/go-ethereum/eth/catalyst" "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/internal/ethapi" + "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/params" @@ -143,7 +145,16 @@ func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) { if ctx.GlobalIsSet(utils.OverrideBerlinFlag.Name) { cfg.Eth.OverrideBerlin = new(big.Int).SetUint64(ctx.GlobalUint64(utils.OverrideBerlinFlag.Name)) } - backend := utils.RegisterEthService(stack, &cfg.Eth) + backend, eth := utils.RegisterEthService(stack, &cfg.Eth) + + // Configure catalyst. + if ctx.GlobalBool(utils.CatalystFlag.Name) { + if eth == nil { + utils.Fatalf("Catalyst does not work in light client mode.") + } + log.Warn("Catalyst mode enabled") + catalyst.New(stack, eth) + } // Configure GraphQL if requested if ctx.GlobalIsSet(utils.GraphQLEnabledFlag.Name) { diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 5f072b58ff2..0ecdf2c79ee 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -1698,14 +1698,16 @@ func SetDNSDiscoveryDefaults(cfg *ethconfig.Config, genesis common.Hash) { } // RegisterEthService adds an Ethereum client to the stack. -func RegisterEthService(stack *node.Node, cfg *ethconfig.Config) ethapi.Backend { +// The second return value is the full node instance, which may be nil if the +// node is running as a light client. +func RegisterEthService(stack *node.Node, cfg *ethconfig.Config) (ethapi.Backend, *eth.Ethereum) { if cfg.SyncMode == downloader.LightSync { backend, err := les.New(stack, cfg) if err != nil { Fatalf("Failed to register the Ethereum service: %v", err) } stack.RegisterAPIs(tracers.APIs(backend.ApiBackend)) - return backend.ApiBackend + return backend.ApiBackend, nil } backend, err := eth.New(stack, cfg) if err != nil { @@ -1718,7 +1720,7 @@ func RegisterEthService(stack *node.Node, cfg *ethconfig.Config) ethapi.Backend } } stack.RegisterAPIs(tracers.APIs(backend.APIBackend)) - return backend.APIBackend + return backend.APIBackend, backend } // RegisterEthStatsService configures the Ethereum Stats daemon and adds it to From 5c627ef431376844575acc5b2fa0a052f7ec142a Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 16 Apr 2021 13:58:24 +0200 Subject: [PATCH 16/43] cmd/utils: disable p2p networking in catalyst mode --- cmd/utils/flags.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 0ecdf2c79ee..08db89327e9 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -1191,10 +1191,11 @@ func SetP2PConfig(ctx *cli.Context, cfg *p2p.Config) { cfg.NetRestrict = list } - if ctx.GlobalBool(DeveloperFlag.Name) { + if ctx.GlobalBool(DeveloperFlag.Name) || ctx.GlobalBool(CatalystFlag.Name) { // --dev mode can't use p2p networking. cfg.MaxPeers = 0 - cfg.ListenAddr = ":0" + cfg.ListenAddr = "" + cfg.NoDial = true cfg.NoDiscovery = true cfg.DiscoveryV5 = false } From 24909f07e53d9d1eba4fa3409cc9ee814d86bb8b Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 16 Apr 2021 13:59:45 +0200 Subject: [PATCH 17/43] core: revert change to tx pool config --- core/tx_pool.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/core/tx_pool.go b/core/tx_pool.go index d82ea120084..5db1d3df329 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -153,8 +153,6 @@ type TxPoolConfig struct { GlobalQueue uint64 // Maximum number of non-executable transaction slots for all accounts Lifetime time.Duration // Maximum amount of time non-executable transaction are queued - - ProcessTxs bool // Whether transaction processing is enabled upon node's launch } // DefaultTxPoolConfig contains the default configurations for the transaction From da28171e7a7d3381b461173b2ed80ed2cbe37dbb Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 16 Apr 2021 14:00:46 +0200 Subject: [PATCH 18/43] eth/catalyst: add copyright header --- eth/catalyst/api.go | 1 - eth/catalyst/catalyst.go | 17 +++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index 48d14ce32d4..614fa4af80f 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -14,7 +14,6 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -// Package catalyst implements the temporary eth1/eth2 RPC integration. package catalyst import ( diff --git a/eth/catalyst/catalyst.go b/eth/catalyst/catalyst.go index 7f1b4d03434..102fc84e036 100644 --- a/eth/catalyst/catalyst.go +++ b/eth/catalyst/catalyst.go @@ -1,3 +1,20 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package catalyst implements the temporary eth1/eth2 RPC integration. package catalyst import ( From 3f3e8792aabbb45d0ae7fd9f021cda4112c2ea80 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 16 Apr 2021 14:07:13 +0200 Subject: [PATCH 19/43] cmd/utils: improve description of catalyst flag --- cmd/utils/flags.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 08db89327e9..59cf32c9834 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -755,10 +755,10 @@ var ( Usage: "External EVM configuration (default = built-in interpreter)", Value: "", } - // Catalyst + CatalystFlag = cli.BoolFlag{ Name: "catalyst", - Usage: "Set geth into catalyst mode", + Usage: "Catalyst mode (eth2 integration testing)", } ) From ef204fe43baf6600fcb5d75a556075419a09e68a Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 16 Apr 2021 14:15:50 +0200 Subject: [PATCH 20/43] eth/catalyst: add test helper for starting eth --- eth/catalyst/api_test.go | 85 ++++++++++++++++------------------------ 1 file changed, 33 insertions(+), 52 deletions(-) diff --git a/eth/catalyst/api_test.go b/eth/catalyst/api_test.go index 21817603c61..aae9d9e2475 100644 --- a/eth/catalyst/api_test.go +++ b/eth/catalyst/api_test.go @@ -93,23 +93,8 @@ func generateTestChainWithFork(n int, fork int) (*core.Genesis, []*types.Block, func TestEth2AssembleBlock(t *testing.T) { genesis, blocks := generateTestChain() - - n, err := node.New(&node.Config{}) - if err != nil { - t.Fatalf("could not get node: %v", err) - } - ethcfg := ðconfig.Config{Genesis: genesis, Ethash: ethash.Config{PowMode: ethash.ModeFake}} - ethservice, err := eth.New(n, ethcfg) - if err != nil { - t.Fatalf("can't create new ethereum service: %v", err) - } - if err := n.Start(); err != nil { - t.Fatalf("can't start test node: %v", err) - } - if _, err := ethservice.BlockChain().InsertChain(blocks[1:9]); err != nil { - t.Fatalf("can't import test blocks: %v", err) - } - ethservice.SetEtherbase(testAddr) + n, ethservice := startEthService(t, genesis, blocks[1:9]) + defer n.Close() api := newConsensusAPI(ethservice) signer := types.NewEIP155Signer(ethservice.BlockChain().Config().ChainID) @@ -135,23 +120,8 @@ func TestEth2AssembleBlock(t *testing.T) { func TestEth2AssembleBlockWithAnotherBlocksTxs(t *testing.T) { genesis, blocks := generateTestChain() - - n, err := node.New(&node.Config{}) - if err != nil { - t.Fatalf("could not get node: %v", err) - } - ethcfg := ðconfig.Config{Genesis: genesis, Ethash: ethash.Config{PowMode: ethash.ModeFake}} - ethservice, err := eth.New(n, ethcfg) - if err != nil { - t.Fatalf("can't create new ethereum service: %v", err) - } - if err := n.Start(); err != nil { - t.Fatalf("can't start test node: %v", err) - } - if _, err := ethservice.BlockChain().InsertChain(blocks[1:9]); err != nil { - t.Fatalf("can't import test blocks: %v", err) - } - ethservice.SetEtherbase(testAddr) + n, ethservice := startEthService(t, genesis, blocks[1:9]) + defer n.Close() api := newConsensusAPI(ethservice) @@ -173,23 +143,8 @@ func TestEth2AssembleBlockWithAnotherBlocksTxs(t *testing.T) { func TestEth2NewBlock(t *testing.T) { genesis, blocks, forkedBlocks := generateTestChainWithFork(10, 4) - - n, err := node.New(&node.Config{}) - if err != nil { - t.Fatalf("could not get node: %v", err) - } - - ethcfg := ðconfig.Config{Genesis: genesis, Ethash: ethash.Config{PowMode: ethash.ModeFake}} - ethservice, err := eth.New(n, ethcfg) - if err != nil { - t.Fatalf("can't create new ethereum service: %v", err) - } - if err := n.Start(); err != nil { - t.Fatalf("can't start test node: %v", err) - } - if _, err := ethservice.BlockChain().InsertChain(blocks[1:5]); err != nil { - t.Fatalf("can't import test blocks: %v", err) - } + n, ethservice := startEthService(t, genesis, blocks[1:5]) + defer n.Close() api := newConsensusAPI(ethservice) for i := 5; i < 10; i++ { @@ -213,7 +168,7 @@ func TestEth2NewBlock(t *testing.T) { exp := ethservice.BlockChain().CurrentBlock().Hash() - // Introduce the fork point + // Introduce the fork point. lastBlockNum := blocks[4].Number() lastBlock := blocks[4] for i := 0; i < 4; i++ { @@ -243,6 +198,32 @@ func TestEth2NewBlock(t *testing.T) { } } +// startEthService creates a full node instance for testing. +func startEthService(t *testing.T, genesis *core.Genesis, blocks []*types.Block) (*node.Node, *eth.Ethereum) { + t.Helper() + + n, err := node.New(&node.Config{}) + if err != nil { + t.Fatal("can't create node:", err) + } + + ethcfg := ðconfig.Config{Genesis: genesis, Ethash: ethash.Config{PowMode: ethash.ModeFake}} + ethservice, err := eth.New(n, ethcfg) + if err != nil { + t.Fatal("can't create eth service:", err) + } + if err := n.Start(); err != nil { + t.Fatal("can't start node:", err) + } + if _, err := ethservice.BlockChain().InsertChain(blocks); err != nil { + n.Close() + t.Fatal("can't import test blocks:", err) + } + ethservice.SetEtherbase(testAddr) + + return n, ethservice +} + //func TestEth2SetHead(t *testing.T) { //genesis, blocks, forkedBlocks := generateTestChainWithFork(10, 5) From 8871cb75cb3e0fcf42a57a50a2ec248549c8a565 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 16 Apr 2021 14:18:37 +0200 Subject: [PATCH 21/43] eth/catalyst: use helper in disabled SetHead test --- eth/catalyst/api_test.go | 91 +++++++++++++++++----------------------- 1 file changed, 39 insertions(+), 52 deletions(-) diff --git a/eth/catalyst/api_test.go b/eth/catalyst/api_test.go index aae9d9e2475..1536e8f648d 100644 --- a/eth/catalyst/api_test.go +++ b/eth/catalyst/api_test.go @@ -198,6 +198,45 @@ func TestEth2NewBlock(t *testing.T) { } } +// func TestEth2SetHead(t *testing.T) { +// genesis, blocks, forkedBlocks := generateTestChainWithFork(10, 5) +// n, ethservice := startEthService(t, genesis, blocks[1:5]) +// defer n.Close() +// +// api := newConsensusAPI(ethservice) +// for i := 5; i < 10; i++ { +// var blockRLP bytes.Buffer +// rlp.Encode(&blockRLP, blocks[i]) +// err := api.NewBlock(blockRLP.Bytes()) +// if err != nil { +// t.Fatalf("Failed to insert block: %v", err) +// } +// } +// api.head = blocks[9].Hash() +// +// if ethservice.BlockChain().CurrentBlock().Hash() != blocks[9].Hash() { +// t.Fatalf("Wrong head") +// } +// +// for i := 0; i < 3; i++ { +// var blockRLP bytes.Buffer +// rlp.Encode(&blockRLP, forkedBlocks[i]) +// err := api.NewBlock(blockRLP.Bytes()) +// if err != nil { +// t.Fatalf("Failed to insert block: %v", err) +// } +// } +// +// api.SetHead(forkedBlocks[2].Hash()) +// +// if ethservice.BlockChain().CurrentBlock().Hash() == forkedBlocks[2].Hash() { +// t.Fatalf("Wrong head after inserting fork %x != %x", blocks[9].Hash(), ethservice.BlockChain().CurrentBlock().Hash()) +// } +// if api.head != forkedBlocks[2].Hash() { +// t.Fatalf("Registered wrong head") +// } +// } + // startEthService creates a full node instance for testing. func startEthService(t *testing.T, genesis *core.Genesis, blocks []*types.Block) (*node.Node, *eth.Ethereum) { t.Helper() @@ -223,55 +262,3 @@ func startEthService(t *testing.T, genesis *core.Genesis, blocks []*types.Block) return n, ethservice } - -//func TestEth2SetHead(t *testing.T) { -//genesis, blocks, forkedBlocks := generateTestChainWithFork(10, 5) - -//n, err := node.New(&node.Config{}) -//if err != nil { -//t.Fatalf("could not get node: %v", err) -//} -//ethservice, err := New(n, &Config{Genesis: genesis, Ethash: ethash.Config{PowMode: ethash.ModeFake}}) -//if err != nil { -//t.Fatalf("can't create new ethereum service: %v", err) -//} -//if err := n.Start(); err != nil { -//t.Fatalf("can't start test node: %v", err) -//} -//if _, err := ethservice.BlockChain().InsertChain(blocks[1:5]); err != nil { -//t.Fatalf("can't import test blocks: %v", err) -//} - -//api := NewEth2API(ethservice) -//for i := 5; i < 10; i++ { -//var blockRLP bytes.Buffer -//rlp.Encode(&blockRLP, blocks[i]) -//err := api.NewBlock(blockRLP.Bytes()) -//if err != nil { -//t.Fatalf("Failed to insert block: %v", err) -//} -//} -//api.head = blocks[9].Hash() - -//if ethservice.BlockChain().CurrentBlock().Hash() != blocks[9].Hash() { -//t.Fatalf("Wrong head") -//} - -//for i := 0; i < 3; i++ { -//var blockRLP bytes.Buffer -//rlp.Encode(&blockRLP, forkedBlocks[i]) -//err := api.NewBlock(blockRLP.Bytes()) -//if err != nil { -//t.Fatalf("Failed to insert block: %v", err) -//} -//} - -//api.SetHead(forkedBlocks[2].Hash()) - -//if ethservice.BlockChain().CurrentBlock().Hash() == forkedBlocks[2].Hash() { -//t.Fatalf("Wrong head after inserting fork %x != %x", blocks[9].Hash(), ethservice.BlockChain().CurrentBlock().Hash()) -//} -//if api.head != forkedBlocks[2].Hash() { -//t.Fatalf("Registered wrong head") -//} -//} From 40cb7cbc58601f1ff7dde77cc1cd63ab0353b406 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 16 Apr 2021 14:23:15 +0200 Subject: [PATCH 22/43] eth/catalyst: improve service registration --- cmd/geth/config.go | 6 +++--- eth/catalyst/catalyst.go | 19 +++++++++++-------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/cmd/geth/config.go b/cmd/geth/config.go index a8039dad41b..c867877ee6f 100644 --- a/cmd/geth/config.go +++ b/cmd/geth/config.go @@ -31,7 +31,6 @@ import ( "github.com/ethereum/go-ethereum/eth/catalyst" "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/internal/ethapi" - "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/params" @@ -152,8 +151,9 @@ func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) { if eth == nil { utils.Fatalf("Catalyst does not work in light client mode.") } - log.Warn("Catalyst mode enabled") - catalyst.New(stack, eth) + if err := catalyst.Register(stack, eth); err != nil { + utils.Fatalf("%v", err) + } } // Configure GraphQL if requested diff --git a/eth/catalyst/catalyst.go b/eth/catalyst/catalyst.go index 102fc84e036..d2279668a52 100644 --- a/eth/catalyst/catalyst.go +++ b/eth/catalyst/catalyst.go @@ -18,25 +18,28 @@ package catalyst import ( + "errors" + "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/rpc" ) -type Service struct { - api *consensusAPI -} +// Register adds catalyst APIs to the node. +func Register(stack *node.Node, backend *eth.Ethereum) error { + if backend.BlockChain().Config().CatalystBlock == nil { + return errors.New("can't enable catalyst service without catalyst fork block in chain config") + } -// New creates a catalyst service and registers it with the node. -func New(stack *node.Node, backend *eth.Ethereum) *Service { - c := &Service{api: newConsensusAPI(backend)} + log.Warn("Catalyst mode enabled") stack.RegisterAPIs([]rpc.API{ { Namespace: "consensus", Version: "1.0", - Service: c.api, + Service: newConsensusAPI(backend), Public: true, }, }) - return c + return nil } From 962079cd2be067920e557a5a48d21d124b68f735 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 16 Apr 2021 14:24:18 +0200 Subject: [PATCH 23/43] cmd/geth: remove consensus API in console test --- cmd/geth/consolecmd_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/geth/consolecmd_test.go b/cmd/geth/consolecmd_test.go index 48583d15f44..c3f41b187c4 100644 --- a/cmd/geth/consolecmd_test.go +++ b/cmd/geth/consolecmd_test.go @@ -31,7 +31,7 @@ import ( ) const ( - ipcAPIs = "admin:1.0 consensus:1.0 debug:1.0 eth:1.0 ethash:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0" + ipcAPIs = "admin:1.0 debug:1.0 eth:1.0 ethash:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0" httpAPIs = "eth:1.0 net:1.0 rpc:1.0 web3:1.0" ) From 71165f42a67f89cfc4f40ca4b537ae6a0208729a Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Fri, 16 Apr 2021 15:18:18 +0200 Subject: [PATCH 24/43] Change signature of InsertChainWithoutSealVerification to only accept one block --- core/blockchain.go | 26 ++------------------------ eth/catalyst/api.go | 2 +- 2 files changed, 3 insertions(+), 25 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index f30a674f787..6093e1cc17a 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -1695,36 +1695,14 @@ func (bc *BlockChain) InsertChain(chain types.Blocks) (int, error) { // InsertChainWithoutSealVerification works exactly the same // except for seal verification, seal verification is omitted -func (bc *BlockChain) InsertChainWithoutSealVerification(chain types.Blocks) (int, error) { - // Sanity check that we have something meaningful to import - if len(chain) == 0 { - return 0, nil - } - +func (bc *BlockChain) InsertChainWithoutSealVerification(block *types.Block) (int, error) { bc.blockProcFeed.Send(true) defer bc.blockProcFeed.Send(false) - // Remove already known canon-blocks - var ( - block, prev *types.Block - ) - // Do a sanity check that the provided chain is actually ordered and linked - for i := 1; i < len(chain); i++ { - block = chain[i] - prev = chain[i-1] - if block.NumberU64() != prev.NumberU64()+1 || block.ParentHash() != prev.Hash() { - // Chain broke ancestry, log a message (programming error) and skip insertion - log.Error("Non contiguous block insert", "number", block.Number(), "hash", block.Hash(), - "parent", block.ParentHash(), "prevnumber", prev.Number(), "prevhash", prev.Hash()) - - return 0, fmt.Errorf("non contiguous insert: item %d is #%d [%x…], item %d is #%d [%x…] (parent [%x…])", i-1, prev.NumberU64(), - prev.Hash().Bytes()[:4], i, block.NumberU64(), block.Hash().Bytes()[:4], block.ParentHash().Bytes()[:4]) - } - } // Pre-checks passed, start the full block imports bc.wg.Add(1) bc.chainmu.Lock() - n, err := bc.insertChain(chain, false) + n, err := bc.insertChain(types.Blocks([]*types.Block{block}), false) bc.chainmu.Unlock() bc.wg.Done() diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index 614fa4af80f..78b19dff61a 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -282,7 +282,7 @@ func (api *consensusAPI) NewBlock(params ExecutableData) (*NewBlockReturn, error number.Add(parent.Number(), big.NewInt(1)) block := insertBlockParamsToBlock(params, number) - _, err := api.eth.BlockChain().InsertChainWithoutSealVerification(types.Blocks([]*types.Block{block})) + _, err := api.eth.BlockChain().InsertChainWithoutSealVerification(block) return &NewBlockReturn{err == nil}, err } From 16be944c5dc33cb61fde70b62df1c0660412917f Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Fri, 16 Apr 2021 15:29:44 +0200 Subject: [PATCH 25/43] Reuse the block number from ExecutableData in NewBlock --- eth/catalyst/api.go | 4 ++-- eth/catalyst/api_test.go | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index 78b19dff61a..3820af82107 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -278,9 +278,9 @@ func (api *consensusAPI) NewBlock(params ExecutableData) (*NewBlockReturn, error if parent == nil { return &NewBlockReturn{false}, fmt.Errorf("could not find parent %x", params.ParentHash) } - number := big.NewInt(0) - number.Add(parent.Number(), big.NewInt(1)) + number := big.NewInt(0) + number.SetUint64(params.Number) block := insertBlockParamsToBlock(params, number) _, err := api.eth.BlockChain().InsertChainWithoutSealVerification(block) diff --git a/eth/catalyst/api_test.go b/eth/catalyst/api_test.go index 1536e8f648d..5d488125e33 100644 --- a/eth/catalyst/api_test.go +++ b/eth/catalyst/api_test.go @@ -159,6 +159,7 @@ func TestEth2NewBlock(t *testing.T) { LogsBloom: blocks[i].Bloom().Bytes(), BlockHash: blocks[i].Hash(), Timestamp: blocks[i].Time(), + Number: uint64(i), } success, err := api.NewBlock(p) if err != nil || !success.Valid { From 33c465b923291080d55734c1ce759beeb8216d74 Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Fri, 16 Apr 2021 15:34:24 +0200 Subject: [PATCH 26/43] Review feedback: a more descriptive name for the execution environment --- eth/catalyst/api.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index 3820af82107..a2a5e1059f6 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -33,7 +33,7 @@ import ( type consensusAPI struct { eth *eth.Ethereum - env *eth2bpenv + env *blockExecutionEnv head common.Hash } @@ -41,7 +41,9 @@ func newConsensusAPI(eth *eth.Ethereum) *consensusAPI { return &consensusAPI{eth: eth} } -type eth2bpenv struct { +// blockExecutionEnv gathers all the data required to execute +// a block, either when assembling it or when inserting it. +type blockExecutionEnv struct { state *state.StateDB tcount int gasPool *core.GasPool @@ -71,7 +73,7 @@ func (api *consensusAPI) makeEnv(parent *types.Block, header *types.Header) erro if err != nil { return err } - api.env = ð2bpenv{ + api.env = &blockExecutionEnv{ state: state, header: header, gasPool: new(core.GasPool).AddGas(header.GasLimit), From 09e38673d3a993331c009b6fe1ce098dad515f05 Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Fri, 16 Apr 2021 15:41:18 +0200 Subject: [PATCH 27/43] remove commented code, it won't work out of the box --- eth/catalyst/api.go | 13 ------------- eth/catalyst/api_test.go | 39 --------------------------------------- 2 files changed, 52 deletions(-) diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index a2a5e1059f6..8c39f2c1820 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -311,18 +311,5 @@ func (api *consensusAPI) FinalizeBlock(blockHash common.Hash) (*GenericResponse, // SetHead is called to perform a force choice. func (api *consensusAPI) SetHead(newHead common.Hash) (*GenericResponse, error) { - //oldBlock := api.eth.BlockChain().CurrentBlock() - - //if oldBlock.Hash() == newHead { - //return nil - //} - - //newBlock := api.eth.BlockChain().GetBlockByHash(newHead) - - //err := api.eth.BlockChain().Reorg(oldBlock, newBlock) - //if err != nil { - //return err - //} - //api.head = newHead return &GenericResponse{false}, nil } diff --git a/eth/catalyst/api_test.go b/eth/catalyst/api_test.go index 5d488125e33..04bdfe61840 100644 --- a/eth/catalyst/api_test.go +++ b/eth/catalyst/api_test.go @@ -199,45 +199,6 @@ func TestEth2NewBlock(t *testing.T) { } } -// func TestEth2SetHead(t *testing.T) { -// genesis, blocks, forkedBlocks := generateTestChainWithFork(10, 5) -// n, ethservice := startEthService(t, genesis, blocks[1:5]) -// defer n.Close() -// -// api := newConsensusAPI(ethservice) -// for i := 5; i < 10; i++ { -// var blockRLP bytes.Buffer -// rlp.Encode(&blockRLP, blocks[i]) -// err := api.NewBlock(blockRLP.Bytes()) -// if err != nil { -// t.Fatalf("Failed to insert block: %v", err) -// } -// } -// api.head = blocks[9].Hash() -// -// if ethservice.BlockChain().CurrentBlock().Hash() != blocks[9].Hash() { -// t.Fatalf("Wrong head") -// } -// -// for i := 0; i < 3; i++ { -// var blockRLP bytes.Buffer -// rlp.Encode(&blockRLP, forkedBlocks[i]) -// err := api.NewBlock(blockRLP.Bytes()) -// if err != nil { -// t.Fatalf("Failed to insert block: %v", err) -// } -// } -// -// api.SetHead(forkedBlocks[2].Hash()) -// -// if ethservice.BlockChain().CurrentBlock().Hash() == forkedBlocks[2].Hash() { -// t.Fatalf("Wrong head after inserting fork %x != %x", blocks[9].Hash(), ethservice.BlockChain().CurrentBlock().Hash()) -// } -// if api.head != forkedBlocks[2].Hash() { -// t.Fatalf("Registered wrong head") -// } -// } - // startEthService creates a full node instance for testing. func startEthService(t *testing.T, genesis *core.Genesis, blocks []*types.Block) (*node.Node, *eth.Ethereum) { t.Helper() From 2c22b5ce121ded7a675c655ec9d3ebfa18eb47e9 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 16 Apr 2021 14:32:57 +0200 Subject: [PATCH 28/43] eth/catalyst: remove zeroNonce --- eth/catalyst/api.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index 8c39f2c1820..f1a366828e1 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -242,8 +242,6 @@ func (api *consensusAPI) AssembleBlock(params AssembleBlockParams) (*ExecutableD }, nil } -var zeroNonce [8]byte - func insertBlockParamsToBlock(params ExecutableData, number *big.Int) *types.Block { header := &types.Header{ ParentHash: params.ParentHash, @@ -260,7 +258,6 @@ func insertBlockParamsToBlock(params ExecutableData, number *big.Int) *types.Blo Time: params.Timestamp, Extra: nil, MixDigest: common.Hash{}, - Nonce: zeroNonce, } block := types.NewBlockWithHeader(header).WithBody(params.Transactions, nil /* uncles */) From 7adf70b90019cca7c6004c6f571d037e5f9cfa55 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 16 Apr 2021 14:35:10 +0200 Subject: [PATCH 29/43] eth/catalyst: unexport API types --- eth/catalyst/api.go | 28 ++++++++++++++-------------- eth/catalyst/api_test.go | 8 ++++---- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index f1a366828e1..12563a06a65 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -82,13 +82,13 @@ func (api *consensusAPI) makeEnv(parent *types.Block, header *types.Header) erro } // Structure described at https://hackmd.io/T9x2mMA4S7us8tJwEB3FDQ -type AssembleBlockParams struct { +type assembleBlockParams struct { ParentHash common.Hash `json:"parent_hash"` Timestamp uint64 `json:"timestamp"` } // Structure described at https://notes.ethereum.org/@n0ble/rayonism-the-merge-spec#Parameters1 -type ExecutableData struct { +type executableData struct { BlockHash common.Hash `json:"blockHash"` ParentHash common.Hash `json:"parentHash"` Miner common.Address `json:"miner"` @@ -104,7 +104,7 @@ type ExecutableData struct { // AssembleBlock creates a new block, inserts it into the chain, and returns the "execution // data" required for eth2 clients to process the new block. -func (api *consensusAPI) AssembleBlock(params AssembleBlockParams) (*ExecutableData, error) { +func (api *consensusAPI) AssembleBlock(params assembleBlockParams) (*executableData, error) { log.Info("Produce block", "parentHash", params.ParentHash) bc := api.eth.BlockChain() @@ -227,7 +227,7 @@ func (api *consensusAPI) AssembleBlock(params AssembleBlockParams) (*ExecutableD block.Header().ReceiptHash = types.DeriveSha(receipts, new(trie.Trie)) - return &ExecutableData{ + return &executableData{ BlockHash: block.Hash(), ParentHash: block.ParentHash(), Miner: block.Coinbase(), @@ -242,7 +242,7 @@ func (api *consensusAPI) AssembleBlock(params AssembleBlockParams) (*ExecutableD }, nil } -func insertBlockParamsToBlock(params ExecutableData, number *big.Int) *types.Block { +func insertBlockParamsToBlock(params executableData, number *big.Int) *types.Block { header := &types.Header{ ParentHash: params.ParentHash, UncleHash: types.EmptyUncleHash, @@ -264,18 +264,18 @@ func insertBlockParamsToBlock(params ExecutableData, number *big.Int) *types.Blo return block } -type NewBlockReturn struct { +type newBlockResponse struct { Valid bool `json:"valid"` } // NewBlock creates an Eth1 block, inserts it in the chain, and either returns true, // or false + an error. This is a bit redundant for go, but simplifies things on the // eth2 side. -func (api *consensusAPI) NewBlock(params ExecutableData) (*NewBlockReturn, error) { +func (api *consensusAPI) NewBlock(params executableData) (*newBlockResponse, error) { // compute block number as parent.number + 1 parent := api.eth.BlockChain().GetBlockByHash(params.ParentHash) if parent == nil { - return &NewBlockReturn{false}, fmt.Errorf("could not find parent %x", params.ParentHash) + return &newBlockResponse{false}, fmt.Errorf("could not find parent %x", params.ParentHash) } number := big.NewInt(0) @@ -283,7 +283,7 @@ func (api *consensusAPI) NewBlock(params ExecutableData) (*NewBlockReturn, error block := insertBlockParamsToBlock(params, number) _, err := api.eth.BlockChain().InsertChainWithoutSealVerification(block) - return &NewBlockReturn{err == nil}, err + return &newBlockResponse{err == nil}, err } // Used in tests to add a the list of transactions from a block to the tx pool. @@ -295,18 +295,18 @@ func (api *consensusAPI) addBlockTxs(block *types.Block) error { return nil } -type GenericResponse struct { +type genericResponse struct { Success bool `json:"success"` } // FinalizeBlock is called to mark a block as synchronized, so // that data that is no longer needed can be removed. -func (api *consensusAPI) FinalizeBlock(blockHash common.Hash) (*GenericResponse, error) { +func (api *consensusAPI) FinalizeBlock(blockHash common.Hash) (*genericResponse, error) { // Stubbed for now, it's not critical - return &GenericResponse{false}, nil + return &genericResponse{false}, nil } // SetHead is called to perform a force choice. -func (api *consensusAPI) SetHead(newHead common.Hash) (*GenericResponse, error) { - return &GenericResponse{false}, nil +func (api *consensusAPI) SetHead(newHead common.Hash) (*genericResponse, error) { + return &genericResponse{false}, nil } diff --git a/eth/catalyst/api_test.go b/eth/catalyst/api_test.go index 04bdfe61840..b276cfcd24b 100644 --- a/eth/catalyst/api_test.go +++ b/eth/catalyst/api_test.go @@ -103,7 +103,7 @@ func TestEth2AssembleBlock(t *testing.T) { t.Fatalf("error signing transaction, err=%v", err) } ethservice.TxPool().AddLocal(tx) - blockParams := AssembleBlockParams{ + blockParams := assembleBlockParams{ ParentHash: blocks[8].ParentHash(), Timestamp: blocks[8].Time(), } @@ -127,7 +127,7 @@ func TestEth2AssembleBlockWithAnotherBlocksTxs(t *testing.T) { // Put the 10th block's tx in the pool and produce a new block api.addBlockTxs(blocks[9]) - blockParams := AssembleBlockParams{ + blockParams := assembleBlockParams{ ParentHash: blocks[9].ParentHash(), Timestamp: blocks[9].Time(), } @@ -148,7 +148,7 @@ func TestEth2NewBlock(t *testing.T) { api := newConsensusAPI(ethservice) for i := 5; i < 10; i++ { - p := ExecutableData{ + p := executableData{ ParentHash: ethservice.BlockChain().CurrentBlock().Hash(), Miner: blocks[i].Coinbase(), StateRoot: blocks[i].Root(), @@ -174,7 +174,7 @@ func TestEth2NewBlock(t *testing.T) { lastBlock := blocks[4] for i := 0; i < 4; i++ { lastBlockNum.Add(lastBlockNum, big.NewInt(1)) - p := ExecutableData{ + p := executableData{ ParentHash: lastBlock.Hash(), Miner: forkedBlocks[i].Coinbase(), StateRoot: forkedBlocks[i].Root(), From 01d16e5a549c67aa6ed618a09d2e6a47df610104 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 16 Apr 2021 16:01:01 +0200 Subject: [PATCH 30/43] eth/catalyst: fix encoding of API objects This makes the API object encoding adhere to the spec as written: all integers are hex string QUANTITY, all bytes data is hex string DATA, and transactions are hex strings instead of JSON objects. --- eth/catalyst/api.go | 80 +++++++++++------------- eth/catalyst/api_test.go | 9 ++- eth/catalyst/api_types.go | 70 +++++++++++++++++++++ eth/catalyst/gen_blockparams.go | 43 +++++++++++++ eth/catalyst/gen_ed.go | 105 ++++++++++++++++++++++++++++++++ 5 files changed, 261 insertions(+), 46 deletions(-) create mode 100644 eth/catalyst/api_types.go create mode 100644 eth/catalyst/gen_blockparams.go create mode 100644 eth/catalyst/gen_ed.go diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index 12563a06a65..fe6f01e2097 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -81,27 +81,6 @@ func (api *consensusAPI) makeEnv(parent *types.Block, header *types.Header) erro return nil } -// Structure described at https://hackmd.io/T9x2mMA4S7us8tJwEB3FDQ -type assembleBlockParams struct { - ParentHash common.Hash `json:"parent_hash"` - Timestamp uint64 `json:"timestamp"` -} - -// Structure described at https://notes.ethereum.org/@n0ble/rayonism-the-merge-spec#Parameters1 -type executableData struct { - BlockHash common.Hash `json:"blockHash"` - ParentHash common.Hash `json:"parentHash"` - Miner common.Address `json:"miner"` - StateRoot common.Hash `json:"stateRoot"` - Number uint64 `json:"number"` - GasLimit uint64 `json:"gasLimit"` - GasUsed uint64 `json:"gasUsed"` - Timestamp uint64 `json:"timestamp"` - ReceiptRoot common.Hash `json:"receiptsRoot"` - LogsBloom []byte `json:"logsBloom"` - Transactions []*types.Transaction `json:"transactions"` -} - // AssembleBlock creates a new block, inserts it into the chain, and returns the "execution // data" required for eth2 clients to process the new block. func (api *consensusAPI) AssembleBlock(params assembleBlockParams) (*executableData, error) { @@ -238,17 +217,44 @@ func (api *consensusAPI) AssembleBlock(params assembleBlockParams) (*executableD Timestamp: block.Time(), ReceiptRoot: block.ReceiptHash(), LogsBloom: block.Bloom().Bytes(), - Transactions: []*types.Transaction(block.Transactions()), + Transactions: encodeTransactions(block.Transactions()), }, nil } -func insertBlockParamsToBlock(params executableData, number *big.Int) *types.Block { +func encodeTransactions(txs []*types.Transaction) [][]byte { + var enc = make([][]byte, len(txs)) + for i, tx := range txs { + enc[i], _ = tx.MarshalBinary() + } + return enc +} + +func decodeTransactions(enc [][]byte) ([]*types.Transaction, error) { + var txs = make([]*types.Transaction, len(enc)) + for i, encTx := range enc { + var tx types.Transaction + if err := tx.UnmarshalBinary(encTx); err != nil { + return nil, fmt.Errorf("invalid transaction %d: %v", i, err) + } + txs[i] = &tx + } + return txs, nil +} + +func insertBlockParamsToBlock(params executableData) (*types.Block, error) { + txs, err := decodeTransactions(params.Transactions) + if err != nil { + return nil, err + } + + number := big.NewInt(0) + number.SetUint64(params.Number) header := &types.Header{ ParentHash: params.ParentHash, UncleHash: types.EmptyUncleHash, Coinbase: params.Miner, Root: params.StateRoot, - TxHash: types.DeriveSha(types.Transactions(params.Transactions), trie.NewStackTrie(nil)), + TxHash: types.DeriveSha(types.Transactions(txs), trie.NewStackTrie(nil)), ReceiptHash: params.ReceiptRoot, Bloom: types.BytesToBloom(params.LogsBloom), Difficulty: big.NewInt(1), @@ -256,16 +262,9 @@ func insertBlockParamsToBlock(params executableData, number *big.Int) *types.Blo GasLimit: params.GasLimit, GasUsed: params.GasUsed, Time: params.Timestamp, - Extra: nil, - MixDigest: common.Hash{}, } - block := types.NewBlockWithHeader(header).WithBody(params.Transactions, nil /* uncles */) - - return block -} - -type newBlockResponse struct { - Valid bool `json:"valid"` + block := types.NewBlockWithHeader(header).WithBody(txs, nil /* uncles */) + return block, nil } // NewBlock creates an Eth1 block, inserts it in the chain, and either returns true, @@ -277,12 +276,12 @@ func (api *consensusAPI) NewBlock(params executableData) (*newBlockResponse, err if parent == nil { return &newBlockResponse{false}, fmt.Errorf("could not find parent %x", params.ParentHash) } + block, err := insertBlockParamsToBlock(params) + if err != nil { + return nil, err + } - number := big.NewInt(0) - number.SetUint64(params.Number) - block := insertBlockParamsToBlock(params, number) - _, err := api.eth.BlockChain().InsertChainWithoutSealVerification(block) - + _, err = api.eth.BlockChain().InsertChainWithoutSealVerification(block) return &newBlockResponse{err == nil}, err } @@ -291,14 +290,9 @@ func (api *consensusAPI) addBlockTxs(block *types.Block) error { for _, tx := range block.Transactions() { api.eth.TxPool().AddLocal(tx) } - return nil } -type genericResponse struct { - Success bool `json:"success"` -} - // FinalizeBlock is called to mark a block as synchronized, so // that data that is no longer needed can be removed. func (api *consensusAPI) FinalizeBlock(blockHash common.Hash) (*genericResponse, error) { diff --git a/eth/catalyst/api_test.go b/eth/catalyst/api_test.go index b276cfcd24b..456b6867bde 100644 --- a/eth/catalyst/api_test.go +++ b/eth/catalyst/api_test.go @@ -154,7 +154,7 @@ func TestEth2NewBlock(t *testing.T) { StateRoot: blocks[i].Root(), GasLimit: blocks[i].GasLimit(), GasUsed: blocks[i].GasUsed(), - Transactions: []*types.Transaction(blocks[i].Transactions()), + Transactions: encodeTransactions(blocks[i].Transactions()), ReceiptRoot: blocks[i].ReceiptHash(), LogsBloom: blocks[i].Bloom().Bytes(), BlockHash: blocks[i].Hash(), @@ -181,7 +181,7 @@ func TestEth2NewBlock(t *testing.T) { Number: lastBlockNum.Uint64(), GasLimit: forkedBlocks[i].GasLimit(), GasUsed: forkedBlocks[i].GasUsed(), - Transactions: []*types.Transaction(blocks[i].Transactions()), + Transactions: encodeTransactions(blocks[i].Transactions()), ReceiptRoot: forkedBlocks[i].ReceiptHash(), LogsBloom: forkedBlocks[i].Bloom().Bytes(), BlockHash: forkedBlocks[i].Hash(), @@ -191,7 +191,10 @@ func TestEth2NewBlock(t *testing.T) { if err != nil || !success.Valid { t.Fatalf("Failed to insert forked block #%d: %v", i, err) } - lastBlock = insertBlockParamsToBlock(p, lastBlockNum) + lastBlock, err = insertBlockParamsToBlock(p) + if err != nil { + t.Fatal(err) + } } if ethservice.BlockChain().CurrentBlock().Hash() != exp { diff --git a/eth/catalyst/api_types.go b/eth/catalyst/api_types.go new file mode 100644 index 00000000000..b280aedb0c6 --- /dev/null +++ b/eth/catalyst/api_types.go @@ -0,0 +1,70 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package catalyst + +import ( + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" +) + +//go:generate go run github.com/fjl/gencodec -type assembleBlockParams -field-override assembleBlockParamsMarshaling -out gen_blockparams.go + +// Structure described at https://hackmd.io/T9x2mMA4S7us8tJwEB3FDQ +type assembleBlockParams struct { + ParentHash common.Hash `json:"parent_hash"` + Timestamp uint64 `json:"timestamp"` +} + +// JSON type overrides for assembleBlockParams. +type assembleBlockParamsMarshaling struct { + Timestamp hexutil.Uint64 +} + +//go:generate go run github.com/fjl/gencodec -type executableData -field-override executableDataMarshaling -out gen_ed.go + +// Structure described at https://notes.ethereum.org/@n0ble/rayonism-the-merge-spec#Parameters1 +type executableData struct { + BlockHash common.Hash `json:"blockHash"` + ParentHash common.Hash `json:"parentHash"` + Miner common.Address `json:"miner"` + StateRoot common.Hash `json:"stateRoot"` + Number uint64 `json:"number"` + GasLimit uint64 `json:"gasLimit"` + GasUsed uint64 `json:"gasUsed"` + Timestamp uint64 `json:"timestamp"` + ReceiptRoot common.Hash `json:"receiptsRoot"` + LogsBloom []byte `json:"logsBloom"` + Transactions [][]byte `json:"transactions"` +} + +// JSON type overrides for executableData. +type executableDataMarshaling struct { + Number hexutil.Uint64 + GasLimit hexutil.Uint64 + GasUsed hexutil.Uint64 + Timestamp hexutil.Uint64 + LogsBloom hexutil.Bytes + Transactions []hexutil.Bytes +} + +type newBlockResponse struct { + Valid bool `json:"valid"` +} + +type genericResponse struct { + Success bool `json:"success"` +} diff --git a/eth/catalyst/gen_blockparams.go b/eth/catalyst/gen_blockparams.go new file mode 100644 index 00000000000..8c36e944a8f --- /dev/null +++ b/eth/catalyst/gen_blockparams.go @@ -0,0 +1,43 @@ +// Code generated by github.com/fjl/gencodec. DO NOT EDIT. + +package catalyst + +import ( + "encoding/json" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" +) + +var _ = (*assembleBlockParamsMarshaling)(nil) + +// MarshalJSON marshals as JSON. +func (a assembleBlockParams) MarshalJSON() ([]byte, error) { + type assembleBlockParams struct { + ParentHash common.Hash `json:"parent_hash"` + Timestamp hexutil.Uint64 `json:"timestamp"` + } + var enc assembleBlockParams + enc.ParentHash = a.ParentHash + enc.Timestamp = hexutil.Uint64(a.Timestamp) + return json.Marshal(&enc) +} + +// UnmarshalJSON unmarshals from JSON. +func (a *assembleBlockParams) UnmarshalJSON(input []byte) error { + type assembleBlockParams struct { + ParentHash *common.Hash `json:"parent_hash"` + Timestamp *hexutil.Uint64 `json:"timestamp"` + } + var dec assembleBlockParams + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.ParentHash != nil { + a.ParentHash = *dec.ParentHash + } + if dec.Timestamp != nil { + a.Timestamp = uint64(*dec.Timestamp) + } + return nil +} diff --git a/eth/catalyst/gen_ed.go b/eth/catalyst/gen_ed.go new file mode 100644 index 00000000000..3fc64989b9b --- /dev/null +++ b/eth/catalyst/gen_ed.go @@ -0,0 +1,105 @@ +// Code generated by github.com/fjl/gencodec. DO NOT EDIT. + +package catalyst + +import ( + "encoding/json" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" +) + +var _ = (*executableDataMarshaling)(nil) + +// MarshalJSON marshals as JSON. +func (e executableData) MarshalJSON() ([]byte, error) { + type executableData struct { + BlockHash common.Hash `json:"blockHash"` + ParentHash common.Hash `json:"parentHash"` + Miner common.Address `json:"miner"` + StateRoot common.Hash `json:"stateRoot"` + Number hexutil.Uint64 `json:"number"` + GasLimit hexutil.Uint64 `json:"gasLimit"` + GasUsed hexutil.Uint64 `json:"gasUsed"` + Timestamp hexutil.Uint64 `json:"timestamp"` + ReceiptRoot common.Hash `json:"receiptsRoot"` + LogsBloom hexutil.Bytes `json:"logsBloom"` + Transactions []hexutil.Bytes `json:"transactions"` + } + var enc executableData + enc.BlockHash = e.BlockHash + enc.ParentHash = e.ParentHash + enc.Miner = e.Miner + enc.StateRoot = e.StateRoot + enc.Number = hexutil.Uint64(e.Number) + enc.GasLimit = hexutil.Uint64(e.GasLimit) + enc.GasUsed = hexutil.Uint64(e.GasUsed) + enc.Timestamp = hexutil.Uint64(e.Timestamp) + enc.ReceiptRoot = e.ReceiptRoot + enc.LogsBloom = e.LogsBloom + if e.Transactions != nil { + enc.Transactions = make([]hexutil.Bytes, len(e.Transactions)) + for k, v := range e.Transactions { + enc.Transactions[k] = v + } + } + return json.Marshal(&enc) +} + +// UnmarshalJSON unmarshals from JSON. +func (e *executableData) UnmarshalJSON(input []byte) error { + type executableData struct { + BlockHash *common.Hash `json:"blockHash"` + ParentHash *common.Hash `json:"parentHash"` + Miner *common.Address `json:"miner"` + StateRoot *common.Hash `json:"stateRoot"` + Number *hexutil.Uint64 `json:"number"` + GasLimit *hexutil.Uint64 `json:"gasLimit"` + GasUsed *hexutil.Uint64 `json:"gasUsed"` + Timestamp *hexutil.Uint64 `json:"timestamp"` + ReceiptRoot *common.Hash `json:"receiptsRoot"` + LogsBloom *hexutil.Bytes `json:"logsBloom"` + Transactions []hexutil.Bytes `json:"transactions"` + } + var dec executableData + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.BlockHash != nil { + e.BlockHash = *dec.BlockHash + } + if dec.ParentHash != nil { + e.ParentHash = *dec.ParentHash + } + if dec.Miner != nil { + e.Miner = *dec.Miner + } + if dec.StateRoot != nil { + e.StateRoot = *dec.StateRoot + } + if dec.Number != nil { + e.Number = uint64(*dec.Number) + } + if dec.GasLimit != nil { + e.GasLimit = uint64(*dec.GasLimit) + } + if dec.GasUsed != nil { + e.GasUsed = uint64(*dec.GasUsed) + } + if dec.Timestamp != nil { + e.Timestamp = uint64(*dec.Timestamp) + } + if dec.ReceiptRoot != nil { + e.ReceiptRoot = *dec.ReceiptRoot + } + if dec.LogsBloom != nil { + e.LogsBloom = *dec.LogsBloom + } + if dec.Transactions != nil { + e.Transactions = make([][]byte, len(dec.Transactions)) + for k, v := range dec.Transactions { + e.Transactions[k] = v + } + } + return nil +} From 0eda836890c967396376224c48441613071193ed Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 16 Apr 2021 16:17:35 +0200 Subject: [PATCH 31/43] eth/catalyst: fix name of parenthash parameter --- eth/catalyst/api_types.go | 2 +- eth/catalyst/gen_blockparams.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/eth/catalyst/api_types.go b/eth/catalyst/api_types.go index b280aedb0c6..d80bf8ed563 100644 --- a/eth/catalyst/api_types.go +++ b/eth/catalyst/api_types.go @@ -25,7 +25,7 @@ import ( // Structure described at https://hackmd.io/T9x2mMA4S7us8tJwEB3FDQ type assembleBlockParams struct { - ParentHash common.Hash `json:"parent_hash"` + ParentHash common.Hash `json:"parentHash"` Timestamp uint64 `json:"timestamp"` } diff --git a/eth/catalyst/gen_blockparams.go b/eth/catalyst/gen_blockparams.go index 8c36e944a8f..2c03b1eef63 100644 --- a/eth/catalyst/gen_blockparams.go +++ b/eth/catalyst/gen_blockparams.go @@ -14,7 +14,7 @@ var _ = (*assembleBlockParamsMarshaling)(nil) // MarshalJSON marshals as JSON. func (a assembleBlockParams) MarshalJSON() ([]byte, error) { type assembleBlockParams struct { - ParentHash common.Hash `json:"parent_hash"` + ParentHash common.Hash `json:"parentHash"` Timestamp hexutil.Uint64 `json:"timestamp"` } var enc assembleBlockParams @@ -26,7 +26,7 @@ func (a assembleBlockParams) MarshalJSON() ([]byte, error) { // UnmarshalJSON unmarshals from JSON. func (a *assembleBlockParams) UnmarshalJSON(input []byte) error { type assembleBlockParams struct { - ParentHash *common.Hash `json:"parent_hash"` + ParentHash *common.Hash `json:"parentHash"` Timestamp *hexutil.Uint64 `json:"timestamp"` } var dec assembleBlockParams From 0a5bf45f339ffb380cfbc1f1da33500697b5924a Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 16 Apr 2021 16:46:14 +0200 Subject: [PATCH 32/43] eth/catalyst: make all params required --- eth/catalyst/api_types.go | 26 ++++---- eth/catalyst/gen_blockparams.go | 19 +++--- eth/catalyst/gen_ed.go | 106 ++++++++++++++++++-------------- 3 files changed, 83 insertions(+), 68 deletions(-) diff --git a/eth/catalyst/api_types.go b/eth/catalyst/api_types.go index d80bf8ed563..d5d351a9915 100644 --- a/eth/catalyst/api_types.go +++ b/eth/catalyst/api_types.go @@ -25,8 +25,8 @@ import ( // Structure described at https://hackmd.io/T9x2mMA4S7us8tJwEB3FDQ type assembleBlockParams struct { - ParentHash common.Hash `json:"parentHash"` - Timestamp uint64 `json:"timestamp"` + ParentHash common.Hash `json:"parentHash" gencodec:"required"` + Timestamp uint64 `json:"timestamp" gencodec:"required"` } // JSON type overrides for assembleBlockParams. @@ -38,17 +38,17 @@ type assembleBlockParamsMarshaling struct { // Structure described at https://notes.ethereum.org/@n0ble/rayonism-the-merge-spec#Parameters1 type executableData struct { - BlockHash common.Hash `json:"blockHash"` - ParentHash common.Hash `json:"parentHash"` - Miner common.Address `json:"miner"` - StateRoot common.Hash `json:"stateRoot"` - Number uint64 `json:"number"` - GasLimit uint64 `json:"gasLimit"` - GasUsed uint64 `json:"gasUsed"` - Timestamp uint64 `json:"timestamp"` - ReceiptRoot common.Hash `json:"receiptsRoot"` - LogsBloom []byte `json:"logsBloom"` - Transactions [][]byte `json:"transactions"` + BlockHash common.Hash `json:"blockHash" gencodec:"required"` + ParentHash common.Hash `json:"parentHash" gencodec:"required"` + Miner common.Address `json:"miner" gencodec:"required"` + StateRoot common.Hash `json:"stateRoot" gencodec:"required"` + Number uint64 `json:"number" gencodec:"required"` + GasLimit uint64 `json:"gasLimit" gencodec:"required"` + GasUsed uint64 `json:"gasUsed" gencodec:"required"` + Timestamp uint64 `json:"timestamp" gencodec:"required"` + ReceiptRoot common.Hash `json:"receiptsRoot" gencodec:"required"` + LogsBloom []byte `json:"logsBloom" gencodec:"required"` + Transactions [][]byte `json:"transactions" gencodec:"required"` } // JSON type overrides for executableData. diff --git a/eth/catalyst/gen_blockparams.go b/eth/catalyst/gen_blockparams.go index 2c03b1eef63..a9a08ec3a80 100644 --- a/eth/catalyst/gen_blockparams.go +++ b/eth/catalyst/gen_blockparams.go @@ -4,6 +4,7 @@ package catalyst import ( "encoding/json" + "errors" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" @@ -14,8 +15,8 @@ var _ = (*assembleBlockParamsMarshaling)(nil) // MarshalJSON marshals as JSON. func (a assembleBlockParams) MarshalJSON() ([]byte, error) { type assembleBlockParams struct { - ParentHash common.Hash `json:"parentHash"` - Timestamp hexutil.Uint64 `json:"timestamp"` + ParentHash common.Hash `json:"parentHash" gencodec:"required"` + Timestamp hexutil.Uint64 `json:"timestamp" gencodec:"required"` } var enc assembleBlockParams enc.ParentHash = a.ParentHash @@ -26,18 +27,20 @@ func (a assembleBlockParams) MarshalJSON() ([]byte, error) { // UnmarshalJSON unmarshals from JSON. func (a *assembleBlockParams) UnmarshalJSON(input []byte) error { type assembleBlockParams struct { - ParentHash *common.Hash `json:"parentHash"` - Timestamp *hexutil.Uint64 `json:"timestamp"` + ParentHash *common.Hash `json:"parentHash" gencodec:"required"` + Timestamp *hexutil.Uint64 `json:"timestamp" gencodec:"required"` } var dec assembleBlockParams if err := json.Unmarshal(input, &dec); err != nil { return err } - if dec.ParentHash != nil { - a.ParentHash = *dec.ParentHash + if dec.ParentHash == nil { + return errors.New("missing required field 'parentHash' for assembleBlockParams") } - if dec.Timestamp != nil { - a.Timestamp = uint64(*dec.Timestamp) + a.ParentHash = *dec.ParentHash + if dec.Timestamp == nil { + return errors.New("missing required field 'timestamp' for assembleBlockParams") } + a.Timestamp = uint64(*dec.Timestamp) return nil } diff --git a/eth/catalyst/gen_ed.go b/eth/catalyst/gen_ed.go index 3fc64989b9b..4c2e4c8ead5 100644 --- a/eth/catalyst/gen_ed.go +++ b/eth/catalyst/gen_ed.go @@ -4,6 +4,7 @@ package catalyst import ( "encoding/json" + "errors" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" @@ -14,17 +15,17 @@ var _ = (*executableDataMarshaling)(nil) // MarshalJSON marshals as JSON. func (e executableData) MarshalJSON() ([]byte, error) { type executableData struct { - BlockHash common.Hash `json:"blockHash"` - ParentHash common.Hash `json:"parentHash"` - Miner common.Address `json:"miner"` - StateRoot common.Hash `json:"stateRoot"` - Number hexutil.Uint64 `json:"number"` - GasLimit hexutil.Uint64 `json:"gasLimit"` - GasUsed hexutil.Uint64 `json:"gasUsed"` - Timestamp hexutil.Uint64 `json:"timestamp"` - ReceiptRoot common.Hash `json:"receiptsRoot"` - LogsBloom hexutil.Bytes `json:"logsBloom"` - Transactions []hexutil.Bytes `json:"transactions"` + BlockHash common.Hash `json:"blockHash" gencodec:"required"` + ParentHash common.Hash `json:"parentHash" gencodec:"required"` + Miner common.Address `json:"miner" gencodec:"required"` + StateRoot common.Hash `json:"stateRoot" gencodec:"required"` + Number hexutil.Uint64 `json:"number" gencodec:"required"` + GasLimit hexutil.Uint64 `json:"gasLimit" gencodec:"required"` + GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"` + Timestamp hexutil.Uint64 `json:"timestamp" gencodec:"required"` + ReceiptRoot common.Hash `json:"receiptsRoot" gencodec:"required"` + LogsBloom hexutil.Bytes `json:"logsBloom" gencodec:"required"` + Transactions []hexutil.Bytes `json:"transactions" gencodec:"required"` } var enc executableData enc.BlockHash = e.BlockHash @@ -49,57 +50,68 @@ func (e executableData) MarshalJSON() ([]byte, error) { // UnmarshalJSON unmarshals from JSON. func (e *executableData) UnmarshalJSON(input []byte) error { type executableData struct { - BlockHash *common.Hash `json:"blockHash"` - ParentHash *common.Hash `json:"parentHash"` - Miner *common.Address `json:"miner"` - StateRoot *common.Hash `json:"stateRoot"` - Number *hexutil.Uint64 `json:"number"` - GasLimit *hexutil.Uint64 `json:"gasLimit"` - GasUsed *hexutil.Uint64 `json:"gasUsed"` - Timestamp *hexutil.Uint64 `json:"timestamp"` - ReceiptRoot *common.Hash `json:"receiptsRoot"` - LogsBloom *hexutil.Bytes `json:"logsBloom"` - Transactions []hexutil.Bytes `json:"transactions"` + BlockHash *common.Hash `json:"blockHash" gencodec:"required"` + ParentHash *common.Hash `json:"parentHash" gencodec:"required"` + Miner *common.Address `json:"miner" gencodec:"required"` + StateRoot *common.Hash `json:"stateRoot" gencodec:"required"` + Number *hexutil.Uint64 `json:"number" gencodec:"required"` + GasLimit *hexutil.Uint64 `json:"gasLimit" gencodec:"required"` + GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"` + Timestamp *hexutil.Uint64 `json:"timestamp" gencodec:"required"` + ReceiptRoot *common.Hash `json:"receiptsRoot" gencodec:"required"` + LogsBloom *hexutil.Bytes `json:"logsBloom" gencodec:"required"` + Transactions []hexutil.Bytes `json:"transactions" gencodec:"required"` } var dec executableData if err := json.Unmarshal(input, &dec); err != nil { return err } - if dec.BlockHash != nil { - e.BlockHash = *dec.BlockHash + if dec.BlockHash == nil { + return errors.New("missing required field 'blockHash' for executableData") } - if dec.ParentHash != nil { - e.ParentHash = *dec.ParentHash + e.BlockHash = *dec.BlockHash + if dec.ParentHash == nil { + return errors.New("missing required field 'parentHash' for executableData") } - if dec.Miner != nil { - e.Miner = *dec.Miner + e.ParentHash = *dec.ParentHash + if dec.Miner == nil { + return errors.New("missing required field 'miner' for executableData") } - if dec.StateRoot != nil { - e.StateRoot = *dec.StateRoot + e.Miner = *dec.Miner + if dec.StateRoot == nil { + return errors.New("missing required field 'stateRoot' for executableData") } - if dec.Number != nil { - e.Number = uint64(*dec.Number) + e.StateRoot = *dec.StateRoot + if dec.Number == nil { + return errors.New("missing required field 'number' for executableData") } - if dec.GasLimit != nil { - e.GasLimit = uint64(*dec.GasLimit) + e.Number = uint64(*dec.Number) + if dec.GasLimit == nil { + return errors.New("missing required field 'gasLimit' for executableData") } - if dec.GasUsed != nil { - e.GasUsed = uint64(*dec.GasUsed) + e.GasLimit = uint64(*dec.GasLimit) + if dec.GasUsed == nil { + return errors.New("missing required field 'gasUsed' for executableData") } - if dec.Timestamp != nil { - e.Timestamp = uint64(*dec.Timestamp) + e.GasUsed = uint64(*dec.GasUsed) + if dec.Timestamp == nil { + return errors.New("missing required field 'timestamp' for executableData") } - if dec.ReceiptRoot != nil { - e.ReceiptRoot = *dec.ReceiptRoot + e.Timestamp = uint64(*dec.Timestamp) + if dec.ReceiptRoot == nil { + return errors.New("missing required field 'receiptsRoot' for executableData") } - if dec.LogsBloom != nil { - e.LogsBloom = *dec.LogsBloom + e.ReceiptRoot = *dec.ReceiptRoot + if dec.LogsBloom == nil { + return errors.New("missing required field 'logsBloom' for executableData") } - if dec.Transactions != nil { - e.Transactions = make([][]byte, len(dec.Transactions)) - for k, v := range dec.Transactions { - e.Transactions[k] = v - } + e.LogsBloom = *dec.LogsBloom + if dec.Transactions == nil { + return errors.New("missing required field 'transactions' for executableData") + } + e.Transactions = make([][]byte, len(dec.Transactions)) + for k, v := range dec.Transactions { + e.Transactions[k] = v } return nil } From 43b88ae197b3b6bbbe759cd2e4a9a861877e0df5 Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Fri, 16 Apr 2021 18:16:40 +0200 Subject: [PATCH 33/43] Finalize and SetHead stubs return success upon call --- eth/catalyst/api.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index fe6f01e2097..0db62ba3ce8 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -297,10 +297,10 @@ func (api *consensusAPI) addBlockTxs(block *types.Block) error { // that data that is no longer needed can be removed. func (api *consensusAPI) FinalizeBlock(blockHash common.Hash) (*genericResponse, error) { // Stubbed for now, it's not critical - return &genericResponse{false}, nil + return &genericResponse{true}, nil } // SetHead is called to perform a force choice. func (api *consensusAPI) SetHead(newHead common.Hash) (*genericResponse, error) { - return &genericResponse{false}, nil + return &genericResponse{true}, nil } From 558649e43644a259e1d7dc44508d01b0b5c395d5 Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Fri, 16 Apr 2021 18:17:22 +0200 Subject: [PATCH 34/43] Remove outdated comment --- eth/catalyst/api.go | 1 - 1 file changed, 1 deletion(-) diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index 0db62ba3ce8..397f27affb3 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -296,7 +296,6 @@ func (api *consensusAPI) addBlockTxs(block *types.Block) error { // FinalizeBlock is called to mark a block as synchronized, so // that data that is no longer needed can be removed. func (api *consensusAPI) FinalizeBlock(blockHash common.Hash) (*genericResponse, error) { - // Stubbed for now, it's not critical return &genericResponse{true}, nil } From 7b0a22d445adbe99095bf751eee1c1bee58a4139 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 16 Apr 2021 18:24:55 +0200 Subject: [PATCH 35/43] eth/catalyst: move Register to api.go --- eth/catalyst/api.go | 22 ++++++++++++++++++++ eth/catalyst/catalyst.go | 45 ---------------------------------------- 2 files changed, 22 insertions(+), 45 deletions(-) delete mode 100644 eth/catalyst/catalyst.go diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index 397f27affb3..6945f0c93df 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -14,9 +14,11 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . +// Package catalyst implements the temporary eth1/eth2 RPC integration. package catalyst import ( + "errors" "fmt" "math/big" "time" @@ -27,10 +29,30 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/node" chainParams "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/trie" ) +// Register adds catalyst APIs to the node. +func Register(stack *node.Node, backend *eth.Ethereum) error { + if backend.BlockChain().Config().CatalystBlock == nil { + return errors.New("can't enable catalyst service without catalyst fork block in chain config") + } + + log.Warn("Catalyst mode enabled") + stack.RegisterAPIs([]rpc.API{ + { + Namespace: "consensus", + Version: "1.0", + Service: newConsensusAPI(backend), + Public: true, + }, + }) + return nil +} + type consensusAPI struct { eth *eth.Ethereum env *blockExecutionEnv diff --git a/eth/catalyst/catalyst.go b/eth/catalyst/catalyst.go deleted file mode 100644 index d2279668a52..00000000000 --- a/eth/catalyst/catalyst.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2020 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Package catalyst implements the temporary eth1/eth2 RPC integration. -package catalyst - -import ( - "errors" - - "github.com/ethereum/go-ethereum/eth" - "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/node" - "github.com/ethereum/go-ethereum/rpc" -) - -// Register adds catalyst APIs to the node. -func Register(stack *node.Node, backend *eth.Ethereum) error { - if backend.BlockChain().Config().CatalystBlock == nil { - return errors.New("can't enable catalyst service without catalyst fork block in chain config") - } - - log.Warn("Catalyst mode enabled") - stack.RegisterAPIs([]rpc.API{ - { - Namespace: "consensus", - Version: "1.0", - Service: newConsensusAPI(backend), - Public: true, - }, - }) - return nil -} From 77e5444a0b9a7cf85aa775f406248d2468fd1c7d Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 16 Apr 2021 18:28:19 +0200 Subject: [PATCH 36/43] eth/catalyst: improve error for misconfigured catalystBlock --- eth/catalyst/api.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index 6945f0c93df..edf7fc436dc 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -37,8 +37,11 @@ import ( // Register adds catalyst APIs to the node. func Register(stack *node.Node, backend *eth.Ethereum) error { - if backend.BlockChain().Config().CatalystBlock == nil { - return errors.New("can't enable catalyst service without catalyst fork block in chain config") + chainconfig := backend.BlockChain().Config() + if chainconfig.CatalystBlock == nil { + return errors.New("catalystBlock is not set in genesis config") + } else if chainconfig.CatalystBlock.Sign() != 0 { + return errors.New("catalystBlock of genesis config must be zero") } log.Warn("Catalyst mode enabled") From b75abfe6a5daf132ee3782309708ace407dfd10e Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 16 Apr 2021 19:31:28 +0200 Subject: [PATCH 37/43] eth/catalyst: remove debug print --- eth/catalyst/api.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index edf7fc436dc..312920bbf65 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -162,10 +162,8 @@ func (api *consensusAPI) AssembleBlock(params assembleBlockParams) (*executableD log.Trace("Not enough gas for further transactions", "have", api.env.gasPool, "want", chainParams.TxGas) break } - tx := txs.Peek() if tx == nil { - fmt.Println("no tx") break } From be735b8e8429bca3de52a033ebb88ab93e06eaf3 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 16 Apr 2021 19:33:13 +0200 Subject: [PATCH 38/43] eth/catalyst: fix tx signer creation types.LatestSigner is for code that doesn't know the block number. When creating the new block in catalyst, the number is known, and MakeSigner should be used. --- eth/catalyst/api.go | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index 312920bbf65..456477b232f 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -152,17 +152,18 @@ func (api *consensusAPI) AssembleBlock(params assembleBlockParams) (*executableD if err != nil { return nil, err } - signer := types.LatestSigner(bc.Config()) - txs := types.NewTransactionsByPriceAndNonce(signer, pending) - - var transactions []*types.Transaction + var ( + signer = types.MakeSigner(bc.Config(), header.Number) + txHeap = types.NewTransactionsByPriceAndNonce(signer, pending) + transactions []*types.Transaction + ) for { if api.env.gasPool.Gas() < chainParams.TxGas { log.Trace("Not enough gas for further transactions", "have", api.env.gasPool, "want", chainParams.TxGas) break } - tx := txs.Peek() + tx := txHeap.Peek() if tx == nil { break } @@ -177,29 +178,29 @@ func (api *consensusAPI) AssembleBlock(params assembleBlockParams) (*executableD case core.ErrGasLimitReached: // Pop the current out-of-gas transaction without shifting in the next from the account log.Trace("Gas limit exceeded for current block", "sender", from) - txs.Pop() + txHeap.Pop() case core.ErrNonceTooLow: // New head notification data race between the transaction pool and miner, shift log.Trace("Skipping transaction with low nonce", "sender", from, "nonce", tx.Nonce()) - txs.Shift() + txHeap.Shift() case core.ErrNonceTooHigh: // Reorg notification data race between the transaction pool and miner, skip account = log.Trace("Skipping account with high nonce", "sender", from, "nonce", tx.Nonce()) - txs.Pop() + txHeap.Pop() case nil: // Everything ok, collect the logs and shift in the next transaction from the same account api.env.tcount++ - txs.Shift() + txHeap.Shift() transactions = append(transactions, tx) default: // Strange error, discard the transaction and get the next in line (note, the // nonce-too-high clause will prevent us from executing in vain). log.Debug("Transaction failed, account skipped", "hash", tx.Hash(), "err", err) - txs.Shift() + txHeap.Shift() } } From b311fc8e59985e555f314b1393b55bcd0d1dc243 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 16 Apr 2021 19:46:17 +0200 Subject: [PATCH 39/43] eth/catalyst: avoid keeping block env This makes 'env' local to AssembleBlock instead of keeping it in the api object. Doing this avoids races for concurrent calls of AssembleBlock. --- eth/catalyst/api.go | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index 456477b232f..b19a0f904bd 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -57,9 +57,7 @@ func Register(stack *node.Node, backend *eth.Ethereum) error { } type consensusAPI struct { - eth *eth.Ethereum - env *blockExecutionEnv - head common.Hash + eth *eth.Ethereum } func newConsensusAPI(eth *eth.Ethereum) *consensusAPI { @@ -69,6 +67,7 @@ func newConsensusAPI(eth *eth.Ethereum) *consensusAPI { // blockExecutionEnv gathers all the data required to execute // a block, either when assembling it or when inserting it. type blockExecutionEnv struct { + chain *core.BlockChain state *state.StateDB tcount int gasPool *core.GasPool @@ -78,38 +77,39 @@ type blockExecutionEnv struct { receipts []*types.Receipt } -func (api *consensusAPI) commitTransaction(tx *types.Transaction, coinbase common.Address) error { +func (env *blockExecutionEnv) commitTransaction(tx *types.Transaction, coinbase common.Address) error { //snap := eth2rpc.current.state.Snapshot() - chain := api.eth.BlockChain() - receipt, err := core.ApplyTransaction(chain.Config(), chain, &coinbase, api.env.gasPool, api.env.state, api.env.header, tx, &api.env.header.GasUsed, *chain.GetVMConfig()) + vmconfig := *env.chain.GetVMConfig() + receipt, err := core.ApplyTransaction(env.chain.Config(), env.chain, &coinbase, env.gasPool, env.state, env.header, tx, &env.header.GasUsed, vmconfig) if err != nil { //w.current.state.RevertToSnapshot(snap) return err } - api.env.txs = append(api.env.txs, tx) - api.env.receipts = append(api.env.receipts, receipt) + env.txs = append(env.txs, tx) + env.receipts = append(env.receipts, receipt) return nil } -func (api *consensusAPI) makeEnv(parent *types.Block, header *types.Header) error { +func (api *consensusAPI) makeEnv(parent *types.Block, header *types.Header) (*blockExecutionEnv, error) { state, err := api.eth.BlockChain().StateAt(parent.Root()) if err != nil { - return err + return nil, err } - api.env = &blockExecutionEnv{ + env := &blockExecutionEnv{ + chain: api.eth.BlockChain(), state: state, header: header, gasPool: new(core.GasPool).AddGas(header.GasLimit), } - return nil + return env, nil } // AssembleBlock creates a new block, inserts it into the chain, and returns the "execution // data" required for eth2 clients to process the new block. func (api *consensusAPI) AssembleBlock(params assembleBlockParams) (*executableData, error) { - log.Info("Produce block", "parentHash", params.ParentHash) + log.Info("Producing block", "parentHash", params.ParentHash) bc := api.eth.BlockChain() parent := bc.GetBlockByHash(params.ParentHash) @@ -148,7 +148,7 @@ func (api *consensusAPI) AssembleBlock(params assembleBlockParams) (*executableD return nil, err } - err = api.makeEnv(parent, header) + env, err := api.makeEnv(parent, header) if err != nil { return nil, err } @@ -159,8 +159,8 @@ func (api *consensusAPI) AssembleBlock(params assembleBlockParams) (*executableD transactions []*types.Transaction ) for { - if api.env.gasPool.Gas() < chainParams.TxGas { - log.Trace("Not enough gas for further transactions", "have", api.env.gasPool, "want", chainParams.TxGas) + if env.gasPool.Gas() < chainParams.TxGas { + log.Trace("Not enough gas for further transactions", "have", env.gasPool, "want", chainParams.TxGas) break } tx := txHeap.Peek() @@ -172,8 +172,8 @@ func (api *consensusAPI) AssembleBlock(params assembleBlockParams) (*executableD // XXX replay protection check is missing // Execute the transaction - api.env.state.Prepare(tx.Hash(), common.Hash{}, api.env.tcount) - err := api.commitTransaction(tx, coinbase) + env.state.Prepare(tx.Hash(), common.Hash{}, env.tcount) + err := env.commitTransaction(tx, coinbase) switch err { case core.ErrGasLimitReached: // Pop the current out-of-gas transaction without shifting in the next from the account @@ -192,7 +192,7 @@ func (api *consensusAPI) AssembleBlock(params assembleBlockParams) (*executableD case nil: // Everything ok, collect the logs and shift in the next transaction from the same account - api.env.tcount++ + env.tcount++ txHeap.Shift() transactions = append(transactions, tx) @@ -204,15 +204,15 @@ func (api *consensusAPI) AssembleBlock(params assembleBlockParams) (*executableD } } - block, err := api.eth.Engine().FinalizeAndAssemble(bc, header, api.env.state, transactions, nil /* uncles */, api.env.receipts) + block, err := api.eth.Engine().FinalizeAndAssemble(bc, header, env.state, transactions, nil /* uncles */, env.receipts) if err != nil { return nil, err } var logs []*types.Log - var receipts = make(types.Receipts, len(api.env.receipts)) + var receipts = make(types.Receipts, len(env.receipts)) hash := block.Hash() - for i, receipt := range api.env.receipts { + for i, receipt := range env.receipts { // add block location fields receipt.BlockHash = hash receipt.BlockNumber = block.Number() From dc8295ae56c5d048742467fcf9f0e5d8d7b7da30 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 16 Apr 2021 20:00:38 +0200 Subject: [PATCH 40/43] eth/catalyst: check error of sender derivation --- eth/catalyst/api.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index b19a0f904bd..53c8ec50328 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -167,13 +167,15 @@ func (api *consensusAPI) AssembleBlock(params assembleBlockParams) (*executableD if tx == nil { break } - - from, _ := types.Sender(signer, tx) - // XXX replay protection check is missing + from, err := types.Sender(signer, tx) + if err != nil { + log.Warn("Discarding invalid transaction in block", "hash", tx.Hash(), "block", env.header.Number, "err", err) + continue + } // Execute the transaction env.state.Prepare(tx.Hash(), common.Hash{}, env.tcount) - err := env.commitTransaction(tx, coinbase) + err = env.commitTransaction(tx, coinbase) switch err { case core.ErrGasLimitReached: // Pop the current out-of-gas transaction without shifting in the next from the account From d00d4da48db6128aa96fdd6fe993532673d42954 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 16 Apr 2021 20:02:57 +0200 Subject: [PATCH 41/43] eth/catalyst: don't derive optional receipt fields In receipts and logs, the block location fields are not part of consensus and don't need to be derived because they will be thrown away. Also remove pointless assignment of the ReceiptHash after this. Block.Header() returns a copy of the header, so modifying the receipt hash doesn't do anything. --- eth/catalyst/api.go | 23 +---------------------- 1 file changed, 1 insertion(+), 22 deletions(-) diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index 53c8ec50328..e5a3d53b97b 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -206,32 +206,11 @@ func (api *consensusAPI) AssembleBlock(params assembleBlockParams) (*executableD } } + // Create the block. block, err := api.eth.Engine().FinalizeAndAssemble(bc, header, env.state, transactions, nil /* uncles */, env.receipts) if err != nil { return nil, err } - - var logs []*types.Log - var receipts = make(types.Receipts, len(env.receipts)) - hash := block.Hash() - for i, receipt := range env.receipts { - // add block location fields - receipt.BlockHash = hash - receipt.BlockNumber = block.Number() - receipt.TransactionIndex = uint(i) - - receipts[i] = new(types.Receipt) - *receipts[i] = *receipt - // Update the block hash in all logs since it is now available and not when the - // receipt/log of individual transactions were created. - for _, log := range receipt.Logs { - log.BlockHash = hash - } - logs = append(logs, receipt.Logs...) - } - - block.Header().ReceiptHash = types.DeriveSha(receipts, new(trie.Trie)) - return &executableData{ BlockHash: block.Hash(), ParentHash: block.ParentHash(), From 5521b0acb56cc7ecf5d856a02f325e1fddfc7d93 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 16 Apr 2021 20:24:04 +0200 Subject: [PATCH 42/43] eth/catalyst: skip the sender error check again, and explain why --- eth/catalyst/api.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index e5a3d53b97b..c7544802f92 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -167,11 +167,10 @@ func (api *consensusAPI) AssembleBlock(params assembleBlockParams) (*executableD if tx == nil { break } - from, err := types.Sender(signer, tx) - if err != nil { - log.Warn("Discarding invalid transaction in block", "hash", tx.Hash(), "block", env.header.Number, "err", err) - continue - } + + // The sender is only for logging purposes, and it doesn't really matter if it's + // correct. + from, _ := types.Sender(signer, tx) // Execute the transaction env.state.Prepare(tx.Hash(), common.Hash{}, env.tcount) From 7eea1cff4121d23ab4c8932ef33ff9b077a20da1 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 16 Apr 2021 21:25:24 +0200 Subject: [PATCH 43/43] eth/catalyst: remove more weird comments --- eth/catalyst/api.go | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index c7544802f92..d6ea691d02f 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -78,17 +78,13 @@ type blockExecutionEnv struct { } func (env *blockExecutionEnv) commitTransaction(tx *types.Transaction, coinbase common.Address) error { - //snap := eth2rpc.current.state.Snapshot() - vmconfig := *env.chain.GetVMConfig() receipt, err := core.ApplyTransaction(env.chain.Config(), env.chain, &coinbase, env.gasPool, env.state, env.header, tx, &env.header.GasUsed, vmconfig) if err != nil { - //w.current.state.RevertToSnapshot(snap) return err } env.txs = append(env.txs, tx) env.receipts = append(env.receipts, receipt) - return nil } @@ -118,7 +114,6 @@ func (api *consensusAPI) AssembleBlock(params assembleBlockParams) (*executableD if parent.Time() >= params.Timestamp { return nil, fmt.Errorf("child timestamp lower than parent's: %d >= %d", parent.Time(), params.Timestamp) } - // this will ensure we're not going off too far in the future if now := uint64(time.Now().Unix()); params.Timestamp > now+1 { wait := time.Duration(params.Timestamp-now) * time.Second log.Info("Producing block too far in the future", "wait", common.PrettyDuration(wait)) @@ -168,8 +163,7 @@ func (api *consensusAPI) AssembleBlock(params assembleBlockParams) (*executableD break } - // The sender is only for logging purposes, and it doesn't really matter if it's - // correct. + // The sender is only for logging purposes, and it doesn't really matter if it's correct. from, _ := types.Sender(signer, tx) // Execute the transaction @@ -275,7 +269,6 @@ func insertBlockParamsToBlock(params executableData) (*types.Block, error) { // or false + an error. This is a bit redundant for go, but simplifies things on the // eth2 side. func (api *consensusAPI) NewBlock(params executableData) (*newBlockResponse, error) { - // compute block number as parent.number + 1 parent := api.eth.BlockChain().GetBlockByHash(params.ParentHash) if parent == nil { return &newBlockResponse{false}, fmt.Errorf("could not find parent %x", params.ParentHash)