diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 1ebc998a40ed..4cd296127c1c 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -152,6 +152,16 @@ var ( Usage: "Sepolia network: pre-configured proof-of-work test network", Category: flags.EthCategory, } + GnosisChainFlag = &cli.BoolFlag{ + Name: "gnosis", + Usage: "Gnosis chain network: pre-configured merged proof-of-authority test network", + Category: flags.EthCategory, + } + ChiadoFlag = &cli.BoolFlag{ + Name: "chiado", + Usage: "Chiado network: pre-configured merged proof-of-authority test network", + Category: flags.EthCategory, + } // Dev mode DeveloperFlag = &cli.BoolFlag{ @@ -928,6 +938,8 @@ var ( TestnetFlags = []cli.Flag{ GoerliFlag, SepoliaFlag, + GnosisChainFlag, + ChiadoFlag, } // NetworkFlags is the flag group of all built-in supported networks. NetworkFlags = append([]cli.Flag{MainnetFlag}, TestnetFlags...) @@ -958,6 +970,12 @@ func MakeDataDir(ctx *cli.Context) string { if ctx.Bool(SepoliaFlag.Name) { return filepath.Join(path, "sepolia") } + if ctx.Bool(GnosisChainFlag.Name) { + return filepath.Join(path, "gnosis") + } + if ctx.Bool(ChiadoFlag.Name) { + return filepath.Join(path, "chiado") + } return path } Fatalf("Cannot determine default data directory, please set manually (--datadir)") @@ -1008,6 +1026,10 @@ func setBootstrapNodes(ctx *cli.Context, cfg *p2p.Config) { urls = params.SepoliaBootnodes case ctx.Bool(GoerliFlag.Name): urls = params.GoerliBootnodes + case ctx.Bool(GnosisChainFlag.Name): + urls = params.GnosisBootnodes + case ctx.Bool(ChiadoFlag.Name): + urls = params.ChiadoBootnodes } // don't apply defaults if BootstrapNodes is already set @@ -1457,6 +1479,10 @@ func SetDataDir(ctx *cli.Context, cfg *node.Config) { cfg.DataDir = filepath.Join(node.DefaultDataDir(), "goerli") case ctx.Bool(SepoliaFlag.Name) && cfg.DataDir == node.DefaultDataDir(): cfg.DataDir = filepath.Join(node.DefaultDataDir(), "sepolia") + case ctx.Bool(GnosisChainFlag.Name) && cfg.DataDir == node.DefaultDataDir(): + cfg.DataDir = filepath.Join(node.DefaultDataDir(), "gnosis") + case ctx.Bool(ChiadoFlag.Name) && cfg.DataDir == node.DefaultDataDir(): + cfg.DataDir = filepath.Join(node.DefaultDataDir(), "chiado") } } @@ -1613,7 +1639,7 @@ func CheckExclusive(ctx *cli.Context, args ...interface{}) { // SetEthConfig applies eth-related command line flags to the config. func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { // Avoid conflicting network flags - CheckExclusive(ctx, MainnetFlag, DeveloperFlag, GoerliFlag, SepoliaFlag) + CheckExclusive(ctx, MainnetFlag, DeveloperFlag, GoerliFlag, SepoliaFlag, GnosisChainFlag, ChiadoFlag) CheckExclusive(ctx, LightServeFlag, SyncModeFlag, "light") CheckExclusive(ctx, DeveloperFlag, ExternalSignerFlag) // Can't use both ephemeral unlocked and external signer if ctx.String(GCModeFlag.Name) == "archive" && ctx.Uint64(TxLookupLimitFlag.Name) != 0 { @@ -1761,6 +1787,18 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { } cfg.Genesis = core.DefaultGoerliGenesisBlock() SetDNSDiscoveryDefaults(cfg, params.GoerliGenesisHash) + case ctx.Bool(GnosisChainFlag.Name): + if !ctx.IsSet(NetworkIdFlag.Name) { + cfg.NetworkId = 100 + } + cfg.Genesis = core.DefaultGnosisGenesisBlock() + SetDNSDiscoveryDefaults(cfg, params.GnosisChainHash) + case ctx.Bool(ChiadoFlag.Name): + if !ctx.IsSet(NetworkIdFlag.Name) { + cfg.NetworkId = 10200 + } + cfg.Genesis = core.DefaultChiadoGenesisBlock() + SetDNSDiscoveryDefaults(cfg, params.ChiadoGenesisHash) case ctx.Bool(DeveloperFlag.Name): if !ctx.IsSet(NetworkIdFlag.Name) { cfg.NetworkId = 1337 @@ -2087,6 +2125,10 @@ func MakeGenesis(ctx *cli.Context) *core.Genesis { genesis = core.DefaultSepoliaGenesisBlock() case ctx.Bool(GoerliFlag.Name): genesis = core.DefaultGoerliGenesisBlock() + case ctx.Bool(GnosisChainFlag.Name): + genesis = core.DefaultGnosisGenesisBlock() + case ctx.Bool(ChiadoFlag.Name): + genesis = core.DefaultChiadoGenesisBlock() case ctx.Bool(DeveloperFlag.Name): Fatalf("Developer chains are ephemeral") } diff --git a/consensus/aura/api.go b/consensus/aura/api.go new file mode 100644 index 000000000000..89e17d579967 --- /dev/null +++ b/consensus/aura/api.go @@ -0,0 +1,32 @@ +// Copyright 2017 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 aura + +import ( + "github.com/ethereum/go-ethereum/consensus" +) + +// API is a user facing RPC API to allow controlling the signer and voting +// mechanisms of the proof-of-authority scheme. +type API struct { + chain consensus.ChainHeaderReader + aura *Aura +} + +func (api *API) Version() string { + return "0.1" +} diff --git a/consensus/aura/aura.go b/consensus/aura/aura.go new file mode 100644 index 000000000000..ae190737b9e8 --- /dev/null +++ b/consensus/aura/aura.go @@ -0,0 +1,591 @@ +// Copyright 2017 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 aura + +import ( + "bytes" + "errors" + "fmt" + "math" + "math/big" + "sync" + "time" + + "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus" + "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/crypto" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/rpc" + "github.com/ethereum/go-ethereum/trie" + lru "github.com/hashicorp/golang-lru" + "github.com/holiman/uint256" + "golang.org/x/crypto/sha3" +) + +const ( + checkpointInterval = 1024 // Number of blocks after which to save the vote snapshot to the database + inmemorySnapshots = 128 // Number of recent vote snapshots to keep in memory + inmemorySignatures = 4096 // Number of recent block signatures to keep in memory + + wiggleTime = 500 * time.Millisecond // Random delay (per signer) to allow concurrent signers +) + +// Clique proof-of-authority protocol constants. +var ( + epochLength = uint64(30000) // Default number of blocks after which to checkpoint and reset the pending votes + + extraVanity = 32 // Fixed number of extra-data prefix bytes reserved for signer vanity + extraSeal = 65 // Fixed number of extra-data suffix bytes reserved for signer seal + + uncleHash = types.CalcUncleHash(nil) // Always Keccak256(RLP([])) as uncles are meaningless outside of PoW. +) + +// Various error messages to mark blocks invalid. These should be private to +// prevent engine specific errors from being referenced in the remainder of the +// codebase, inherently breaking if the engine is swapped out. Please put common +// error types into the consensus package. +var ( + // errUnknownBlock is returned when the list of signers is requested for a block + // that is not part of the local blockchain. + errUnknownBlock = errors.New("unknown block") + + // errInvalidCheckpointBeneficiary is returned if a checkpoint/epoch transition + // block has a beneficiary set to non-zeroes. + errInvalidCheckpointBeneficiary = errors.New("beneficiary in checkpoint block non-zero") + + // errInvalidVote is returned if a nonce value is something else that the two + // allowed constants of 0x00..0 or 0xff..f. + errInvalidVote = errors.New("vote nonce not 0x00..0 or 0xff..f") + + // errInvalidCheckpointVote is returned if a checkpoint/epoch transition block + // has a vote nonce set to non-zeroes. + errInvalidCheckpointVote = errors.New("vote nonce in checkpoint block non-zero") + + // errMissingVanity is returned if a block's extra-data section is shorter than + // 32 bytes, which is required to store the signer vanity. + errMissingVanity = errors.New("extra-data 32 byte vanity prefix missing") + + // errMissingSignature is returned if a block's extra-data section doesn't seem + // to contain a 65 byte secp256k1 signature. + errMissingSignature = errors.New("extra-data 65 byte suffix signature missing") + + // errExtraSigners is returned if non-checkpoint block contain signer data in + // their extra-data fields. + errExtraSigners = errors.New("non-checkpoint block contains extra signer list") + + // errInvalidValidatorSeal is returned if the extra data field length is not + // equal to the length of a seal + errInvalidExtraData = errors.New("extra data field in block header is invalid") + + // errInvalidCheckpointSigners is returned if a checkpoint block contains an + // invalid list of signers (i.e. non divisible by 20 bytes, or not the correct + // ones). + errInvalidCheckpointSigners = errors.New("invalid signer list on checkpoint block") + + // errInvalidMixDigest is returned if a block's mix digest is non-zero. + errInvalidMixDigest = errors.New("non-zero mix digest") + + // errInvalidUncleHash is returned if a block contains an non-empty uncle list. + errInvalidUncleHash = errors.New("non empty uncle hash") + + // errInvalidDifficulty is returned if the difficulty of a block is not either + // of 1 or 2, or if the value does not match the turn of the signer. + errInvalidDifficulty = errors.New("invalid difficulty") + + // ErrInvalidTimestamp is returned if the timestamp of a block is lower than + // the previous block's timestamp + the minimum block period. + ErrInvalidTimestamp = errors.New("invalid timestamp") + + // errInvalidVotingChain is returned if an authorization list is attempted to + // be modified via out-of-range or non-contiguous headers. + errInvalidVotingChain = errors.New("invalid voting chain") + + // errUnauthorized is returned if a header is signed by a non-authorized entity. + errUnauthorized = errors.New("unauthorized") + + // errWaitTransactions is returned if an empty block is attempted to be sealed + // on an instant chain (0 second period). It's important to refuse these as the + // block reward is zero, so an empty block just bloats the chain... fast. + errWaitTransactions = errors.New("waiting for transactions") +) + +// SignerFn is a signer callback function to request a hash to be signed by a +// backing account. +type SignerFn func(accounts.Account, []byte) ([]byte, error) + +// sigHash returns the hash which is used as input for the proof-of-authority +// signing. It is the hash of the entire header apart from the 65 byte signature +// contained at the end of the extra data. +// +// Note, the method requires the extra data to be at least 65 bytes, otherwise it +// panics. This is done to avoid accidentally using both forms (signature present +// or not), which could be abused to produce different hashes for the same header. +func sigHash(header *types.Header) (hash common.Hash) { + hasher := sha3.NewLegacyKeccak256() + + rlp.Encode(hasher, []interface{}{ + header.ParentHash, + header.UncleHash, + header.Coinbase, + header.Root, + header.TxHash, + header.ReceiptHash, + header.Bloom, + header.Difficulty, + header.Number, + header.GasLimit, + header.GasUsed, + header.Time, + header.Extra[:len(header.Extra)-65], // Yes, this will panic if extra is too short + header.MixDigest, + header.Nonce, + }) + hasher.Sum(hash[:0]) + return hash +} + +// ecrecover extracts the Ethereum account address from a signed header. +func ecrecover(header *types.Header, sigcache *lru.ARCCache) (common.Address, error) { + // If the signature's already cached, return that + hash := header.Hash() + if address, known := sigcache.Get(hash); known { + return address.(common.Address), nil + } + // Retrieve the signature from the header extra-data + if len(header.Extra) < extraSeal { + return common.Address{}, errMissingSignature + } + signature := header.Extra[len(header.Extra)-extraSeal:] + + // Recover the public key and the Ethereum address + pubkey, err := crypto.Ecrecover(sigHash(header).Bytes(), signature) + if err != nil { + return common.Address{}, err + } + var signer common.Address + copy(signer[:], crypto.Keccak256(pubkey[1:])[12:]) + + sigcache.Add(hash, signer) + return signer, nil +} + +// Clique is the proof-of-authority consensus engine proposed to support the +// Ethereum testnet following the Ropsten attacks. +type Aura struct { + config *params.AuthorityRoundParams // Consensus engine configuration parameters + db ethdb.Database // Database to store and retrieve snapshot checkpoints + + recents *lru.ARCCache // Snapshots for recent block to speed up reorgs + signatures *lru.ARCCache // Signatures of recent blocks to speed up mining + + proposals map[common.Address]bool // Current list of proposals we are pushing + + signer common.Address // Ethereum address of the signing key + signFn SignerFn // Signer function to authorize hashes with + lock sync.RWMutex // Protects the signer fields +} + +// New creates a Aura proof-of-authority consensus engine with the initial +// signers set to the ones provided by the user. +func New(config *params.AuthorityRoundParams, db ethdb.Database) *Aura { + // Set any missing consensus parameters to their defaults + conf := *config + // Allocate the snapshot caches and create the engine + recents, _ := lru.NewARC(inmemorySnapshots) + signatures, _ := lru.NewARC(inmemorySignatures) + + return &Aura{ + config: &conf, + db: db, + recents: recents, + signatures: signatures, + proposals: make(map[common.Address]bool), + } +} + +// Author implements consensus.Engine, returning the Ethereum address recovered +// from the signature in the header's extra-data section. +func (a *Aura) Author(header *types.Header) (common.Address, error) { + return ecrecover(header, a.signatures) +} + +// VerifyHeader checks whether a header conforms to the consensus rules. +func (a *Aura) VerifyHeader(chain consensus.ChainHeaderReader, header *types.Header) error { + return a.verifyHeader(chain, header, nil) +} + +// VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers. The +// method returns a quit channel to abort the operations and a results channel to +// retrieve the async verifications (the order is that of the input slice). +func (a *Aura) VerifyHeaders(chain consensus.ChainHeaderReader, headers []*types.Header) (chan<- struct{}, <-chan error) { + abort := make(chan struct{}) + results := make(chan error, len(headers)) + + go func() { + for i, header := range headers { + err := a.verifyHeader(chain, header, headers[:i]) + + select { + case <-abort: + return + case results <- err: + } + } + }() + return abort, results +} + +// verifyHeader checks whether a header conforms to the consensus rules.The +// caller may optionally pass in a batch of parents (ascending order) to avoid +// looking those up from the database. This is useful for concurrently verifying +// a batch of new headers. +func (a *Aura) verifyHeader(chain consensus.ChainHeaderReader, header *types.Header, parents []*types.Header) error { + // accept all blocks + return nil +} + +// VerifyUncles implements consensus.Engine, always returning an error for any +// uncles as this consensus mechanism doesn't permit uncles. +func (a *Aura) VerifyUncles(chain consensus.ChainReader, block *types.Block) error { + if len(block.Uncles()) > 0 { + return errors.New("uncles not allowed") + } + return nil +} + +// VerifySeal implements consensus.Engine, checking whether the signature contained +// in the header satisfies the consensus protocol requirements. +func (a *Aura) VerifySeal(chain consensus.ChainHeaderReader, header *types.Header) error { + return a.verifySeal(chain, header, nil) +} + +// verifySeal checks whether the signature contained in the header satisfies the +// consensus protocol requirements. The method accepts an optional list of parent +// headers that aren't yet part of the local blockchain to generate the snapshots +// from. +func (a *Aura) verifySeal(chain consensus.ChainHeaderReader, header *types.Header, parents []*types.Header) error { + // Verifying the genesis block is not supported + number := header.Number.Uint64() + if number == 0 { + return errUnknownBlock + } + return nil +} + +// Prepare implements consensus.Engine, preparing all the consensus fields of the +// header for running the transactions on top. +func (a *Aura) Prepare(chain consensus.ChainHeaderReader, header *types.Header) error { + return nil +} + +// RewardKind - The kind of block reward. +// Depending on the consensus engine the allocated block reward might have +// different semantics which could lead e.g. to different reward values. +type RewardKind uint16 + +const ( + // RewardAuthor - attributed to the block author. + RewardAuthor RewardKind = 0 + // RewardEmptyStep - attributed to the author(s) of empty step(s) included in the block (AuthorityRound engine). + RewardEmptyStep RewardKind = 1 + // RewardExternal - attributed by an external protocol (e.g. block reward contract). + RewardExternal RewardKind = 2 + // RewardUncle - attributed to the block uncle(s) with given difference. + RewardUncle RewardKind = 3 +) + +type Reward struct { + Beneficiary common.Address + Kind RewardKind + Amount uint256.Int +} + +type BlockRewardContract struct { + blockNum uint64 + address common.Address // On-chain address. +} + +type BlockRewardContractList []BlockRewardContract + +func (r BlockRewardContractList) Less(i, j int) bool { return r[i].blockNum < r[j].blockNum } +func (r BlockRewardContractList) Len() int { return len(r) } +func (r BlockRewardContractList) Swap(i, j int) { r[i], r[j] = r[j], r[i] } + +type BlockReward struct { + blockNum uint64 + amount *uint256.Int +} + +type BlockRewardList []BlockReward + +func (a *Aura) CalculateRewards(header *types.Header, _ []*types.Header, chain consensus.ChainHeaderReader, statedb *state.StateDB) ([]Reward, error) { + var rewardContractAddress BlockRewardContract + rewardContractAddress = BlockRewardContract{ + blockNum: 9186425, + address: common.HexToAddress("0x481c034c6d9441db23ea48de68bcae812c5d39ba"), + } + if /*foundContract */ true { + beneficiaries := []common.Address{header.Coinbase} + rewardKind := []RewardKind{RewardAuthor} + var amounts []*uint256.Int + beneficiaries, amounts = callBlockRewardAbi(rewardContractAddress.address, beneficiaries, rewardKind, chain, header, statedb) + rewards := make([]Reward, len(amounts)) + for i, amount := range amounts { + rewards[i].Beneficiary = beneficiaries[i] + rewards[i].Kind = RewardExternal + rewards[i].Amount = *amount + } + return rewards, nil + } + + r := Reward{Beneficiary: header.Coinbase, Kind: RewardAuthor, Amount: *uint256.NewInt(0)} + return []Reward{r}, nil +} +func callBlockRewardAbi(contractAddr common.Address, beneficiaries []common.Address, rewardKind []RewardKind, chain consensus.ChainHeaderReader, header *types.Header, statedb *state.StateDB) ([]common.Address, []*uint256.Int) { + castedKind := make([]uint16, len(rewardKind)) + for i := range rewardKind { + castedKind[i] = uint16(rewardKind[i]) + } + packed, err := blockRewardAbi().Pack("reward", beneficiaries, castedKind) + if err != nil { + panic(err) + } + out, err := syscall(contractAddr, packed, chain, header, statedb) + if err != nil { + panic(err) + } + if len(out) == 0 { + return nil, nil + } + res, err := blockRewardAbi().Unpack("reward", out) + if err != nil { + panic(err) + } + beneficiariesRes := res[0].([]common.Address) + rewardsBig := res[1].([]*big.Int) + rewardsU256 := make([]*uint256.Int, len(rewardsBig)) + for i := 0; i < len(rewardsBig); i++ { + var overflow bool + rewardsU256[i], overflow = uint256.FromBig(rewardsBig[i]) + if overflow { + panic("Overflow in callBlockRewardAbi") + } + } + return beneficiariesRes, rewardsU256 +} + +var ( + blockRewardABIJSON = `[ + { + "constant": false, + "inputs": [ + { + "name": "benefactors", + "type": "address[]" + }, + { + "name": "kind", + "type": "uint16[]" + } + ], + "name": "reward", + "outputs": [ + { + "name": "", + "type": "address[]" + }, + { + "name": "", + "type": "uint256[]" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + } +]` + + withdrawalABIJSON = `[ + { + "constant": false, + "inputs": [ + { + "name": "maxNumberOfFailedWithdrawalsToProcess", + "type": "uint256" + }, + { + "name": "amounts", + "type": "uint64[]" + }, + { + "name": "addresses", + "type": "address[]" + } + ], + "name": "executeSystemWithdrawals", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +]` +) + +func blockRewardAbi() abi.ABI { + a, err := abi.JSON(bytes.NewReader([]byte(blockRewardABIJSON))) + if err != nil { + panic(err) + } + return a +} + +func (a *Aura) applyRewards(header *types.Header, state *state.StateDB, chain consensus.ChainHeaderReader) error { + rewards, err := a.CalculateRewards(header, nil, chain, state) + if err != nil { + return err + } + for _, r := range rewards { + state.AddBalance(r.Beneficiary, r.Amount.ToBig()) + } + return nil +} + +func withdrawalAbi() abi.ABI { + a, err := abi.JSON(bytes.NewReader([]byte(withdrawalABIJSON))) + if err != nil { + panic(err) + } + return a +} + +func syscall(contractaddr common.Address, data []byte, chain consensus.ChainHeaderReader, header *types.Header, statedb *state.StateDB) ([]byte, error) { + sysaddr := common.HexToAddress("fffffffffffffffffffffffffffffffffffffffe") + msg := &core.Message{ + To: &contractaddr, + From: sysaddr, + Nonce: 0, + Value: big.NewInt(0), + GasLimit: math.MaxUint64, + GasPrice: big.NewInt(0), + GasFeeCap: nil, + GasTipCap: nil, + Data: data, + AccessList: nil, + BlobHashes: nil, + SkipAccountChecks: false, + } + txctx := core.NewEVMTxContext(msg) + blkctx := core.NewEVMBlockContext(header, chain.(*core.BlockChain), nil) + evm := vm.NewEVM(blkctx, txctx, statedb, chain.Config(), vm.Config{ /*Debug: true, Tracer: logger.NewJSONLogger(nil, os.Stdout)*/ }) + ret, _, err := evm.Call(vm.AccountRef(sysaddr), contractaddr, data, math.MaxUint64, new(big.Int)) + if err != nil { + panic(err) + } + statedb.Finalise(true) + return ret, err +} + +// Finalize implements consensus.Engine, ensuring no uncles are set, nor block +// rewards given, and returns the final block. +func (a *Aura) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, withdrawals []*types.Withdrawal) { + if err := a.applyRewards(header, state, chain); err != nil { + panic(fmt.Sprintf("error applying reward %v", err)) + } + + // No block rewards in PoA, so the state remains as is and uncles are dropped + header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) + header.UncleHash = types.CalcUncleHash(nil) +} + +func (a *Aura) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt, withdrawals []*types.Withdrawal) (*types.Block, error) { + a.Finalize(chain, header, state, txs, uncles, withdrawals) + + // Assemble and return the final block for sealing + return types.NewBlock(header, txs, nil, receipts, trie.NewStackTrie(nil)), nil +} + +// Authorize injects a private key into the consensus engine to mint new blocks +// with. +func (a *Aura) Authorize(signer common.Address, signFn SignerFn) { + a.lock.Lock() + defer a.lock.Unlock() + + a.signer = signer + a.signFn = signFn +} + +// Seal implements consensus.Engine, attempting to create a sealed block using +// the local signing credentials. +func (a *Aura) Seal(chain consensus.ChainHeaderReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error { + return nil +} + +// Returns difficulty constant from config +func (a *Aura) CalcDifficulty(chain consensus.ChainHeaderReader, time uint64, parent *types.Header) *big.Int { + return big.NewInt(0) +} + +// SealHash returns the hash of a block prior to it being sealed. +func (a *Aura) SealHash(header *types.Header) common.Hash { + return sigHash(header) +} + +// Close implements consensus.Engine. It's a noop for clique as there is are no background threads. +func (a *Aura) Close() error { + return nil +} + +// APIs implements consensus.Engine, returning the user facing RPC API to allow +// controlling the signer voting. +func (a *Aura) APIs(chain consensus.ChainHeaderReader) []rpc.API { + return []rpc.API{{ + Namespace: "aura", + Version: "1.0", + Service: &API{chain: chain, aura: a}, + Public: false, + }} +} + +func (a *Aura) ExecuteSystemWithdrawals(withdrawals []*types.Withdrawal, chain consensus.ChainHeaderReader, header *types.Header, statedb *state.StateDB) error { + withdrawalContactAddress := common.HexToAddress("0x0B98057eA310F4d31F2a452B414647007d1645d9") + maxFailedWithdrawalsToProcess := big.NewInt(4) + amounts := make([]uint64, 0, len(withdrawals)) + addresses := make([]common.Address, 0, len(withdrawals)) + for _, w := range withdrawals { + amounts = append(amounts, w.Amount) + addresses = append(addresses, w.Address) + } + + packed, err := withdrawalAbi().Pack("executeSystemWithdrawals", maxFailedWithdrawalsToProcess, amounts, addresses) + if err != nil { + return err + } + + _, err = syscall(withdrawalContactAddress, packed, chain, header, statedb) + if err != nil { + log.Warn("ExecuteSystemWithdrawals", "err", err) + } + return err +} diff --git a/consensus/aura/auraabi/gen_block_reward.go b/consensus/aura/auraabi/gen_block_reward.go new file mode 100644 index 000000000000..ab8577b63e24 --- /dev/null +++ b/consensus/aura/auraabi/gen_block_reward.go @@ -0,0 +1,193 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package auraabi + +import ( + "math/big" + "strings" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/common" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription +) + +// BlockRewardABI is the input ABI used to generate the binding from. +const BlockRewardABI = "[{\"constant\":false,\"inputs\":[{\"name\":\"benefactors\",\"type\":\"address[]\"},{\"name\":\"kind\",\"type\":\"uint16[]\"}],\"name\":\"reward\",\"outputs\":[{\"name\":\"\",\"type\":\"address[]\"},{\"name\":\"\",\"type\":\"uint256[]\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]" + +// BlockReward is an auto generated Go binding around an Ethereum contract. +type BlockReward struct { + BlockRewardCaller // Read-only binding to the contract + BlockRewardTransactor // Write-only binding to the contract + BlockRewardFilterer // Log filterer for contract events +} + +// BlockRewardCaller is an auto generated read-only Go binding around an Ethereum contract. +type BlockRewardCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// BlockRewardTransactor is an auto generated write-only Go binding around an Ethereum contract. +type BlockRewardTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// BlockRewardFilterer is an auto generated log filtering Go binding around an Ethereum contract events. +type BlockRewardFilterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// BlockRewardSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type BlockRewardSession struct { + Contract *BlockReward // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// BlockRewardCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type BlockRewardCallerSession struct { + Contract *BlockRewardCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// BlockRewardTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type BlockRewardTransactorSession struct { + Contract *BlockRewardTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// BlockRewardRaw is an auto generated low-level Go binding around an Ethereum contract. +type BlockRewardRaw struct { + Contract *BlockReward // Generic contract binding to access the raw methods on +} + +// BlockRewardCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type BlockRewardCallerRaw struct { + Contract *BlockRewardCaller // Generic read-only contract binding to access the raw methods on +} + +// BlockRewardTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type BlockRewardTransactorRaw struct { + Contract *BlockRewardTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewBlockReward creates a new instance of BlockReward, bound to a specific deployed contract. +func NewBlockReward(address common.Address, backend bind.ContractBackend) (*BlockReward, error) { + contract, err := bindBlockReward(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &BlockReward{BlockRewardCaller: BlockRewardCaller{contract: contract}, BlockRewardTransactor: BlockRewardTransactor{contract: contract}, BlockRewardFilterer: BlockRewardFilterer{contract: contract}}, nil +} + +// NewBlockRewardCaller creates a new read-only instance of BlockReward, bound to a specific deployed contract. +func NewBlockRewardCaller(address common.Address, caller bind.ContractCaller) (*BlockRewardCaller, error) { + contract, err := bindBlockReward(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &BlockRewardCaller{contract: contract}, nil +} + +// NewBlockRewardTransactor creates a new write-only instance of BlockReward, bound to a specific deployed contract. +func NewBlockRewardTransactor(address common.Address, transactor bind.ContractTransactor) (*BlockRewardTransactor, error) { + contract, err := bindBlockReward(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &BlockRewardTransactor{contract: contract}, nil +} + +// NewBlockRewardFilterer creates a new log filterer instance of BlockReward, bound to a specific deployed contract. +func NewBlockRewardFilterer(address common.Address, filterer bind.ContractFilterer) (*BlockRewardFilterer, error) { + contract, err := bindBlockReward(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &BlockRewardFilterer{contract: contract}, nil +} + +// bindBlockReward binds a generic wrapper to an already deployed contract. +func bindBlockReward(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := abi.JSON(strings.NewReader(BlockRewardABI)) + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, parsed, caller, transactor, filterer), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_BlockReward *BlockRewardRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _BlockReward.Contract.BlockRewardCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_BlockReward *BlockRewardRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _BlockReward.Contract.BlockRewardTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_BlockReward *BlockRewardRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _BlockReward.Contract.BlockRewardTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_BlockReward *BlockRewardCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _BlockReward.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_BlockReward *BlockRewardTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _BlockReward.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_BlockReward *BlockRewardTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _BlockReward.Contract.contract.Transact(opts, method, params...) +} + +// Reward is a paid mutator transaction binding the contract method 0xf91c2898. +// +// Solidity: function reward(address[] benefactors, uint16[] kind) returns(address[], uint256[]) +func (_BlockReward *BlockRewardTransactor) Reward(opts *bind.TransactOpts, benefactors []common.Address, kind []uint16) (*types.Transaction, error) { + return _BlockReward.contract.Transact(opts, "reward", benefactors, kind) +} + +// Reward is a paid mutator transaction binding the contract method 0xf91c2898. +// +// Solidity: function reward(address[] benefactors, uint16[] kind) returns(address[], uint256[]) +func (_BlockReward *BlockRewardSession) Reward(benefactors []common.Address, kind []uint16) (*types.Transaction, error) { + return _BlockReward.Contract.Reward(&_BlockReward.TransactOpts, benefactors, kind) +} + +// Reward is a paid mutator transaction binding the contract method 0xf91c2898. +// +// Solidity: function reward(address[] benefactors, uint16[] kind) returns(address[], uint256[]) +func (_BlockReward *BlockRewardTransactorSession) Reward(benefactors []common.Address, kind []uint16) (*types.Transaction, error) { + return _BlockReward.Contract.Reward(&_BlockReward.TransactOpts, benefactors, kind) +} diff --git a/consensus/beacon/consensus.go b/consensus/beacon/consensus.go index c5d2a12a7b26..10539e2ad9bb 100644 --- a/consensus/beacon/consensus.go +++ b/consensus/beacon/consensus.go @@ -23,6 +23,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/consensus/aura" "github.com/ethereum/go-ethereum/consensus/misc" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" @@ -341,18 +342,19 @@ func (beacon *Beacon) Prepare(chain consensus.ChainHeaderReader, header *types.H // Finalize implements consensus.Engine and processes withdrawals on top. func (beacon *Beacon) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, withdrawals []*types.Withdrawal) { - if !beacon.IsPoSHeader(header) { - beacon.ethone.Finalize(chain, header, state, txs, uncles, nil) - return - } - // Withdrawals processing. - for _, w := range withdrawals { - // Convert amount from gwei to wei. - amount := new(big.Int).SetUint64(w.Amount) - amount = amount.Mul(amount, big.NewInt(params.GWei)) - state.AddBalance(w.Address, amount) + // GNOSIS: force calling the underlying consensus engine since + // it only calls the reward contract. A cleaner approach would + // be to add the call to the reward contract here. + beacon.ethone.Finalize(chain, header, state, txs, uncles, nil) + + // withdrawals processing. + if withdrawals != nil { + if auraEngine, ok := beacon.ethone.(*aura.Aura); ok { + if err := auraEngine.ExecuteSystemWithdrawals(withdrawals, chain, header, state); err != nil { + panic(err) + } + } } - // No block reward which is issued by consensus layer instead. } // FinalizeAndAssemble implements consensus.Engine, setting the final state and diff --git a/core/blockchain.go b/core/blockchain.go index c9b80bc2fffb..be3afcd85d2a 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -957,7 +957,7 @@ func (bc *BlockChain) Stop() { // - HEAD: So we don't need to reprocess any blocks in the general case // - HEAD-1: So we don't do large reorgs if our HEAD becomes an uncle // - HEAD-127: So we have a hard limit on the number of blocks reexecuted - if !bc.cacheConfig.TrieDirtyDisabled { + if /* !bc.cacheConfig.TrieDirtyDisabled */ false { triedb := bc.triedb for _, offset := range []uint64{0, 1, TriesInMemory - 1} { diff --git a/core/forkid/forkid.go b/core/forkid/forkid.go index f536019dac12..36820b951f79 100644 --- a/core/forkid/forkid.go +++ b/core/forkid/forkid.go @@ -293,5 +293,13 @@ func gatherForks(config *params.ChainConfig) ([]uint64, []uint64) { if len(forksByTime) > 0 && forksByTime[0] == 0 { forksByTime = forksByTime[1:] } - return forksByBlock, forksByTime + // hack-insert the poa reward contract block + var hackedForksByBlock []uint64 + for i := range forksByBlock { + if i > 0 && forksByBlock[i-1] < 9186425 && forksByBlock[i] > 9186425 { + hackedForksByBlock = append(hackedForksByBlock, 9186425) + } + hackedForksByBlock = append(hackedForksByBlock, forksByBlock[i]) + } + return hackedForksByBlock, forksByTime } diff --git a/core/genesis.go b/core/genesis.go index 1e56845d8ad3..c5d604b64a88 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -23,6 +23,7 @@ import ( "errors" "fmt" "math/big" + "os" "strings" "github.com/ethereum/go-ethereum/common" @@ -63,6 +64,7 @@ type Genesis struct { GasUsed uint64 `json:"gasUsed"` ParentHash common.Hash `json:"parentHash"` BaseFee *big.Int `json:"baseFeePerGas"` + Signature []byte `json:"signature,omitempty"` } func ReadGenesis(db ethdb.Database) (*Genesis, error) { @@ -193,6 +195,11 @@ func CommitGenesisState(db ethdb.Database, triedb *trie.Database, blockhash comm genesis = DefaultGoerliGenesisBlock() case params.SepoliaGenesisHash: genesis = DefaultSepoliaGenesisBlock() + case params.GnosisChainHash: + genesis = DefaultGnosisGenesisBlock() + case params.ChiadoGenesisHash: + genesis = DefaultChiadoGenesisBlock() + } if genesis != nil { alloc = genesis.Alloc @@ -205,11 +212,12 @@ func CommitGenesisState(db ethdb.Database, triedb *trie.Database, blockhash comm // GenesisAccount is an account in the state of the genesis block. type GenesisAccount struct { - Code []byte `json:"code,omitempty"` - Storage map[common.Hash]common.Hash `json:"storage,omitempty"` - Balance *big.Int `json:"balance" gencodec:"required"` - Nonce uint64 `json:"nonce,omitempty"` - PrivateKey []byte `json:"secretKey,omitempty"` // for tests + Constructor []byte `json:"constructor,omitempty"` + Code []byte `json:"code,omitempty"` + Storage map[common.Hash]common.Hash `json:"storage,omitempty"` + Balance *big.Int `json:"balance" gencodec:"required"` + Nonce uint64 `json:"nonce,omitempty"` + PrivateKey []byte `json:"secretKey,omitempty"` // for tests } // field type overrides for gencodec @@ -387,6 +395,7 @@ func LoadChainConfig(db ethdb.Database, genesis *Genesis) (*params.ChainConfig, if stored != (common.Hash{}) { storedcfg := rawdb.ReadChainConfig(db, stored) if storedcfg != nil { + storedcfg.TerminalTotalDifficultyPassed = true return storedcfg, nil } } @@ -420,6 +429,24 @@ func (g *Genesis) configOrDefault(ghash common.Hash) *params.ChainConfig { return params.SepoliaChainConfig case ghash == params.GoerliGenesisHash: return params.GoerliChainConfig + case ghash == params.GnosisChainHash: + // Load the config from the chainspec + params.GnosisChainConfig.Aura = new(params.AuthorityRoundParams) + data, err := os.ReadFile("xdai.json") + if err != nil { + panic(fmt.Sprintf("could not find the gnosis chain spec file: %v", err)) + } + json.Unmarshal(data, params.GnosisChainConfig.Aura) + return params.GnosisChainConfig + case ghash == params.ChiadoGenesisHash: + // Load the config from the chainspec + params.GnosisChainConfig.Aura = new(params.AuthorityRoundParams) + data, err := os.ReadFile("chiado.json") + if err != nil { + panic(fmt.Sprintf("could not find the chiado spec file: %v", err)) + } + json.Unmarshal(data, params.ChiadoConfig.Aura) + return params.GnosisChainConfig default: return params.AllEthashProtocolChanges } @@ -443,6 +470,7 @@ func (g *Genesis) ToBlock() *types.Block { Difficulty: g.Difficulty, MixDigest: g.Mixhash, Coinbase: g.Coinbase, + Signature: g.Signature, Root: root, } if g.GasLimit == 0 { @@ -549,6 +577,59 @@ func DefaultSepoliaGenesisBlock() *Genesis { } } +// DefaultSepoliaGenesisBlock returns the Sepolia network genesis block. +func DefaultGnosisGenesisBlock() *Genesis { + return &Genesis{ + Config: params.GnosisChainConfig, + Nonce: 0, + GasLimit: 0x989680, + Difficulty: big.NewInt(0x20000), + Timestamp: 0, + Signature: make([]byte, 65), + Alloc: GenesisAlloc{ + common.HexToAddress("0x0000000000000000000000000000000000000001"): GenesisAccount{ + Balance: big.NewInt(1), + }, + common.HexToAddress("0x0000000000000000000000000000000000000002"): GenesisAccount{ + Balance: big.NewInt(1), + }, + common.HexToAddress("0x0000000000000000000000000000000000000003"): GenesisAccount{ + Balance: big.NewInt(1), + }, + common.HexToAddress("0x0000000000000000000000000000000000000004"): GenesisAccount{ + Balance: big.NewInt(1), + }, + }, + } +} + +// DefaultSepoliaGenesisBlock returns the Sepolia network genesis block. +func DefaultChiadoGenesisBlock() *Genesis { + // TODO incorrect + return &Genesis{ + Config: params.GnosisChainConfig, + Nonce: 0, + GasLimit: 0x989680, + Difficulty: big.NewInt(0x20000), + Timestamp: 0, + Signature: make([]byte, 65), + Alloc: GenesisAlloc{ + common.HexToAddress("0x0000000000000000000000000000000000000001"): GenesisAccount{ + Balance: big.NewInt(1), + }, + common.HexToAddress("0x0000000000000000000000000000000000000002"): GenesisAccount{ + Balance: big.NewInt(1), + }, + common.HexToAddress("0x0000000000000000000000000000000000000003"): GenesisAccount{ + Balance: big.NewInt(1), + }, + common.HexToAddress("0x0000000000000000000000000000000000000004"): GenesisAccount{ + Balance: big.NewInt(1), + }, + }, + } +} + // DeveloperGenesisBlock returns the 'geth --dev' genesis block. func DeveloperGenesisBlock(period uint64, gasLimit uint64, faucet common.Address) *Genesis { // Override the default period to the user requested one diff --git a/core/rawdb/database.go b/core/rawdb/database.go index e864bcb2e88e..eb7a7eba11e4 100644 --- a/core/rawdb/database.go +++ b/core/rawdb/database.go @@ -204,6 +204,8 @@ func NewDatabaseWithFreezer(db ethdb.KeyValueStore, ancient string, namespace st printChainMetadata(db) return nil, err } + // GNOSIS deactivate the freezer in this branch, it missing a lot of data and + // this branch is only meant for "normal" geths to snap sync from it. // Since the freezer can be stored separately from the user's key-value database, // there's a fairly high probability that the user requests invalid combinations // of the freezer and database. Ensure that we don't shoot ourselves in the foot @@ -226,70 +228,70 @@ func NewDatabaseWithFreezer(db ethdb.KeyValueStore, ancient string, namespace st // If the genesis hash is empty, we have a new key-value store, so nothing to // validate in this method. If, however, the genesis hash is not nil, compare // it to the freezer content. - if kvgenesis, _ := db.Get(headerHashKey(0)); len(kvgenesis) > 0 { - if frozen, _ := frdb.Ancients(); frozen > 0 { - // If the freezer already contains something, ensure that the genesis blocks - // match, otherwise we might mix up freezers across chains and destroy both - // the freezer and the key-value store. - frgenesis, err := frdb.Ancient(ChainFreezerHashTable, 0) - if err != nil { - printChainMetadata(db) - return nil, fmt.Errorf("failed to retrieve genesis from ancient %v", err) - } else if !bytes.Equal(kvgenesis, frgenesis) { - printChainMetadata(db) - return nil, fmt.Errorf("genesis mismatch: %#x (leveldb) != %#x (ancients)", kvgenesis, frgenesis) - } - // Key-value store and freezer belong to the same network. Ensure that they - // are contiguous, otherwise we might end up with a non-functional freezer. - if kvhash, _ := db.Get(headerHashKey(frozen)); len(kvhash) == 0 { - // Subsequent header after the freezer limit is missing from the database. - // Reject startup if the database has a more recent head. - if head := *ReadHeaderNumber(db, ReadHeadHeaderHash(db)); head > frozen-1 { - // Find the smallest block stored in the key-value store - // in range of [frozen, head] - var number uint64 - for number = frozen; number <= head; number++ { - if present, _ := db.Has(headerHashKey(number)); present { - break - } - } - // We are about to exit on error. Print database metdata beore exiting - printChainMetadata(db) - return nil, fmt.Errorf("gap in the chain between ancients [0 - #%d] and leveldb [#%d - #%d] ", - frozen-1, number, head) - } - // Database contains only older data than the freezer, this happens if the - // state was wiped and reinited from an existing freezer. - } - // Otherwise, key-value store continues where the freezer left off, all is fine. - // We might have duplicate blocks (crash after freezer write but before key-value - // store deletion, but that's fine). - } else { - // If the freezer is empty, ensure nothing was moved yet from the key-value - // store, otherwise we'll end up missing data. We check block #1 to decide - // if we froze anything previously or not, but do take care of databases with - // only the genesis block. - if ReadHeadHeaderHash(db) != common.BytesToHash(kvgenesis) { - // Key-value store contains more data than the genesis block, make sure we - // didn't freeze anything yet. - if kvblob, _ := db.Get(headerHashKey(1)); len(kvblob) == 0 { - printChainMetadata(db) - return nil, errors.New("ancient chain segments already extracted, please set --datadir.ancient to the correct path") - } - // Block #1 is still in the database, we're allowed to init a new freezer - } - // Otherwise, the head header is still the genesis, we're allowed to init a new - // freezer. - } - } + // if kvgenesis, _ := db.Get(headerHashKey(0)); len(kvgenesis) > 0 { + // if frozen, _ := frdb.Ancients(); frozen > 0 { + // // If the freezer already contains something, ensure that the genesis blocks + // // match, otherwise we might mix up freezers across chains and destroy both + // // the freezer and the key-value store. + // frgenesis, err := frdb.Ancient(ChainFreezerHashTable, 0) + // if err != nil { + // printChainMetadata(db) + // return nil, fmt.Errorf("failed to retrieve genesis from ancient %v", err) + // } else if !bytes.Equal(kvgenesis, frgenesis) { + // printChainMetadata(db) + // return nil, fmt.Errorf("genesis mismatch: %#x (leveldb) != %#x (ancients)", kvgenesis, frgenesis) + // } + // // Key-value store and freezer belong to the same network. Ensure that they + // // are contiguous, otherwise we might end up with a non-functional freezer. + // if kvhash, _ := db.Get(headerHashKey(frozen)); len(kvhash) == 0 { + // // Subsequent header after the freezer limit is missing from the database. + // // Reject startup if the database has a more recent head. + // if head := *ReadHeaderNumber(db, ReadHeadHeaderHash(db)); head > frozen-1 { + // // Find the smallest block stored in the key-value store + // // in range of [frozen, head] + // var number uint64 + // for number = frozen; number <= head; number++ { + // if present, _ := db.Has(headerHashKey(number)); present { + // break + // } + // } + // // We are about to exit on error. Print database metdata beore exiting + // printChainMetadata(db) + // return nil, fmt.Errorf("gap in the chain between ancients [0 - #%d] and leveldb [#%d - #%d] ", + // frozen-1, number, head) + // } + // // Database contains only older data than the freezer, this happens if the + // // state was wiped and reinited from an existing freezer. + // } + // // Otherwise, key-value store continues where the freezer left off, all is fine. + // // We might have duplicate blocks (crash after freezer write but before key-value + // // store deletion, but that's fine). + // } else { + // // If the freezer is empty, ensure nothing was moved yet from the key-value + // // store, otherwise we'll end up missing data. We check block #1 to decide + // // if we froze anything previously or not, but do take care of databases with + // // only the genesis block. + // if ReadHeadHeaderHash(db) != common.BytesToHash(kvgenesis) { + // // Key-value store contains more data than the genesis block, make sure we + // // didn't freeze anything yet. + // if kvblob, _ := db.Get(headerHashKey(1)); len(kvblob) == 0 { + // printChainMetadata(db) + // return nil, errors.New("ancient chain segments already extracted, please set --datadir.ancient to the correct path") + // } + // // Block #1 is still in the database, we're allowed to init a new freezer + // } + // // Otherwise, the head header is still the genesis, we're allowed to init a new + // // freezer. + // } + // } // Freezer is consistent with the key-value database, permit combining the two - if !frdb.readonly { - frdb.wg.Add(1) - go func() { - frdb.freeze(db) - frdb.wg.Done() - }() - } + // if !frdb.readonly { + // frdb.wg.Add(1) + // go func() { + // frdb.freeze(db) + // frdb.wg.Done() + // }() + // } return &freezerdb{ ancientRoot: ancient, KeyValueStore: db, diff --git a/core/state_transition.go b/core/state_transition.go index 022238c8d640..297d3fc290af 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -388,15 +388,18 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { if rules.IsLondon { effectiveTip = cmath.BigMin(msg.GasTipCap, new(big.Int).Sub(msg.GasFeeCap, st.evm.Context.BaseFee)) } + fee := new(big.Int).SetUint64(st.gasUsed()) + fee.Mul(fee, effectiveTip) + st.state.AddBalance(st.evm.Context.Coinbase, fee) if st.evm.Config.NoBaseFee && msg.GasFeeCap.Sign() == 0 && msg.GasTipCap.Sign() == 0 { // Skip fee payment when NoBaseFee is set and the fee fields // are 0. This avoids a negative effectiveTip being applied to // the coinbase when simulating calls. } else { - fee := new(big.Int).SetUint64(st.gasUsed()) - fee.Mul(fee, effectiveTip) - st.state.AddBalance(st.evm.Context.Coinbase, fee) + burntContractAddress := common.HexToAddress("0x6BBe78ee9e474842Dbd4AB4987b3CeFE88426A92") // *st.evm.ChainConfig().Eip1559FeeCollector + burnAmount := new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), st.evm.Context.BaseFee) + st.state.AddBalance(burntContractAddress, burnAmount) } return &ExecutionResult{ diff --git a/core/types/block.go b/core/types/block.go index e1f1feb7a2ec..50509f7a9458 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -78,6 +78,8 @@ type Header struct { Extra []byte `json:"extraData" gencodec:"required"` MixDigest common.Hash `json:"mixHash"` Nonce BlockNonce `json:"nonce"` + Step uint64 `json:"step,omitempty"` + Signature []byte `json:"signature"` // BaseFee was added by EIP-1559 and is ignored in legacy headers. BaseFee *big.Int `json:"baseFeePerGas" rlp:"optional"` diff --git a/core/types/gen_header_rlp.go b/core/types/gen_header_rlp.go index f488931f1fb0..1a42da5b3921 100644 --- a/core/types/gen_header_rlp.go +++ b/core/types/gen_header_rlp.go @@ -5,8 +5,14 @@ package types -import "github.com/ethereum/go-ethereum/rlp" -import "io" +import ( + "errors" + "io" + "fmt" + + "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/common" +) func (obj *Header) EncodeRLP(_w io.Writer) error { w := rlp.NewEncoderBuffer(_w) @@ -38,8 +44,13 @@ func (obj *Header) EncodeRLP(_w io.Writer) error { w.WriteUint64(obj.GasUsed) w.WriteUint64(obj.Time) w.WriteBytes(obj.Extra) - w.WriteBytes(obj.MixDigest[:]) - w.WriteBytes(obj.Nonce[:]) + if len(obj.Signature) == 0 { + w.WriteBytes(obj.MixDigest[:]) + w.WriteBytes(obj.Nonce[:]) + } else { + w.WriteUint64(obj.Step) + w.WriteBytes(obj.Signature) + } _tmp1 := obj.BaseFee != nil _tmp2 := obj.WithdrawalsHash != nil _tmp3 := obj.ExcessDataGas != nil @@ -78,3 +89,136 @@ func (obj *Header) EncodeRLP(_w io.Writer) error { w.ListEnd(_tmp0) return w.Flush() } + +func (h *Header) DecodeRLP(s *rlp.Stream) error { + _, err := s.List() + if err != nil { + return err + } + var b []byte + if b, err = s.Bytes(); err != nil { + return fmt.Errorf("read ParentHash: %w", err) + } + if len(b) != 32 { + return fmt.Errorf("wrong size for ParentHash: %d", len(b)) + } + copy(h.ParentHash[:], b) + if b, err = s.Bytes(); err != nil { + return fmt.Errorf("read UncleHash: %w", err) + } + if len(b) != 32 { + return fmt.Errorf("wrong size for UncleHash: %d", len(b)) + } + copy(h.UncleHash[:], b) + if b, err = s.Bytes(); err != nil { + return fmt.Errorf("read Coinbase: %w", err) + } + if len(b) != 20 { + return fmt.Errorf("wrong size for Coinbase: %d", len(b)) + } + copy(h.Coinbase[:], b) + if b, err = s.Bytes(); err != nil { + return fmt.Errorf("read Root: %w", err) + } + if len(b) != 32 { + return fmt.Errorf("wrong size for Root: %d", len(b)) + } + copy(h.Root[:], b) + if b, err = s.Bytes(); err != nil { + return fmt.Errorf("read TxHash: %w", err) + } + if len(b) != 32 { + return fmt.Errorf("wrong size for TxHash: %d", len(b)) + } + copy(h.TxHash[:], b) + if b, err = s.Bytes(); err != nil { + return fmt.Errorf("read ReceiptHash: %w", err) + } + if len(b) != 32 { + return fmt.Errorf("wrong size for ReceiptHash: %d", len(b)) + } + copy(h.ReceiptHash[:], b) + if b, err = s.Bytes(); err != nil { + return fmt.Errorf("read Bloom: %w", err) + } + if len(b) != 256 { + return fmt.Errorf("wrong size for Bloom: %d", len(b)) + } + copy(h.Bloom[:], b) + if h.Difficulty, err = s.BigInt(); err != nil { + return fmt.Errorf("read Difficulty: %w", err) + } + if h.Number, err = s.BigInt(); err != nil { + return fmt.Errorf("read Number: %w", err) + } + if h.GasLimit, err = s.Uint(); err != nil { + return fmt.Errorf("read GasLimit: %w", err) + } + if h.GasUsed, err = s.Uint(); err != nil { + return fmt.Errorf("read GasUsed: %w", err) + } + if h.Time, err = s.Uint(); err != nil { + return fmt.Errorf("read Time: %w", err) + } + if h.Extra, err = s.Bytes(); err != nil { + return fmt.Errorf("read Extra: %w", err) + } + + _, size, err := s.Kind() + if err != nil { + return fmt.Errorf("read MixDigest: %w", err) + } + if size != 32 { // AuRa + if h.Step, err = s.Uint(); err != nil { + return fmt.Errorf("read AuRaStep: %w", err) + } + if h.Signature, err = s.Bytes(); err != nil { + return fmt.Errorf("read AuRaSeal: %w", err) + } + } else { + if b, err = s.Bytes(); err != nil { + return fmt.Errorf("read MixDigest: %w", err) + } + copy(h.MixDigest[:], b) + if b, err = s.Bytes(); err != nil { + return fmt.Errorf("read Nonce: %w", err) + } + if len(b) != 8 { + return fmt.Errorf("wrong size for Nonce: %d", len(b)) + } + copy(h.Nonce[:], b) + } + + // BaseFee + if h.BaseFee, err = s.BigInt(); err != nil { + if errors.Is(err, rlp.EOL) { + h.BaseFee = nil + if err := s.ListEnd(); err != nil { + return fmt.Errorf("close header struct (no BaseFee): %w", err) + } + return nil + } + return fmt.Errorf("read BaseFee: %w", err) + } + + // WithdrawalsHash + if b, err = s.Bytes(); err != nil { + if errors.Is(err, rlp.EOL) { + if err := s.ListEnd(); err != nil { + return fmt.Errorf("close header struct (no WithdrawalsHash): %w", err) + } + return nil + } + return fmt.Errorf("read WithdrawalsHash: %w", err) + } + if len(b) != 32 { + return fmt.Errorf("wrong size for UncleHash: %d", len(b)) + } + h.WithdrawalsHash = new(common.Hash) + copy((*h.WithdrawalsHash)[:], b) + + if err := s.ListEnd(); err != nil { + return fmt.Errorf("close header struct: %w", err) + } + return nil +} diff --git a/core/vm/evm.go b/core/vm/evm.go index 36336d8cbd4c..49084dbdfb21 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -465,9 +465,9 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, ret, err := evm.interpreter.Run(contract, nil, false) // Check whether the max code size has been exceeded, assign err if the case. - if err == nil && evm.chainRules.IsEIP158 && len(ret) > params.MaxCodeSize { - err = ErrMaxCodeSizeExceeded - } + // if err == nil && evm.chainRules.IsEIP158 && len(ret) > params.MaxCodeSize { + // err = ErrMaxCodeSizeExceeded + // } // Reject code starting with 0xEF if EIP-3541 is enabled. if err == nil && len(ret) >= 1 && ret[0] == 0xEF && evm.chainRules.IsLondon { diff --git a/eth/protocols/eth/handshake.go b/eth/protocols/eth/handshake.go index 9a2769fa0d12..c2df3a5a4232 100644 --- a/eth/protocols/eth/handshake.go +++ b/eth/protocols/eth/handshake.go @@ -67,11 +67,13 @@ func (p *Peer) Handshake(network uint64, td *big.Int, head common.Hash, genesis } p.td, p.head = status.TD, status.Head + // GNOSIS: the total difficulty on the main chain is much larger than + // 100 bits. // TD at mainnet block #7753254 is 76 bits. If it becomes 100 million times // larger, it will still fit within 100 bits - if tdlen := p.td.BitLen(); tdlen > 100 { - return fmt.Errorf("too large total difficulty: bitlen %d", tdlen) - } + // if tdlen := p.td.BitLen(); tdlen > 100 { + // return fmt.Errorf("too large total difficulty: bitlen %d", tdlen) + // } return nil } @@ -100,8 +102,8 @@ func (p *Peer) readStatus(network uint64, status *StatusPacket, genesis common.H if status.Genesis != genesis { return fmt.Errorf("%w: %x (!= %x)", errGenesisMismatch, status.Genesis, genesis) } - if err := forkFilter(status.ForkID); err != nil { - return fmt.Errorf("%w: %v", errForkIDRejected, err) - } + // if err := forkFilter(status.ForkID); err != nil { + // return fmt.Errorf("%w: %v", errForkIDRejected, err) + // } return nil } diff --git a/go.mod b/go.mod index aeb1b58539c6..6d5d776fb682 100644 --- a/go.mod +++ b/go.mod @@ -36,6 +36,7 @@ require ( github.com/gorilla/websocket v1.4.2 github.com/graph-gophers/graphql-go v1.3.0 github.com/hashicorp/go-bexpr v0.1.10 + github.com/hashicorp/golang-lru v1.0.2 github.com/holiman/bloomfilter/v2 v2.0.3 github.com/holiman/uint256 v1.2.2-0.20230321075855-87b91420868c github.com/huin/goupnp v1.0.3 diff --git a/go.sum b/go.sum index 449ab2683296..82eb7a38fe20 100644 --- a/go.sum +++ b/go.sum @@ -227,6 +227,8 @@ github.com/graph-gophers/graphql-go v1.3.0/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLt github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE= github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= +github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= diff --git a/params/bootnodes.go b/params/bootnodes.go index edc6e06cb3dc..b52e959a2fa5 100644 --- a/params/bootnodes.go +++ b/params/bootnodes.go @@ -56,6 +56,22 @@ var GoerliBootnodes = []string{ "enode://d2b720352e8216c9efc470091aa91ddafc53e222b32780f505c817ceef69e01d5b0b0797b69db254c586f493872352f5a022b4d8479a00fc92ec55f9ad46a27e@88.99.70.182:30303", } +// GnosisBootnodes are the enode URLs of the P2P bootstrap nodes running on the +// Gnosis chain network. +var GnosisBootnodes = []string{ + // TODO +} + +// ChiadoBootnodes are the enode URLs of the P2P bootstrap nodes running on the +// Chiado test network. +var ChiadoBootnodes = []string{ + "enode://7dd44af6138120f328bb031eb56e00985c149319d4f1e33275b30be7fddadd8ccd9f7b9c3b35a16136a61e85b2b2d1de073f30ec1d0ddf576a33be8ff48d88d0@139.144.26.89:30303", + "enode://317b9cee65ccf1d747b00e604242bfa3ae367beee8f149e28c5b2b88820f855ea7b5a75eb5327cfc3d8ca97adbf71538468290a46592ed7009f3fb394ec752f1@139.144.26.115:30303", + "enode://b77ae97906155ebbb83fd32c87ab0aa57372a24abbd8aa4bae679f048b726de4a195709f613be4981e44b24640bc89e4824427d94e9a37afc148da8250c8ab2d@139.144.26.101:30303", + "enode://69f8abfa3b0221161f8c19014b90857a18742554af27af73fd779c486728750a0ff11b873975f104fc5276a3a7c3b5b68cb3c26c815e9f78462901895d652124@139.144.26.85:30303", + "enode://ac7fc76f9b2ab343fb2d091365a7f46d17018e525cbedfbf24b247c76657e934ef4df61cc2f6dad6bfcf722425e03e1a8a6e4e4b52743acc2319cb8ebf27d742@170.187.154.239:30303", +} + var V5Bootnodes = []string{ // Teku team's bootnode "enr:-KG4QOtcP9X1FbIMOe17QNMKqDxCpm14jcX5tiOE4_TyMrFqbmhPZHK_ZPG2Gxb1GE2xdtodOfx9-cgvNtxnRyHEmC0ghGV0aDKQ9aX9QgAAAAD__________4JpZIJ2NIJpcIQDE8KdiXNlY3AyNTZrMaEDhpehBDbZjM_L9ek699Y7vhUJ-eAdMyQW_Fil522Y0fODdGNwgiMog3VkcIIjKA", diff --git a/params/config.go b/params/config.go index 455abe206239..49356d5457c5 100644 --- a/params/config.go +++ b/params/config.go @@ -28,12 +28,16 @@ var ( MainnetGenesisHash = common.HexToHash("0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3") SepoliaGenesisHash = common.HexToHash("0x25a5cc106eea7138acab33231d7160d69cb777ee0c2c553fcddf5138993e6dd9") GoerliGenesisHash = common.HexToHash("0xbf7e331f7f7c1dd2e05159666b3bf8bc7a8a3a9eb1d518969eab529dd9b88c1a") + GnosisChainHash = common.HexToHash("0x4f1dd23188aab3a76b463e4af801b52b1248ef073c648cbdc4c9333d3da79756") + // TODO fix that + ChiadoGenesisHash = common.HexToHash("0x4f1dd23188aab3a76b463e4af801b52b1248ef073c648cbdc4c9333d3da79756") ) func newUint64(val uint64) *uint64 { return &val } var ( - MainnetTerminalTotalDifficulty, _ = new(big.Int).SetString("58_750_000_000_000_000_000_000", 0) + MainnetTerminalTotalDifficulty, _ = new(big.Int).SetString("58_750_000_000_000_000_000_000", 0) + GnosisChainTerminalTotalDifficulty, _ = new(big.Int).SetString("8_626_000_110_427_540_000_000_000_000_000_000_000_000_000_000", 0) // MainnetChainConfig is the chain parameters to run a node on the main network. MainnetChainConfig = &ChainConfig{ @@ -105,6 +109,46 @@ var ( Epoch: 30000, }, } + GnosisChainConfig = &ChainConfig{ + ChainID: big.NewInt(100), + HomesteadBlock: big.NewInt(0), + DAOForkBlock: big.NewInt(0), + DAOForkSupport: true, + EIP150Block: big.NewInt(0), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(1_604_400), + PetersburgBlock: big.NewInt(2_508_800), + IstanbulBlock: big.NewInt(7_298_030), + BerlinBlock: big.NewInt(16_101_500), + LondonBlock: big.NewInt(19_040_000), + ShanghaiTime: newUint64(1690889660), + TerminalTotalDifficulty: GnosisChainTerminalTotalDifficulty, + TerminalTotalDifficultyPassed: true, + Aura: &AuthorityRoundParams{}, + } + ChiadoConfig = &ChainConfig{ + // TODO + ChainID: big.NewInt(100200), + HomesteadBlock: big.NewInt(0), + DAOForkBlock: big.NewInt(0), + DAOForkSupport: true, + EIP150Block: big.NewInt(0), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(1_604_400), + PetersburgBlock: big.NewInt(7_280_000), + IstanbulBlock: big.NewInt(7_298_030), + MuirGlacierBlock: big.NewInt(9_200_000), + BerlinBlock: big.NewInt(16_101_500), + LondonBlock: big.NewInt(19_040_000), + ArrowGlacierBlock: big.NewInt(19_040_000), + GrayGlacierBlock: big.NewInt(19_040_000), + TerminalTotalDifficultyPassed: true, + Aura: nil, + } // AllEthashProtocolChanges contains every protocol change (EIPs) introduced // and accepted by the Ethereum core developers into the Ethash consensus. AllEthashProtocolChanges = &ChainConfig{ @@ -228,6 +272,8 @@ var NetworkNames = map[string]string{ MainnetChainConfig.ChainID.String(): "mainnet", GoerliChainConfig.ChainID.String(): "goerli", SepoliaChainConfig.ChainID.String(): "sepolia", + GnosisChainConfig.ChainID.String(): "gnosis", + ChiadoConfig.ChainID.String(): "chiado", } // ChainConfig is the core config which determines the blockchain settings. @@ -275,8 +321,12 @@ type ChainConfig struct { TerminalTotalDifficultyPassed bool `json:"terminalTotalDifficultyPassed,omitempty"` // Various consensus engines - Ethash *EthashConfig `json:"ethash,omitempty"` - Clique *CliqueConfig `json:"clique,omitempty"` + Ethash *EthashConfig `json:"ethash,omitempty"` + Clique *CliqueConfig `json:"clique,omitempty"` + Aura *AuthorityRoundParams `json:"aura,omitempty"` +} + +type AuthorityRoundParams struct { } // EthashConfig is the consensus engine configs for proof-of-work based sealing. @@ -325,6 +375,12 @@ func (c *ChainConfig) Description() string { } else { banner += "Consensus: Beacon (proof-of-stake), merged from Clique (proof-of-authority)\n" } + case c.Aura != nil: + if c.TerminalTotalDifficulty == nil { + banner += "Consensus: Aura (proof-of-authority)\n" + } else { + banner += "Consensus: Beacon (proof-of-stake), merged from Aura (proof-of-authority)\n" + } default: banner += "Consensus: unknown\n" }