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/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/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 bb1800ea919..8e794833961 100644
--- a/core/vm/jump_table.go
+++ b/core/vm/jump_table.go
@@ -57,11 +57,30 @@ 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 b0adf37d0c2..436b55522c1 100644
--- a/core/vm/opcodes.go
+++ b/core/vm/opcodes.go
@@ -101,8 +101,10 @@ 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.
@@ -272,14 +274,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",
@@ -396,147 +400,149 @@ 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,
+ "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..094db818900 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,10 @@ 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 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
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),
}
}