diff --git a/nimbus/p2p/clique/recent_snaps.nim b/nimbus/p2p/clique/recent_snaps.nim index 29eb73de5..46cd2ac6a 100644 --- a/nimbus/p2p/clique/recent_snaps.nim +++ b/nimbus/p2p/clique/recent_snaps.nim @@ -230,7 +230,7 @@ proc setDebug*(rs: var RecentSnaps; debug: bool) = proc getRecentSnaps*(rs: var RecentSnaps; args: RecentArgs): auto {. gcsafe, raises: [Defect,CatchableError].} = ## Get snapshot from cache or disk - rs.say "getRecentSnap #", args.blockNumber.truncate(uint64) + rs.say "getRecentSnap #", args.blockNumber rs.cache.getLruItem: RecentDesc(cfg: rs.cfg, debug: rs.debug, diff --git a/nimbus/p2p/clique/snapshot.nim b/nimbus/p2p/clique/snapshot.nim index 55ce82309..2f7642994 100644 --- a/nimbus/p2p/clique/snapshot.nim +++ b/nimbus/p2p/clique/snapshot.nim @@ -74,7 +74,6 @@ proc getPrettyPrinters(s: var Snapshot): var PrettyPrinters = proc pp(s: var Snapshot; h: var AddressHistory): string = ppExceptionWrap: toSeq(h.keys) - .mapIt(it.truncate(uint64)) .sorted .mapIt("#" & $it & ":" & s.pp(h[it.u256])) .join(",") @@ -85,7 +84,7 @@ proc pp(s: var Snapshot; v: Vote): string = ppExceptionWrap: "(" & &"address={s.pp(v.address)}" & &",signer={s.pp(v.signer)}" & - &",blockNumber={v.blockNumber.truncate(uint64)}" & + &",blockNumber={v.blockNumber}" & &",{authorized(v.authorize)}" & ")" proc votesList(s: var Snapshot; sep: string): string = @@ -234,7 +233,7 @@ proc applySnapshot*(s: var Snapshot; header = headers[headersIndex] number = header.blockNumber - s.say "applySnapshot processing #", number.truncate(uint64) + s.say "applySnapshot processing #", number # Remove any votes on checkpoint blocks if (number mod s.cfg.epoch) == 0: diff --git a/tests/test_clique.nim b/tests/test_clique.nim index 390334b42..7553bede7 100644 --- a/tests/test_clique.nim +++ b/tests/test_clique.nim @@ -17,6 +17,7 @@ import stint, unittest2 + # clique/snapshot_test.go(99): func TestClique(t *testing.T) { proc cliqueMain*(noisy = defined(debug)) = ## Clique PoA Snapshot @@ -27,12 +28,11 @@ proc cliqueMain*(noisy = defined(debug)) = ## suite "Clique PoA Snapshot": var - pool = newTesterPool() + pool = newVoterPool().setDebug(noisy) + const skipSet = {999} testSet = {0 .. 999} - pool.setDebug(noisy) - # clique/snapshot_test.go(379): for i, tt := range tests { for tt in voterSamples.filterIt(it.id in testSet): @@ -45,12 +45,11 @@ proc cliqueMain*(noisy = defined(debug)) = 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) - - # see clique/snapshot_test.go(425): for j, block := range blocks { - for voter in tt.votes: - pool.appendVoter(voter) - pool.commitVoterChain + pool + .resetVoterChain(tt.signers, tt.epoch) + # see clique/snapshot_test.go(425): for j, block := range blocks { + .appendVoter(tt.votes) + .commitVoterChain # see clique/snapshot_test.go(476): snap, err := engine.snapshot( [..] let topHeader = pool.topVoterHeader diff --git a/tests/test_clique/pool.nim b/tests/test_clique/pool.nim index ffd78b809..9a54db890 100644 --- a/tests/test_clique/pool.nim +++ b/tests/test_clique/pool.nim @@ -51,19 +51,18 @@ type # Private Helpers # ------------------------------------------------------------------------------ -proc bespokeGenesis(file: string): CustomGenesis = - ## Find genesis block - if file == "": - let networkId = getConfiguration().net.networkId - result.genesis = defaultGenesisBlockForNetwork(networkId) - else: - doAssert file.loadCustomGenesis(result) - result.config.poaEngine = true - proc chain(ap: TesterPool): BaseChainDB = ## Getter ap.engine.cfgInternal.dbChain +proc getBlockHeader(ap: TesterPool; number: BlockNumber): BlockHeader = + ## Shortcut => db/db_chain.getBlockHeader() + doAssert ap.chain.getBlockHeader(number, result) + +proc getBlockHeader(ap: TesterPool; hash: Hash256): BlockHeader = + ## Shortcut => db/db_chain.getBlockHeader() + doAssert ap.chain.getBlockHeader(hash, result) + proc isZero(a: openArray[byte]): bool = result = true for w in a: @@ -189,12 +188,32 @@ proc ppBlockHeader(ap: TesterPool; v: BlockHeader; delim: string): string = &"{sep}nonce={ap.ppNonce(v.nonce)}" & &"{sep}extraData={ap.ppExtraData(v.extraData)})" +# ------------------------------------------------------------------------------ +# Private: Constructor helpers +# ------------------------------------------------------------------------------ + proc initPrettyPrinters(pp: var PrettyPrinters; ap: TesterPool) = pp.nonce = proc(v:BlockNonce): string = ap.ppNonce(v) pp.address = proc(v:EthAddress): string = ap.ppAddress(v) pp.extraData = proc(v:Blob): string = ap.ppExtraData(v) pp.blockHeader = proc(v:BlockHeader; d:string): string = ap.ppBlockHeader(v,d) +proc initTesterPool(ap: TesterPool): TesterPool {.discardable.} = + result = ap + result.boot.config.poaEngine = true + result.prng = initRand(prngSeed) + result.batch = @[newSeq[BlockHeader]()] + result.accounts = initTable[string,PrivateKey]() + result.xSeals = initTable[XSealKey,XSealValue]() + result.names = initTable[EthAddress,string]() + result.engine = newCliqueCfg( + dbChain = BaseChainDB(), + period = initDuration(seconds = 1)) + .initClique + result.engine.setDebug(false) + result.engine.cfgInternal.prettyPrint.initPrettyPrinters(result) + result.resetChainDb(@[]) + # ------------------------------------------------------------------------------ # Public functions # ------------------------------------------------------------------------------ @@ -203,33 +222,26 @@ proc getPrettyPrinters*(t: TesterPool): var PrettyPrinters = ## Mixin for pretty printers, see `clique/clique_cfg.pp()` t.engine.cfgInternal.prettyPrint +proc setDebug*(ap: TesterPool; debug=true): TesterPool {.inline,discardable,} = + ## Set debugging mode on/off + result = ap + ap.debug = debug + ap.engine.setDebug(debug) + proc say*(t: TesterPool; v: varargs[string,`$`]) = if t.debug: stderr.write v.join & "\n" -proc newTesterPool*(epoch = 0.u256; genesisTemplate = ""): TesterPool = - new result - result.boot = genesisTemplate.bespokeGenesis - result.prng = initRand(prngSeed) - result.batch = @[newSeq[BlockHeader]()] - result.accounts = initTable[string,PrivateKey]() - result.xSeals = initTable[XSealKey,XSealValue]() - result.names = initTable[EthAddress,string]() - result.engine = newCliqueCfg( - dbChain = BaseChainDB(), - period = initDuration(seconds = 1), - epoch = epoch) - .initClique - result.engine.setDebug(false) - result.engine.cfgInternal.prettyPrint.initPrettyPrinters(result) - result.resetChainDb(@[]) - - -proc setDebug*(ap: TesterPool; debug = true) = - ## Set debugging mode on/off - ap.debug = debug - ap.engine.setDebug(debug) +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) # clique/snapshot_test.go(62): func (ap *testerAccountPool) address(account [..] @@ -277,13 +289,9 @@ proc snapshot*(ap: TesterPool; number: BlockNumber; hash: Hash256; parent: openArray[BlockHeader]): auto = ## Call p2p/clique.snapshotInternal() if ap.debug: - var header = ap.chain.getBlockHeader(number) - ap.say "*** snapshot argument: ", ap.pp(header,24) - while true: - doAssert ap.chain.getBlockHeader(header.parentHash,header) - ap.say " parent header: ", ap.pp(header,24) - if header.blockNumber.isZero: - break + var header = ap.getBlockHeader(number) + ap.say "*** snapshot argument: #", number + ap.sayHeaderChain(8) when false: # all addresses are typically pp-mappable ap.say " address map: ", toSeq(ap.names.pairs) .mapIt(&"@{it[1]}:{it[0]}") @@ -292,13 +300,41 @@ proc snapshot*(ap: TesterPool; number: BlockNumber; hash: Hash256; ap.engine.snapshotInternal(number, hash, parent) +# ------------------------------------------------------------------------------ +# Public: Constructor +# ------------------------------------------------------------------------------ + +proc newVoterPool*(genesisTemplate = ""): TesterPool = + new result + if genesisTemplate == "": + let networkId = getConfiguration().net.networkId + result.boot.genesis = defaultGenesisBlockForNetwork(networkId) + else: + # Find genesis block + doAssert genesisTemplate.loadCustomGenesis(result.boot) + result.initTesterPool + +proc newVoterPool*(customGenesis: CustomGenesis): TesterPool = + TesterPool(boot: customGenesis).initTesterPool + # ------------------------------------------------------------------------------ # Public: set up & manage voter database # ------------------------------------------------------------------------------ -proc resetVoterChain*(ap: TesterPool; - signers: openArray[string]; epoch: uint64) = +proc setVoterAccount*(ap: TesterPool; account: string; + prvKey: PrivateKey): TesterPool {.discardable.} = + ## Manually define/import account + result = ap + ap.accounts[account] = prvKey + let address = prvKey.toPublicKey.toCanonicalAddress + ap.names[address] = account + + +proc resetVoterChain*(ap: TesterPool; signers: openArray[string]; + epoch = 0): 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, [..] @@ -315,15 +351,18 @@ proc resetVoterChain*(ap: TesterPool; # store modified genesis block and epoch ap.resetChainDb(extraData) - ap.engine.cfgInternal.epoch = epoch + ap.engine.cfgInternal.epoch = epoch.uint # clique/snapshot_test.go(415): blocks, _ := core.GenerateChain(&config, [..] -proc appendVoter*(ap: TesterPool; voter: TesterVote) = +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.chain.getBlockHeader(0.u256) + ap.getBlockHeader(0.u256) else: ap.batch[^1][^1] @@ -361,8 +400,18 @@ proc appendVoter*(ap: TesterPool; voter: TesterVote) = ap.batch[^1].add header -proc commitVoterChain*(ap: TesterPool) = +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): TesterPool {.discardable.} = ## Write the headers from the voter header batch list to the block chain DB + result = ap + # Create a pristine blockchain with the genesis injected for headers in ap.batch: if 0 < headers.len: diff --git a/tests/test_clique/voter_samples.nim b/tests/test_clique/voter_samples.nim index 1f6ccf9e0..ff3e20b37 100644 --- a/tests/test_clique/voter_samples.nim +++ b/tests/test_clique/voter_samples.nim @@ -29,7 +29,7 @@ type TestSpecs* = object ## Define the various voting scenarios to test id*: int ## Test id info*: string ## Test description - epoch*: uint64 ## Number of blocks in an epoch (unset = 30000) + epoch*: int ## Number of blocks in an epoch (unset = 30000) signers*: seq[string] ## Initial list of authorized signers in the ## genesis votes*: seq[TesterVote] ## Chain of signed blocks, potentially influencing