From e895c0baebea6f863489793bae2f3921d8ab6d8e Mon Sep 17 00:00:00 2001 From: tersec Date: Sat, 25 May 2024 14:12:14 +0000 Subject: [PATCH] rm Clique consensus method support and Goerli network (#2219) * rm Clique consensus method support and Goerli network * rm a few more SealingEngineRef and GoerliNets --- .../nodocker/engine/engine_env.nim | 11 +- hive_integration/nodocker/engine/node.nim | 4 - .../nodocker/graphql/graphql_sim.nim | 2 +- hive_integration/nodocker/rpc/test_env.nim | 14 +- nimbus/common/chain_config.nim | 37 -- nimbus/common/common.nim | 29 +- nimbus/common/hardforks.nim | 4 - nimbus/config.nim | 11 - nimbus/core/chain/chain_desc.nim | 11 +- nimbus/core/chain/persist_blocks.nim | 37 +- nimbus/core/clique.nim | 99 ---- nimbus/core/clique/clique_cfg.nim | 168 ------ nimbus/core/clique/clique_defs.nim | 261 --------- nimbus/core/clique/clique_desc.nim | 186 ------- nimbus/core/clique/clique_genvote.nim | 208 -------- nimbus/core/clique/clique_helpers.nim | 111 ---- nimbus/core/clique/clique_sealer.nim | 261 --------- nimbus/core/clique/clique_snapshot.nim | 442 --------------- nimbus/core/clique/clique_verify.nim | 426 --------------- nimbus/core/clique/snapshot/ballot.nim | 213 -------- .../core/clique/snapshot/snapshot_apply.nim | 182 ------- nimbus/core/clique/snapshot/snapshot_desc.nim | 190 ------- nimbus/core/executor/process_block.nim | 1 - nimbus/core/sealer.nim | 155 ------ nimbus/core/tx_pool/tx_chain.nim | 15 - nimbus/nimbus.nim | 38 +- nimbus/nimbus_desc.nim | 5 - nimbus/sync/legacy.nim | 21 +- premix/configuration.nim | 4 +- tests/test_clique.nim | 360 ------------- tests/test_clique/pool.nim | 503 ------------------ tests/test_clique/private.key | 1 - tests/test_clique/voter_samples.nim | 421 --------------- tests/test_configuration.nim | 10 +- tests/test_coredb/coredb_test_xx.nim | 40 +- tests/test_forkid.nim | 1 - tests/test_genesis.nim | 16 - 37 files changed, 28 insertions(+), 4470 deletions(-) delete mode 100644 nimbus/core/clique.nim delete mode 100644 nimbus/core/clique/clique_cfg.nim delete mode 100644 nimbus/core/clique/clique_defs.nim delete mode 100644 nimbus/core/clique/clique_desc.nim delete mode 100644 nimbus/core/clique/clique_genvote.nim delete mode 100644 nimbus/core/clique/clique_helpers.nim delete mode 100644 nimbus/core/clique/clique_sealer.nim delete mode 100644 nimbus/core/clique/clique_snapshot.nim delete mode 100644 nimbus/core/clique/clique_verify.nim delete mode 100644 nimbus/core/clique/snapshot/ballot.nim delete mode 100644 nimbus/core/clique/snapshot/snapshot_apply.nim delete mode 100644 nimbus/core/clique/snapshot/snapshot_desc.nim delete mode 100644 nimbus/core/sealer.nim delete mode 100644 tests/test_clique.nim delete mode 100644 tests/test_clique/pool.nim delete mode 100644 tests/test_clique/private.key delete mode 100644 tests/test_clique/voter_samples.nim diff --git a/hive_integration/nodocker/engine/engine_env.nim b/hive_integration/nodocker/engine/engine_env.nim index f45bb4f79..f148431de 100644 --- a/hive_integration/nodocker/engine/engine_env.nim +++ b/hive_integration/nodocker/engine/engine_env.nim @@ -18,7 +18,6 @@ import ../../../nimbus/[ config, constants, - core/sealer, core/chain, core/tx_pool, core/tx_pool/tx_item, @@ -45,7 +44,6 @@ type com : CommonRef node : EthereumNode server : RpcHttpServer - sealer : SealingEngineRef ttd : DifficultyInt client : RpcHttpClient sync : BeaconSyncRef @@ -93,7 +91,7 @@ proc newEngineEnv*(conf: var NimbusConf, chainFile: string, enableAuth: bool): E chain = newChain(com) com.initializeEmptyDb() - let txPool = TxPoolRef.new(com, conf.engineSigner) + let txPool = TxPoolRef.new(com, ZERO_ADDRESS) node.addEthHandlerCapability( node.peerPool, @@ -117,9 +115,6 @@ proc newEngineEnv*(conf: var NimbusConf, chainFile: string, enableAuth: bool): E echo "Failed to create rpc server: ", error quit(QuitFailure) - sealer = SealingEngineRef.new( - chain, ctx, conf.engineSigner, - txPool, EngineStopped) sync = if com.ttd().isSome: BeaconSyncRef.init(node, chain, ctx.rng, conf.maxPeers, id=conf.tcpPort.int) else: @@ -135,8 +130,6 @@ proc newEngineEnv*(conf: var NimbusConf, chainFile: string, enableAuth: bool): E if chainFile.len > 0: if not importRlpBlock(chainFolder / chainFile, com): quit(QuitFailure) - elif not enableAuth: - sealer.start() server.start() @@ -153,7 +146,6 @@ proc newEngineEnv*(conf: var NimbusConf, chainFile: string, enableAuth: bool): E com : com, node : node, server : server, - sealer : sealer, client : client, sync : sync, txPool : txPool, @@ -165,7 +157,6 @@ proc close*(env: EngineEnv) = if not env.sync.isNil: env.sync.stop() waitFor env.client.close() - waitFor env.sealer.stop() waitFor env.server.closeWait() proc setRealTTD*(env: EngineEnv) = diff --git a/hive_integration/nodocker/engine/node.nim b/hive_integration/nodocker/engine/node.nim index 6e231b03e..3a60c09de 100644 --- a/hive_integration/nodocker/engine/node.nim +++ b/hive_integration/nodocker/engine/node.nim @@ -17,7 +17,6 @@ import transaction, vm_state, vm_types, - core/clique, core/dao, core/validate, core/chain/chain_desc, @@ -107,9 +106,6 @@ proc setBlock*(c: ChainRef; header: BlockHeader; let dbTx = c.db.beginTransaction() defer: dbTx.dispose() - var cliqueState = c.clique.cliqueSave - defer: c.clique.cliqueRestore(cliqueState) - c.com.hardForkTransition(header) # Needed for figuring out whether KVT cleanup is due (see at the end) diff --git a/hive_integration/nodocker/graphql/graphql_sim.nim b/hive_integration/nodocker/graphql/graphql_sim.nim index bb7f91011..3326b9312 100644 --- a/hive_integration/nodocker/graphql/graphql_sim.nim +++ b/hive_integration/nodocker/graphql/graphql_sim.nim @@ -84,7 +84,7 @@ proc main() = ) com.initializeEmptyDb() - let txPool = TxPoolRef.new(com, conf.engineSigner) + let txPool = TxPoolRef.new(com, ZERO_ADDRESS) discard importRlpBlock(blocksFile, com) let ctx = setupGraphqlContext(com, ethNode, txPool) diff --git a/hive_integration/nodocker/rpc/test_env.nim b/hive_integration/nodocker/rpc/test_env.nim index 35b699057..da1c0cc2d 100644 --- a/hive_integration/nodocker/rpc/test_env.nim +++ b/hive_integration/nodocker/rpc/test_env.nim @@ -17,7 +17,7 @@ import ../../../nimbus/config, ../../../nimbus/rpc, ../../../nimbus/utils/utils, - ../../../nimbus/core/[chain, tx_pool, sealer], + ../../../nimbus/core/[chain, tx_pool], ../../../tests/test_helpers, ./vault @@ -28,7 +28,6 @@ type vault*: Vault rpcClient*: RpcClient rpcServer: RpcServer - sealingEngine: SealingEngineRef stopServer: StopServerProc const @@ -85,28 +84,20 @@ proc setupEnv*(): TestEnv = com.initializeEmptyDb() let chainRef = newChain(com) - let txPool = TxPoolRef.new(com, conf.engineSigner) + let txPool = TxPoolRef.new(com, ZERO_ADDRESS) # txPool must be informed of active head # so it can know the latest account state let head = com.db.getCanonicalHead() doAssert txPool.smartHead(head) - let sealingEngine = SealingEngineRef.new( - chainRef, ethCtx, conf.engineSigner, - txPool, EngineStopped - ) - let rpcServer = setupRpcServer(ethCtx, com, ethNode, txPool, conf) let rpcClient = newRpcHttpClient() waitFor rpcClient.connect("127.0.0.1", Port(8545), false) let stopServer = stopRpcHttpServer - sealingEngine.start() - let t = TestEnv( rpcClient: rpcClient, - sealingEngine: sealingEngine, rpcServer: rpcServer, vault : newVault(chainID, gasPrice, rpcClient), stopServer: stopServer @@ -116,5 +107,4 @@ proc setupEnv*(): TestEnv = proc stopEnv*(t: TestEnv) = waitFor t.rpcClient.close() - waitFor t.sealingEngine.stop() t.stopServer(t.rpcServer) diff --git a/nimbus/common/chain_config.nim b/nimbus/common/chain_config.nim index 8a81de3d2..91763404d 100644 --- a/nimbus/common/chain_config.nim +++ b/nimbus/common/chain_config.nim @@ -54,7 +54,6 @@ const CustomNet* = 0.NetworkId # these are public network id MainNet* = 1.NetworkId - GoerliNet* = 5.NetworkId SepoliaNet* = 11155111.NetworkId HoleskyNet* = 17000.NetworkId @@ -356,10 +355,6 @@ proc validateChainConfig*(conf: ChainConfig): bool = if cur.time.isSome: lastTimeBasedFork = cur - if conf.clique.period.isSome or - conf.clique.epoch.isSome: - conf.consensusType = ConsensusType.POA - proc parseGenesis*(data: string): Genesis {.gcsafe.} = try: @@ -473,29 +468,6 @@ proc chainConfigForNetwork*(id: NetworkId): ChainConfig = terminalTotalDifficulty: some(mainNetTTD), shanghaiTime: some(1_681_338_455.EthTime) ) - of GoerliNet: - ChainConfig( - clique: CliqueOptions(period: some(15), epoch: some(30000)), - consensusType: ConsensusType.POA, - chainId: GoerliNet.ChainId, - # Genesis: # 2015-07-30 15:26:13 UTC - homesteadBlock: some(0.toBlockNumber), # Included in genesis - daoForkSupport: false, - eip150Block: some(0.toBlockNumber), # Included in genesis - eip150Hash: toDigest("0000000000000000000000000000000000000000000000000000000000000000"), - eip155Block: some(0.toBlockNumber), # Included in genesis - eip158Block: some(0.toBlockNumber), # Included in genesis - byzantiumBlock: some(0.toBlockNumber), # Included in genesis - constantinopleBlock: some(0.toBlockNumber), # Included in genesis - petersburgBlock: some(0.toBlockNumber), # Included in genesis - istanbulBlock: some(1_561_651.toBlockNumber), # 2019-10-30 13:53:05 UTC - muirGlacierBlock: some(4_460_644.toBlockNumber), # Skipped in Goerli - berlinBlock: some(4_460_644.toBlockNumber), # 2021-03-18 05:29:51 UTC - londonBlock: some(5_062_605.toBlockNumber), # 2021-07-01 03:19:39 UTC - terminalTotalDifficulty: some(10790000.u256), - shanghaiTime: some(1_678_832_736.EthTime), - cancunTime: some(1_705_473_120.EthTime), # 2024-01-17 06:32:00 - ) of SepoliaNet: const sepoliaTTD = parse("17000000000000000",UInt256) ChainConfig( @@ -553,15 +525,6 @@ proc genesisBlockForNetwork*(id: NetworkId): Genesis difficulty: 17179869184.u256, alloc: decodePrealloc(mainnetAllocData) ) - of GoerliNet: - Genesis( - nonce: 0.toBlockNonce, - timestamp: EthTime(0x5c51a607), - extraData: hexToSeqByte("0x22466c6578692069732061207468696e6722202d204166726900000000000000e0a2bd4258d2768837baa26a28fe71dc079f84c70000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), - gasLimit: 0xa00000, - difficulty: 1.u256, - alloc: decodePrealloc(goerliAllocData) - ) of SepoliaNet: Genesis( nonce: 0.toBlockNonce, diff --git a/nimbus/common/common.nim b/nimbus/common/common.nim index 8cadb208d..030ddefa8 100644 --- a/nimbus/common/common.nim +++ b/nimbus/common/common.nim @@ -13,7 +13,7 @@ import std/[options], chronicles, eth/trie/trie_defs, - ../core/[pow, clique, casper], + ../core/[pow, casper], ../db/[core_db, ledger, storage_types], ../utils/[utils, ec_recover], ".."/[constants, errors], @@ -91,9 +91,6 @@ type pow: PowRef ## Wrapper around `hashimotoLight()` and lookup cache - poa: Clique - ## For non-PoA networks this descriptor is ignored. - pos: CasperRef ## Proof Of Stake descriptor @@ -161,10 +158,6 @@ proc init(com : CommonRef, com.ldgType = (if ldgType == LedgerType(0): LedgerCache else: ldgType) com.pruneHistory= pruneHistory - # Initalise the PoA state regardless of whether it is needed on the current - # network. For non-PoA networks this descriptor is ignored. - com.poa = newClique(com.db, com.cliquePeriod, com.cliqueEpoch) - # Always initialise the PoW epoch cache even though it migh no be used com.pow = PowRef.new com.pos = CasperRef.new @@ -281,7 +274,6 @@ proc clone*(com: CommonRef, db: CoreDbRef): CommonRef = currentFork : com.currentFork, consensusType: com.consensusType, pow : com.pow, - poa : com.poa, pos : com.pos, ldgType : com.ldgType, pruneHistory : com.pruneHistory) @@ -353,19 +345,10 @@ func forkGTE*(com: CommonRef, fork: HardFork): bool = com.currentFork >= fork # TODO: move this consensus code to where it belongs -proc minerAddress*(com: CommonRef; header: BlockHeader): EthAddress +func minerAddress*(com: CommonRef; header: BlockHeader): EthAddress {.gcsafe, raises: [CatchableError].} = - if com.consensusType != ConsensusType.POA: - # POW and POS return header.coinbase - return header.coinbase - - # POA return ecRecover - let account = header.ecRecover - if account.isErr: - let msg = "Could not recover account address: " & $account.error - raise newException(ValidationError, msg) - - account.value + # POW and POS return header.coinbase + return header.coinbase func forkId*(com: CommonRef, head, time: uint64): ForkID {.gcsafe.} = ## EIP 2364/2124 @@ -436,10 +419,6 @@ func startOfHistory*(com: CommonRef): Hash256 = ## Getter com.startOfHistory -func poa*(com: CommonRef): Clique = - ## Getter - com.poa - func pow*(com: CommonRef): PowRef = ## Getter com.pow diff --git a/nimbus/common/hardforks.nim b/nimbus/common/hardforks.nim index 6648c12e2..c2fe5fe03 100644 --- a/nimbus/common/hardforks.nim +++ b/nimbus/common/hardforks.nim @@ -23,10 +23,6 @@ type # algorithm: Ethash POW - # Proof of Authority - # algorithm: Clique - POA - # Proof of Stake # algorithm: Casper POS diff --git a/nimbus/config.nim b/nimbus/config.nim index 73a7dad72..fc1c05cba 100644 --- a/nimbus/config.nim +++ b/nimbus/config.nim @@ -189,14 +189,6 @@ type abbr: "e" name: "import-key" }: InputFile - engineSigner* {. - desc: "Set the signer address(as 20 bytes hex) and enable sealing engine to run and " & - "producing blocks at specified interval (only PoA/Clique supported)" - defaultValue: ZERO_ADDRESS - defaultValueDesc: "" - abbr: "s" - name: "engine-signer" }: EthAddress - verifyFrom* {. desc: "Enable extra verification when current block number greater than verify-from" defaultValueDesc: "" @@ -616,7 +608,6 @@ proc getNetworkId(conf: NimbusConf): Option[NetworkId] = let network = toLowerAscii(conf.network) case network of "mainnet": return some MainNet - of "goerli" : return some GoerliNet of "sepolia": return some SepoliaNet of "holesky": return some HoleskyNet else: @@ -693,8 +684,6 @@ proc getBootNodes*(conf: NimbusConf): seq[ENode] = case conf.networkId of MainNet: bootstrapNodes.setBootnodes(MainnetBootnodes) - of GoerliNet: - bootstrapNodes.setBootnodes(GoerliBootnodes) of SepoliaNet: bootstrapNodes.setBootnodes(SepoliaBootnodes) of HoleskyNet: diff --git a/nimbus/core/chain/chain_desc.nim b/nimbus/core/chain/chain_desc.nim index 0e65aa147..766af0e5f 100644 --- a/nimbus/core/chain/chain_desc.nim +++ b/nimbus/core/chain/chain_desc.nim @@ -14,8 +14,7 @@ import ../../common/common, ../../utils/utils, ../../vm_types, - ../pow, - ../clique + ../pow export common @@ -62,12 +61,12 @@ proc newChain*(com: CommonRef, vmState: vmState, ) -proc newChain*(com: CommonRef): ChainRef = +func newChain*(com: CommonRef): ChainRef = ## Constructor for the `Chain` descriptor object. All sub-object descriptors ## are initialised with defaults. So is extra block chain validation ## * `enabled` for PoA networks (such as Goerli) ## * `disabled` for non-PaA networks - let extraValidation = com.consensus in {ConsensusType.POA, ConsensusType.POS} + let extraValidation = com.consensus == ConsensusType.POS ChainRef( com: com, validateBlock: true, @@ -81,10 +80,6 @@ proc vmState*(c: ChainRef): BaseVMState = ## Getter c.vmState -proc clique*(c: ChainRef): Clique = - ## Getter - c.com.poa - proc pow*(c: ChainRef): PowRef = ## Getter c.com.pow diff --git a/nimbus/core/chain/persist_blocks.nim b/nimbus/core/chain/persist_blocks.nim index e7b1ef74e..d7ebf54a9 100644 --- a/nimbus/core/chain/persist_blocks.nim +++ b/nimbus/core/chain/persist_blocks.nim @@ -14,8 +14,6 @@ import ../../db/ledger, ../../vm_state, ../../vm_types, - ../clique/clique_verify, - ../clique, ../executor, ../validate, ./chain_desc, @@ -79,9 +77,6 @@ proc persistBlocksImpl(c: ChainRef; headers: openArray[BlockHeader]; let dbTx = c.db.beginTransaction() defer: dbTx.dispose() - var cliqueState = c.clique.cliqueSave - defer: c.clique.cliqueRestore(cliqueState) - c.com.hardForkTransition(headers[0]) # Note that `0 < headers.len`, assured when called from `persistBlocks()` @@ -110,15 +105,14 @@ proc persistBlocksImpl(c: ChainRef; headers: openArray[BlockHeader]; if c.validateBlock and c.extraValidation and c.verifyFrom <= header.blockNumber: - if c.com.consensus != ConsensusType.POA: - let res = c.com.validateHeaderAndKinship( - header, - body, - checkSealOK = false) # TODO: how to checkseal from here - if res.isErr: - debug "block validation error", - msg = res.error - return ValidationResult.Error + let res = c.com.validateHeaderAndKinship( + header, + body, + checkSealOK = false) # TODO: how to checkseal from here + if res.isErr: + debug "block validation error", + msg = res.error + return ValidationResult.Error if c.generateWitness: vmState.generateWitness = true @@ -137,21 +131,6 @@ proc persistBlocksImpl(c: ChainRef; headers: openArray[BlockHeader]; if validationResult != ValidationResult.OK: return validationResult - if c.validateBlock and c.extraValidation and - c.verifyFrom <= header.blockNumber: - - if c.com.consensus == ConsensusType.POA: - var parent = if 0 < i: @[headers[i-1]] else: @[] - let rc = c.clique.cliqueVerify(c.com, header, parent) - if rc.isOk: - # mark it off so it would not auto-restore previous state - c.clique.cliqueDispose(cliqueState) - else: - debug "PoA header verification failed", - blockNumber = header.blockNumber, - msg = $rc.error - return ValidationResult.Error - if c.generateWitness: let dbTx = c.db.beginTransaction() defer: dbTx.dispose() diff --git a/nimbus/core/clique.nim b/nimbus/core/clique.nim deleted file mode 100644 index ba480f7b0..000000000 --- a/nimbus/core/clique.nim +++ /dev/null @@ -1,99 +0,0 @@ -# Nimbus -# Copyright (c) 2018-2024 Status Research & Development GmbH -# Licensed under either of -# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or -# http://www.apache.org/licenses/LICENSE-2.0) -# * MIT license ([LICENSE-MIT](LICENSE-MIT) or -# http://opensource.org/licenses/MIT) -# at your option. This file may not be copied, modified, or distributed except -# according to those terms. - -## -## EIP-225 Clique PoA Consensus Protocol -## ===================================== -## -## For details see -## `EIP-225 `_ -## and -## `go-ethereum `_ -## - -import - ./clique/[clique_cfg, clique_defs, clique_desc], - ./clique/snapshot/[ballot, snapshot_desc], - stew/results - -{.push raises: [].} - -# Note that mining is unsupported. Unused code ported from the Go -# implementation is stashed into the `clique_unused` module. -export - clique_cfg, - clique_defs, - clique_desc.Clique - -type - CliqueState* = ##\ - ## Descriptor state snapshot which can be used for implementing - ## transaction trasnaction handling. Nore the `Snapshot` type - ## inside the `Result[]` is most probably opaque. - Result[Snapshot,void] - -# ------------------------------------------------------------------------------ -# Public -# ------------------------------------------------------------------------------ - -proc newClique*(db: CoreDbRef, cliquePeriod: EthTime, cliqueEpoch: int): Clique = - ## Constructor for a new Clique proof-of-authority consensus engine. The - ## initial state of the engine is `empty`, there are no authorised signers. - ## - ## If chain_config provides `Period` or `Epoch`, then `Period` or `Epoch` - ## will be taken from chain_config. Otherwise, default value in `newCliqueCfg` - ## will be used - - let cfg = db.newCliqueCfg - if cliquePeriod > 0: - cfg.period = cliquePeriod - if cliqueEpoch > 0: - cfg.epoch = cliqueEpoch - cfg.newClique - -proc cliqueSave*(c: Clique): CliqueState = - ## Save current `Clique` state. This state snapshot saves the internal - ## data that make up the list of authorised signers (see `cliqueSigners()` - ## below.) - ok(c.snapshot) - -proc cliqueRestore*(c: Clique; state: var CliqueState) = - ## Restore current `Clique` state from a saved snapshot. - ## - ## For the particular `state` argument this fuction is disabled with - ## `cliqueDispose()`. So it can be savely wrapped in a `defer:` statement. - ## In transaction lingo, this would then be the rollback function. - if state.isOk: - c.snapshot = state.value - -proc cliqueDispose*(c: Clique; state: var CliqueState) = - ## Disable the function `cliqueDispose()` for the particular `state` - ## argument. - ## - ## In transaction lingo, this would be the commit function if - ## `cliqueRestore()` was wrapped in a `defer:` statement. - state = err(CliqueState) - -proc cliqueSigners*(c: Clique): seq[EthAddress] = - ## Retrieve the sorted list of authorized signers for the current state - ## of the `Clique` descriptor. - ## - ## Note that the return argument list is sorted on-the-fly each time this - ## function is invoked. - c.snapshot.ballot.authSigners - -proc cliqueSignersLen*(c: Clique): int = - ## Get the number of authorized signers for the current state of the - ## `Clique` descriptor. The result is equivalent to `c.cliqueSigners.len`. - c.snapshot.ballot.authSignersLen - -# ------------------------------------------------------------------------------ -# End -# ------------------------------------------------------------------------------ diff --git a/nimbus/core/clique/clique_cfg.nim b/nimbus/core/clique/clique_cfg.nim deleted file mode 100644 index 5e5c310a9..000000000 --- a/nimbus/core/clique/clique_cfg.nim +++ /dev/null @@ -1,168 +0,0 @@ -# Nimbus -# Copyright (c) 2018 Status Research & Development GmbH -# Licensed under either of -# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or -# http://www.apache.org/licenses/LICENSE-2.0) -# * MIT license ([LICENSE-MIT](LICENSE-MIT) or -# http://opensource.org/licenses/MIT) -# at your option. This file may not be copied, modified, or distributed except -# according to those terms. - -## -## Clique PoA Conmmon Config -## ========================= -## -## Constants used by Clique proof-of-authority consensus protocol, see -## `EIP-225 `_ -## and -## `go-ethereum `_ -## -{.push raises: [].} - -import - std/[random, times], - ethash, - ../../db/core_db, - ../../utils/ec_recover, - ./clique_defs - -export - core_db - -const - prngSeed = 42 - -type - CliqueCfg* = ref object of RootRef - db*: CoreDbRef - ## All purpose (incl. blockchain) database. - - nSnaps*: uint64 - ## Number of snapshots stored on disk (for logging troublesshoting) - - snapsData*: uint64 - ## Raw payload stored on disk (for logging troublesshoting) - - period: EthTime - ## Time between blocks to enforce. - - ckpInterval: int - ## Number of blocks after which to save the vote snapshot to the - ## disk database. - - roThreshold: int - ## Number of blocks after which a chain segment is considered immutable - ## (ie. soft finality). It is used by the downloader as a hard limit - ## against deep ancestors, by the blockchain against deep reorgs, by the - ## freezer as the cutoff threshold and by clique as the snapshot trust - ## limit. - - prng: Rand - ## PRNG state for internal random generator. This PRNG is - ## cryptographically insecure but with reproducible data stream. - - signatures: EcRecover - ## Recent block signatures cached to speed up mining. - - epoch: int - ## The number of blocks after which to checkpoint and reset the pending - ## votes.Suggested 30000 for the testnet to remain analogous to the - ## mainnet ethash epoch. - - logInterval: Duration - ## Time interval after which the `snapshotApply()` function main loop - ## produces logging entries. - -# ------------------------------------------------------------------------------ -# Public constructor -# ------------------------------------------------------------------------------ - -proc newCliqueCfg*(db: CoreDbRef): CliqueCfg = - result = CliqueCfg( - db: db, - epoch: EPOCH_LENGTH, - period: BLOCK_PERIOD, - ckpInterval: CHECKPOINT_INTERVAL, - roThreshold: FULL_IMMUTABILITY_THRESHOLD, - logInterval: SNAPS_LOG_INTERVAL_MICSECS, - signatures: EcRecover.init(), - prng: initRand(prngSeed)) - -# ------------------------------------------------------------------------------ -# Public helper funcion -# ------------------------------------------------------------------------------ - -# clique/clique.go(145): func ecrecover(header [..] -proc ecRecover*( - cfg: CliqueCfg; - header: BlockHeader; - ): auto = - cfg.signatures.ecRecover(header) - -# ------------------------------------------------------------------------------ -# Public setters -# ------------------------------------------------------------------------------ - -proc `epoch=`*(cfg: CliqueCfg; epoch: SomeInteger) = - ## Setter - cfg.epoch = if 0 < epoch: epoch - else: EPOCH_LENGTH - -proc `period=`*(cfg: CliqueCfg; period: EthTime) = - ## Setter - cfg.period = if period != EthTime(0): period - else: BLOCK_PERIOD - -proc `ckpInterval=`*(cfg: CliqueCfg; numBlocks: SomeInteger) = - ## Setter - cfg.ckpInterval = if 0 < numBlocks: numBlocks - else: CHECKPOINT_INTERVAL - -proc `roThreshold=`*(cfg: CliqueCfg; numBlocks: SomeInteger) = - ## Setter - cfg.roThreshold = if 0 < numBlocks: numBlocks - else: FULL_IMMUTABILITY_THRESHOLD - -proc `logInterval=`*(cfg: CliqueCfg; duration: Duration) = - ## Setter - cfg.logInterval = if duration != Duration(): duration - else: SNAPS_LOG_INTERVAL_MICSECS - -# ------------------------------------------------------------------------------ -# Public PRNG, may be overloaded -# ------------------------------------------------------------------------------ - -method rand*(cfg: CliqueCfg; max: Natural): int {.gcsafe, base, raises: [].} = - ## The method returns a random number base on an internal PRNG providing a - ## reproducible stream of random data. This function is supposed to be used - ## exactly when repeatability comes in handy. Never to be used for crypto key - ## generation or like (except testing.) - cfg.prng.rand(max) - -# ------------------------------------------------------------------------------ -# Public getter -# ------------------------------------------------------------------------------ - -proc epoch*(cfg: CliqueCfg): BlockNumber = - ## Getter - cfg.epoch.u256 - -proc period*(cfg: CliqueCfg): EthTime = - ## Getter - cfg.period - -proc ckpInterval*(cfg: CliqueCfg): BlockNumber = - ## Getter - cfg.ckpInterval.u256 - -proc roThreshold*(cfg: CliqueCfg): int = - ## Getter - cfg.roThreshold - -proc logInterval*(cfg: CliqueCfg): Duration = - ## Getter - cfg.logInterval - -# ------------------------------------------------------------------------------ -# End -# ------------------------------------------------------------------------------ diff --git a/nimbus/core/clique/clique_defs.nim b/nimbus/core/clique/clique_defs.nim deleted file mode 100644 index 5d7ec4db8..000000000 --- a/nimbus/core/clique/clique_defs.nim +++ /dev/null @@ -1,261 +0,0 @@ -# Nimbus -# Copyright (c) 2018 Status Research & Development GmbH -# Licensed under either of -# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or -# http://www.apache.org/licenses/LICENSE-2.0) -# * MIT license ([LICENSE-MIT](LICENSE-MIT) or -# http://opensource.org/licenses/MIT) -# at your option. This file may not be copied, modified, or distributed except -# according to those terms. - -## -## Clique PoA Constants & Types -## ============================ -## -## Constants used by Clique proof-of-authority consensus protocol, see -## `EIP-225 `_ -## and -## `go-ethereum `_ -## - -import - std/[times], - eth/common, - stew/results, - stint - -{.push raises: [].} - -# ------------------------------------------------------------------------------ -# Constants copied from eip-225 specs & implementation -# ------------------------------------------------------------------------------ - -const - # clique/clique.go(48): const ( [..] - CHECKPOINT_INTERVAL* = ##\ - ## Number of blocks after which to save the vote snapshot to the database - 4 * 1024 - - INMEMORY_SNAPSHOTS* = ##\ - ## Number of recent vote snapshots to keep in memory. - 128 - - WIGGLE_TIME* = ##\ - ## PoA mining only (currently unsupported). - ## - ## Random delay (per signer) to allow concurrent signers - initDuration(seconds = 0, milliseconds = 500) - - # clique/clique.go(57): var ( [..] - BLOCK_PERIOD* = ##\ - ## Minimum difference in seconds between two consecutive block timestamps. - ## Suggested time is 15s for the `testnet` to remain analogous to the - ## `mainnet` ethash target. - EthTime 15 - - EXTRA_VANITY* = ##\ - ## Fixed number of extra-data prefix bytes reserved for signer vanity. - ## Suggested 32 bytes to retain the current extra-data allowance and/or use. - 32 - - NONCE_AUTH* = ##\ - ## Magic nonce number 0xffffffffffffffff to vote on adding a new signer. - 0xffffffffffffffffu64.toBlockNonce - - NONCE_DROP* = ##\ - ## Magic nonce number 0x0000000000000000 to vote on removing a signer. - 0x0000000000000000u64.toBlockNonce - - DIFF_NOTURN* = ##\ - ## Block score (difficulty) for blocks containing out-of-turn signatures. - ## Suggested 1 since it just needs to be an arbitrary baseline constant. - 1.u256 - - DIFF_INTURN* = ##\ - ## Block score (difficulty) for blocks containing in-turn signatures. - ## Suggested 2 to show a slight preference over out-of-turn signatures. - 2.u256 - - # params/network_params.go(60): FullImmutabilityThreshold = 90000 - FULL_IMMUTABILITY_THRESHOLD* = ##\ - ## Number of blocks after which a chain segment is considered immutable (ie. - ## soft finality). It is used by the downloader as a hard limit against - ## deep ancestors, by the blockchain against deep reorgs, by the freezer as - ## the cutoff threshold and by clique as the snapshot trust limit. - 90000 - - # Other - SNAPS_LOG_INTERVAL_MICSECS* = ##\ - ## Time interval after which the `snapshotApply()` function main loop - ## produces logging entries. The original value from the Go reference - ## implementation has 8 seconds (which seems a bit long.) For the first - ## 300k blocks in the Goerli chain, typical execution time in tests was - ## mostly below 300 micro secs. - initDuration(microSeconds = 200) - -# ------------------------------------------------------------------------------ -# Error tokens -# ------------------------------------------------------------------------------ - -type - # clique/clique.go(76): var ( [..] - CliqueErrorType* = enum - resetCliqueError = ##\ - ## Default/reset value (use `cliqueNoError` below rather than this valie) - (0, "no error") - - errUnknownBlock = ##\ - ## is returned when the list of signers is requested for a block that is - ## not part of the local blockchain. - "unknown block" - - errInvalidCheckpointBeneficiary = ##\ - ## is returned if a checkpoint/epoch transition block has a beneficiary - ## set to non-zeroes. - "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. - "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. - "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. - "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. - "extra-data 65 byte signature suffix missing" - - errExtraSigners = ##\ - ## is returned if non-checkpoint block contain signer data in their - ## extra-data fields. - "non-checkpoint block contains extra signer list" - - errInvalidCheckpointSigners = ##\ - ## is returned if a checkpoint block contains an invalid list of signers - ## (i.e. non divisible by 20 bytes). - "invalid signer list on checkpoint block" - - errMismatchingCheckpointSigners = ##\ - ## is returned if a checkpoint block contains a list of signers different - ## than the one the local node calculated. - "mismatching signer list on checkpoint block" - - errInvalidMixDigest = ##\ - ## is returned if a block's mix digest is non-zero. - "non-zero mix digest" - - errInvalidUncleHash = ##\ - ## is returned if a block contains an non-empty uncle list. - "non empty uncle hash" - - errInvalidDifficulty = ##\ - ## is returned if the difficulty of a block neither 1 or 2. - "invalid difficulty" - - errWrongDifficulty = ##\ - ## is returned if the difficulty of a block doesn't match the turn of - ## the signer. - "wrong difficulty" - - errInvalidTimestamp = ##\ - ## is returned if the timestamp of a block is lower than the previous - ## block's timestamp + the minimum block period. - "invalid timestamp" - - errInvalidVotingChain = ##\ - ## is returned if an authorization list is attempted to be modified via - ## out-of-range or non-contiguous headers. - "invalid voting chain" - - errUnauthorizedSigner = ##\ - ## is returned if a header is signed by a non-authorized entity. - "unauthorized signer" - - errRecentlySigned = ##\ - ## is returned if a header is signed by an authorized entity that - ## already signed a header recently, thus is temporarily not allowed to. - "recently signed" - - - # additional errors sources elsewhere - # ----------------------------------- - - errPublicKeyToShort = ##\ - ## Cannot retrieve public key - "cannot retrieve public key: too short" - - # imported from consensus/errors.go - errUnknownAncestor = ##\ - ## is returned when validating a block requires an ancestor that is - ## unknown. - "unknown ancestor" - - errFutureBlock = ##\ - ## is returned when a block's timestamp is in the future according to - ## the current node. - "block in the future" - - # additional/bespoke errors, manually added - # ----------------------------------------- - - errUnknownHash = "No header found for hash value" - errEmptyLruCache = "No snapshot available" - - errNotInitialised = ##\ - ## Initalisation value for `Result` entries - "Not initialised" - - errSetLruSnaps = ##\ - ## Attempt to assign a value to a non-existing slot - "Missing LRU slot for snapshot" - - errEcRecover = ##\ - ## Subsytem error" - "ecRecover failed" - - errSnapshotLoad ## DB subsytem error - errSnapshotStore ## .. - errSnapshotClone - - errCliqueGasLimitOrBaseFee - errCliqueExceedsGasLimit - errCliqueGasRepriceFork - errCliqueSealSigFn - - errCliqueStopped = "process was interrupted" - errCliqueUnclesNotAllowed = "uncles not allowed" - - # not really an error - nilCliqueSealNoBlockYet = "Sealing paused, waiting for transactions" - nilCliqueSealSignedRecently = "Signed recently, must wait for others" - -# ------------------------------------------------------------------------------ -# More types and constants -# ------------------------------------------------------------------------------ - -type - CliqueError* = ##\ - ## Error message, tinned component + explanatory text (if any) - (CliqueErrorType,string) - - CliqueOkResult* = ##\ - ## Standard ok/error result type for `Clique` functions - Result[void,CliqueError] - -const - cliqueNoError* = ##\ - ## No-error constant - (resetCliqueError, "") - -# ------------------------------------------------------------------------------ -# End -# ------------------------------------------------------------------------------ diff --git a/nimbus/core/clique/clique_desc.nim b/nimbus/core/clique/clique_desc.nim deleted file mode 100644 index 729db16c7..000000000 --- a/nimbus/core/clique/clique_desc.nim +++ /dev/null @@ -1,186 +0,0 @@ -# Nimbus -# Copyright (c) 2018 Status Research & Development GmbH -# Licensed under either of -# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or -# http://www.apache.org/licenses/LICENSE-2.0) -# * MIT license ([LICENSE-MIT](LICENSE-MIT) or -# http://opensource.org/licenses/MIT) -# at your option. This file may not be copied, modified, or distributed except -# according to those terms. - -## -## Descriptor Objects for Clique PoA Consensus Protocol -## ==================================================== -## -## For details see -## `EIP-225 `_ -## and -## `go-ethereum `_ -## - -{.push raises: [].} - -import - std/tables, - ./clique_cfg, - ./clique_defs, - ./snapshot/snapshot_desc, - chronicles, - eth/keys, - stew/[keyed_queue, results] - -type - RawSignature* = array[RawSignatureSize, byte] - - # clique/clique.go(142): type SignerFn func(signer [..] - CliqueSignerFn* = ## Hashes and signs the data to be signed by - ## a backing account - proc(signer: EthAddress; - message: openArray[byte]): Result[RawSignature, cstring] - {.gcsafe, raises: [CatchableError].} - - Proposals = Table[EthAddress,bool] - - CliqueSnapKey* = ##\ - ## Internal key used for the LRU cache (derived from Hash256). - array[32,byte] - - CliqueSnapLru = ##\ - ## Snapshots cache - KeyedQueue[CliqueSnapKey,Snapshot] - - CliqueFailed* = ##\ - ## Last failed state: block hash and error result - (Hash256, CliqueError) - - # clique/clique.go(172): type Clique struct { [..] - Clique* = ref object ##\ - ## Clique is the proof-of-authority consensus engine proposed to support - ## the Ethereum testnet following the Ropsten attacks. - - signer*: EthAddress ##\ - ## Ethereum address of the current signing key - - signFn*: CliqueSignerFn ## Signer function to authorize hashes with - - cfg: CliqueCfg ##\ - ## Common engine parameters to fine tune behaviour - - recents: CliqueSnapLru ##\ - ## Snapshots cache for recent block search - - snapshot: Snapshot ##\ - ## Last successful snapshot - - failed: CliqueFailed ##\ - ## Last verification error (if any) - - proposals: Proposals ##\ - ## Cu1rrent list of proposals we are pushing - - applySnapsMinBacklog: bool ##\ - ## Epoch is a restart and sync point. Eip-225 requires that the epoch - ## header contains the full list of currently authorised signers. - ## - ## If this flag is set `true`, then the `cliqueSnapshot()` function will - ## walk back to the1 `epoch` header with at least `cfg.roThreshold` blocks - ## apart from the current header. This is how it is done in the reference - ## implementation. - ## - ## Leving the flag `false`, the assumption is that all the checkponts - ## before have been vetted already regardless of the current branch. So - ## the nearest `epoch` header is used. - -logScope: - topics = "clique PoA constructor" - -# ------------------------------------------------------------------------------ -# Public constructor -# ------------------------------------------------------------------------------ - -# clique/clique.go(191): func New(config [..] -proc newClique*(cfg: CliqueCfg): Clique = - ## Initialiser for Clique proof-of-authority consensus engine with the - ## initial signers set to the ones provided by the user. - result = Clique(cfg: cfg, - snapshot: cfg.newSnapshot(BlockHeader()), - proposals: initTable[EthAddress,bool]()) - -# ------------------------------------------------------------------------------ -# Public debug/pretty print -# ------------------------------------------------------------------------------ - -proc `$`*(e: CliqueError): string = - ## Join text fragments - result = $e[0] - if e[1] != "": - result &= ": " & e[1] - -# ------------------------------------------------------------------------------ -# Public getters -# ------------------------------------------------------------------------------ - -proc recents*( - c: Clique; - ): var KeyedQueue[CliqueSnapKey,Snapshot] - = - ## Getter - c.recents - -proc proposals*(c: Clique): var Proposals = - ## Getter - c.proposals - -proc snapshot*(c: Clique): Snapshot = - ## Getter, last successfully processed snapshot. - c.snapshot - -proc failed*(c: Clique): CliqueFailed = - ## Getter, last snapshot error. - c.failed - -proc cfg*(c: Clique): CliqueCfg = - ## Getter - c.cfg - -proc db*(c: Clique): CoreDbRef = - ## Getter - c.cfg.db - -proc applySnapsMinBacklog*(c: Clique): bool = - ## Getter. - ## - ## If this flag is set `true`, then the `cliqueSnapshot()` function will - ## walk back to the `epoch` header with at least `cfg.roThreshold` blocks - ## apart from the current header. This is how it is done in the reference - ## implementation. - ## - ## Setting the flag `false` which is the default, the assumption is that all - ## the checkponts before have been vetted already regardless of the current - ## branch. So the nearest `epoch` header is used. - c.applySnapsMinBacklog - -# ------------------------------------------------------------------------------ -# Public setters -# ------------------------------------------------------------------------------ - -proc `db=`*(c: Clique; db: CoreDbRef) = - ## Setter, re-set database - c.cfg.db = db - c.proposals = initTable[EthAddress,bool]() - -proc `snapshot=`*(c: Clique; snaps: Snapshot) = - ## Setter - c.snapshot = snaps - -proc `failed=`*(c: Clique; failure: CliqueFailed) = - ## Setter - c.failed = failure - -proc `applySnapsMinBacklog=`*(c: Clique; value: bool) = - ## Setter - c.applySnapsMinBacklog = value - -# ------------------------------------------------------------------------------ -# End -# ------------------------------------------------------------------------------ diff --git a/nimbus/core/clique/clique_genvote.nim b/nimbus/core/clique/clique_genvote.nim deleted file mode 100644 index dca2e9e28..000000000 --- a/nimbus/core/clique/clique_genvote.nim +++ /dev/null @@ -1,208 +0,0 @@ -# Nimbus -# Copyright (c) 2018-2024 Status Research & Development GmbH -# Licensed under either of -# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or -# http://www.apache.org/licenses/LICENSE-2.0) -# * MIT license ([LICENSE-MIT](LICENSE-MIT) or -# http://opensource.org/licenses/MIT) -# at your option. This file may not be copied, modified, or distributed except -# according to those terms. - -## -## Generate PoA Voting Header -## ========================== -## -## For details see -## `EIP-225 `_ -## and -## `go-ethereum `_ -## - -import - std/[sequtils], - eth/[common, keys], - ../../constants, - ./clique_cfg, - ./clique_defs, - ./clique_desc, - ./clique_helpers - -{.push raises: [].} - -# ------------------------------------------------------------------------------ -# Private functions -# ------------------------------------------------------------------------------ - -# clique/snapshot_test.go(49): func (ap *testerAccountPool) [..] -proc extraCheckPoint(header: var BlockHeader; signers: openArray[EthAddress]) = - ## creates a Clique checkpoint signer section from the provided list - ## of authorized signers and embeds it into the provided header. - header.extraData.setLen(EXTRA_VANITY) - header.extraData.add signers.mapIt(toSeq(it)).concat - header.extraData.add 0.byte.repeat(EXTRA_SEAL) - -# clique/snapshot_test.go(77): func (ap *testerAccountPool) sign(header n[..] -proc sign(header: var BlockHeader; signer: PrivateKey) = - ## sign calculates a Clique digital signature for the given block and embeds - ## it back into the header. - # - # Sign the header and embed the signature in extra data - let - hashData = header.hashSealHeader.data - signature = signer.sign(SkMessage(hashData)).toRaw - extraLen = header.extraData.len - header.extraData.setLen(extraLen - EXTRA_SEAL) - header.extraData.add signature - -# ------------------------------------------------------------------------------ -# Public functions -# ------------------------------------------------------------------------------ - -# clique/snapshot_test.go(415): blocks, _ := core.GenerateChain(&config, [..] -proc cliqueGenvote*( - c: Clique; - voter: EthAddress; # new voter account/identity - seal: PrivateKey; # signature key - parent: BlockHeader; - elapsed = EthTime(0); - voteInOk = false; # vote in the new voter if `true` - outOfTurn = false; - checkPoint: seq[EthAddress] = @[]): BlockHeader = - ## Generate PoA voting header (as opposed to `epoch` synchronisation header.) - ## The function arguments are as follows: - ## - ## :c: - ## Clique descriptor. see the `newClique()` object constructor. - ## - ## :voter: - ## New voter account address to vote in or out (see `voteInOk`). A trivial - ## example for the first block #1 header would be choosing one of the - ## accounts listed in the `extraData` field fo the genesis header (note - ## that Goerli has exactly one of those accounts.) This trivial example - ## has no effect on the authorised voters' list. - ## - ## :seal: - ## Private key related to an authorised voter account. Again, a trivial - ## example for the block #1 header would be to (know and) use the - ## associated key for one of the accounts listed in the `extraData` field - ## fo the genesis header. - ## - ## :parent: - ## parent header to chain with (not necessarily on block chain yet). For - ## a block #1 header as a trivial example, this would be the genesis - ## header. - ## - ## :elapsed: - ## Optional timestamp distance from parent. This value defaults to valid - ## minimum time interval `c.cfg.period` - ## - ## :voteInOk: - ## Role of voting account. If `true`, the `voter` account address is voted - ## in to be accepted as authorised account. If `false`, the `voter` account - ## is voted to be removed (if it exists as authorised account, at all.) - ## - ## :outOfTurn: - ## Must be `false` if the `voter` is `in-turn` which is defined as the - ## property of a header block number retrieving the `seal` account address - ## when used as list index (modulo list-length) into the (internally - ## calculated and sorted) list of authorised signers. Absence of this - ## property is called `out-of-turn`. - ## - ## The classification `in-turn` and `out-of-turn` is used only with a - ## multi mining strategy where an `in-turn` block is slightly preferred. - ## Nevertheless, this property is to be locked into the block chain. In a - ## trivial example of an authorised signers list with exactly one entry, - ## all block numbers are zero modulo one, so are `in-turn`, and - ## `outOfTurn` would be left `false`. - ## - ## :checkPoint: - ## List of currently authorised signers. According to the Clique protocol - ## EIP-225, this list must be the same as the internally computed list of - ## authorised signers from the block chain. - ## - ## This list must appear on an `epoch` block and nowhere else. An `epoch` - ## block is a block where the block number is a multiple of `c.cfg.epoch`. - ## Typically, `c.cfg.epoch` is initialised as `30'000`. - ## - let timeElapsed = if elapsed == EthTime(0): c.cfg.period else: elapsed - - result = BlockHeader( - parentHash: parent.blockHash, - ommersHash: EMPTY_UNCLE_HASH, - stateRoot: parent.stateRoot, - timestamp: parent.timestamp + timeElapsed, - txRoot: EMPTY_ROOT_HASH, - receiptRoot: EMPTY_ROOT_HASH, - blockNumber: parent.blockNumber + 1, - gasLimit: parent.gasLimit, - # - # clique/snapshot_test.go(417): gen.SetCoinbase(accounts.address( [..] - coinbase: voter, - # - # clique/snapshot_test.go(418): if tt.votes[j].auth { - nonce: if voteInOk: NONCE_AUTH else: NONCE_DROP, - # - # clique/snapshot_test.go(436): header.Difficulty = diffInTurn [..] - difficulty: if outOfTurn: DIFF_NOTURN else: DIFF_INTURN, - # - extraData: 0.byte.repeat(EXTRA_VANITY + EXTRA_SEAL)) - - # clique/snapshot_test.go(432): if auths := tt.votes[j].checkpoint; [..] - if 0 < checkPoint.len: - result.extraCheckPoint(checkPoint) - - # Generate the signature and embed it into the header - result.sign(seal) - - -proc cliqueGenvote*( - c: Clique; voter: EthAddress; seal: PrivateKey; - elapsed = EthTime(0); - voteInOk = false; - outOfTurn = false; - checkPoint: seq[EthAddress] = @[]): BlockHeader - {.gcsafe, raises: [CatchableError].} = - ## Variant of `clique_genvote()` where the `parent` is the canonical head - ## on the block chain database. - ## - ## Trivial example (aka smoke test): - ## - ## :signature: `S` - ## :account address: `a(S)` - ## :genesis: extraData contains exactly one signer `a(S)` - ## - ## [..] - ## - ## | import pkg/[times], .. - ## | import p2p/[chain,clique], p2p/clique/clique_genvote, .. - ## - ## [..] - ## - ## | var db: CoreDbRef = ... - ## | var c = db.newChain - ## - ## - ## | \# overwrite, typically initialised at 15s - ## | const threeSecs = initDuration(seconds = 3) - ## | c.clique.cfg.period = threeSecs - ## - ## - ## | \# create first block (assuming empty block chain), mind `a(S)`, `S` - ## | let header = c.clique.clique_genvote(`a(S)`, `S`, elapsed = threeSecs) - ## - ## [..] - ## - ## let ok = c.persistBlocks(@[header],@[BlockBody()]) - ## - ## [..] - ## - c.cliqueGenvote(voter, seal, - parent = c.cfg.db.getCanonicalHead, - elapsed = elapsed, - voteInOk = voteInOk, - outOfTurn = outOfTurn, - checkPoint = checkPoint) - -# ------------------------------------------------------------------------------ -# End -# ------------------------------------------------------------------------------ diff --git a/nimbus/core/clique/clique_helpers.nim b/nimbus/core/clique/clique_helpers.nim deleted file mode 100644 index d1c09457f..000000000 --- a/nimbus/core/clique/clique_helpers.nim +++ /dev/null @@ -1,111 +0,0 @@ -# Nimbus -# Copyright (c) 2018 Status Research & Development GmbH -# Licensed under either of -# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or -# http://www.apache.org/licenses/LICENSE-2.0) -# * MIT license ([LICENSE-MIT](LICENSE-MIT) or -# http://opensource.org/licenses/MIT) -# at your option. This file may not be copied, modified, or distributed except -# according to those terms. - -## -## Tools & Utils for Clique PoA Consensus Protocol -## =============================================== -## -## For details see -## `EIP-225 `_ -## and -## `go-ethereum `_ -## - -import - std/[algorithm, times], - ../../constants, - ../../utils/utils, - ./clique_defs, - eth/[common, rlp], - stew/[objects, results], - stint - -type - EthSortOrder* = enum - EthDescending = SortOrder.Descending.ord - EthAscending = SortOrder.Ascending.ord - -{.push raises: [].} - -# ------------------------------------------------------------------------------ -# Private helpers -# ------------------------------------------------------------------------------ - -func zeroItem[T](t: typedesc[T]): T = - discard - -# ------------------------------------------------------------------------------ -# Public functions -# ------------------------------------------------------------------------------ - -proc isZero*[T: EthAddress|Hash256|Duration](a: T): bool = - ## `true` if `a` is all zero - a == zeroItem(T) - -proc sorted*(e: openArray[EthAddress]; order = EthAscending): seq[EthAddress] = - proc eCmp(x, y: EthAddress): int = - for n in 0 ..< x.len: - if x[n] < y[n]: - return -1 - elif y[n] < x[n]: - return 1 - e.sorted(cmp = eCmp, order = order.ord.SortOrder) - - -proc cliqueResultErr*(w: CliqueError): CliqueOkResult = - ## Return error result (syntactic sugar) - err(w) - - -proc extraDataAddresses*(extraData: Blob): seq[EthAddress] = - ## Extract signer addresses from extraData header field - - proc toEthAddress(a: openArray[byte]; start: int): EthAddress = - toArray(EthAddress.len, a[start ..< start + EthAddress.len]) - - if EXTRA_VANITY + EXTRA_SEAL < extraData.len and - ((extraData.len - (EXTRA_VANITY + EXTRA_SEAL)) mod EthAddress.len) == 0: - var addrOffset = EXTRA_VANITY - while addrOffset + EthAddress.len <= extraData.len - EXTRA_SEAL: - result.add extraData.toEthAddress(addrOffset) - addrOffset += EthAddress.len - - -# core/types/block.go(343): func (b *Block) WithSeal(header [..] -proc withHeader*(b: EthBlock; header: BlockHeader): EthBlock = - ## New block with the data from `b` but the header replaced with the - ## argument one. - EthBlock(header: header, - txs: b.txs, - uncles: b.uncles) - -# ------------------------------------------------------------------------------ -# Seal hash support -# ------------------------------------------------------------------------------ - -# clique/clique.go(730): func encodeSigHeader(w [..] -proc encodeSealHeader*(header: BlockHeader): seq[byte] = - ## Cut sigature off `extraData` header field and consider new `baseFee` - ## field for Eip1559. - doAssert EXTRA_SEAL < header.extraData.len - - var rlpHeader = header - rlpHeader.extraData.setLen(header.extraData.len - EXTRA_SEAL) - - rlp.encode(rlpHeader) - -# clique/clique.go(688): func SealHash(header *types.Header) common.Hash { -proc hashSealHeader*(header: BlockHeader): Hash256 = - ## Returns the hash of a block prior to it being sealed. - header.encodeSealHeader.keccakHash - -# ------------------------------------------------------------------------------ -# End -# ------------------------------------------------------------------------------ diff --git a/nimbus/core/clique/clique_sealer.nim b/nimbus/core/clique/clique_sealer.nim deleted file mode 100644 index 8eaba4cf4..000000000 --- a/nimbus/core/clique/clique_sealer.nim +++ /dev/null @@ -1,261 +0,0 @@ -# Nimbus -# Copyright (c) 2018-2023 Status Research & Development GmbH -# Licensed under either of -# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or -# http://www.apache.org/licenses/LICENSE-2.0) -# * MIT license ([LICENSE-MIT](LICENSE-MIT) or -# http://opensource.org/licenses/MIT) -# at your option. This file may not be copied, modified, or distributed except -# according to those terms. - -## -## Mining Support for Clique PoA Consensus Protocol -## ===================!============================= -## -## For details see -## `EIP-225 `_ -## and -## `go-ethereum `_ -## - -{.push raises: [].} - -import - std/[sequtils], - chronicles, - chronos, - eth/keys, - "../.."/[constants, utils/ec_recover], - ../../common/common, - ./clique_cfg, - ./clique_defs, - ./clique_desc, - ./clique_helpers, - ./clique_snapshot, - ./clique_verify, - ./snapshot/[ballot, snapshot_desc] - -logScope: - topics = "clique PoA Mining" - -# ------------------------------------------------------------------------------ -# Private Helpers -# ------------------------------------------------------------------------------ - -proc isValidVote(s: Snapshot; a: EthAddress; authorize: bool): bool {.gcsafe, raises: [].} = - s.ballot.isValidVote(a, authorize) - -proc isSigner*(s: Snapshot; address: EthAddress): bool {.gcsafe, raises: [].} = - ## See `clique_verify.isSigner()` - s.ballot.isAuthSigner(address) - -# clique/snapshot.go(319): func (s *Snapshot) inturn(number [..] -proc inTurn*(s: Snapshot; number: BlockNumber, signer: EthAddress): bool {.gcsafe, raises: [].} = - ## See `clique_verify.inTurn()` - let ascSignersList = s.ballot.authSigners - for offset in 0 ..< ascSignersList.len: - if ascSignersList[offset] == signer: - return (number mod ascSignersList.len.u256) == offset.u256 - -# ------------------------------------------------------------------------------ -# Private functions -# ------------------------------------------------------------------------------ - -# clique/clique.go(681): func calcDifficulty(snap [..] -proc calcDifficulty(s: Snapshot; signer: EthAddress): DifficultyInt {.gcsafe, raises: [].} = - if s.inTurn(s.blockNumber + 1, signer): - DIFF_INTURN - else: - DIFF_NOTURN - -proc recentBlockNumber*(s: Snapshot; - a: EthAddress): Result[BlockNumber,void] {.gcsafe, raises: [].} = - ## Return `BlockNumber` for `address` argument (if any) - for (number,recent) in s.recents.pairs: - if recent == a: - return ok(number) - return err() - -# ------------------------------------------------------------------------------ -# Public functions -# ------------------------------------------------------------------------------ - -# clique/clique.go(506): func (c *Clique) Prepare(chain [..] -proc prepare*(c: Clique; parent: BlockHeader, header: var BlockHeader): CliqueOkResult - {.gcsafe, raises: [CatchableError].} = - ## For the Consensus Engine, `prepare()` initializes the consensus fields - ## of a block header according to the rules of a particular engine. - ## - ## This implementation prepares all the consensus fields of the header for - ## running the transactions on top. - - # Assemble the voting snapshot to check which votes make sense - let rc = c.cliqueSnapshot(parent.blockHash, @[]) - if rc.isErr: - return err(rc.error) - - # if we are not voting, coinbase should be filled with zero - # because other subsystem e.g txpool can produce block header - # with non zero coinbase. if that coinbase is one of the signer - # and the nonce is zero, that signer will be vote out from - # signer list - header.coinbase.reset - - let modEpoch = (parent.blockNumber+1) mod c.cfg.epoch - if modEpoch.isZero.not: - # Gather all the proposals that make sense voting on - var addresses: seq[EthAddress] - for (address,authorize) in c.proposals.pairs: - if c.snapshot.isValidVote(address, authorize): - addresses.add address - - # If there's pending proposals, cast a vote on them - if 0 < addresses.len: - header.coinbase = addresses[c.cfg.rand(addresses.len-1)] - header.nonce = if header.coinbase in c.proposals: NONCE_AUTH - else: NONCE_DROP - - # Set the correct difficulty - header.difficulty = c.snapshot.calcDifficulty(c.signer) - - # Ensure the extra data has all its components - header.extraData.setLen(EXTRA_VANITY) - if modEpoch.isZero: - header.extraData.add c.snapshot.ballot.authSigners.mapIt(toSeq(it)).concat - header.extraData.add 0.byte.repeat(EXTRA_SEAL) - - # Mix digest is reserved for now, set to empty - header.mixDigest.reset - - # Ensure the timestamp has the correct delay - header.timestamp = parent.timestamp + c.cfg.period - if header.timestamp < EthTime.now(): - header.timestamp = EthTime.now() - - ok() - -proc prepareForSeal*(c: Clique; prepHeader: BlockHeader; header: var BlockHeader) {.gcsafe, raises: [].} = - # TODO: use system.move? - header.nonce = prepHeader.nonce - header.extraData = prepHeader.extraData - header.mixDigest = prepHeader.mixDigest - -# clique/clique.go(589): func (c *Clique) Authorize(signer [..] -proc authorize*(c: Clique; signer: EthAddress; signFn: CliqueSignerFn) {.gcsafe, raises: [].} = - ## Injects private key into the consensus engine to mint new blocks with. - c.signer = signer - c.signFn = signFn - -# clique/clique.go(724): func CliqueRLP(header [..] -proc cliqueRlp*(header: BlockHeader): seq[byte] {.gcsafe, raises: [].} = - ## Returns the rlp bytes which needs to be signed for the proof-of-authority - ## sealing. The RLP to sign consists 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. - header.encodeSealHeader - -# clique/clique.go(688): func SealHash(header *types.Header) common.Hash { -proc sealHash*(header: BlockHeader): Hash256 {.gcsafe, raises: [].} = - ## For the Consensus Engine, `sealHash()` returns the hash of a block prior - ## to it being sealed. - ## - ## This implementation returns the hash of a block prior to it being sealed. - header.hashSealHeader - - -# clique/clique.go(599): func (c *Clique) Seal(chain [..] -proc seal*(c: Clique; ethBlock: var EthBlock): - Result[void,CliqueError] {.gcsafe, - raises: [CatchableError].} = - ## This implementation attempts to create a sealed block using the local - ## signing credentials. - - var header = ethBlock.header - - # Sealing the genesis block is not supported - if header.blockNumber.isZero: - return err((errUnknownBlock, "")) - - # For 0-period chains, refuse to seal empty blocks (no reward but would spin - # sealing) - if c.cfg.period == 0 and ethBlock.txs.len == 0: - info $nilCliqueSealNoBlockYet - return err((nilCliqueSealNoBlockYet, "")) - - # Don't hold the signer fields for the entire sealing procedure - let - signer = c.signer - signFn = c.signFn - - # Bail out if we're unauthorized to sign a block - let rc = c.cliqueSnapshot(header.parentHash) - if rc.isErr: - return err(rc.error) - if not c.snapshot.isSigner(signer): - return err((errUnauthorizedSigner, "")) - - # If we're amongst the recent signers, wait for the next block - let seen = c.snapshot.recentBlockNumber(signer) - if seen.isOk: - # Signer is among recents, only wait if the current block does not - # shift it out - if header.blockNumber < seen.value + c.snapshot.signersThreshold.u256: - info $nilCliqueSealSignedRecently - return err((nilCliqueSealSignedRecently, "")) - - when false: - # Sweet, the protocol permits us to sign the block, wait for our time - var delay = header.timestamp - EthTime.now() - if header.difficulty == DIFF_NOTURN: - # It's not our turn explicitly to sign, delay it a bit - let wiggle = c.snapshot.signersThreshold.int64 * WIGGLE_TIME - # Kludge for limited rand() argument range - if wiggle.inSeconds < (int.high div 1000).int64: - let rndWiggleMs = c.cfg.rand(wiggle.inMilliseconds.int) - delay += initDuration(milliseconds = rndWiggleMs) - else: - let rndWiggleSec = c.cfg.rand((wiggle.inSeconds and int.high).int) - delay += initDuration(seconds = rndWiggleSec) - - trace "Out-of-turn signing requested", - wiggle = $wiggle - - # Sign all the things! - try: - let signature = signFn(signer,header.cliqueRlp) - if signature.isErr: - return err((errCliqueSealSigFn,$signature.error)) - let extraLen = header.extraData.len - if EXTRA_SEAL < extraLen: - header.extraData.setLen(extraLen - EXTRA_SEAL) - header.extraData.add signature.value - except CatchableError as exc: - return err((errCliqueSealSigFn, - "Error when signing block header: " & exc.msg)) - - ethBlock = ethBlock.withHeader(header) - ok() - -# clique/clique.go(673): func (c *Clique) CalcDifficulty(chain [..] -proc calcDifficulty*(c: Clique; - parent: BlockHeader): Result[DifficultyInt,CliqueError] - {.gcsafe, raises: [CatchableError].} = - ## For the Consensus Engine, `calcDifficulty()` is the difficulty adjustment - ## algorithm. It returns the difficulty that a new block should have. - ## - ## This implementation returns the difficulty that a new block should have: - ## * DIFF_NOTURN(2) if BLOCK_NUMBER % SIGNER_COUNT != SIGNER_INDEX - ## * DIFF_INTURN(1) if BLOCK_NUMBER % SIGNER_COUNT == SIGNER_INDEX - let rc = c.cliqueSnapshot(parent) - if rc.isErr: - return err(rc.error) - return ok(c.snapshot.calcDifficulty(c.signer)) - - -# ------------------------------------------------------------------------------ -# End -# ------------------------------------------------------------------------------ diff --git a/nimbus/core/clique/clique_snapshot.nim b/nimbus/core/clique/clique_snapshot.nim deleted file mode 100644 index 8cec4c7f9..000000000 --- a/nimbus/core/clique/clique_snapshot.nim +++ /dev/null @@ -1,442 +0,0 @@ -# Nimbus -# Copyright (c) 2018-2024 Status Research & Development GmbH -# Licensed under either of -# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or -# http://www.apache.org/licenses/LICENSE-2.0) -# * MIT license ([LICENSE-MIT](LICENSE-MIT) or -# http://opensource.org/licenses/MIT) -# at your option. This file may not be copied, modified, or distributed except -# according to those terms. - -## -## Snapshot for Clique PoA Consensus Protocol -## ========================================== -## -## For details see -## `EIP-225 `_ -## and -## `go-ethereum `_ -## - -import - std/[sequtils, strutils], - chronicles, - eth/[keys], - stew/[keyed_queue, results], - ../../utils/prettify, - "."/[clique_cfg, clique_defs, clique_desc], - ./snapshot/[snapshot_apply, snapshot_desc] - -type - # Internal sub-descriptor for `LocalSnapsDesc` - LocalPivot = object - header: BlockHeader - hash: Hash256 - - # Internal sub-descriptor for `LocalSnapsDesc` - LocalPath = object - snaps: Snapshot ## snapshot for given hash - chain: seq[BlockHeader] ## header chain towards snapshot - error: CliqueError ## error message - - # Internal sub-descriptor for `LocalSnapsDesc` - LocalSubChain = object - first: int ## first chain[] element to be used - top: int ## length of chain starting at position 0 - - LocalSnaps = object - c: Clique - start: LocalPivot ## start here searching for checkpoints - trail: LocalPath ## snapshot location - subChn: LocalSubChain ## chain[] sub-range - parents: HeadersHolderRef ## explicit parents - - HeadersHolderRef* = ref object - headers*: seq[BlockHeader] - -{.push raises: [].} - -logScope: - topics = "clique PoA snapshot" - -static: - const stopCompilerGossip {.used.} = 42.toSI - -# ------------------------------------------------------------------------------ -# Private debugging functions, pretty printing -# ------------------------------------------------------------------------------ - -template say(d: var LocalSnaps; v: varargs[untyped]): untyped = - discard - # uncomment body to enable, note that say() prints on - # d.c.cfg.say v - -#proc pp(a: Hash256): string = -# if a == EMPTY_ROOT_HASH: -# "*blank-root*" -# elif a == EMPTY_SHA3: -# "*empty-sha3*" -# else: -# a.data.mapIt(it.toHex(2)).join[56 .. 63].toLowerAscii - -#proc pp(q: openArray[BlockHeader]; n: int): string = -# result = "[" -# if 5 < n: -# result &= toSeq(q[0 .. 2]).mapIt("#" & $it.blockNumber).join(", ") -# result &= " .." & $n & ".. #" & $q[n-1].blockNumber -# else: -# result &= toSeq(q[0 ..< n]).mapIt("#" & $it.blockNumber).join(", ") -# result &= "]" - -#proc pp(b: BlockNumber, q: openArray[BlockHeader]; n: int): string = -# "#" & $b & " + " & q.pp(n) - - -#proc pp(q: openArray[BlockHeader]): string = -# q.pp(q.len) - -#proc pp(b: BlockNumber, q: openArray[BlockHeader]): string = -# b.pp(q, q.len) - - -#proc pp(h: BlockHeader, q: openArray[BlockHeader]; n: int): string = -# "headers=(" & h.blockNumber.pp(q,n) & ")" - -#proc pp(h: BlockHeader, q: openArray[BlockHeader]): string = -# h.pp(q,q.len) - -#proc pp(t: var LocalPath; w: var LocalSubChain): string = -# var (a, b) = (w.first, w.top) -# if a == 0 and b == 0: b = t.chain.len -# "trail=(#" & $t.snaps.blockNumber & " + " & t.chain[a ..< b].pp & ")" - -#proc pp(t: var LocalPath): string = -# var w = LocalSubChain() -# t.pp(w) - -#proc pp(err: CliqueError): string = -# "(" & $err[0] & "," & err[1] & ")" - -# ------------------------------------------------------------------------------ -# Private helpers -# ------------------------------------------------------------------------------ - -proc maxCheckPointLe(d: var LocalSnaps; number: BlockNumber): BlockNumber = - let epc = number mod d.c.cfg.ckpInterval - if epc < number: - number - epc - else: - # epc == number => number < ckpInterval - 0.u256 - -proc isCheckPoint(d: var LocalSnaps; number: BlockNumber): bool = - (number mod d.c.cfg.ckpInterval).isZero - -proc isEpoch(d: var LocalSnaps; number: BlockNumber): bool = - (number mod d.c.cfg.epoch).isZero - -proc isSnapshotPosition(d: var LocalSnaps; number: BlockNumber): bool = - # clique/clique.go(394): if number == 0 || (number%c.config.Epoch [..] - if d.isEpoch(number): - if number.isZero: - # At the genesis => snapshot the initial state. - return true - if not d.c.applySnapsMinBacklog: - return true - if d.c.cfg.roThreshold < d.trail.chain.len: - # We have piled up more headers than allowed to be re-orged (chain - # reinit from a freezer), regard checkpoint trusted and snapshot it. - return true - -# ------------------------------------------------------------------------------ -# Private functions -# ------------------------------------------------------------------------------ - -func len*(list: HeadersHolderRef): int = - list.headers.len - -func `[]`*(list: HeadersHolderRef, idx: int): BlockHeader = - list.headers[idx] - -func `[]`*(list: HeadersHolderRef, idx: BackwardsIndex): BlockHeader = - list.headers[list.headers.len - int(idx)] - -proc findSnapshot(d: var LocalSnaps): bool = - ## Search for a snapshot starting at current header starting at the pivot - ## value `d.start`. The snapshot returned in `trail` is a clone of the - ## cached snapshot and can be modified later. - - var - (header, hash) = (d.start.header, d.start.hash) - parentsLen = d.parents.len - - # For convenience, ignore the current header as top parents list entry - if 0 < parentsLen and d.parents[^1] == header: - parentsLen.dec - - while true: - #d.say "findSnapshot ", header.pp(d.parents, parentsLen), - # " trail=", d.trail.chain.pp - - let number = header.blockNumber - - # Check whether the snapshot was recently visited and cached - block: - let rc = d.c.recents.lruFetch(hash.data) - if rc.isOk: - d.trail.snaps = rc.value.cloneSnapshot - # d.say "findSnapshot cached ", d.trail.pp - trace "Found recently cached voting snapshot", - blockNumber = number, - blockHash = hash - return true - - # If an on-disk checkpoint snapshot can be found, use that - if d.isCheckPoint(number): - let rc = d.c.cfg.loadSnapshot(hash) - if rc.isOk: - d.trail.snaps = rc.value.cloneSnapshot - d.say "findSnapshot on disk ", d.trail.pp - trace "Loaded voting snapshot from disk", - blockNumber = number, - blockHash = hash - # clique/clique.go(386): snap = s - return true - - # Note that epoch is a restart and sync point. Eip-225 requires that the - # epoch header contains the full list of currently authorised signers. - if d.isSnapshotPosition(number): - # clique/clique.go(395): checkpoint := chain.GetHeaderByNumber [..] - d.trail.snaps = d.c.cfg.newSnapshot(header) - let rc = d.c.cfg.storeSnapshot(d.trail.snaps) - if rc.isOk: - d.say "findSnapshot ", d.trail.pp - trace "Stored voting snapshot to disk", - blockNumber = number, - blockHash = hash, - nSnaps = d.c.cfg.nSnaps, - snapsTotal = d.c.cfg.snapsData.toSI - return true - - # No snapshot for this header, get the parent header and move backward - hash = header.parentHash - # Add to batch (reversed list order, biggest block number comes first) - d.trail.chain.add header - - # Assign parent header - if 0 < parentsLen: - # If we have explicit parents, pop it from the parents list - parentsLen.dec - header = d.parents[parentsLen] - # clique/clique.go(416): if header.Hash() != hash [..] - if header.blockHash != hash: - d.trail.error = (errUnknownAncestor,"") - return false - - # No explicit parents (or no more parents left), reach out to the database - elif not d.c.cfg.db.getBlockHeader(hash, header): - d.trail.error = (errUnknownAncestor,"") - return false - - # => while loop - - # notreached - raiseAssert "findSnapshot(): wrong exit from forever-loop" - - -proc applyTrail(d: var LocalSnaps): CliqueOkResult - {.gcsafe, raises: [CatchableError].} = - ## Apply any `trail` headers on top of the snapshot `snap` - if d.subChn.first < d.subChn.top: - block: - # clique/clique.go(434): snap, err := snap.apply(headers) - d.say "applyTrail ", d.trail.pp(d.subChn) - let rc = d.trail.snaps.snapshotApplySeq( - d.trail.chain, d.subChn.top-1, d.subChn.first) - if rc.isErr: - d.say "applyTrail snaps=#", d.trail.snaps.blockNumber, - " err=", rc.error.pp - return err(rc.error) - d.say "applyTrail snaps=#", d.trail.snaps.blockNumber - - # If we've generated a new checkpoint snapshot, save to disk - if d.isCheckPoint(d.trail.snaps.blockNumber): - - var rc = d.c.cfg.storeSnapshot(d.trail.snaps) - if rc.isErr: - return err(rc.error) - - d.say "applyTrail chechkpoint #", d.trail.snaps.blockNumber - trace "Stored voting snapshot to disk", - blockNumber = d.trail.snaps.blockNumber, - blockHash = d.trail.snaps.blockHash, - nSnaps = d.c.cfg.nSnaps, - snapsTotal = d.c.cfg.snapsData.toSI - ok() - - -proc updateSnapshot(d: var LocalSnaps): SnapshotResult - {.gcsafe, raises: [CatchableError].} = - ## Find snapshot for header `d.start.header` and assign it to the LRU cache. - ## This function was expects thet the LRU cache already has a slot allocated - ## for the snapshot having run `getLruSnaps()`. - - d.say "updateSnapshot begin ", d.start.header.blockNumber.pp(d.parents) - - # Search for previous snapshots - if not d.findSnapshot: - return err(d.trail.error) - - # Initialise range for header chain[] to be applied to `d.trail.snaps` - d.subChn.top = d.trail.chain.len - - # Previous snapshot found, apply any pending trail headers on top of it - if 0 < d.subChn.top: - let - first = d.trail.chain[^1].blockNumber - last = d.trail.chain[0].blockNumber - ckpt = d.maxCheckPointLe(last) - - # If there is at least one checkpoint part of the trail sequence, make sure - # that we can store the latest one. This will be done by the `applyTrail()` - # handler for the largest block number in the sequence (note that the trail - # block numbers are in reverse order.) - if first <= ckpt and ckpt < last: - # Split the trail sequence so that the first one has the checkpoint - # entry with largest block number. - let inx = (last - ckpt).truncate(int) - - # First part (note reverse block numbers.) - d.subChn.first = inx - let rc = d.applyTrail - if rc.isErr: - return err(rc.error) - - # Second part (note reverse block numbers.) - d.subChn.first = 0 - d.subChn.top = inx - - var rc = d.applyTrail - if rc.isErr: - return err(rc.error) - - # clique/clique.go(438): c.recents.Add(snap.Hash, snap) - discard d.c.recents.lruAppend( - d.trail.snaps.blockHash.data, d.trail.snaps, INMEMORY_SNAPSHOTS) - - if 1 < d.trail.chain.len: - d.say "updateSnapshot ok #", d.trail.snaps.blockNumber, - " trail.len=", d.trail.chain.len - - ok(d.trail.snaps) - -# ------------------------------------------------------------------------------ -# Public functions -# ------------------------------------------------------------------------------ - -proc cliqueSnapshotSeq*(c: Clique; header: BlockHeader; - parents: HeadersHolderRef): SnapshotResult - {.gcsafe, raises: [CatchableError].} = - ## Create authorisation state snapshot of a given point in the block chain - ## and store it in the `Clique` descriptor to be retrievable as `c.snapshot` - ## if successful. - ## - ## If the `parents[]` argument list top element (if any) is the same as the - ## `header` argument, this top element is silently ignored. - ## - ## If this function is successful, the compiled `Snapshot` will also be - ## stored in the `Clique` descriptor which can be retrieved later - ## via `c.snapshot`. - block: - let rc = c.recents.lruFetch(header.blockHash.data) - if rc.isOk: - c.snapshot = rc.value - return ok(rc.value) - - # Avoid deep copy, sequence will not be changed by `updateSnapshot()` - - var snaps = LocalSnaps( - c: c, - parents: parents, - start: LocalPivot( - header: header, - hash: header.blockHash)) - - let rc = snaps.updateSnapshot - if rc.isOk: - c.snapshot = rc.value - - rc - - -proc cliqueSnapshotSeq*(c: Clique; hash: Hash256; - parents: HeadersHolderRef): SnapshotResult - {.gcsafe,raises: [CatchableError].} = - ## Create authorisation state snapshot of a given point in the block chain - ## and store it in the `Clique` descriptor to be retrievable as `c.snapshot` - ## if successful. - ## - ## If the `parents[]` argument list top element (if any) is the same as the - ## `header` argument, this top element is silently ignored. - ## - ## If this function is successful, the compiled `Snapshot` will also be - ## stored in the `Clique` descriptor which can be retrieved later - ## via `c.snapshot`. - block: - let rc = c.recents.lruFetch(hash.data) - if rc.isOk: - c.snapshot = rc.value - return ok(rc.value) - - var header: BlockHeader - if not c.cfg.db.getBlockHeader(hash, header): - return err((errUnknownHash,"")) - - # Avoid deep copy, sequence will not be changed by `updateSnapshot()` - - var snaps = LocalSnaps( - c: c, - parents: parents, - start: LocalPivot( - header: header, - hash: hash)) - - let rc = snaps.updateSnapshot - if rc.isOk: - c.snapshot = rc.value - - rc - - -# clique/clique.go(369): func (c *Clique) snapshot(chain [..] -proc cliqueSnapshot*(c: Clique; header: BlockHeader; - parents: var seq[BlockHeader]): SnapshotResult - {.gcsafe, raises: [CatchableError].} = - let list = HeadersHolderRef( - headers: toSeq(parents) - ) - c.cliqueSnapshotSeq(header,list) - -proc cliqueSnapshot*(c: Clique;hash: Hash256; - parents: openArray[BlockHeader]): SnapshotResult - {.gcsafe, raises: [CatchableError].} = - let list = HeadersHolderRef( - headers: toSeq(parents) - ) - c.cliqueSnapshotSeq(hash,list) - -proc cliqueSnapshot*(c: Clique; header: BlockHeader): SnapshotResult - {.gcsafe,raises: [CatchableError].} = - ## Short for `cliqueSnapshot(c,header,@[])` - let blind = HeadersHolderRef() - c.cliqueSnapshotSeq(header, blind) - -proc cliqueSnapshot*(c: Clique; hash: Hash256): SnapshotResult - {.gcsafe,raises: [CatchableError].} = - ## Short for `cliqueSnapshot(c,hash,@[])` - let blind = HeadersHolderRef() - c.cliqueSnapshotSeq(hash, blind) - -# ------------------------------------------------------------------------------ -# End -# ------------------------------------------------------------------------------ diff --git a/nimbus/core/clique/clique_verify.nim b/nimbus/core/clique/clique_verify.nim deleted file mode 100644 index 49f5969da..000000000 --- a/nimbus/core/clique/clique_verify.nim +++ /dev/null @@ -1,426 +0,0 @@ -# Nimbus -# Copyright (c) 2018-2024 Status Research & Development GmbH -# Licensed under either of -# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or -# http://www.apache.org/licenses/LICENSE-2.0) -# * MIT license ([LICENSE-MIT](LICENSE-MIT) or -# http://opensource.org/licenses/MIT) -# at your option. This file may not be copied, modified, or distributed except -# according to those terms. - -## -## Verify Headers for Clique PoA Consensus Protocol -## ================================================ -## -## Note that mining in currently unsupported by `NIMBUS` -## -## For details see -## `EIP-225 `_ -## and -## `go-ethereum `_ -## - -import - std/[strformat, sequtils], - ../../utils/utils, - ../../common/common, - ../gaslimit, - ./clique_cfg, - ./clique_defs, - ./clique_desc, - ./clique_helpers, - ./clique_snapshot, - ./snapshot/[ballot, snapshot_desc], - chronicles, - stew/results - -{.push raises: [].} - -logScope: - topics = "clique PoA verify header" - -# ------------------------------------------------------------------------------ -# Private helpers -# ------------------------------------------------------------------------------ - -# consensus/misc/forks.go(30): func VerifyForkHashes(config [..] -proc verifyForkHashes(com: CommonRef; header: BlockHeader): CliqueOkResult - {.gcsafe, raises: [ValueError].} = - ## Verify that blocks conforming to network hard-forks do have the correct - ## hashes, to avoid clients going off on different chains. - - if com.eip150Block.isSome and - com.eip150Block.get == header.blockNumber: - - # If the homestead reprice hash is set, validate it - let - eip150 = com.eip150Hash - hash = header.blockHash - - if eip150 != hash: - return err((errCliqueGasRepriceFork, - &"Homestead gas reprice fork: have {eip150}, want {hash}")) - - return ok() - -proc signersThreshold*(s: Snapshot): int = - ## Minimum number of authorised signers needed. - s.ballot.authSignersThreshold - - -proc recentBlockNumber*(s: Snapshot; a: EthAddress): Result[BlockNumber,void] = - ## Return `BlockNumber` for `address` argument (if any) - for (number,recent) in s.recents.pairs: - if recent == a: - return ok(number) - return err() - - -proc isSigner*(s: Snapshot; address: EthAddress): bool = - ## Checks whether argukment ``address` is in signers list - s.ballot.isAuthSigner(address) - - -# clique/snapshot.go(319): func (s *Snapshot) inturn(number [..] -proc inTurn*(s: Snapshot; number: BlockNumber, signer: EthAddress): bool = - ## Returns `true` if a signer at a given block height is in-turn. - let ascSignersList = s.ballot.authSigners - if 0 < ascSignersList.len: - let offset = (number mod ascSignersList.len.u256).truncate(int64) - return ascSignersList[offset] == signer - -# ------------------------------------------------------------------------------ -# Private functions -# ------------------------------------------------------------------------------ - -# clique/clique.go(463): func (c *Clique) verifySeal(chain [..] -proc verifySeal(c: Clique; header: BlockHeader): CliqueOkResult = - ## Check 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. - - # Verifying the genesis block is not supported - if header.blockNumber.isZero: - return err((errUnknownBlock,"")) - - # Get current snapshot - let snapshot = c.snapshot - - # Verify availability of the cached snapshot - doAssert snapshot.blockHash == header.parentHash - - # Resolve the authorization key and check against signers - let signer = c.cfg.ecRecover(header) - if signer.isErr: - return err((errEcRecover,$signer.error)) - - if not snapshot.isSigner(signer.value): - return err((errUnauthorizedSigner,"")) - - let seen = snapshot.recentBlockNumber(signer.value) - if seen.isOk: - # Signer is among recents, only fail if the current block does not - # shift it out - # clique/clique.go(486): if limit := uint64(len(snap.Signers)/2 + 1); [..] - if header.blockNumber - snapshot.signersThreshold.u256 < seen.value: - return err((errRecentlySigned,"")) - - # Ensure that the difficulty corresponds to the turn-ness of the signer - if snapshot.inTurn(header.blockNumber, signer.value): - if header.difficulty != DIFF_INTURN: - return err((errWrongDifficulty,"INTURN expected")) - else: - if header.difficulty != DIFF_NOTURN: - return err((errWrongDifficulty,"NOTURN expected")) - - ok() - - -# clique/clique.go(314): func (c *Clique) verifyCascadingFields(chain [..] -proc verifyCascadingFields(c: Clique; com: CommonRef; header: BlockHeader; - parents: HeadersHolderRef): CliqueOkResult - {.gcsafe, raises: [CatchableError].} = - ## Verify all the header fields that are not standalone, rather depend on a - ## batch of previous headers. 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. - - # The genesis block is the always valid dead-end - if header.blockNumber.isZero: - return ok() - - # Ensure that the block's timestamp isn't too close to its parent - var parent: BlockHeader - if 0 < parents.len: - parent = parents[^1] - elif not c.db.getBlockHeader(header.blockNumber-1, parent): - return err((errUnknownAncestor,"")) - - if parent.blockNumber != header.blockNumber-1 or - parent.blockHash != header.parentHash: - return err((errUnknownAncestor,"")) - - # clique/clique.go(330): if parent.Time+c.config.Period > header.Time { - if header.timestamp < parent.timestamp + c.cfg.period: - return err((errInvalidTimestamp,"")) - - # Verify that the gasUsed is <= gasLimit - block: - # clique/clique.go(333): if header.GasUsed > header.GasLimit { - let (used, limit) = (header.gasUsed, header.gasLimit) - if limit < used: - return err((errCliqueExceedsGasLimit, - &"invalid gasUsed: have {used}, gasLimit {limit}")) - - # Verify `GasLimit` or `BaseFee` depending on whether before or after - # EIP-1559/London fork. - block: - # clique/clique.go(337): if !chain.Config().IsLondon(header.Number) { - let rc = com.validateGasLimitOrBaseFee(header, parent) - if rc.isErr: - return err((errCliqueGasLimitOrBaseFee, rc.error)) - - # Retrieve the snapshot needed to verify this header and cache it - block: - # clique/clique.go(350): snap, err := c.snapshot(chain, number-1, .. - let rc = c.cliqueSnapshotSeq(header.parentHash, parents) - if rc.isErr: - return err(rc.error) - - # If the block is a checkpoint block, verify the signer list - if (header.blockNumber mod c.cfg.epoch.u256) == 0: - var addrList = header.extraData.extraDataAddresses - # not using `authSigners()` here as it is too slow - if c.snapshot.ballot.authSignersLen != addrList.len or - not c.snapshot.ballot.isAuthSigner(addrList): - return err((errMismatchingCheckpointSigners,"")) - - # All basic checks passed, verify the seal and return - return c.verifySeal(header) - - -proc verifyHeaderFields(c: Clique; header: BlockHeader): CliqueOkResult = - ## Check header fields, the ones that do not depend on a parent block. - # clique/clique.go(250): number := header.Number.Uint64() - - # Don't waste time checking blocks from the future - if EthTime.now() < header.timestamp: - return err((errFutureBlock,"")) - - # Checkpoint blocks need to enforce zero beneficiary - let isCheckPoint = (header.blockNumber mod c.cfg.epoch.u256).isZero - if isCheckPoint and not header.coinbase.isZero: - return err((errInvalidCheckpointBeneficiary,"")) - - # Nonces must be 0x00..0 or 0xff..f, zeroes enforced on checkpoints - if header.nonce != NONCE_AUTH and header.nonce != NONCE_DROP: - return err((errInvalidVote,"")) - if isCheckPoint and header.nonce != NONCE_DROP: - return err((errInvalidCheckpointVote,"")) - - # Check that the extra-data contains both the vanity and signature - if header.extraData.len < EXTRA_VANITY: - return err((errMissingVanity,"")) - if header.extraData.len < EXTRA_VANITY + EXTRA_SEAL: - return err((errMissingSignature,"")) - - # Ensure that the extra-data contains a signer list on a checkpoint, - # but none otherwise - let signersBytes = header.extraData.len - EXTRA_VANITY - EXTRA_SEAL - if not isCheckPoint: - if signersBytes != 0: - return err((errExtraSigners,"")) - elif (signersBytes mod EthAddress.len) != 0: - return err((errInvalidCheckpointSigners,"")) - - # Ensure that the mix digest is zero as we do not have fork protection - # currently - if not header.mixDigest.isZero: - return err((errInvalidMixDigest,"")) - - # Ensure that the block does not contain any uncles which are meaningless - # in PoA - if header.ommersHash != EMPTY_UNCLE_HASH: - return err((errInvalidUncleHash,"")) - - # Ensure that the block's difficulty is meaningful (may not be correct at - # this point) - if not header.blockNumber.isZero: - # Note that neither INTURN or NOTURN should be zero (but this might be - # subject to change as it is explicitely checked for in `clique.go`) - let diffy = header.difficulty - # clique/clique.go(246): if header.Difficulty == nil || (header.Difficulty.. - if diffy.isZero or (diffy != DIFF_INTURN and diffy != DIFF_NOTURN): - return err((errInvalidDifficulty,"")) - - # verify that the gas limit is <= 2^63-1 - when header.gasLimit.typeof isnot int64: - if int64.high < header.gasLimit: - return err((errCliqueExceedsGasLimit, - &"invalid gasLimit: have {header.gasLimit}, must be int64")) - ok() - - -# clique/clique.go(246): func (c *Clique) verifyHeader(chain [..] -proc cliqueVerifyImpl(c: Clique; com: CommonRef; header: BlockHeader; - parents: HeadersHolderRef): CliqueOkResult - {.gcsafe, raises: [CatchableError].} = - ## Check 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. - c.failed = (ZERO_HASH256,cliqueNoError) - - block: - # Check header fields independent of parent blocks - let rc = c.verifyHeaderFields(header) - if rc.isErr: - c.failed = (header.blockHash, rc.error) - return err(rc.error) - - block: - # If all checks passed, validate any special fields for hard forks - let rc = com.verifyForkHashes(header) - if rc.isErr: - c.failed = (header.blockHash, rc.error) - return err(rc.error) - - # All basic checks passed, verify cascading fields - result = c.verifyCascadingFields(com, header, parents) - if result.isErr: - c.failed = (header.blockHash, result.error) - -proc cliqueVerifySeq*(c: Clique; com: CommonRef; header: BlockHeader; - parents: HeadersHolderRef): CliqueOkResult - {.gcsafe, raises: [CatchableError].} = - ## Check 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. - ## - ## On success, the latest authorised signers list is available via the - ## fucntion `c.cliqueSigners()`. Otherwise, the latest error is also stored - ## in the `Clique` descriptor - ## - ## If there is an error, this error is also stored within the `Clique` - ## descriptor and can be retrieved via `c.failed` along with the hash/ID of - ## the failed block header. - block: - let rc = c.cliqueVerifyImpl(com, header, parents) - if rc.isErr: - return rc - - # Adjust current shapshot (the function `cliqueVerifyImpl()` typically - # works with the parent snapshot. - block: - let rc = c.cliqueSnapshotSeq(header, parents) - if rc.isErr: - return err(rc.error) - - ok() - -proc cliqueVerifySeq(c: Clique; com: CommonRef; - headers: HeadersHolderRef): CliqueOkResult - {.gcsafe, raises: [CatchableError].} = - ## This function verifies a batch of headers checking each header for - ## consensus rules conformance. The `headers` list is supposed to - ## contain a chain of headers, i e. `headers[i]` is parent to `headers[i+1]`. - ## - ## On success, the latest authorised signers list is available via the - ## fucntion `c.cliqueSigners()`. Otherwise, the latest error is also stored - ## in the `Clique` descriptor - ## - ## If there is an error, this error is also stored within the `Clique` - ## descriptor and can be retrieved via `c.failed` along with the hash/ID of - ## the failed block header. - ## - ## Note that the sequence argument must be write-accessible, even though it - ## will be left untouched by this function. - if 0 < headers.len: - - block: - let blind = HeadersHolderRef() - let rc = c.cliqueVerifyImpl(com, headers[0],blind) - if rc.isErr: - return rc - - for n in 1 ..< headers.len: - let parent = HeadersHolderRef( - headers: headers.headers[n-1 .. n-1] # is actually a single item squence - ) - let rc = c.cliqueVerifyImpl(com, headers[n],parent) - if rc.isErr: - return rc - - # Adjust current shapshot (the function `cliqueVerifyImpl()` typically - # works with the parent snapshot. - block: - let rc = c.cliqueSnapshot(headers[^1]) - if rc.isErr: - return err(rc.error) - - ok() - -# ------------------------------------------------------------------------------ -# Public functions -# ------------------------------------------------------------------------------ - -proc cliqueVerify*(c: Clique; com: CommonRef; header: BlockHeader; - parents: openArray[BlockHeader]): CliqueOkResult - {.gcsafe, raises: [CatchableError].} = - ## Check whether a header conforms to the consensus rules. The caller may - ## optionally pass on a batch of parents (ascending order) to avoid looking - ## those up from the database. This function updates the list of authorised - ## signers (see `cliqueSigners()` below.) - ## - ## On success, the latest authorised signers list is available via the - ## fucntion `c.cliqueSigners()`. Otherwise, the latest error is also stored - ## in the `Clique` descriptor and is accessible as `c.failed`. - ## - ## This function is not transaction-save, that is the internal state of - ## the authorised signers list has the state of the last update after a - ## successful header verification. The hash of the failing header together - ## with the error message is then accessible as `c.failed`. - ## - ## Use the directives `cliqueSave()`, `cliqueDispose()`, and/or - ## `cliqueRestore()` for transaction. - let list = HeadersHolderRef( - headers: toSeq(parents) - ) - c.cliqueVerifySeq(com, header, list) - -# clique/clique.go(217): func (c *Clique) VerifyHeader(chain [..] -proc cliqueVerify*(c: Clique; com: CommonRef; header: BlockHeader): CliqueOkResult - {.gcsafe, raises: [CatchableError].} = - ## Consensus rules verifier without optional parents list. - let blind = HeadersHolderRef() - c.cliqueVerifySeq(com, header, blind) - -proc cliqueVerify*(c: Clique; com: CommonRef; - headers: openArray[BlockHeader]): CliqueOkResult - {.gcsafe, raises: [CatchableError].} = - ## This function verifies a batch of headers checking each header for - ## consensus rules conformance (see also the other `cliqueVerify()` function - ## instance.) The `headers` list is supposed to contain a chain of headers, - ## i.e. `headers[i]` is parent to `headers[i+1]`. - ## - ## On success, the latest authorised signers list is available via the - ## fucntion `c.cliqueSigners()`. Otherwise, the latest error is also stored - ## in the `Clique` descriptor and is accessible as `c.failed`. - ## - ## This function is not transaction-save, that is the internal state of - ## the authorised signers list has the state of the last update after a - ## successful header verification. The hash of the failing header together - ## with the error message is then accessible as `c.failed`. - ## - ## Use the directives `cliqueSave()`, `cliqueDispose()`, and/or - ## `cliqueRestore()` for transaction. - let list = HeadersHolderRef( - headers: toSeq(headers) - ) - c.cliqueVerifySeq(com, list) - -# ------------------------------------------------------------------------------ -# End -# ------------------------------------------------------------------------------ diff --git a/nimbus/core/clique/snapshot/ballot.nim b/nimbus/core/clique/snapshot/ballot.nim deleted file mode 100644 index 5fbee7ddf..000000000 --- a/nimbus/core/clique/snapshot/ballot.nim +++ /dev/null @@ -1,213 +0,0 @@ -# Nimbus -# Copyright (c) 2018 Status Research & Development GmbH -# Licensed under either of -# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or -# http://www.apache.org/licenses/LICENSE-2.0) -# * MIT license ([LICENSE-MIT](LICENSE-MIT) or -# http://opensource.org/licenses/MIT) -# at your option. This file may not be copied, modified, or distributed except -# according to those terms. - -## -## Votes Management for Clique PoA Consensus Protocol -## ================================================= -## -## For details see -## `EIP-225 `_ -## and -## `go-ethereum `_ -## - -import - std/[sequtils, tables], - ../clique_helpers, - eth/common - -type - Vote* = object - ## Vote represent single votes that an authorized signer made to modify - ## the list of authorizations. - signer*: EthAddress ## authorized signer that cast this vote - address*: EthAddress ## account being voted on to change its - ## authorization type (`true` or `false`) - blockNumber*: BlockNumber ## block number the vote was cast in - ## (expire old votes) - authorize*: bool ## authorization type, whether to authorize or - ## deauthorize the voted account - - Tally = object - authorize: bool - signers: Table[EthAddress,Vote] - - Ballot* = object - votes: Table[EthAddress,Tally] ## votes by account -> signer - authSig: Table[EthAddress,bool] ## currently authorised signers - authRemoved: bool ## last `addVote()` action was removing an - ## authorised signer from the `authSig` list - -{.push raises: [].} - -# ------------------------------------------------------------------------------ -# Public debugging/pretty-printer support -# ------------------------------------------------------------------------------ - -proc votesInternal*(t: var Ballot): seq[(EthAddress,EthAddress,Vote)] = - for account,tally in t.votes.pairs: - for signer,vote in tally.signers.pairs: - result.add (account, signer, vote) - -# ------------------------------------------------------------------------------ -# Public constructor -# ------------------------------------------------------------------------------ - -proc initBallot*(t: var Ballot) = - ## Ininialise an empty `Ballot` descriptor. - t.votes = initTable[EthAddress,Tally]() - t.authSig = initTable[EthAddress,bool]() - -proc initBallot*(t: var Ballot; signers: openArray[EthAddress]) = - ## Ininialise `Ballot` with a given authorised signers list - t.initBallot - for a in signers: - t.authSig[a] = true - -# ------------------------------------------------------------------------------ -# Public getters -# ------------------------------------------------------------------------------ - -proc authSigners*(t: var Ballot): seq[EthAddress] = - ## Sorted ascending list of authorised signer addresses - toSeq(t.authSig.keys).sorted(EthAscending) - -proc authSignersLen*(t: var Ballot): int = - ## Returns the number of currently known authorised signers. - t.authSig.len - -proc isAuthSignersListShrunk*(t: var Ballot): bool = - ## Check whether the authorised signers list was shrunk recently after - ## appying `addVote()` - t.authRemoved - -proc authSignersThreshold*(t: var Ballot): int = - ## Returns the minimum number of authorised signers needed for authorising - ## a addres for voting. This is currently - ## :: - ## 1 + half of the number of authorised signers - ## - 1 + (t.authSig.len div 2) - -# ------------------------------------------------------------------------------ -# Public functions -# ------------------------------------------------------------------------------ - -proc isAuthSigner*(t: var Ballot; addresses: var seq[EthAddress]): bool = - ## Check whether all `addresses` entries are authorised signers. - ## - ## Using this function should be preferable over `authSigners()` which has - ## complexity `O(log n)` while this function runs with `O(n)`. - for a in addresses: - if a notin t.authSig: - return false - true - -proc isAuthSigner*(t: var Ballot; address: EthAddress): bool = - ## Check whether `address` is an authorised signer - address in t.authSig - -proc delVote*(t: var Ballot; signer, address: EthAddress) {. - gcsafe, raises: [KeyError].} = - ## Remove a particular previously added vote. - if address in t.votes: - if signer in t.votes[address].signers: - if t.votes[address].signers.len <= 1: - t.votes.del(address) - else: - t.votes[address].signers.del(signer) - - -proc flushVotes*(t: var Ballot) = - ## Reset/flush pending votes, authorised signers remain the same. - t.votes.clear - - -# clique/snapshot.go(141): func (s *Snapshot) validVote(address [..] -proc isValidVote*(t: var Ballot; address: EthAddress; authorize: bool): bool = - ## Check whether voting would have an effect in `addVote()` - if address in t.authSig: not authorize else: authorize - - -proc addVote*(t: var Ballot; vote: Vote) {. - gcsafe, raises: [KeyError].} = - ## Add a new vote collecting the signers for the particular voting address. - ## - ## Unless it is the first vote for this address, the authorisation type - ## `true` or `false` of the vote must match the previous one. For the first - ## vote, the authorisation type `true` is accepted if the address is not an - ## authorised signer, and `false` if it is an authorised signer. Otherwise - ## the vote is ignored. - ## - ## If the number of signers for the particular address are at least - ## `authSignersThreshold()`, the status of this address will change as - ## follows. - ## * If the authorisation type is `true`, the address is added - ## to the list of authorised signers. - ## * If the authorisation type is `false`, the address is removed - ## from the list of authorised signers. - t.authRemoved = false - var - numVotes = 0 - authOk = vote.authorize - - # clique/snapshot.go(147): if !s.validVote(address, [..] - if not t.isValidVote(vote.address, vote.authorize): - - # Corner case: touch votes for this account - if t.votes.hasKey(vote.address): - let refVote = t.votes[vote.address] - numVotes = refVote.signers.len - authOk = refVote.authorize - - elif not t.votes.hasKey(vote.address): - # Collect inital vote - t.votes[vote.address] = Tally( - authorize: vote.authorize, - signers: {vote.signer: vote}.toTable) - numVotes = 1 - - elif t.votes[vote.address].authorize == vote.authorize: - # Collect additional vote - t.votes[vote.address].signers[vote.signer] = vote - numVotes = t.votes[vote.address].signers.len - - else: - return - - # clique/snapshot.go(262): if tally := snap.Tally[header.Coinbase]; [..] - - # Vote passed, update the list of authorised signers if enough votes - if numVotes < t.authSignersThreshold: - return - - var obsolete = @[vote.address] - if authOk: - # Has minimum votes, so add it - t.authSig[vote.address] = true - else: - # clique/snapshot.go(266): delete(snap.Signers, [..] - t.authSig.del(vote.address) - t.authRemoved = true - - # Not a signer anymore => remove it everywhere - for key,value in t.votes.mpairs: - if vote.address in value.signers: - if 1 < value.signers.len: - value.signers.del(vote.address) - else: - obsolete.add key - - for key in obsolete: - t.votes.del(key) - -# ------------------------------------------------------------------------------ -# End -# ------------------------------------------------------------------------------ diff --git a/nimbus/core/clique/snapshot/snapshot_apply.nim b/nimbus/core/clique/snapshot/snapshot_apply.nim deleted file mode 100644 index 4e06e8685..000000000 --- a/nimbus/core/clique/snapshot/snapshot_apply.nim +++ /dev/null @@ -1,182 +0,0 @@ -# Nimbus -# Copyright (c) 2018 Status Research & Development GmbH -# Licensed under either of -# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or -# http://www.apache.org/licenses/LICENSE-2.0) -# * MIT license ([LICENSE-MIT](LICENSE-MIT) or -# http://opensource.org/licenses/MIT) -# at your option. This file may not be copied, modified, or distributed except -# according to those terms. - -## -## Snapshot Processor for Clique PoA Consensus Protocol -## ==================================================== -## -## For details see -## `EIP-225 `_ -## and -## `go-ethereum `_ -## - -import - std/times, - chronicles, - eth/common, - stew/results, - ".."/[clique_cfg, clique_defs], - "."/[ballot, snapshot_desc] - -{.push raises: [].} - -logScope: - topics = "clique PoA snapshot-apply" - -# ------------------------------------------------------------------------------ -# Private functions -# ------------------------------------------------------------------------------ - -template pairWalkIj(first, last: int; offTop: Positive; code: untyped) = - if first <= last: - for n in first .. last - offTop: - let - i {.inject.} = n - j {.inject.} = n + 1 - code - else: - for n in first.countdown(last + offTop): - let - i {.inject.} = n - j {.inject.} = n - 1 - code - -template doWalkIt(first, last: int; code: untyped) = - if first <= last: - for n in first .. last: - let it {.inject.} = n - code - else: - for n in first.countdown(last): - let it {.inject.} = n - code - -# ------------------------------------------------------------------------------ -# Public functions -# ------------------------------------------------------------------------------ - -# clique/snapshot.go(185): func (s *Snapshot) apply(headers [..] -proc snapshotApplySeq*(s: Snapshot; headers: var seq[BlockHeader], - first, last: int): CliqueOkResult - {.gcsafe, raises: [CatchableError].} = - ## Initialises an authorization snapshot `snap` by applying the `headers` - ## to the argument snapshot desciptor `s`. - - # Sanity check that the headers can be applied - if headers[first].blockNumber != s.blockNumber + 1: - return err((errInvalidVotingChain,"")) - # clique/snapshot.go(191): for i := 0; i < len(headers)-1; i++ { - first.pairWalkIj(last, 1): - if headers[j].blockNumber != headers[i].blockNumber+1: - return err((errInvalidVotingChain,"")) - - # Iterate through the headers and create a new snapshot - let - start = getTime() - var - logged = start - - # clique/snapshot.go(206): for i, header := range headers [..] - first.doWalkIt(last): - let - # headersIndex => also used for logging at the end of this loop - headersIndex = it - header = headers[headersIndex] - number = header.blockNumber - - # Remove any votes on checkpoint blocks - if (number mod s.cfg.epoch).isZero: - # Note that the correctness of the authorised accounts list is verified in - # clique/clique.verifyCascadingFields(), - # see clique/clique.go(355): if number%c.config.Epoch == 0 { - # This means, the account list passed with the epoch header is verified - # to be the same as the one we already have. - # - # clique/snapshot.go(210): snap.Votes = nil - s.ballot.flushVotes - - # Delete the oldest signer from the recent list to allow it signing again - block: - let limit = s.ballot.authSignersThreshold.u256 - if limit <= number: - s.recents.del(number - limit) - - # Resolve the authorization key and check against signers - let signer = s.cfg.ecRecover(header) - if signer.isErr: - return err((errEcRecover,$signer.error)) - - if not s.ballot.isAuthSigner(signer.value): - return err((errUnauthorizedSigner,"")) - - for recent in s.recents.values: - if recent == signer.value: - return err((errRecentlySigned,"")) - s.recents[number] = signer.value - - # Header authorized, discard any previous vote from the signer - # clique/snapshot.go(233): for i, vote := range snap.Votes { - s.ballot.delVote(signer = signer.value, address = header.coinbase) - - # Tally up the new vote from the signer - # clique/snapshot.go(244): var authorize bool - var authOk = false - if header.nonce == NONCE_AUTH: - authOk = true - elif header.nonce != NONCE_DROP: - return err((errInvalidVote,"")) - let vote = Vote(address: header.coinbase, - signer: signer.value, - blockNumber: number, - authorize: authOk) - # clique/snapshot.go(253): if snap.cast(header.Coinbase, authorize) { - s.ballot.addVote(vote) - - # clique/snapshot.go(269): if limit := uint64(len(snap.Signers)/2 [..] - if s.ballot.isAuthSignersListShrunk: - # Signer list shrunk, delete any leftover recent caches - let limit = s.ballot.authSignersThreshold.u256 - if limit <= number: - # Pop off least block number from the list - let item = number - limit - s.recents.del(item) - - # If we're taking too much time (ecrecover), notify the user once a while - if s.cfg.logInterval < getTime() - logged: - debug "Reconstructing voting history", - processed = headersIndex, - total = headers.len, - elapsed = getTime() - start - logged = getTime() - - let sinceStart = getTime() - start - if s.cfg.logInterval < sinceStart: - debug "Reconstructed voting history", - processed = headers.len, - elapsed = sinceStart - - # clique/snapshot.go(303): snap.Number += uint64(len(headers)) - doAssert headers[last].blockNumber == s.blockNumber+(1+(last-first).abs).u256 - s.blockNumber = headers[last].blockNumber - s.blockHash = headers[last].blockHash - - ok() - - -proc snapshotApply*(s: Snapshot; headers: var seq[BlockHeader]): CliqueOkResult - {.gcsafe, raises: [CatchableError].} = - if headers.len == 0: - return ok() - s.snapshotApplySeq(headers, 0, headers.len - 1) - -# ------------------------------------------------------------------------------ -# End -# ------------------------------------------------------------------------------ diff --git a/nimbus/core/clique/snapshot/snapshot_desc.nim b/nimbus/core/clique/snapshot/snapshot_desc.nim deleted file mode 100644 index 352e410b8..000000000 --- a/nimbus/core/clique/snapshot/snapshot_desc.nim +++ /dev/null @@ -1,190 +0,0 @@ -# Nimbus -# Copyright (c) 2021-2024 Status Research & Development GmbH -# Licensed under either of -# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or -# http://www.apache.org/licenses/LICENSE-2.0) -# * MIT license ([LICENSE-MIT](LICENSE-MIT) or -# http://opensource.org/licenses/MIT) -# at your option. This file may not be copied, modified, or distributed except -# according to those terms. - -## -## Snapshot Structure for Clique PoA Consensus Protocol -## ==================================================== -## -## For details see -## `EIP-225 `_ -## and -## `go-ethereum `_ -## - -import - std/tables, - chronicles, - eth/rlp, - results, - ../../../db/[core_db, storage_types], - ../clique_cfg, - ../clique_defs, - ../clique_helpers, - ./ballot - -export tables - -type - SnapshotResult* = ##\ - ## Snapshot/error result type - Result[Snapshot,CliqueError] - - AddressHistory* = Table[BlockNumber,EthAddress] - - SnapshotData* = object - blockNumber: BlockNumber ## block number where snapshot was created on - blockHash: Hash256 ## block hash where snapshot was created on - recents: AddressHistory ## recent signers for spam protections - - # clique/snapshot.go(58): Recents map[uint64]common.Address [..] - ballot: Ballot ## Votes => authorised signers - - # clique/snapshot.go(50): type Snapshot struct [..] - Snapshot* = ref object ## Snapshot is the state of the authorization - ## voting at a given point in time. - cfg: CliqueCfg ## parameters to fine tune behavior - data*: SnapshotData ## real snapshot - -{.push raises: [].} - -logScope: - topics = "clique PoA snapshot" - -# ------------------------------------------------------------------------------ -# Private functions needed to support RLP conversion -# ------------------------------------------------------------------------------ - -template logTxt(info: static[string]): static[string] = - "Clique " & info - -proc append[K,V](rw: var RlpWriter; tab: Table[K,V]) = - rw.startList(tab.len) - for key,value in tab.pairs: - rw.append((key,value)) - -proc read[K,V](rlp: var Rlp; Q: type Table[K,V]): Q {.gcsafe, raises: [RlpError].} = - for w in rlp.items: - let (key,value) = w.read((K,V)) - result[key] = value - -# ------------------------------------------------------------------------------ -# Private constructor helper -# ------------------------------------------------------------------------------ - -# clique/snapshot.go(72): func newSnapshot(config [..] -proc initSnapshot(s: Snapshot; cfg: CliqueCfg; - number: BlockNumber; hash: Hash256; signers: openArray[EthAddress]) = - ## Initalise a new snapshot. - s.cfg = cfg - s.data.blockNumber = number - s.data.blockHash = hash - s.data.recents = initTable[BlockNumber,EthAddress]() - s.data.ballot.initBallot(signers) - -# ------------------------------------------------------------------------------ -# Public Constructor -# ------------------------------------------------------------------------------ - -proc newSnapshot*(cfg: CliqueCfg; header: BlockHeader): Snapshot = - ## Create a new snapshot for the given header. The header need not be on the - ## block chain, yet. The trusted signer list is derived from the - ## `extra data` field of the header. - new result - let signers = header.extraData.extraDataAddresses - result.initSnapshot(cfg, header.blockNumber, header.blockHash, signers) - -# ------------------------------------------------------------------------------ -# Public getters -# ------------------------------------------------------------------------------ - -proc cfg*(s: Snapshot): CliqueCfg = - ## Getter - s.cfg - -proc blockNumber*(s: Snapshot): BlockNumber = - ## Getter - s.data.blockNumber - -proc blockHash*(s: Snapshot): Hash256 = - ## Getter - s.data.blockHash - -proc recents*(s: Snapshot): var AddressHistory = - ## Retrieves the list of recently added addresses - s.data.recents - -proc ballot*(s: Snapshot): var Ballot = - ## Retrieves the ballot box descriptor with the votes - s.data.ballot - -# ------------------------------------------------------------------------------ -# Public setters -# ------------------------------------------------------------------------------ - -proc `blockNumber=`*(s: Snapshot; number: BlockNumber) = - ## Getter - s.data.blockNumber = number - -proc `blockHash=`*(s: Snapshot; hash: Hash256) = - ## Getter - s.data.blockHash = hash - -# ------------------------------------------------------------------------------ -# Public load/store support -# ------------------------------------------------------------------------------ - -# clique/snapshot.go(88): func loadSnapshot(config [..] -proc loadSnapshot*(cfg: CliqueCfg; hash: Hash256): - Result[Snapshot,CliqueError] = - ## Load an existing snapshot from the database. - var - s = Snapshot(cfg: cfg) - try: - let rc = s.cfg.db.newKvt().get(hash.cliqueSnapshotKey.toOpenArray) - if rc.isOk: - s.data = rc.value.decode(SnapshotData) - else: - if rc.error.error != KvtNotFound: - error logTxt "get() failed", error=($$rc.error) - return err((errSnapshotLoad,"")) - except RlpError as e: - return err((errSnapshotLoad, $e.name & ": " & e.msg)) - ok(s) - -# clique/snapshot.go(104): func (s *Snapshot) store(db [..] -proc storeSnapshot*(cfg: CliqueCfg; s: Snapshot): CliqueOkResult = - ## Insert the snapshot into the database. - let - key = s.data.blockHash.cliqueSnapshotKey - val = rlp.encode(s.data) - db = s.cfg.db.newKvt(offSite = true) # bypass block chain txs - defer: db.forget() - let rc = db.put(key.toOpenArray, val) - if rc.isErr: - error logTxt "put() failed", `error`=($$rc.error) - db.saveOffSite() - - cfg.nSnaps.inc - cfg.snapsData += val.len.uint - ok() - -# ------------------------------------------------------------------------------ -# Public deep copy -# ------------------------------------------------------------------------------ - -proc cloneSnapshot*(s: Snapshot): Snapshot = - ## Clone the snapshot - Snapshot( - cfg: s.cfg, # copy ref - data: s.data) # copy data - -# ------------------------------------------------------------------------------ -# End -# ------------------------------------------------------------------------------ diff --git a/nimbus/core/executor/process_block.nim b/nimbus/core/executor/process_block.nim index 5e3059848..dbfbefc23 100644 --- a/nimbus/core/executor/process_block.nim +++ b/nimbus/core/executor/process_block.nim @@ -16,7 +16,6 @@ import ../../transaction, ../../vm_state, ../../vm_types, - ../clique, ../dao, ./calculate_reward, ./executor_helpers, diff --git a/nimbus/core/sealer.nim b/nimbus/core/sealer.nim deleted file mode 100644 index bbb572540..000000000 --- a/nimbus/core/sealer.nim +++ /dev/null @@ -1,155 +0,0 @@ -# Nimbus -# Copyright (c) 2018-2024 Status Research & Development GmbH -# Licensed under either of -# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE)) -# * MIT license ([LICENSE-MIT](LICENSE-MIT)) -# at your option. -# This file may not be copied, modified, or distributed except according to -# those terms. - -import - pkg/[chronos, - stew/results, - chronicles, - eth/keys], - ".."/[config, - constants], - "."/[ - chain, - tx_pool, - validate], - "."/clique/[ - clique_desc, - clique_cfg, - clique_sealer], - ../utils/utils, - ../common/[common, context] - -type - EngineState* = enum - EngineStopped, - EngineRunning, - EnginePostMerge - - SealingEngineRef* = ref SealingEngineObj - SealingEngineObj = object of RootObj - state: EngineState - engineLoop: Future[void] - chain*: ChainRef - ctx: EthContext - signer: EthAddress - txPool: TxPoolRef - -proc validateSealer*(conf: NimbusConf, ctx: EthContext, chain: ChainRef): Result[void, string] = - if conf.engineSigner == ZERO_ADDRESS: - return err("signer address should not zero, use --engine-signer to set signer address") - - let res = ctx.am.getAccount(conf.engineSigner) - if res.isErr: - return err("signer address not in registered accounts, use --import-key/account to register the account") - - let acc = res.get() - if not acc.unlocked: - return err("signer account not unlocked, please unlock it first via rpc/password file") - - let com = chain.com - if com.consensus != ConsensusType.POA: - return err("currently only PoA engine is supported") - - ok() - -proc generateBlock(engine: SealingEngineRef, - outBlock: var EthBlock): Result[void, string] = - - let bundle = engine.txPool.assembleBlock().valueOr: - return err(error) - outBlock = bundle.blk - - if engine.chain.com.consensus == ConsensusType.POS: - # Stop the block generator if we reach TTD - engine.state = EnginePostMerge - - if engine.state != EnginePostMerge: - # Post merge, Clique should not be executing - let sealRes = engine.chain.clique.seal(outBlock) - if sealRes.isErr: - return err("error sealing block header: " & $sealRes.error) - - debug "generated block", - blockNumber = outBlock.header.blockNumber, - blockHash = blockHash(outBlock.header) - - ok() - -proc sealingLoop(engine: SealingEngineRef): Future[void] {.async.} = - let clique = engine.chain.clique - - proc signerFunc(signer: EthAddress, message: openArray[byte]): - Result[RawSignature, cstring] {.gcsafe.} = - let - hashData = keccakHash(message) - ctx = engine.ctx - acc = ctx.am.getAccount(signer).tryGet() - rawSign = sign(acc.privateKey, SkMessage(hashData.data)).toRaw - - ok(rawSign) - - clique.authorize(engine.signer, signerFunc) - - # convert times.Duration to chronos.Duration - let period = chronos.seconds(clique.cfg.period.int64) - - while engine.state == EngineRunning: - # the sealing engine will tick every `cliquePeriod` seconds - await sleepAsync(period) - - if engine.state != EngineRunning: - break - - # deviation from 'correct' sealing engine: - # - no queue for chain reorgs - # - no async lock/guard against race with sync algo - var blk: EthBlock - let blkRes = engine.generateBlock(blk) - if blkRes.isErr: - error "sealing engine generateBlock error", msg=blkRes.error - break - - let res = engine.chain.persistBlocks([blk.header], [ - BlockBody(transactions: blk.txs, uncles: blk.uncles) - ]) - - if res == ValidationResult.Error: - error "sealing engine: persistBlocks error" - break - - discard engine.txPool.smartHead(blk.header) # add transactions update jobs - info "block generated", number=blk.header.blockNumber - -proc new*(_: type SealingEngineRef, - chain: ChainRef, - ctx: EthContext, - signer: EthAddress, - txPool: TxPoolRef, - initialState: EngineState): SealingEngineRef = - SealingEngineRef( - chain: chain, - ctx: ctx, - signer: signer, - txPool: txPool, - state: initialState - ) - -proc start*(engine: SealingEngineRef) = - ## Starts sealing engine. - if engine.state == EngineStopped: - engine.state = EngineRunning - engine.engineLoop = sealingLoop(engine) - info "sealing engine started" - -proc stop*(engine: SealingEngineRef) {.async.} = - ## Stop sealing engine from producing more blocks. - if engine.state == EngineRunning: - engine.state = EngineStopped - await engine.engineLoop.cancelAndWait() - info "sealing engine stopped" diff --git a/nimbus/core/tx_pool/tx_chain.nim b/nimbus/core/tx_pool/tx_chain.nim index 0b84c0b3b..46b6c25e2 100644 --- a/nimbus/core/tx_pool/tx_chain.nim +++ b/nimbus/core/tx_pool/tx_chain.nim @@ -20,7 +20,6 @@ import ../../vm_state, ../../vm_types, ../eip4844, - ../clique/[clique_sealer, clique_desc, clique_cfg], ../pow/difficulty, ../executor, ../casper, @@ -84,12 +83,6 @@ proc prepareHeader(dh: TxChainRef; parent: BlockHeader, timestamp: EthTime) dh.prepHeader.timestamp, parent) dh.prepHeader.coinbase = dh.miner dh.prepHeader.mixDigest.reset - of ConsensusType.POA: - discard dh.com.poa.prepare(parent, dh.prepHeader) - # beware POA header.coinbase != signerAddress - # but BaseVMState.minerAddress == signerAddress - # - minerAddress is extracted from header.extraData - # - header.coinbase is from clique engine of ConsensusType.POS: dh.com.pos.prepare(dh.prepHeader) @@ -98,8 +91,6 @@ proc prepareForSeal(dh: TxChainRef; header: var BlockHeader) {.gcsafe, raises: [ of ConsensusType.POW: # do nothing, tx pool was designed with POW in mind discard - of ConsensusType.POA: - dh.com.poa.prepareForSeal(dh.prepHeader, header) of ConsensusType.POS: dh.com.pos.prepareForSeal(header) @@ -107,12 +98,6 @@ proc getTimestamp(dh: TxChainRef, parent: BlockHeader): EthTime = case dh.com.consensus of ConsensusType.POW: EthTime.now() - of ConsensusType.POA: - let timestamp = parent.timestamp + dh.com.poa.cfg.period - if timestamp < EthTime.now(): - EthTime.now() - else: - timestamp of ConsensusType.POS: dh.com.pos.timestamp diff --git a/nimbus/nimbus.nim b/nimbus/nimbus.nim index 388298087..33c1d50c2 100644 --- a/nimbus/nimbus.nim +++ b/nimbus/nimbus.nim @@ -25,8 +25,6 @@ import ./core/eip4844, ./core/block_import, ./db/core_db/persistent, - ./core/clique/clique_desc, - ./core/clique/clique_sealer, ./sync/protocol, ./sync/handlers @@ -49,7 +47,7 @@ proc importBlocks(conf: NimbusConf, com: CommonRef) = proc basicServices(nimbus: NimbusNode, conf: NimbusConf, com: CommonRef) = - nimbus.txPool = TxPoolRef.new(com, conf.engineSigner) + nimbus.txPool = TxPoolRef.new(com, ZERO_ADDRESS) # txPool must be informed of active head # so it can know the latest account state @@ -208,40 +206,6 @@ proc localServices(nimbus: NimbusNode, conf: NimbusConf, nimbus.setupRpc(conf, com, protocols) - if conf.engineSigner != ZERO_ADDRESS and not com.forkGTE(MergeFork): - let res = nimbus.ctx.am.getAccount(conf.engineSigner) - if res.isErr: - error "Failed to get account", - msg = res.error, - hint = "--key-store or --import-key" - quit(QuitFailure) - - let rs = validateSealer(conf, nimbus.ctx, nimbus.chainRef) - if rs.isErr: - fatal "Engine signer validation error", msg = rs.error - quit(QuitFailure) - - proc signFunc(signer: EthAddress, message: openArray[byte]): Result[RawSignature, cstring] {.gcsafe.} = - let - hashData = keccakHash(message) - acc = nimbus.ctx.am.getAccount(signer).tryGet() - rawSign = sign(acc.privateKey, SkMessage(hashData.data)).toRaw - - ok(rawSign) - - nimbus.chainRef.clique.authorize(conf.engineSigner, signFunc) - - # disable sealing engine if beacon engine enabled - if not com.forkGTE(MergeFork): - nimbus.sealingEngine = SealingEngineRef.new( - nimbus.chainRef, nimbus.ctx, conf.engineSigner, - nimbus.txPool, EngineStopped - ) - - # only run sealing engine if there is a signer - if conf.engineSigner != ZERO_ADDRESS: - nimbus.sealingEngine.start() - # metrics server if conf.metricsEnabled: info "Starting metrics HTTP server", address = conf.metricsAddress, port = conf.metricsPort diff --git a/nimbus/nimbus_desc.nim b/nimbus/nimbus_desc.nim index 58734af1e..d06d34543 100644 --- a/nimbus/nimbus_desc.nim +++ b/nimbus/nimbus_desc.nim @@ -12,7 +12,6 @@ import eth/p2p, metrics/chronos_httpserver, ./rpc/rpc_server, - ./core/sealer, ./core/chain, ./core/tx_pool, ./sync/peers, @@ -29,7 +28,6 @@ export p2p, chronos_httpserver, rpc_server, - sealer, chain, tx_pool, peers, @@ -50,7 +48,6 @@ type engineApiServer*: NimbusHttpServerRef ethNode*: EthereumNode state*: NimbusState - sealingEngine*: SealingEngineRef ctx*: EthContext chainRef*: ChainRef txPool*: TxPoolRef @@ -71,8 +68,6 @@ proc stop*(nimbus: NimbusNode, conf: NimbusConf) {.async, gcsafe.} = await nimbus.httpServer.stop() if nimbus.engineApiServer.isNil.not: await nimbus.engineApiServer.stop() - if conf.engineSigner != ZERO_ADDRESS and nimbus.sealingEngine.isNil.not: - await nimbus.sealingEngine.stop() if conf.maxPeers > 0: await nimbus.networkLoop.cancelAndWait() if nimbus.peerManager.isNil.not: diff --git a/nimbus/sync/legacy.nim b/nimbus/sync/legacy.nim index d893434aa..bdcb8895e 100644 --- a/nimbus/sync/legacy.nim +++ b/nimbus/sync/legacy.nim @@ -17,7 +17,7 @@ import eth/p2p/[private/p2p_types, peer_pool], stew/byteutils, "."/[protocol, types], - ../core/[chain, clique/clique_sealer, eip4844, gaslimit, withdrawals], + ../core/[chain, eip4844, gaslimit, withdrawals], ../core/pow/difficulty, ../constants, ../utils/utils, @@ -151,15 +151,6 @@ proc validateDifficulty(ctx: LegacySyncRef, let com = ctx.chain.com case consensusType - of ConsensusType.POA: - let rc = ctx.chain.clique.calcDifficulty(parentHeader) - if rc.isErr: - return false - if header.difficulty < rc.get(): - trace "provided header difficulty is too low", - expect=rc.get(), get=header.difficulty - return false - of ConsensusType.POW: let calcDiffc = com.calcDifficulty(header.timestamp, parentHeader) if header.difficulty < calcDiffc: @@ -212,16 +203,6 @@ proc validateHeader(ctx: LegacySyncRef, header: BlockHeader, if not ctx.validateDifficulty(header, parentHeader, consensusType): return false - if consensusType == ConsensusType.POA: - let period = com.cliquePeriod - # Timestamp diff between blocks is lower than PERIOD (clique) - if parentHeader.timestamp + period > header.timestamp: - trace "invalid timestamp diff (lower than period)", - parent=parentHeader.timestamp, - header=header.timestamp, - period - return false - var res = com.validateGasLimitOrBaseFee(header, parentHeader) if res.isErr: trace "validate gaslimit error", diff --git a/premix/configuration.nim b/premix/configuration.nim index 598424e0a..f46e295ab 100644 --- a/premix/configuration.nim +++ b/premix/configuration.nim @@ -17,7 +17,6 @@ import from ../nimbus/common/chain_config import MainNet, - GoerliNet, SepoliaNet, HoleskyNet @@ -73,10 +72,9 @@ proc processU256(val: string, o: var UInt256): ConfigStatus = o = parse(val, UInt256) result = Success -proc processNetId(val: string, o: var NetworkId): ConfigStatus = +func processNetId(val: string, o: var NetworkId): ConfigStatus = case val.toLowerAscii() of "main": o = MainNet - of "goerli": o = GoerliNet of "sepolia": o = SepoliaNet of "holesky": o = HoleskyNet diff --git a/tests/test_clique.nim b/tests/test_clique.nim deleted file mode 100644 index bd4a53ead..000000000 --- a/tests/test_clique.nim +++ /dev/null @@ -1,360 +0,0 @@ -# Nimbus -# Copyright (c) 2022-2024 Status Research & Development GmbH -# Licensed under either of -# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or -# http://www.apache.org/licenses/LICENSE-2.0) -# * MIT license ([LICENSE-MIT](LICENSE-MIT) or -# http://opensource.org/licenses/MIT) -# at your option. This file may not be copied, modified, or distributed except -# according to those terms. - -import - std/[algorithm, os, sequtils, strformat, strutils, times], - chronicles, - eth/keys, - stint, - unittest2, - ../nimbus/core/[chain, - clique, - clique/clique_snapshot, - clique/clique_desc, - clique/clique_helpers - ], - ../nimbus/common/[common,context], - ../nimbus/utils/[ec_recover, utils], - ../nimbus/[config, constants], - ./test_clique/pool, - ./replay/undump_blocks_gz - -const - baseDir = [".", "tests", ".." / "tests", $DirSep] # path containg repo - repoDir = ["test_clique", "replay", "status"] # alternative repos - - goerliCapture = "goerli68161.txt.gz" - groupReplayTransactions = 7 - -# ------------------------------------------------------------------------------ -# Helpers -# ------------------------------------------------------------------------------ - -proc getBlockHeader(ap: TesterPool; number: BlockNumber): BlockHeader = - ## Shortcut => db/core_db.getBlockHeader() - doAssert ap.db.getBlockHeader(number, result) - -proc ppSecs(elapsed: Duration): string = - result = $elapsed.inSeconds - let ns = elapsed.inNanoseconds mod 1_000_000_000 - if ns != 0: - # to rounded decimal seconds - let ds = (ns + 5_000_000i64) div 10_000_000i64 - result &= &".{ds:02}" - result &= "s" - -proc ppRow(elapsed: Duration): string = - let ms = elapsed.inMilliSeconds + 500 - "x".repeat(ms div 1000) - -proc findFilePath(file: string): string = - result = "?unknown?" / file - for dir in baseDir: - for repo in repoDir: - let path = dir / repo / file - if path.fileExists: - return path - -proc setTraceLevel = - discard - when defined(chronicles_runtime_filtering) and loggingEnabled: - setLogLevel(LogLevel.TRACE) - -proc setErrorLevel = - discard - when defined(chronicles_runtime_filtering) and loggingEnabled: - setLogLevel(LogLevel.ERROR) - -# ------------------------------------------------------------------------------ -# Test Runners -# ------------------------------------------------------------------------------ - -# clique/snapshot_test.go(99): func TestClique(t *testing.T) { -proc runCliqueSnapshot(noisy = true; postProcessOk = false; - testIds = {0'u16 .. 999'u16}; skipIds = {0'u16}-{0'u16}) = - ## Clique PoA Snapshot - ## :: - ## Tests that Clique signer voting is evaluated correctly for various - ## simple and complex scenarios, as well as that a few special corner - ## cases fail correctly. - ## - let postProcessInfo = if postProcessOk: ", Transaction Finaliser Applied" - else: ", Without Finaliser" - suite &"Clique PoA Snapshot{postProcessInfo}": - var pool = newVoterPool() - - setErrorLevel() - if noisy: - pool.noisy = true - setTraceLevel() - - # clique/snapshot_test.go(379): for i, tt := range tests { - for voterSample in voterSamples.filterIt(it.id.uint16 in testIds): - let tt = voterSample - test &"Snapshots {tt.id:2}: {tt.info.substr(0,50)}...": - pool.say "\n" - - # Noisily skip this test - if tt.id.uint16 in skipIds: - skip() - - else: - # Assemble a chain of headers from the cast votes - # see clique/snapshot_test.go(407): config := *params.TestChainConfig - pool - .resetVoterChain(tt.signers, tt.epoch, tt.runBack) - # see clique/snapshot_test.go(425): for j, block := range blocks { - .appendVoter(tt.votes) - .commitVoterChain(postProcessOk) - - # see clique/snapshot_test.go(477): if err != nil { - if tt.failure != cliqueNoError[0]: - # Note that clique/snapshot_test.go does not verify _here_ against - # the scheduled test error -- rather this voting error is supposed - # to happen earlier (processed at clique/snapshot_test.go(467)) when - # assembling the block chain (sounds counter intuitive to the author - # of this source file as the scheduled errors are _clique_ related). - check pool.failed[1][0] == tt.failure - else: - let - expected = tt.results.mapIt("@" & it).sorted - snapResult = pool.pp(pool.cliqueSigners).sorted - pool.say "*** snap state=", pool.pp(pool.snapshot,16) - pool.say " result=[", snapResult.join(",") & "]" - pool.say " expected=[", expected.join(",") & "]" - - # Verify the final list of signers against the expected ones - check snapResult == expected - -proc runGoerliReplay(noisy = true; showElapsed = false, - captureFile = goerliCapture, - startAtBlock = 0u64; stopAfterBlock = 0u64) = - var - pool = newVoterPool() - cache: array[groupReplayTransactions,(seq[BlockHeader],seq[BlockBody])] - cInx = 0 - stoppedOk = false - - let - fileInfo = captureFile.splitFile.name.split(".")[0] - filePath = captureFile.findFilePath - - pool.verifyFrom = startAtBlock - - setErrorLevel() - if noisy: - pool.noisy = true - setTraceLevel() - - let stopThreshold = if stopAfterBlock == 0u64: uint64.high.u256 - else: stopAfterBlock.u256 - - suite &"Replay Goerli chain from {fileInfo} capture": - - for w in filePath.undumpBlocksGz: - - if w[0][0].blockNumber == 0.u256: - # Verify Genesis - doAssert w[0][0] == pool.getBlockHeader(0.u256) - - else: - # Condense in cache - cache[cInx] = w - cInx.inc - - # Handy for partial tests - if stopThreshold < cache[cInx-1][0][0].blockNumber: - stoppedOk = true - break - - # Run from cache if complete set - if cache.len <= cInx: - cInx = 0 - let - first = cache[0][0][0].blockNumber - last = cache[^1][0][^1].blockNumber - blkRange = &"#{first}..#{last}" - info = if first <= startAtBlock.u256 and startAtBlock.u256 <= last: - &", verification #{startAtBlock}.." - else: - "" - test &"Goerli Blocks {blkRange} ({cache.len} transactions{info})": - let start = getTime() - for (headers,bodies) in cache: - let addedPersistBlocks = pool.chain.persistBlocks(headers,bodies) - check addedPersistBlocks == ValidationResult.Ok - if addedPersistBlocks != ValidationResult.Ok: return - if showElapsed and startAtBlock.u256 <= last: - let - elpd = getTime() - start - info = &"{elpd.ppSecs:>7} {pool.cliqueSignersLen} {elpd.ppRow}" - echo &"\n elapsed {blkRange:<17} {info}" - - # Rest from cache - if 0 < cInx: - let - first = cache[0][0][0].blockNumber - last = cache[cInx-1][0][^1].blockNumber - blkRange = &"#{first}..#{last}" - info = if first <= startAtBlock.u256 and startAtBlock.u256 <= last: - &", Verification #{startAtBlock}.." - else: - "" - test &"Goerli Blocks {blkRange} ({cache.len} transactions{info})": - let start = getTime() - for (headers,bodies) in cache: - let addedPersistBlocks = pool.chain.persistBlocks(headers,bodies) - check addedPersistBlocks == ValidationResult.Ok - if addedPersistBlocks != ValidationResult.Ok: return - if showElapsed and startAtBlock.u256 <= last: - let - elpsd = getTime() - start - info = &"{elpsd.ppSecs:>7} {pool.cliqueSignersLen} {elpsd.ppRow}" - echo &"\n elapsed {blkRange:<17} {info}" - - if stoppedOk: - test &"Runner stopped after reaching #{stopThreshold}": - discard - - -proc runGoerliBaybySteps(noisy = true; - captureFile = goerliCapture, - stopAfterBlock = 0u64) = - var - pool = newVoterPool() - stoppedOk = false - - setErrorLevel() - if noisy: - pool.noisy = true - setTraceLevel() - - let - fileInfo = captureFile.splitFile.name.split(".")[0] - filePath = captureFile.findFilePath - stopThreshold = if stopAfterBlock == 0u64: 20.u256 - else: stopAfterBlock.u256 - - suite &"Replay Goerli chain from {fileInfo} capture, single blockwise": - - for w in filePath.undumpBlocksGz: - if stoppedOk: - break - if w[0][0].blockNumber == 0.u256: - # Verify Genesis - doAssert w[0][0] == pool.getBlockHeader(0.u256) - else: - for n in 0 ..< w[0].len: - let - header = w[0][n] - body = w[1][n] - var - parents = w[0][0 ..< n] - test &"Goerli Block #{header.blockNumber} + {parents.len} parents": - check pool.chain.clique.cliqueSnapshot(header,parents).isOk - let addedPersistBlocks = pool.chain.persistBlocks(@[header],@[body]) - check addedPersistBlocks == ValidationResult.Ok - if addedPersistBlocks != ValidationResult.Ok: return - # Handy for partial tests - if stopThreshold <= header.blockNumber: - stoppedOk = true - break - - if stoppedOk: - test &"Runner stopped after reaching #{stopThreshold}": - discard - -proc cliqueMiscTests() = - let - prvKeyFile = "private.key".findFilePath - - suite "clique misc": - test "signer func": - let - engineSigner = "658bdf435d810c91414ec09147daa6db62406379" - privateKey = prvKeyFile - conf = makeConfig(@["--engine-signer:" & engineSigner, "--import-key:" & privateKey]) - ctx = newEthContext() - - check ctx.am.importPrivateKey(string conf.importKey).isOk() - check ctx.am.getAccount(conf.engineSigner).isOk() - - proc signFunc(signer: EthAddress, message: openArray[byte]): Result[RawSignature, cstring] {.gcsafe.} = - let - hashData = keccakHash(message) - acc = ctx.am.getAccount(conf.engineSigner).tryGet() - rawSign = sign(acc.privateKey, SkMessage(hashData.data)).toRaw - - ok(rawSign) - - let signerFn: CliqueSignerFn = signFunc - var header: BlockHeader - header.extraData.setLen(EXTRA_VANITY) - header.extraData.add 0.byte.repeat(EXTRA_SEAL) - - let signature = signerFn(conf.engineSigner, header.encodeSealHeader).get() - let extraLen = header.extraData.len - if EXTRA_SEAL < extraLen: - header.extraData.setLen(extraLen - EXTRA_SEAL) - header.extraData.add signature - - let resAddr = ecRecover(header) - check resAddr.isOk - check resAddr.value == conf.engineSigner - -# ------------------------------------------------------------------------------ -# Main function(s) -# ------------------------------------------------------------------------------ - -proc cliqueMain*(noisy = defined(debug)) = - noisy.runCliqueSnapshot(true) - noisy.runCliqueSnapshot(false) - noisy.runGoerliBaybySteps - noisy.runGoerliReplay(startAtBlock = 31100u64) - cliqueMiscTests() - -when isMainModule: - let - skipIDs = {999} - # A new capture file can be generated using - # `test_clique/indiump.dumpGroupNl()` - # placed at the end of - # `p2p/chain/persist_blocks.persistBlocks()`. - captureFile = goerliCapture - #captureFile = "dump-stream.out.gz" - - proc goerliReplay(noisy = true; - showElapsed = true; - captureFile = captureFile; - startAtBlock = 0u64; - stopAfterBlock = 0u64) = - runGoerliReplay( - noisy = noisy, - showElapsed = showElapsed, - captureFile = captureFile, - startAtBlock = startAtBlock, - stopAfterBlock = stopAfterBlock) - - # local path is: nimbus-eth1/tests - let noisy = defined(debug) - - noisy.runCliqueSnapshot(true) - noisy.runCliqueSnapshot(false) - noisy.runGoerliBaybySteps - false.runGoerliReplay(startAtBlock = 31100u64) - - #noisy.goerliReplay(startAtBlock = 31100u64) - #noisy.goerliReplay(startAtBlock = 194881u64, stopAfterBlock = 198912u64) - - cliqueMiscTests() - -# ------------------------------------------------------------------------------ -# End -# ------------------------------------------------------------------------------ diff --git a/tests/test_clique/pool.nim b/tests/test_clique/pool.nim deleted file mode 100644 index bae8ea778..000000000 --- a/tests/test_clique/pool.nim +++ /dev/null @@ -1,503 +0,0 @@ -# Nimbus -# Copyright (c) 2021-2024 Status Research & Development GmbH -# Licensed under either of -# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or -# http://www.apache.org/licenses/LICENSE-2.0) -# * MIT license ([LICENSE-MIT](LICENSE-MIT) or -# http://opensource.org/licenses/MIT) -# at your option. This file may not be copied, modified, or distributed except -# according to those terms. - -import - std/[algorithm, sequtils, strformat, strutils, tables], - eth/keys, - ethash, - secp256k1/abi, - stew/objects, - ../../nimbus/core/[chain, clique], # must be early (compilation annoyance) - ../../nimbus/common/common, - ../../nimbus/core/clique/[clique_desc, clique_genvote, - clique_helpers, clique_snapshot], - ../../nimbus/core/clique/snapshot/[ballot, snapshot_desc], - ../../nimbus/[config, constants], - ./voter_samples as vs - -export - vs, snapshot_desc - -const - prngSeed = 42 - ## The `TestSpecs` sample depends on this seed, - -type - XSealKey = array[EXTRA_SEAL,byte] - XSealValue = object - blockNumber: uint64 - account: string - - TesterPool* = ref object ## Pool to maintain currently active tester accounts, - ## mapped from textual names used in the tests below - ## to actual Ethereum private keys capable of signing - ## transactions. - prng: uint32 ## random state - accounts: Table[string,PrivateKey] ## accounts table - networkId: NetworkId - boot: NetworkParams ## imported Genesis configuration - batch: seq[seq[BlockHeader]] ## collect header chains - chain: ChainRef - - names: Table[EthAddress,string] ## reverse lookup for debugging - xSeals: Table[XSealKey,XSealValue] ## collect signatures for debugging - noisy*: bool - -# ------------------------------------------------------------------------------ -# Private Prng (Clique keeps generated addresses sorted) -# ------------------------------------------------------------------------------ - -proc posixPrngInit(state: var uint32; seed: uint32) = - state = seed - -proc posixPrngRand(state: var uint32): byte = - ## POSIX.1-2001 example of a rand() implementation, see manual page rand(3). - ## - ## Clique relies on the even/odd position of an address after sorting. For - ## address generation, the Nim PRNG was used which seems to have changed - ## with Nim 1.6.11 (Linux, Windoes only.) - ## - ## The `TestSpecs` sample depends on `prngSeed` and `posixPrngRand()`. - state = state * 1103515245 + 12345; - let val = (state shr 16) and 32767 # mod 2^31 - (val shr 8).byte # Extract second byte - -# ------------------------------------------------------------------------------ -# Private Helpers -# ------------------------------------------------------------------------------ - -proc getBlockHeader(ap: TesterPool; number: BlockNumber): BlockHeader = - ## Shortcut => db/core_db.getBlockHeader() - doAssert ap.chain.clique.db.getBlockHeader(number, result) - -proc getBlockHeader(ap: TesterPool; hash: Hash256): BlockHeader = - ## Shortcut => db/core_db.getBlockHeader() - doAssert ap.chain.clique.db.getBlockHeader(hash, result) - -proc isZero(a: openArray[byte]): bool = - result = true - for w in a: - if w != 0: - return false - -proc rand(ap: TesterPool): byte = - ap.prng.posixPrngRand().byte - -proc newPrivateKey(ap: TesterPool): PrivateKey = - ## Roughly modelled after `random(PrivateKey,getRng()[])` with - ## non-secure but reproducible PRNG - var data{.noinit.}: array[SkRawSecretKeySize,byte] - for n in 0 ..< data.len: - data[n] = ap.rand - # verify generated key, see keys.random(PrivateKey) from eth/keys.nim - var dataPtr0 = cast[ptr byte](unsafeAddr data[0]) - doAssert secp256k1_ec_seckey_verify( - secp256k1_context_no_precomp, dataPtr0) == 1 - # Convert to PrivateKey - PrivateKey.fromRaw(data).value - -proc privateKey(ap: TesterPool; account: string): PrivateKey = - ## Return private key for given tester `account` - if account != "": - if account in ap.accounts: - result = ap.accounts[account] - else: - result = ap.newPrivateKey - ap.accounts[account] = result - let address = result.toPublicKey.toCanonicalAddress - ap.names[address] = account - -# ------------------------------------------------------------------------------ -# Private pretty printer call backs -# ------------------------------------------------------------------------------ - -proc findName(ap: TesterPool; address: EthAddress): string = - ## Find name for a particular address - if address notin ap.names: - ap.names[address] = &"X{ap.names.len+1}" - ap.names[address] - -proc findSignature(ap: TesterPool; sig: openArray[byte]): XSealValue = - ## Find a previusly registered signature - if sig.len == XSealKey.len: - let key = toArray(XSealKey.len,sig) - if key in ap.xSeals: - result = ap.xSeals[key] - -proc pp(ap: TesterPool; v: BlockNonce): string = - ## Pretty print nonce - if v == NONCE_AUTH: - "AUTH" - elif v == NONCE_DROP: - "DROP" - else: - &"0x{v.toHex}" - -proc pp(ap: TesterPool; v: EthAddress): string = - ## Pretty print address - if v.isZero: - result = "@0" - else: - let a = ap.findName(v) - if a == "": - result = &"@{v}" - else: - result = &"@{a}" - -proc pp*(ap: TesterPool; v: openArray[EthAddress]): seq[string] = - ## Pretty print address list - toSeq(v).mapIt(ap.pp(it)) - -proc pp(ap: TesterPool; v: Blob): string = - ## Visualise `extraData` field - - if v.len < EXTRA_VANITY + EXTRA_SEAL or - ((v.len - (EXTRA_VANITY + EXTRA_SEAL)) mod EthAddress.len) != 0: - result = &"0x{v.toHex}[{v.len}]" - else: - var data = v - # - # extra vanity prefix - let vanity = data[0 ..< EXTRA_VANITY] - data = data[EXTRA_VANITY ..< data.len] - result = if vanity.isZero: "0u256+" else: &"{vanity.toHex}+" - # - # list of addresses - if EthAddress.len + EXTRA_SEAL <= data.len: - var glue = "[" - while EthAddress.len + EXTRA_SEAL <= data.len: - let address = toArray(EthAddress.len,data[0 ..< EthAddress.len]) - data = data[EthAddress.len ..< data.len] - result &= &"{glue}{ap.pp(address)}" - glue = "," - result &= "]+" - # - # signature - let val = ap.findSignature(data) - if val.account != "": - result &= &"<#{val.blockNumber},{val.account}>" - elif data.isZero: - result &= &"<0>" - else: - let sig = SkSignature.fromRaw(data) - if sig.isOk: - result &= &"<{sig.value.toHex}>" - else: - result &= &"0x{data.toHex}[{data.len}]" - -proc pp(ap: TesterPool; v: Vote): string = - proc authorized(b: bool): string = - if b: "authorise" else: "de-authorise" - "(" & - &"address={ap.pp(v.address)}" & - &",signer={ap.pp(v.signer)}" & - &",blockNumber=#{v.blockNumber}" & - &",{authorized(v.authorize)}" & ")" - -proc pp(ap: TesterPool; h: AddressHistory): string = - toSeq(h.keys) - .sorted - .mapIt("#" & $it & ":" & ap.pp(h[it.u256])) - .join(",") - -proc votesList(ap: TesterPool; s: Snapshot; sep: string): string = - proc s3Cmp(a, b: (string,string,Vote)): int = - result = cmp(a[0], b[0]) - if result == 0: - result = cmp(a[1], b[1]) - let votes = s.ballot.votesInternal - votes.mapIt((ap.pp(it[0]),ap.pp(it[1]),it[2])) - .sorted(cmp = s3Cmp) - .mapIt(ap.pp(it[2])) - .join(sep) - -proc signersList(ap: TesterPool; s: Snapshot): string = - ap.pp(s.ballot.authSigners).sorted.join(",") - -proc pp*(ap: TesterPool; s: Snapshot; delim: string): string = - ## Pretty print descriptor - let - p1 = if 0 < delim.len: delim else: ";" - p2 = if 0 < delim.len and delim[0] == '\n': delim & ' '.repeat(7) else: ";" - "(" & - &"blockNumber=#{s.blockNumber}" & - &"{p1}recents=" & "{" & ap.pp(s.recents) & "}" & - &"{p1}signers=" & "{" & ap.signersList(s) & "}" & - &"{p1}votes=[" & ap.votesList(s,p2) & "])" - -proc pp*(ap: TesterPool; s: Snapshot; indent = 0): string = - ## Pretty print descriptor - let delim = if 0 < indent: "\n" & ' '.repeat(indent) else: " " - ap.pp(s, delim) - -proc pp(ap: TesterPool; v: BlockHeader; delim: string): string = - ## Pretty print block header - let sep = if 0 < delim.len: delim else: ";" - &"(blockNumber=#{v.blockNumber}" & - &"{sep}parentHash={v.parentHash}" & - &"{sep}selfHash={v.blockHash}" & - &"{sep}stateRoot={v.stateRoot}" & - &"{sep}coinbase={ap.pp(v.coinbase)}" & - &"{sep}nonce={ap.pp(v.nonce)}" & - &"{sep}extraData={ap.pp(v.extraData)})" - -proc pp(ap: TesterPool; v: BlockHeader; indent = 3): string = - ## Pretty print block header, NL delimited, indented fields - let delim = if 0 < indent: "\n" & ' '.repeat(indent) else: " " - ap.pp(v, delim) - -# ------------------------------------------------------------------------------ -# Private: Constructor helpers -# ------------------------------------------------------------------------------ - -proc resetChainDb(ap: TesterPool; extraData: Blob; debug = false) = - ## Setup new block chain with bespoke genesis - # new genesis block - if 0 < extraData.len: - ap.boot.genesis.extraData = extraData - - let com = CommonRef.new( - newCoreDbRef DefaultDbMemory, - networkId = ap.networkId, - params = ap.boot) - ap.chain = newChain(com) - com.initializeEmptyDb() - ap.noisy = debug - -proc initTesterPool(ap: TesterPool): TesterPool {.discardable.} = - result = ap - result.prng.posixPrngInit(prngSeed) - result.batch = @[newSeq[BlockHeader]()] - result.accounts = initTable[string,PrivateKey]() - result.xSeals = initTable[XSealKey,XSealValue]() - result.names = initTable[EthAddress,string]() - result.resetChainDb(@[]) - -# ------------------------------------------------------------------------------ -# Public: pretty printer support -# ------------------------------------------------------------------------------ - -proc say*(t: TesterPool; v: varargs[string,`$`]) = - if t.noisy: - stderr.write v.join & "\n" - -proc sayHeaderChain*(ap: TesterPool; indent = 0): TesterPool {.discardable.} = - result = ap - let pfx = ' '.repeat(indent) - var top = if 0 < ap.batch[^1].len: ap.batch[^1][^1] - else: ap.getBlockHeader(0.u256) - ap.say pfx, " top header: " & ap.pp(top, 16+indent) - while not top.blockNumber.isZero: - top = ap.getBlockHeader(top.parentHash) - ap.say pfx, "parent header: " & ap.pp(top, 16+indent) - -# ------------------------------------------------------------------------------ -# Public: Constructor -# ------------------------------------------------------------------------------ - -proc newVoterPool*(networkId = GoerliNet): TesterPool = - TesterPool( - networkId: networkId, - boot: networkParams(networkId) - ).initTesterPool - -# ------------------------------------------------------------------------------ -# Public: getter -# ------------------------------------------------------------------------------ - -proc chain*(ap: TesterPool): ChainRef = - ## Getter - ap.chain - -proc clique*(ap: TesterPool): Clique = - ## Getter - ap.chain.clique - -proc db*(ap: TesterPool): CoreDbRef = - ## Getter - ap.clique.db - -proc cliqueSigners*(ap: TesterPool): seq[EthAddress] = - ## Getter - ap.clique.cliqueSigners - -proc cliqueSignersLen*(ap: TesterPool): int = - ## Getter - ap.clique.cliqueSignersLen - -proc snapshot*(ap: TesterPool): Snapshot = - ## Getter - ap.clique.snapshot - -proc failed*(ap: TesterPool): CliqueFailed = - ## Getter - ap.clique.failed - -# ------------------------------------------------------------------------------ -# Public: setter -# ------------------------------------------------------------------------------ - -proc `verifyFrom=`*(ap: TesterPool; verifyFrom: uint64) = - ## Setter, block number where `Clique` should start - ap.chain.verifyFrom = verifyFrom - -# ------------------------------------------------------------------------------ -# Public functions -# ------------------------------------------------------------------------------ - -# clique/snapshot_test.go(62): func (ap *testerAccountPool) address(account [..] -proc address*(ap: TesterPool; account: string): EthAddress = - ## retrieves the Ethereum address of a tester account by label, creating - ## a new account if no previous one exists yet. - if account != "": - result = ap.privateKey(account).toPublicKey.toCanonicalAddress - -# ------------------------------------------------------------------------------ -# Public: set up & manage voter database -# ------------------------------------------------------------------------------ - -proc resetVoterChain*(ap: TesterPool; signers: openArray[string]; - epoch = 0; runBack = true): TesterPool {.discardable.} = - ## Reset the batch list for voter headers and update genesis block - result = ap - - ap.batch = @[newSeq[BlockHeader]()] - - # clique/snapshot_test.go(384): signers := make([]common.Address, [..] - let signers = signers.mapIt(ap.address(it)).sorted(EthAscending) - - var extraData = 0.byte.repeat(EXTRA_VANITY) - - # clique/snapshot_test.go(399): for j, signer := range signers { - for signer in signers: - extraData.add signer.toSeq - - # clique/snapshot_test.go(397): - extraData.add 0.byte.repeat(EXTRA_SEAL) - - # store modified genesis block and epoch - ap.resetChainDb(extraData, ap.noisy) - ap.clique.cfg.epoch = epoch - ap.clique.applySnapsMinBacklog = runBack - - -# clique/snapshot_test.go(415): blocks, _ := core.GenerateChain(&config, [..] -proc appendVoter*(ap: TesterPool; - voter: TesterVote): TesterPool {.discardable.} = - ## Append a voter header to the block chain batch list - result = ap - - doAssert 0 < ap.batch.len # see initTesterPool() and resetVoterChain() - let parent = if ap.batch[^1].len == 0: - ap.getBlockHeader(0.u256) - else: - ap.batch[^1][^1] - - let header = ap.chain.clique.cliqueGenvote( - voter = ap.address(voter.voted), - seal = ap.privateKey(voter.signer), - parent = parent, - elapsed = EthTime(100), - voteInOk = voter.auth, - outOfTurn = voter.noTurn, - checkPoint = voter.checkpoint.mapIt(ap.address(it)).sorted(EthAscending)) - - if 0 < voter.checkpoint.len: - doAssert (header.blockNumber mod ap.clique.cfg.epoch).isZero - - # Register for debugging - let - extraLen = header.extraData.len - extraSeal = header.extraData[extraLen - EXTRA_SEAL ..< extraLen] - ap.xSeals[toArray(XSealKey.len,extraSeal)] = XSealValue( - blockNumber: header.blockNumber.truncate(uint64), - account: voter.signer) - - if voter.newbatch: - ap.batch.add @[] - ap.batch[^1].add header - - -proc appendVoter*(ap: TesterPool; - voters: openArray[TesterVote]): TesterPool {.discardable.} = - ## Append a list of voter headers to the block chain batch list - result = ap - for voter in voters: - ap.appendVoter(voter) - - -proc commitVoterChain*(ap: TesterPool; postProcessOk = false; - stopFaultyHeader = false): TesterPool {.discardable.} = - ## Write the headers from the voter header batch list to the block chain DB. - ## - ## If `postProcessOk` is set, an additional verification step is added at - ## the end of each transaction. - ## - ## if `stopFaultyHeader` is set, the function stops immediately on error. - ## Otherwise the offending block is removed, the rest of the batch is - ## adjusted and applied again repeatedly. - result = ap - - var reChainOk = false - for n in 0 ..< ap.batch.len: - block forLoop: - - var headers = ap.batch[n] - while true: - if headers.len == 0: - break forLoop # continue with for loop - - ap.say &"*** transaction ({n}) list: [", - headers.mapIt(&"#{it.blockNumber}").join(", "), "]" - - # Realign rest of transaction to existing block chain - if reChainOk: - var parent = ap.chain.clique.db.getCanonicalHead - for i in 0 ..< headers.len: - headers[i].parentHash = parent.blockHash - headers[i].blockNumber = parent.blockNumber + 1 - parent = headers[i] - - # Perform transaction into the block chain - let bodies = BlockBody().repeat(headers.len) - if ap.chain.persistBlocks(headers,bodies) == ValidationResult.OK: - break - if stopFaultyHeader: - return - - # If the offending block is the last one of the last transaction, - # then there is nothing to do. - let culprit = headers.filterIt(ap.failed[0] == it.blockHash) - doAssert culprit.len == 1 - let number = culprit[0].blockNumber - if n + 1 == ap.batch.len and number == headers[^1].blockNumber: - return - - # Remove offending block and try again for the rest - ap.say "*** persistBlocks failed, omitting block #", culprit - let prevLen = headers.len - headers = headers.filterIt(number != it.blockNumber) - doAssert headers.len < prevLen - reChainOk = true - - if ap.noisy: - ap.say "*** snapshot argument: #", headers[^1].blockNumber - ap.sayHeaderChain(8) - when false: # all addresses are typically pp-mappable - ap.say " address map: ", toSeq(ap.names.pairs) - .mapIt(&"@{it[1]}:{it[0]}") - .sorted - .join("\n" & ' '.repeat(23)) - if postProcessOk: - discard ap.clique.cliqueSnapshot(headers[^1]) - -# ------------------------------------------------------------------------------ -# End -# ------------------------------------------------------------------------------ diff --git a/tests/test_clique/private.key b/tests/test_clique/private.key deleted file mode 100644 index 7f43fa8aa..000000000 --- a/tests/test_clique/private.key +++ /dev/null @@ -1 +0,0 @@ -9c647b8b7c4e7c3490668fb6c11473619db80c93704c70893d3813af4090c39c \ No newline at end of file diff --git a/tests/test_clique/voter_samples.nim b/tests/test_clique/voter_samples.nim deleted file mode 100644 index 218928137..000000000 --- a/tests/test_clique/voter_samples.nim +++ /dev/null @@ -1,421 +0,0 @@ -# Nimbus -# Copyright (c) 2021-2024 Status Research & Development GmbH -# Licensed under either of -# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or -# http://www.apache.org/licenses/LICENSE-2.0) -# * MIT license ([LICENSE-MIT](LICENSE-MIT) or -# http://opensource.org/licenses/MIT) -# at your option. This file may not be copied, modified, or distributed except -# according to those terms. - -# Test cases from https://github.com/ethereum/EIPs/blob/master/EIPS/eip-225.md - -import - ../../nimbus/core/clique/clique_defs - -type - TesterVote* = object ## VoterBlock represents a single block signed by a - ## particular account, where the account may or may not - ## have cast a Clique vote. - signer*: string ##\ - ## Account that signed this particular block - - voted*: string ##\ - ## Optional value if the signer voted on adding/removing ## someone - - auth*: bool ##\ - ## Whether the vote was to authorize (or deauthorize) - - checkpoint*: seq[string] ##\ - ## List of authorized signers if this is an epoch block - - noTurn*: bool ##\ - ## Initialise `NOTURN` if `true`, otherwise use `INTURN`. This is not - ## part of Go ref test implementation. The flag used here to avoid what - ## is implemented as `fakeDiff` kludge in the Go ref test implementation. - ## - ## Note that the `noTurn` value depends on the sort order of the - ## calculated authorised signers account address list. These account - ## addresses in turn (no pun intended) depend on the private keys of - ## these accounts. Now, the private keys are generated on-the-fly by a - ## PRNG which re-seeded the same for each test. So the sort order is - ## predictable and the correct value of the `noTurn` flag can be set - ## by sort of experimenting with the tests (and/or refering to earlier - ## woking test specs.) - - newbatch*: bool - - - TestSpecs* = object ## Defining genesis and the various voting scenarios - ## to test (see `votes`.) - id*: int ##\ - ## Test id - - info*: string ##\ - ## Test description - - epoch*: int ##\ - ## Number of blocks in an epoch (unset = 30000) - - runBack*: bool ##\ - ## Set `applySnapsMinBacklog` flag - - signers*: seq[string] ##\ - ## Initial list of authorized signers in the genesis - - votes*: seq[TesterVote] ##\ - ## Chain of signed blocks, potentially influencing auths - - results*: seq[string] ##\ - ## Final list of authorized signers after all blocks - - failure*: CliqueErrorType ##\ - ## Failure if some block is invalid according to the rules - -const - # Define the various voting scenarios to test - voterSamples* = [ - # clique/snapshot_test.go(108): { - TestSpecs( - id: 1, - info: "Single signer, no votes cast", - signers: @["A"], - votes: @[TesterVote(signer: "A")], - results: @["A"]), - - TestSpecs( - id: 2, - info: "Single signer, voting to add two others (only accept first, "& - "second needs 2 votes)", - signers: @["A"], - votes: @[TesterVote(signer: "A", voted: "B", auth: true), - TesterVote(signer: "B"), - TesterVote(signer: "A", voted: "C", auth: true)], - results: @["A", "B"]), - - TestSpecs( - id: 3, - info: "Two signers, voting to add three others (only accept first " & - "two, third needs 3 votes already)", - signers: @["A", "B"], - votes: @[TesterVote(signer: "A", voted: "C", auth: true), - TesterVote(signer: "B", voted: "C", auth: true), - TesterVote(signer: "A", voted: "D", auth: true, noTurn: true), - TesterVote(signer: "B", voted: "D", auth: true), - TesterVote(signer: "C", noTurn: true), - TesterVote(signer: "A", voted: "E", auth: true, noTurn: true), - TesterVote(signer: "B", voted: "E", auth: true, noTurn: true)], - results: @["A", "B", "C", "D"]), - - TestSpecs( - id: 4, - info: "Single signer, dropping itself (weird, but one less " & - "cornercase by explicitly allowing this)", - signers: @["A"], - votes: @[TesterVote(signer: "A", voted: "A")]), - - TestSpecs( - id: 5, - info: "Two signers, actually needing mutual consent to drop either " & - "of them (not fulfilled)", - signers: @["A", "B"], - votes: @[TesterVote(signer: "A", voted: "B")], - results: @["A", "B"]), - - TestSpecs( - id: 6, - info: "Two signers, actually needing mutual consent to drop either " & - "of them (fulfilled)", - signers: @["A", "B"], - votes: @[TesterVote(signer: "A", voted: "B"), - TesterVote(signer: "B", voted: "B")], - results: @["A"]), - - TestSpecs( - id: 7, - info: "Three signers, two of them deciding to drop the third", - signers: @["A", "B", "C"], - votes: @[TesterVote(signer: "A", voted: "C", noTurn: true), - TesterVote(signer: "B", voted: "C", noTurn: true)], - results: @["A", "B"]), - - TestSpecs( - id: 8, - info: "Four signers, consensus of two not being enough to drop anyone", - signers: @["A", "B", "C", "D"], - votes: @[TesterVote(signer: "A", voted: "C", noTurn: true), - TesterVote(signer: "B", voted: "C", noTurn: true)], - results: @["A", "B", "C", "D"]), - - TestSpecs( - id: 9, - info: "Four signers, consensus of three already being enough to " & - "drop someone", - signers: @["A", "B", "C", "D"], - votes: @[TesterVote(signer: "A", voted: "D", noTurn: true), - TesterVote(signer: "B", voted: "D", noTurn: true), - TesterVote(signer: "C", voted: "D", noTurn: true)], - results: @["A", "B", "C"]), - - TestSpecs( - id: 10, - info: "Authorizations are counted once per signer per target", - signers: @["A", "B"], - votes: @[TesterVote(signer: "A", voted: "C", auth: true), - TesterVote(signer: "B"), - TesterVote(signer: "A", voted: "C", auth: true), - TesterVote(signer: "B"), - TesterVote(signer: "A", voted: "C", auth: true)], - results: @["A", "B"]), - - TestSpecs( - id: 11, - info: "Authorizing multiple accounts concurrently is permitted", - signers: @["A", "B"], - votes: @[TesterVote(signer: "A", voted: "C", auth: true), - TesterVote(signer: "B"), - TesterVote(signer: "A", voted: "D", auth: true), - TesterVote(signer: "B"), - TesterVote(signer: "A"), - TesterVote(signer: "B", voted: "D", auth: true), - TesterVote(signer: "A", noTurn: true), - TesterVote(signer: "B", voted: "C", auth: true, noTurn: true)], - results: @["A", "B", "C", "D"]), - - TestSpecs( - id: 12, - info: "Deauthorizations are counted once per signer per target", - signers: @["A", "B"], - votes: @[TesterVote(signer: "A", voted: "B"), - TesterVote(signer: "B"), - TesterVote(signer: "A", voted: "B"), - TesterVote(signer: "B"), - TesterVote(signer: "A", voted: "B")], - results: @["A", "B"]), - - TestSpecs( - id: 13, - info: "Deauthorizing multiple accounts concurrently is permitted", - signers: @["A", "B", "C", "D"], - votes: @[TesterVote(signer: "A", voted: "C", noTurn: true), - TesterVote(signer: "B", noTurn: true), - TesterVote(signer: "C", noTurn: true), - TesterVote(signer: "A", voted: "D", noTurn: true), - TesterVote(signer: "B"), - TesterVote(signer: "C", noTurn: true), - TesterVote(signer: "A"), - TesterVote(signer: "B", voted: "D", noTurn: true), - TesterVote(signer: "C", voted: "D", noTurn: true), - TesterVote(signer: "A", noTurn: true), - TesterVote(signer: "B", voted: "C", noTurn: true)], - results: @["A", "B"]), - - TestSpecs( - id: 14, - info: "Votes from deauthorized signers are discarded immediately " & - "(deauth votes)", - signers: @["A", "B", "C"], - votes: @[TesterVote(signer: "C", voted: "B", noTurn: true), - TesterVote(signer: "A", voted: "C"), - TesterVote(signer: "B", voted: "C", noTurn: true), - TesterVote(signer: "A", voted: "B", noTurn: true)], - results: @["A", "B"]), - - TestSpecs( - id: 15, - info: "Votes from deauthorized signers are discarded immediately " & - "(auth votes)", - signers: @["A", "B", "C"], - votes: @[TesterVote(signer: "C", voted: "D", auth: true, noTurn: true), - TesterVote(signer: "A", voted: "C"), - TesterVote(signer: "B", voted: "C", noTurn: true), - TesterVote(signer: "A", voted: "D", auth: true, noTurn: true)], - results: @["A", "B"]), - - TestSpecs( - id: 16, - info: "Cascading changes are not allowed, only the account being " & - "voted on may change", - signers: @["A", "B", "C", "D"], - votes: @[TesterVote(signer: "A", voted: "C", noTurn: true), - TesterVote(signer: "B", noTurn: true), - TesterVote(signer: "C", noTurn: true), - TesterVote(signer: "A", voted: "D", noTurn: true), - TesterVote(signer: "B", voted: "C"), - TesterVote(signer: "C", noTurn: true), - TesterVote(signer: "A"), - TesterVote(signer: "B", voted: "D", noTurn: true), - TesterVote(signer: "C", voted: "D", noTurn: true)], - results: @["A", "B", "C"]), - - TestSpecs( - id: 17, - info: "Changes reaching consensus out of bounds (via a deauth) " & - "execute on touch", - signers: @["A", "B", "C", "D"], - votes: @[TesterVote(signer: "A", voted: "C", noTurn: true), - TesterVote(signer: "B", noTurn: true), - TesterVote(signer: "C", noTurn: true), - TesterVote(signer: "A", voted: "D", noTurn: true), - TesterVote(signer: "B", voted: "C"), - TesterVote(signer: "C", noTurn: true), - TesterVote(signer: "A"), - TesterVote(signer: "B", voted: "D", noTurn: true), - TesterVote(signer: "C", voted: "D", noTurn: true), - TesterVote(signer: "A", noTurn: true), - TesterVote(signer: "C", voted: "C", auth: true, noTurn: true)], - results: @["A", "B"]), - - TestSpecs( - id: 18, - info: "Changes reaching consensus out of bounds (via a deauth) " & - "may go out of consensus on first touch", - signers: @["A", "B", "C", "D"], - votes: @[TesterVote(signer: "A", voted: "C", noTurn: true), - TesterVote(signer: "B", noTurn: true), - TesterVote(signer: "C", noTurn: true), - TesterVote(signer: "A", voted: "D", noTurn: true), - TesterVote(signer: "B", voted: "C"), - TesterVote(signer: "C", noTurn: true), - TesterVote(signer: "A"), - TesterVote(signer: "B", voted: "D", noTurn: true), - TesterVote(signer: "C", voted: "D", noTurn: true), - TesterVote(signer: "A", noTurn: true), - TesterVote(signer: "B", voted: "C", auth: true, noTurn: true)], - results: @["A", "B", "C"]), - - TestSpecs( - id: 19, - info: "Ensure that pending votes don't survive authorization status " & - "changes. This corner case can only appear if a signer is " & - "quickly added, removed and then readded (or the inverse), " & - "while one of the original voters dropped. If a past vote is " & - "left cached in the system somewhere, this will interfere " & - "with the final signer outcome.", - signers: @["A", "B", "C", "D", "E"], - votes: @[ - # Authorize F, 3 votes needed - TesterVote(signer: "A", voted: "F", auth: true, noTurn: true), - TesterVote(signer: "B", voted: "F", auth: true), - TesterVote(signer: "C", voted: "F", auth: true, noTurn: true), - - # Deauthorize F, 4 votes needed (leave A's previous vote "unchanged") - TesterVote(signer: "D", voted: "F", noTurn: true), - TesterVote(signer: "E", voted: "F", noTurn: true), - TesterVote(signer: "B", voted: "F", noTurn: true), - TesterVote(signer: "C", voted: "F"), - - # Almost authorize F, 2/3 votes needed - TesterVote(signer: "D", voted: "F", auth: true), - TesterVote(signer: "E", voted: "F", auth: true, noTurn: true), - - # Deauthorize A, 3 votes needed - TesterVote(signer: "B", voted: "A", noTurn: true), - TesterVote(signer: "C", voted: "A"), - TesterVote(signer: "D", voted: "A", noTurn: true), - - # Finish authorizing F, 3/3 votes needed - TesterVote(signer: "B", voted: "F", auth: true, noTurn: true)], - results: @["B", "C", "D", "E", "F"]), - - TestSpecs( - id: 20, - info: "Epoch transitions reset all votes to allow chain checkpointing", - epoch: 3, - signers: @["A", "B"], - votes: @[TesterVote(signer: "A", voted: "C", auth: true), - TesterVote(signer: "B"), - TesterVote(signer: "A", checkpoint: @["A", "B"]), - TesterVote(signer: "B", voted: "C", auth: true)], - results: @["A", "B"]), - - TestSpecs( - id: 21, - info: "An unauthorized signer should not be able to sign blocks", - signers: @["A"], - votes: @[TesterVote(signer: "B", noTurn: true)], - failure: errUnauthorizedSigner), - - TestSpecs( - id: 22, - info: "An authorized signer that signed recenty should not be able " & - "to sign again", - signers: @["A", "B"], - votes: @[TesterVote(signer: "A"), - TesterVote(signer: "A")], - failure: errRecentlySigned), - - TestSpecs( - id: 23, - info: "Recent signatures should not reset on checkpoint blocks " & - "imported in a batch ", - epoch: 3, - signers: @["A", "B", "C"], - votes: @[TesterVote(signer: "A", noTurn: true), - TesterVote(signer: "B", noTurn: true), - TesterVote(signer: "A", checkpoint: @["A", "B", "C"], - noTurn: true), - TesterVote(signer: "A", noTurn: true)], - - # Setting the `runBack` flag forces the shapshot handler searching for - # a checkpoint before entry 3. So the checkpont will be ignored for - # re-setting the system so that address `A` of block #3 is found in the - # list of recent signers (see documentation of the flag - # `applySnapsMinBacklog` for the `Clique` descriptor.) - # - # As far as I understand, there was no awareness of the tranaction batch - # in the Go implementation -- jordan. - runBack: true, - failure: errRecentlySigned), - - # The last test does not differ from the previous one with the current - # test environment. - TestSpecs( - id: 24, - info: "Recent signatures (revisted) should not reset on checkpoint " & - "blocks imported in a batch " & - "(https://github.com/ethereum/go-ethereum/issues/17593). "& - "Whilst this seems overly specific and weird, it was a "& - "Rinkeby consensus split.", - epoch: 3, - signers: @["A", "B", "C"], - votes: @[TesterVote(signer: "A", noTurn: true), - TesterVote(signer: "B", noTurn: true), - TesterVote(signer: "A", checkpoint: @["A", "B", "C"], - noTurn: true), - TesterVote(signer: "A", newbatch: true, noTurn: true)], - - # Setting the `runBack` flag forces the shapshot handler searching for - # a checkpoint before entry 3. So the checkpont will be ignored for - # re-setting the system so that address `A` of block #3 is found in the - # list of recent signers (see documentation of the flag - # `applySnapsMinBacklog` for the `Clique` descriptor.) - # - # As far as I understand, there was no awareness of the tranaction batch - # in the Go implementation -- jordan. - runBack: true, - failure: errRecentlySigned), - - # Not found in Go reference implementation - TestSpecs( - id: 25, - info: "Test 23/24 with using the most recent checkpoint", - epoch: 3, - signers: @["A", "B", "C"], - votes: @[TesterVote(signer: "A", noTurn: true), - TesterVote(signer: "B", noTurn: true), - TesterVote(signer: "A", checkpoint: @["A", "B", "C"], - noTurn: true), - TesterVote(signer: "A", noTurn: true)], - results: @["A", "B", "C"])] - -static: - # For convenience, make sure that IDs are increasing - for n in 1 ..< voterSamples.len: - if voterSamples[n-1].id < voterSamples[n].id: - continue - echo "voterSamples[", n, "] == ", voterSamples[n].id, " expected ", - voterSamples[n-1].id + 1, " or greater" - doAssert voterSamples[n-1].id < voterSamples[n].id - -# End diff --git a/tests/test_configuration.nim b/tests/test_configuration.nim index 8f04739d0..9a7b0abac 100644 --- a/tests/test_configuration.nim +++ b/tests/test_configuration.nim @@ -86,12 +86,12 @@ proc configurationMain*() = let conf = makeConfig(@["--custom-network:" & genesisFile]) check conf.networkId == 123.NetworkId - test "network-id not set, goerli set": - let conf = makeConfig(@["--network:goerli"]) - check conf.networkId == GoerliNet + test "network-id not set, sepolia set": + let conf = makeConfig(@["--network:sepolia"]) + check conf.networkId == SepoliaNet - test "network-id set, goerli set": - let conf = makeConfig(@["--network:goerli", "--network:123"]) + test "network-id set, sepolia set": + let conf = makeConfig(@["--network:sepolia", "--network:123"]) check conf.networkId == 123.NetworkId test "rpc-api": diff --git a/tests/test_coredb/coredb_test_xx.nim b/tests/test_coredb/coredb_test_xx.nim index 11eaf26db..442aec5e1 100644 --- a/tests/test_coredb/coredb_test_xx.nim +++ b/tests/test_coredb/coredb_test_xx.nim @@ -81,21 +81,6 @@ let # will run over all avail files in parent folder files: @["00000.era1"]) # on external repo - # Goerli will be abondoned in future - goerliSample = CaptureSpecs( - builtIn: true, - name: "goerli", - network: GoerliNet, - files: @["goerli68161.txt.gz"]) # on local replay folder - - goerliSampleEx = CaptureSpecs( - builtIn: true, - name: "goerli", - network: GoerliNet, - files: @[ - "goerli482304.txt.gz", # on nimbus-eth1-blobs/replay - "goerli482305-504192.txt.gz"]) - # ------------------ # Supposed to run mostly on defaults, object name tag: m=memory, r=rocksDB @@ -142,35 +127,12 @@ let dbType = AristoDbRocks) - # Goerli will be abondoned in future - goerliTest0m* = goerliSample - .cloneWith( - name = "-am-some", - numBlocks = 1_000) - - goerliTest1m* = goerliSample - .cloneWith( - name = "-am", - numBlocks = high(int)) - - goerliTest2m* = goerliSampleEx - .cloneWith( - name = "-ex-am", - numBlocks = high(int)) - - goerliTest3r* = goerliSampleEx - .cloneWith( - name = "-ex-ar", - numBlocks = high(int), - dbType = AristoDbRocks) - # ------------------ allSamples* = [ mainTest0m, mainTest1m, mainTest2r, mainTest3r, mainTest4r, - mainTest5m, mainTest6r, - goerliTest0m, goerliTest1m, goerliTest2m, goerliTest3r + mainTest5m, mainTest6r ] # End diff --git a/tests/test_forkid.nim b/tests/test_forkid.nim index f54020c83..56e9eabd0 100644 --- a/tests/test_forkid.nim +++ b/tests/test_forkid.nim @@ -141,7 +141,6 @@ template runGenesisTimeIdTests() = proc forkIdMain*() = suite "Fork ID tests": runTest(MainNet, "MainNet") - runTest(GoerliNet, "GoerliNet") runTest(SepoliaNet, "SepoliaNet") runTest(HoleskyNet, "HoleskyNet") test "Genesis Time Fork ID": diff --git a/tests/test_genesis.nim b/tests/test_genesis.nim index be173880b..285597745 100644 --- a/tests/test_genesis.nim +++ b/tests/test_genesis.nim @@ -36,10 +36,6 @@ proc genesisTest() = let b = makeGenesis(MainNet) check(b.blockHash == "D4E56740F876AEF8C010B86A40D5F56745A118D0906A34E69AEC8C0DB1CB8FA3".toDigest) - test "Correct goerlinet hash": - let b = makeGenesis(GoerliNet) - check(b.blockHash == "bf7e331f7f7c1dd2e05159666b3bf8bc7a8a3a9eb1d518969eab529dd9b88c1a".toDigest) - test "Correct sepolia hash": let b = makeGenesis(SepoliaNet) check b.blockHash == "25a5cc106eea7138acab33231d7160d69cb777ee0c2c553fcddf5138993e6dd9".toDigest @@ -60,18 +56,6 @@ proc customGenesisTest() = check cgb.config.consensusType == ConsensusType.POW check cgc.config.consensusType == ConsensusType.POW - test "calaveras.json": - var cg: NetworkParams - check loadNetworkParams("calaveras.json".findFilePath, cg) - let com = CommonRef.new(newCoreDbRef DefaultDbMemory, params = cg) - let stateRoot = "664c93de37eb4a72953ea42b8c046cdb64c9f0b0bca5505ade8d970d49ebdb8c".toDigest - let genesisHash = "eb9233d066c275efcdfed8037f4fc082770176aefdbcb7691c71da412a5670f2".toDigest - check com.genesisHeader.stateRoot == stateRoot - check com.genesisHeader.blockHash == genesisHash - check com.consensus == ConsensusType.POA - check com.cliquePeriod == 30 - check com.cliqueEpoch == 30000 - test "Devnet4.json (aka Kintsugi in all but chainId)": var cg: NetworkParams check loadNetworkParams("devnet4.json".findFilePath, cg)