eth/catalyst: add catalyst API prototype (#22641)
This change adds the --catalyst flag, enabling an RPC API for eth2 integration. In this initial version, catalyst mode also disables all peer-to-peer networking. Co-authored-by: Mikhail Kalinin <noblesse.knight@gmail.com> Co-authored-by: Felix Lange <fjl@twurst.com>
This commit is contained in:
parent
4f3ba6742f
commit
f79cce5de9
@ -28,6 +28,7 @@ import (
|
|||||||
"gopkg.in/urfave/cli.v1"
|
"gopkg.in/urfave/cli.v1"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
"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/eth/ethconfig"
|
||||||
"github.com/ethereum/go-ethereum/internal/ethapi"
|
"github.com/ethereum/go-ethereum/internal/ethapi"
|
||||||
"github.com/ethereum/go-ethereum/metrics"
|
"github.com/ethereum/go-ethereum/metrics"
|
||||||
@ -143,7 +144,17 @@ func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) {
|
|||||||
if ctx.GlobalIsSet(utils.OverrideBerlinFlag.Name) {
|
if ctx.GlobalIsSet(utils.OverrideBerlinFlag.Name) {
|
||||||
cfg.Eth.OverrideBerlin = new(big.Int).SetUint64(ctx.GlobalUint64(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.")
|
||||||
|
}
|
||||||
|
if err := catalyst.Register(stack, eth); err != nil {
|
||||||
|
utils.Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Configure GraphQL if requested
|
// Configure GraphQL if requested
|
||||||
if ctx.GlobalIsSet(utils.GraphQLEnabledFlag.Name) {
|
if ctx.GlobalIsSet(utils.GraphQLEnabledFlag.Name) {
|
||||||
|
@ -151,6 +151,7 @@ var (
|
|||||||
utils.EVMInterpreterFlag,
|
utils.EVMInterpreterFlag,
|
||||||
utils.MinerNotifyFullFlag,
|
utils.MinerNotifyFullFlag,
|
||||||
configFileFlag,
|
configFileFlag,
|
||||||
|
utils.CatalystFlag,
|
||||||
}
|
}
|
||||||
|
|
||||||
rpcFlags = []cli.Flag{
|
rpcFlags = []cli.Flag{
|
||||||
|
@ -235,6 +235,7 @@ var AppHelpFlagGroups = []flags.FlagGroup{
|
|||||||
utils.SnapshotFlag,
|
utils.SnapshotFlag,
|
||||||
utils.BloomFilterSizeFlag,
|
utils.BloomFilterSizeFlag,
|
||||||
cli.HelpFlag,
|
cli.HelpFlag,
|
||||||
|
utils.CatalystFlag,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -755,6 +755,11 @@ var (
|
|||||||
Usage: "External EVM configuration (default = built-in interpreter)",
|
Usage: "External EVM configuration (default = built-in interpreter)",
|
||||||
Value: "",
|
Value: "",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CatalystFlag = cli.BoolFlag{
|
||||||
|
Name: "catalyst",
|
||||||
|
Usage: "Catalyst mode (eth2 integration testing)",
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// MakeDataDir retrieves the currently requested data directory, terminating
|
// MakeDataDir retrieves the currently requested data directory, terminating
|
||||||
@ -1186,10 +1191,11 @@ func SetP2PConfig(ctx *cli.Context, cfg *p2p.Config) {
|
|||||||
cfg.NetRestrict = list
|
cfg.NetRestrict = list
|
||||||
}
|
}
|
||||||
|
|
||||||
if ctx.GlobalBool(DeveloperFlag.Name) {
|
if ctx.GlobalBool(DeveloperFlag.Name) || ctx.GlobalBool(CatalystFlag.Name) {
|
||||||
// --dev mode can't use p2p networking.
|
// --dev mode can't use p2p networking.
|
||||||
cfg.MaxPeers = 0
|
cfg.MaxPeers = 0
|
||||||
cfg.ListenAddr = ":0"
|
cfg.ListenAddr = ""
|
||||||
|
cfg.NoDial = true
|
||||||
cfg.NoDiscovery = true
|
cfg.NoDiscovery = true
|
||||||
cfg.DiscoveryV5 = false
|
cfg.DiscoveryV5 = false
|
||||||
}
|
}
|
||||||
@ -1693,14 +1699,16 @@ func SetDNSDiscoveryDefaults(cfg *ethconfig.Config, genesis common.Hash) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RegisterEthService adds an Ethereum client to the stack.
|
// 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 {
|
if cfg.SyncMode == downloader.LightSync {
|
||||||
backend, err := les.New(stack, cfg)
|
backend, err := les.New(stack, cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Fatalf("Failed to register the Ethereum service: %v", err)
|
Fatalf("Failed to register the Ethereum service: %v", err)
|
||||||
}
|
}
|
||||||
stack.RegisterAPIs(tracers.APIs(backend.ApiBackend))
|
stack.RegisterAPIs(tracers.APIs(backend.ApiBackend))
|
||||||
return backend.ApiBackend
|
return backend.ApiBackend, nil
|
||||||
}
|
}
|
||||||
backend, err := eth.New(stack, cfg)
|
backend, err := eth.New(stack, cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -1713,7 +1721,7 @@ func RegisterEthService(stack *node.Node, cfg *ethconfig.Config) ethapi.Backend
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
stack.RegisterAPIs(tracers.APIs(backend.APIBackend))
|
stack.RegisterAPIs(tracers.APIs(backend.APIBackend))
|
||||||
return backend.APIBackend
|
return backend.APIBackend, backend
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegisterEthStatsService configures the Ethereum Stats daemon and adds it to
|
// RegisterEthStatsService configures the Ethereum Stats daemon and adds it to
|
||||||
|
@ -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 {
|
func CalcDifficulty(config *params.ChainConfig, time uint64, parent *types.Header) *big.Int {
|
||||||
next := new(big.Int).Add(parent.Number, big1)
|
next := new(big.Int).Add(parent.Number, big1)
|
||||||
switch {
|
switch {
|
||||||
|
case config.IsCatalyst(next):
|
||||||
|
return big.NewInt(1)
|
||||||
case config.IsMuirGlacier(next):
|
case config.IsMuirGlacier(next):
|
||||||
return calcDifficultyEip2384(time, parent)
|
return calcDifficultyEip2384(time, parent)
|
||||||
case config.IsConstantinople(next):
|
case config.IsConstantinople(next):
|
||||||
|
@ -1693,6 +1693,22 @@ func (bc *BlockChain) InsertChain(chain types.Blocks) (int, error) {
|
|||||||
return n, err
|
return n, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InsertChainWithoutSealVerification works exactly the same
|
||||||
|
// except for seal verification, seal verification is omitted
|
||||||
|
func (bc *BlockChain) InsertChainWithoutSealVerification(block *types.Block) (int, error) {
|
||||||
|
bc.blockProcFeed.Send(true)
|
||||||
|
defer bc.blockProcFeed.Send(false)
|
||||||
|
|
||||||
|
// Pre-checks passed, start the full block imports
|
||||||
|
bc.wg.Add(1)
|
||||||
|
bc.chainmu.Lock()
|
||||||
|
n, err := bc.insertChain(types.Blocks([]*types.Block{block}), false)
|
||||||
|
bc.chainmu.Unlock()
|
||||||
|
bc.wg.Done()
|
||||||
|
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
// insertChain is the internal implementation of InsertChain, which assumes that
|
// insertChain is the internal implementation of InsertChain, which assumes that
|
||||||
// 1) chains are contiguous, and 2) The chain mutex is held.
|
// 1) chains are contiguous, and 2) The chain mutex is held.
|
||||||
//
|
//
|
||||||
|
@ -223,6 +223,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
|
|||||||
}); err != nil {
|
}); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
eth.miner = miner.New(eth, &config.Miner, chainConfig, eth.EventMux(), eth.engine, eth.isLocalBlock)
|
eth.miner = miner.New(eth, &config.Miner, chainConfig, eth.EventMux(), eth.engine, eth.isLocalBlock)
|
||||||
eth.miner.SetExtra(makeExtraData(config.Miner.ExtraData))
|
eth.miner.SetExtra(makeExtraData(config.Miner.ExtraData))
|
||||||
|
|
||||||
|
302
eth/catalyst/api.go
Normal file
302
eth/catalyst/api.go
Normal file
@ -0,0 +1,302 @@
|
|||||||
|
// 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
// Package catalyst implements the temporary eth1/eth2 RPC integration.
|
||||||
|
package catalyst
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"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/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 {
|
||||||
|
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")
|
||||||
|
stack.RegisterAPIs([]rpc.API{
|
||||||
|
{
|
||||||
|
Namespace: "consensus",
|
||||||
|
Version: "1.0",
|
||||||
|
Service: newConsensusAPI(backend),
|
||||||
|
Public: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type consensusAPI struct {
|
||||||
|
eth *eth.Ethereum
|
||||||
|
}
|
||||||
|
|
||||||
|
func newConsensusAPI(eth *eth.Ethereum) *consensusAPI {
|
||||||
|
return &consensusAPI{eth: eth}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
|
header *types.Header
|
||||||
|
txs []*types.Transaction
|
||||||
|
receipts []*types.Receipt
|
||||||
|
}
|
||||||
|
|
||||||
|
func (env *blockExecutionEnv) commitTransaction(tx *types.Transaction, coinbase common.Address) error {
|
||||||
|
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 {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
env.txs = append(env.txs, tx)
|
||||||
|
env.receipts = append(env.receipts, receipt)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *consensusAPI) makeEnv(parent *types.Block, header *types.Header) (*blockExecutionEnv, error) {
|
||||||
|
state, err := api.eth.BlockChain().StateAt(parent.Root())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
env := &blockExecutionEnv{
|
||||||
|
chain: api.eth.BlockChain(),
|
||||||
|
state: state,
|
||||||
|
header: header,
|
||||||
|
gasPool: new(core.GasPool).AddGas(header.GasLimit),
|
||||||
|
}
|
||||||
|
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("Producing 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)
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
env, err := api.makeEnv(parent, header)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
signer = types.MakeSigner(bc.Config(), header.Number)
|
||||||
|
txHeap = types.NewTransactionsByPriceAndNonce(signer, pending)
|
||||||
|
transactions []*types.Transaction
|
||||||
|
)
|
||||||
|
for {
|
||||||
|
if env.gasPool.Gas() < chainParams.TxGas {
|
||||||
|
log.Trace("Not enough gas for further transactions", "have", env.gasPool, "want", chainParams.TxGas)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
tx := txHeap.Peek()
|
||||||
|
if tx == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
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
|
||||||
|
log.Trace("Gas limit exceeded for current block", "sender", from)
|
||||||
|
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())
|
||||||
|
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())
|
||||||
|
txHeap.Pop()
|
||||||
|
|
||||||
|
case nil:
|
||||||
|
// Everything ok, collect the logs and shift in the next transaction from the same account
|
||||||
|
env.tcount++
|
||||||
|
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)
|
||||||
|
txHeap.Shift()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the block.
|
||||||
|
block, err := api.eth.Engine().FinalizeAndAssemble(bc, header, env.state, transactions, nil /* uncles */, env.receipts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &executableData{
|
||||||
|
BlockHash: block.Hash(),
|
||||||
|
ParentHash: block.ParentHash(),
|
||||||
|
Miner: block.Coinbase(),
|
||||||
|
StateRoot: block.Root(),
|
||||||
|
Number: block.NumberU64(),
|
||||||
|
GasLimit: block.GasLimit(),
|
||||||
|
GasUsed: block.GasUsed(),
|
||||||
|
Timestamp: block.Time(),
|
||||||
|
ReceiptRoot: block.ReceiptHash(),
|
||||||
|
LogsBloom: block.Bloom().Bytes(),
|
||||||
|
Transactions: encodeTransactions(block.Transactions()),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
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(txs), trie.NewStackTrie(nil)),
|
||||||
|
ReceiptHash: params.ReceiptRoot,
|
||||||
|
Bloom: types.BytesToBloom(params.LogsBloom),
|
||||||
|
Difficulty: big.NewInt(1),
|
||||||
|
Number: number,
|
||||||
|
GasLimit: params.GasLimit,
|
||||||
|
GasUsed: params.GasUsed,
|
||||||
|
Time: params.Timestamp,
|
||||||
|
}
|
||||||
|
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,
|
||||||
|
// 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) {
|
||||||
|
parent := api.eth.BlockChain().GetBlockByHash(params.ParentHash)
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = api.eth.BlockChain().InsertChainWithoutSealVerification(block)
|
||||||
|
return &newBlockResponse{err == nil}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Used in tests to add a the list of transactions from a block to the tx pool.
|
||||||
|
func (api *consensusAPI) addBlockTxs(block *types.Block) error {
|
||||||
|
for _, tx := range block.Transactions() {
|
||||||
|
api.eth.TxPool().AddLocal(tx)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
return &genericResponse{true}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetHead is called to perform a force choice.
|
||||||
|
func (api *consensusAPI) SetHead(newHead common.Hash) (*genericResponse, error) {
|
||||||
|
return &genericResponse{true}, nil
|
||||||
|
}
|
229
eth/catalyst/api_test.go
Normal file
229
eth/catalyst/api_test.go
Normal file
@ -0,0 +1,229 @@
|
|||||||
|
// 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package catalyst
|
||||||
|
|
||||||
|
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/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)
|
||||||
|
)
|
||||||
|
|
||||||
|
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()
|
||||||
|
//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,
|
||||||
|
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 TestEth2AssembleBlock(t *testing.T) {
|
||||||
|
genesis, blocks := generateTestChain()
|
||||||
|
n, ethservice := startEthService(t, genesis, blocks[1:9])
|
||||||
|
defer n.Close()
|
||||||
|
|
||||||
|
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)
|
||||||
|
blockParams := assembleBlockParams{
|
||||||
|
ParentHash: blocks[8].ParentHash(),
|
||||||
|
Timestamp: blocks[8].Time(),
|
||||||
|
}
|
||||||
|
execData, err := api.AssembleBlock(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 TestEth2AssembleBlockWithAnotherBlocksTxs(t *testing.T) {
|
||||||
|
genesis, blocks := generateTestChain()
|
||||||
|
n, ethservice := startEthService(t, genesis, blocks[1:9])
|
||||||
|
defer n.Close()
|
||||||
|
|
||||||
|
api := newConsensusAPI(ethservice)
|
||||||
|
|
||||||
|
// Put the 10th block's tx in the pool and produce a new block
|
||||||
|
api.addBlockTxs(blocks[9])
|
||||||
|
blockParams := assembleBlockParams{
|
||||||
|
ParentHash: blocks[9].ParentHash(),
|
||||||
|
Timestamp: blocks[9].Time(),
|
||||||
|
}
|
||||||
|
execData, err := api.AssembleBlock(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 TestEth2NewBlock(t *testing.T) {
|
||||||
|
genesis, blocks, forkedBlocks := generateTestChainWithFork(10, 4)
|
||||||
|
n, ethservice := startEthService(t, genesis, blocks[1:5])
|
||||||
|
defer n.Close()
|
||||||
|
|
||||||
|
api := newConsensusAPI(ethservice)
|
||||||
|
for i := 5; i < 10; i++ {
|
||||||
|
p := executableData{
|
||||||
|
ParentHash: ethservice.BlockChain().CurrentBlock().Hash(),
|
||||||
|
Miner: blocks[i].Coinbase(),
|
||||||
|
StateRoot: blocks[i].Root(),
|
||||||
|
GasLimit: blocks[i].GasLimit(),
|
||||||
|
GasUsed: blocks[i].GasUsed(),
|
||||||
|
Transactions: encodeTransactions(blocks[i].Transactions()),
|
||||||
|
ReceiptRoot: blocks[i].ReceiptHash(),
|
||||||
|
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 {
|
||||||
|
t.Fatalf("Failed to insert block: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exp := ethservice.BlockChain().CurrentBlock().Hash()
|
||||||
|
|
||||||
|
// Introduce the fork point.
|
||||||
|
lastBlockNum := blocks[4].Number()
|
||||||
|
lastBlock := blocks[4]
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
lastBlockNum.Add(lastBlockNum, big.NewInt(1))
|
||||||
|
p := executableData{
|
||||||
|
ParentHash: lastBlock.Hash(),
|
||||||
|
Miner: forkedBlocks[i].Coinbase(),
|
||||||
|
StateRoot: forkedBlocks[i].Root(),
|
||||||
|
Number: lastBlockNum.Uint64(),
|
||||||
|
GasLimit: forkedBlocks[i].GasLimit(),
|
||||||
|
GasUsed: forkedBlocks[i].GasUsed(),
|
||||||
|
Transactions: encodeTransactions(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, err = insertBlockParamsToBlock(p)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ethservice.BlockChain().CurrentBlock().Hash() != exp {
|
||||||
|
t.Fatalf("Wrong head after inserting fork %x != %x", exp, ethservice.BlockChain().CurrentBlock().Hash())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
70
eth/catalyst/api_types.go
Normal file
70
eth/catalyst/api_types.go
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
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:"parentHash" gencodec:"required"`
|
||||||
|
Timestamp uint64 `json:"timestamp" gencodec:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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" 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.
|
||||||
|
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"`
|
||||||
|
}
|
46
eth/catalyst/gen_blockparams.go
Normal file
46
eth/catalyst/gen_blockparams.go
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
|
||||||
|
|
||||||
|
package catalyst
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"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:"parentHash" gencodec:"required"`
|
||||||
|
Timestamp hexutil.Uint64 `json:"timestamp" gencodec:"required"`
|
||||||
|
}
|
||||||
|
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:"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 {
|
||||||
|
return errors.New("missing required field 'parentHash' for assembleBlockParams")
|
||||||
|
}
|
||||||
|
a.ParentHash = *dec.ParentHash
|
||||||
|
if dec.Timestamp == nil {
|
||||||
|
return errors.New("missing required field 'timestamp' for assembleBlockParams")
|
||||||
|
}
|
||||||
|
a.Timestamp = uint64(*dec.Timestamp)
|
||||||
|
return nil
|
||||||
|
}
|
117
eth/catalyst/gen_ed.go
Normal file
117
eth/catalyst/gen_ed.go
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
|
||||||
|
|
||||||
|
package catalyst
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"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" 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
|
||||||
|
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" 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 {
|
||||||
|
return errors.New("missing required field 'blockHash' for executableData")
|
||||||
|
}
|
||||||
|
e.BlockHash = *dec.BlockHash
|
||||||
|
if dec.ParentHash == nil {
|
||||||
|
return errors.New("missing required field 'parentHash' for executableData")
|
||||||
|
}
|
||||||
|
e.ParentHash = *dec.ParentHash
|
||||||
|
if dec.Miner == nil {
|
||||||
|
return errors.New("missing required field 'miner' for executableData")
|
||||||
|
}
|
||||||
|
e.Miner = *dec.Miner
|
||||||
|
if dec.StateRoot == nil {
|
||||||
|
return errors.New("missing required field 'stateRoot' for executableData")
|
||||||
|
}
|
||||||
|
e.StateRoot = *dec.StateRoot
|
||||||
|
if dec.Number == nil {
|
||||||
|
return errors.New("missing required field 'number' for executableData")
|
||||||
|
}
|
||||||
|
e.Number = uint64(*dec.Number)
|
||||||
|
if dec.GasLimit == nil {
|
||||||
|
return errors.New("missing required field 'gasLimit' for executableData")
|
||||||
|
}
|
||||||
|
e.GasLimit = uint64(*dec.GasLimit)
|
||||||
|
if dec.GasUsed == nil {
|
||||||
|
return errors.New("missing required field 'gasUsed' for executableData")
|
||||||
|
}
|
||||||
|
e.GasUsed = uint64(*dec.GasUsed)
|
||||||
|
if dec.Timestamp == nil {
|
||||||
|
return errors.New("missing required field 'timestamp' for executableData")
|
||||||
|
}
|
||||||
|
e.Timestamp = uint64(*dec.Timestamp)
|
||||||
|
if dec.ReceiptRoot == nil {
|
||||||
|
return errors.New("missing required field 'receiptsRoot' for executableData")
|
||||||
|
}
|
||||||
|
e.ReceiptRoot = *dec.ReceiptRoot
|
||||||
|
if dec.LogsBloom == nil {
|
||||||
|
return errors.New("missing required field 'logsBloom' for executableData")
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
@ -244,16 +244,16 @@ var (
|
|||||||
//
|
//
|
||||||
// This configuration is intentionally not using keyed fields to force anyone
|
// This configuration is intentionally not using keyed fields to force anyone
|
||||||
// adding flags to the config to also have to set these fields.
|
// 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
|
// AllCliqueProtocolChanges contains every protocol change (EIPs) introduced
|
||||||
// and accepted by the Ethereum core developers into the Clique consensus.
|
// and accepted by the Ethereum core developers into the Clique consensus.
|
||||||
//
|
//
|
||||||
// This configuration is intentionally not using keyed fields to force anyone
|
// This configuration is intentionally not using keyed fields to force anyone
|
||||||
// adding flags to the config to also have to set these fields.
|
// 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))
|
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)
|
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)
|
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
|
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)
|
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
|
// Various consensus engines
|
||||||
Ethash *EthashConfig `json:"ethash,omitempty"`
|
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)
|
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
|
// IsEWASM returns whether num represents a block number after the EWASM fork
|
||||||
func (c *ChainConfig) IsEWASM(num *big.Int) bool {
|
func (c *ChainConfig) IsEWASM(num *big.Int) bool {
|
||||||
return isForked(c.EWASMBlock, num)
|
return isForked(c.EWASMBlock, num)
|
||||||
@ -623,7 +629,7 @@ type Rules struct {
|
|||||||
ChainID *big.Int
|
ChainID *big.Int
|
||||||
IsHomestead, IsEIP150, IsEIP155, IsEIP158 bool
|
IsHomestead, IsEIP150, IsEIP155, IsEIP158 bool
|
||||||
IsByzantium, IsConstantinople, IsPetersburg, IsIstanbul bool
|
IsByzantium, IsConstantinople, IsPetersburg, IsIstanbul bool
|
||||||
IsBerlin bool
|
IsBerlin, IsCatalyst bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rules ensures c's ChainID is not nil.
|
// Rules ensures c's ChainID is not nil.
|
||||||
@ -643,5 +649,6 @@ func (c *ChainConfig) Rules(num *big.Int) Rules {
|
|||||||
IsPetersburg: c.IsPetersburg(num),
|
IsPetersburg: c.IsPetersburg(num),
|
||||||
IsIstanbul: c.IsIstanbul(num),
|
IsIstanbul: c.IsIstanbul(num),
|
||||||
IsBerlin: c.IsBerlin(num),
|
IsBerlin: c.IsBerlin(num),
|
||||||
|
IsCatalyst: c.IsCatalyst(num),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user