diff --git a/nimbus/db/storage_types.nim b/nimbus/db/storage_types.nim index 86922b776..89582c6cf 100644 --- a/nimbus/db/storage_types.nim +++ b/nimbus/db/storage_types.nim @@ -15,19 +15,12 @@ type terminalHash safeHash finalizedHash - snapSyncStatus - snapSyncAccount - snapSyncProof DbKey* = object # The first byte stores the key type. The rest are key-specific values data*: array[33, byte] dataEndPos*: uint8 # the last populated position in the data - DbXKey* = object - data*: array[65, byte] - dataEndPos*: uint8 # the last populated position in the data - proc genericHashKey*(h: Hash256): DbKey {.inline.} = result.data[0] = byte ord(genericHash) result.data[1 .. 32] = h.data @@ -86,28 +79,9 @@ proc finalizedHashKey*(): DbKey {.inline.} = result.data[0] = byte ord(finalizedHash) result.dataEndPos = uint8 1 -proc snapSyncStatusKey*(h: Hash256): DbKey = - result.data[0] = byte ord(snapSyncStatus) - result.data[1 .. 32] = h.data - result.dataEndPos = uint8 32 - -proc snapSyncAccountKey*(h, b: Hash256): DbXKey = - result.data[0] = byte ord(snapSyncAccount) - result.data[1 .. 32] = h.data - result.data[33 .. 64] = b.data - result.dataEndPos = uint8 64 - -proc snapSyncProofKey*(h: Hash256): DbKey = - result.data[0] = byte ord(snapSyncProof) - result.data[1 .. 32] = h.data - result.dataEndPos = uint8 32 - -template toOpenArray*(k: DbKey|DbXKey): openArray[byte] = +template toOpenArray*(k: DbKey): openArray[byte] = k.data.toOpenArray(0, int(k.dataEndPos)) -proc hash*(k: DbKey|DbXKey): Hash = - result = hash(k.toOpenArray) - proc `==`*(a, b: DbKey): bool {.inline.} = a.toOpenArray == b.toOpenArray diff --git a/nimbus/nimbus.nim b/nimbus/nimbus.nim index 2ecb245f8..9765b5995 100644 --- a/nimbus/nimbus.nim +++ b/nimbus/nimbus.nim @@ -148,7 +148,8 @@ proc setupP2P(nimbus: NimbusNode, conf: NimbusConf, of SyncMode.Full: FullSyncRef.init(nimbus.ethNode, conf.maxPeers, tickerOK).start of SyncMode.Snap: - SnapSyncRef.init(nimbus.ethNode, conf.maxPeers).start + SnapSyncRef.init(nimbus.ethNode, nimbus.chainRef, nimbus.ctx.rng, + conf.maxPeers, tickerOK).start of SyncMode.Default: discard diff --git a/nimbus/sync/full.nim b/nimbus/sync/full.nim index c6a0e88ca..ce82a6a46 100644 --- a/nimbus/sync/full.nim +++ b/nimbus/sync/full.nim @@ -1,5 +1,4 @@ -# Nimbus - New sync approach - A fusion of snap, trie, beam and other methods -# +# Nimbus # Copyright (c) 2021 Status Research & Development GmbH # Licensed under either of # * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or @@ -10,13 +9,11 @@ # except according to those terms. import - std/hashes, + eth/[common/eth_types, p2p], chronicles, chronos, - eth/[common/eth_types, p2p, p2p/peer_pool, p2p/private/p2p_types], - stew/keyed_queue, - ./protocol, - ./full/[full_desc, worker] + stew/[interval_set, sorted_set], + "."/[full/worker, sync_desc, sync_sched, protocol] {.push raises: [Defect].} @@ -24,153 +21,32 @@ logScope: topics = "full-sync" type - ActiveBuddies = ##\ - ## List of active workers - KeyedQueue[Peer,BuddyRef] - - FullSyncRef* = ref object of CtxRef - pool: PeerPool ## for starting the system - buddies: ActiveBuddies ## LRU cache with worker descriptors - tickerOk: bool ## Ticker logger - singleRunLock: bool ## For worker initialisation - monitorLock: bool ## For worker monitor - activeMulti: int ## Activated runners + FullSyncRef* = RunnerSyncRef[CtxData,BuddyData] # ------------------------------------------------------------------------------ -# Private helpers +# Virtual methods/interface, `mixin` functions # ------------------------------------------------------------------------------ -proc nsCtx(buddy: BuddyRef): FullSyncRef = - buddy.ctx.FullSyncRef +proc runSetup(ctx: FullCtxRef; ticker: bool): bool = + worker.setup(ctx,ticker) -proc hash(peer: Peer): Hash = - ## Needed for `buddies` table key comparison - hash(peer.remote.id) +proc runRelease(ctx: FullCtxRef) = + worker.release(ctx) -# ------------------------------------------------------------------------------ -# Private functions -# ------------------------------------------------------------------------------ +proc runStart(buddy: FullBuddyRef): bool = + worker.start(buddy) -proc workerLoop(buddy: BuddyRef) {.async.} = - let - ctx = buddy.nsCtx - peer = buddy.peer - trace "Starting peer worker", peer, - peers=ctx.pool.len, workers=ctx.buddies.len, maxWorkers=ctx.buddiesMax +proc runStop(buddy: FullBuddyRef) = + worker.stop(buddy) - # Continue until stopped - while not buddy.ctrl.stopped: - if ctx.monitorLock: - await sleepAsync(500.milliseconds) - continue +proc runPool(buddy: FullBuddyRef) = + worker.runPool(buddy) - # Rotate connection table so the most used entry is at the top/right - # end. So zombies will implicitely be pushed left. - discard ctx.buddies.lruFetch(peer) +proc runSingle(buddy: FullBuddyRef) {.async.} = + await worker.runSingle(buddy) - # Invoke `runPool()` over all buddies if requested - if ctx.poolMode: - # Grab `monitorLock` (was `false` as checked above) and wait until clear - # to run as the only activated instance. - ctx.monitorLock = true - while 0 < ctx.activeMulti: - await sleepAsync(500.milliseconds) - while ctx.singleRunLock: - await sleepAsync(500.milliseconds) - trace "Starting pool mode for repair & recovery" - for w in ctx.buddies.nextValues: - buddy.runPool() - trace "Pool mode done" - ctx.monitorLock = false - continue - - await sleepAsync(50.milliseconds) - - # Multi mode - if buddy.ctrl.multiOk: - if not ctx.singleRunLock: - ctx.activeMulti.inc - # Continue doing something, work a bit - await buddy.runMulti() - ctx.activeMulti.dec - continue - - # Single mode as requested. The `multiOk` flag for this worker was just - # found `false` in the pervious clause. - if not ctx.singleRunLock: - # Lock single instance mode and wait for other workers to finish - ctx.singleRunLock = true - while 0 < ctx.activeMulti: - await sleepAsync(500.milliseconds) - # Run single instance and release afterwards - await buddy.runSingle() - ctx.singleRunLock = false - - # End while - - buddy.stop() - - trace "Peer worker done", peer, ctrlState=buddy.ctrl.state, - peers=ctx.pool.len, workers=ctx.buddies.len, maxWorkers=ctx.buddiesMax - - -proc onPeerConnected(ctx: FullSyncRef; peer: Peer) = - # Check for known entry (which should not exist.) - if ctx.buddies.hasKey(peer): - trace "Reconnecting zombie peer rejected", peer, - peers=ctx.pool.len, workers=ctx.buddies.len, maxWorkers=ctx.buddiesMax - return - - # Initialise worker for this peer - let buddy = BuddyRef(ctx: ctx, peer: peer) - if not buddy.start(): - trace "Ignoring useless peer", peer, - peers=ctx.pool.len, workers=ctx.buddies.len, maxWorkers=ctx.buddiesMax - buddy.ctrl.zombie = true - return - - # Check for table overflow. An overflow might happen if there are zombies - # in the table (though preventing them from re-connecting for a while.) - if ctx.buddiesMax <= ctx.buddies.len: - let leastPeer = ctx.buddies.shift.value.data - if leastPeer.ctrl.zombie: - trace "Dequeuing zombie peer", leastPeer, - peers=ctx.pool.len, workers=ctx.buddies.len, maxWorkers=ctx.buddiesMax - discard - else: - # This could happen if there are idle entries in the table, i.e. - # somehow hanging runners. - trace "Peer table full! Dequeuing least used entry", leastPeer, - peers=ctx.pool.len, workers=ctx.buddies.len, maxWorkers=ctx.buddiesMax - leastPeer.stop() - leastPeer.ctrl.zombie = true - - # Add peer entry - discard ctx.buddies.lruAppend(peer, buddy, ctx.buddiesMax) - - # Run worker - asyncSpawn buddy.workerLoop() - - -proc onPeerDisconnected(ctx: FullSyncRef, peer: Peer) = - let - peers = ctx.pool.len - maxWorkers = ctx.buddiesMax - rc = ctx.buddies.eq(peer) - if rc.isErr: - debug "Disconnected from unregistered peer", peer, peers, - workers=ctx.buddies.len, maxWorkers - return - if rc.value.ctrl.zombie: - # Don't disconnect, leave them fall out of the LRU cache. The effect is, - # that reconnecting might be blocked, for a while. - trace "Disconnected zombie", peer, peers, - workers=ctx.buddies.len, maxWorkers - else: - rc.value.ctrl.stopped = true # in case it is hanging somewhere - ctx.buddies.del(peer) - trace "Disconnected buddy", peer, peers, - workers=ctx.buddies.len, maxWorkers +proc runMulti(buddy: FullBuddyRef) {.async.} = + await worker.runMulti(buddy) # ------------------------------------------------------------------------------ # Public functions @@ -180,38 +56,15 @@ proc init*( T: type FullSyncRef; ethNode: EthereumNode; maxPeers: int; - enableTicker: bool): T = - ## Constructor - # Leave one extra slot so that it can holds a *zombie* even if all slots - # are full. The effect is that a re-connect on the latest zombie will be - # rejected as long as its worker descriptor is registered. - let lruSize = max(1,maxPeers+1) - result = T( - buddiesMax: lruSize, - chain: ethNode.chain, - pool: ethNode.peerPool, - tickerOk: enableTicker) - result.buddies.init(lruSize) + enableTicker = false): T = + new result + result.initSync(ethNode, maxPeers, enableTicker) proc start*(ctx: FullSyncRef) = - ## Set up syncing. This call should come early. - var po = PeerObserver( - onPeerConnected: - proc(p: Peer) {.gcsafe.} = - ctx.onPeerConnected(p), - onPeerDisconnected: - proc(p: Peer) {.gcsafe.} = - ctx.onPeerDisconnected(p)) - - # Initialise sub-systems - doAssert ctx.workerSetup(ctx.tickerOk) - po.setProtocol eth - ctx.pool.addObserver(ctx, po) + doAssert ctx.startSync() proc stop*(ctx: FullSyncRef) = - ## Stop syncing - ctx.pool.delObserver(ctx) - ctx.workerRelease() + ctx.stopSync() # ------------------------------------------------------------------------------ # End diff --git a/nimbus/sync/full/ticker.nim b/nimbus/sync/full/ticker.nim index c0f72f509..dda71b044 100644 --- a/nimbus/sync/full/ticker.nim +++ b/nimbus/sync/full/ticker.nim @@ -1,5 +1,4 @@ -# Nimbus - Fetch account and storage states from peers efficiently -# +# Nimbus # Copyright (c) 2021 Status Research & Development GmbH # Licensed under either of # * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or @@ -15,7 +14,7 @@ import eth/[common/eth_types, p2p], stint, ../../utils/prettify, - ".."/[timer_helper, types] + ../timer_helper {.push raises: [Defect].} @@ -33,7 +32,7 @@ type TickerStatsUpdater* = proc: TickerStats {.gcsafe, raises: [Defect].} - Ticker* = ref object + TickerRef* = ref object nBuddies: int lastStats: TickerStats lastTick: uint64 @@ -56,9 +55,9 @@ proc pp(n: BlockNumber): string = proc pp(n: Option[BlockNumber]): string = if n.isNone: "n/a" else: n.get.pp -proc setLogTicker(t: Ticker; at: Moment) {.gcsafe.} +proc setLogTicker(t: TickerRef; at: Moment) {.gcsafe.} -proc runLogTicker(t: Ticker) {.gcsafe.} = +proc runLogTicker(t: TickerRef) {.gcsafe.} = let data = t.statsCb() if data != t.lastStats or @@ -83,7 +82,7 @@ proc runLogTicker(t: Ticker) {.gcsafe.} = t.setLogTicker(Moment.fromNow(tickerLogInterval)) -proc setLogTicker(t: Ticker; at: Moment) = +proc setLogTicker(t: TickerRef; at: Moment) = if not t.logTicker.isNil: t.logTicker = safeSetTimer(at, runLogTicker, t) @@ -91,16 +90,16 @@ proc setLogTicker(t: Ticker; at: Moment) = # Public constructor and start/stop functions # ------------------------------------------------------------------------------ -proc init*(T: type Ticker; cb: TickerStatsUpdater): T = +proc init*(T: type TickerRef; cb: TickerStatsUpdater): T = ## Constructor T(statsCb: cb) -proc start*(t: Ticker) = +proc start*(t: TickerRef) = ## Re/start ticker unconditionally #debug "Started ticker" t.logTicker = safeSetTimer(Moment.fromNow(tickerStartDelay), runLogTicker, t) -proc stop*(t: Ticker) = +proc stop*(t: TickerRef) = ## Stop ticker unconditionally t.logTicker = nil #debug "Stopped ticker" @@ -109,7 +108,7 @@ proc stop*(t: Ticker) = # Public functions # ------------------------------------------------------------------------------ -proc startBuddy*(t: Ticker) = +proc startBuddy*(t: TickerRef) = ## Increment buddies counter and start ticker unless running. if t.nBuddies <= 0: t.nBuddies = 1 @@ -117,7 +116,7 @@ proc startBuddy*(t: Ticker) = else: t.nBuddies.inc -proc stopBuddy*(t: Ticker) = +proc stopBuddy*(t: TickerRef) = ## Decrement buddies counter and stop ticker if there are no more registered ## buddies. t.nBuddies.dec diff --git a/nimbus/sync/full/worker.nim b/nimbus/sync/full/worker.nim index ef94c9723..42dddb640 100644 --- a/nimbus/sync/full/worker.nim +++ b/nimbus/sync/full/worker.nim @@ -1,4 +1,4 @@ -# nim-eth +# Nimbus # Copyright (c) 2018-2021 Status Research & Development GmbH # Licensed and distributed under either of # * MIT license (license terms in the root directory or at @@ -62,9 +62,9 @@ import chronos, eth/[common/eth_types, p2p], stew/[byteutils, interval_set, sorted_set], - ../../utils, - ../protocol, - "."/[full_desc, ticker] + "../.."/[db/db_chain, utils], + ".."/[protocol, sync_desc], + ./ticker {.push raises:[Defect].} @@ -85,63 +85,64 @@ const ## staged. 50 -static: - doAssert stagedWorkItemsTrigger < maxStagedWorkItems - type - BlockRangeSetRef* = ##\ + BlockRangeSetRef = ##\ ## Disjunct sets of block number intervals IntervalSetRef[BlockNumber,UInt256] - BlockRange* = ##\ + BlockRange = ##\ ## Block number interval Interval[BlockNumber,UInt256] - WorkItemQueue* = ##\ + WorkItemQueue = ##\ ## Block intervals sorted by least block number SortedSet[BlockNumber,WorkItemRef] - WorkItemWalkRef* = ##\ + WorkItemWalkRef = ##\ ## Fast traversal descriptor for `WorkItemQueue` SortedSetWalkRef[BlockNumber,WorkItemRef] - WorkItemRef* = ref object + WorkItemRef = ref object ## Block worker item wrapper for downloading a block range - blocks: BlockRange ## Block numbers to fetch - topHash: Option[Hash256] ## Fetch by top hash rather than blocks - headers: seq[BlockHeader] ## Block headers received - hashes: seq[Hash256] ## Hashed from `headers[]` for convenience - bodies: seq[BlockBody] ## Block bodies received + blocks: BlockRange ## Block numbers to fetch + topHash: Option[Hash256] ## Fetch by top hash rather than blocks + headers: seq[BlockHeader] ## Block headers received + hashes: seq[Hash256] ## Hashed from `headers[]` for convenience + bodies: seq[BlockBody] ## Block bodies received - BuddyDataEx = ref object of BuddyDataRef + BuddyData* = object ## Local descriptor data extension - bestNumber: Option[BlockNumber] ## Largest block number reported + bestNumber: Option[BlockNumber] ## Largest block number reported - CtxDataEx = ref object of CtxDataRef + CtxData* = object ## Globally shared data extension - backtrack: Option[Hash256] ## Find reverse block after re-org - unprocessed: BlockRangeSetRef ## Block ranges to fetch - staged: WorkItemQueue ## Blocks fetched but not stored yet - untrusted: seq[Peer] ## Clean up list - trusted: HashSet[Peer] ## Peers ready for delivery - topPersistent: BlockNumber ## Up to this block number stored OK - ticker: Ticker ## Logger ticker + backtrack: Option[Hash256] ## Find reverse block after re-org + unprocessed: BlockRangeSetRef ## Block ranges to fetch + staged: WorkItemQueue ## Blocks fetched but not stored yet + untrusted: seq[Peer] ## Clean up list + trusted: HashSet[Peer] ## Peers ready for delivery + topPersistent: BlockNumber ## Up to this block number stored OK + ticker: TickerRef ## Logger ticker + + FullBuddyRef* = ##\ + ## Extended worker peer descriptor + BuddyRef[CtxData,BuddyData] + + FullCtxRef* = ##\ + ## Extended global descriptor + CtxRef[CtxData] let - highBlockRange = - BlockRange.new(high(BlockNumber),high(BlockNumber)) + highBlockNumber = high(BlockNumber) + highBlockRange = BlockRange.new(highBlockNumber,highBlockNumber) + +static: + doAssert stagedWorkItemsTrigger < maxStagedWorkItems # ------------------------------------------------------------------------------ # Private helpers # ------------------------------------------------------------------------------ -proc getOrHigh(b: Option[BlockNumber]): BlockNumber = - ## Syntactic sugar - if b.isSome: b.get else: high(BlockNumber) - -proc getOrHigh(b: Option[BlockRange]): BlockRange = - if b.isSome: b.get else: highBlockRange - proc hash(peer: Peer): Hash = ## Mixin `HashSet[Peer]` handler hash(cast[pointer](peer)) @@ -165,7 +166,7 @@ proc reduce(ivSet: BlockRangeSetRef; wi: WorkItemRef): Uint256 = proc pp(n: BlockNumber): string = ## Dedicated pretty printer (`$` is defined elsewhere using `UInt256`) - if n == high(BlockNumber): "high" else:"#" & $n + if n == highBlockNumber: "high" else:"#" & $n proc `$`(iv: BlockRange): string = ## Needed for macro generated DSL files like `snap.nim` because the @@ -188,27 +189,15 @@ proc `$`(brs: BlockRangeSetRef): string = # Private getters # ------------------------------------------------------------------------------ -proc local(buddy: BuddyRef): BuddyDataEx = - ## Parameters local to this peer worker - buddy.data.BuddyDataEx - -proc pool(ctx: CtxRef): CtxDataEx = - ## Parameters shared between all peer workers - ctx.data.CtxDataEx - -proc pool(buddy: BuddyRef): CtxDataEx = - ## Ditto - buddy.ctx.data.CtxDataEx - -proc nextUnprocessed(pool: CtxDataEx): Option[BlockNumber] = +proc nextUnprocessed(desc: var CtxData): Option[BlockNumber] = ## Pseudo getter - let rc = pool.unprocessed.ge() + let rc = desc.unprocessed.ge() if rc.isOK: result = some(rc.value.minPt) -proc nextStaged(pool: CtxDataEx): Option[BlockRange] = +proc nextStaged(desc: var CtxData): Option[BlockRange] = ## Pseudo getter - let rc = pool.staged.ge(low(BlockNumber)) + let rc = desc.staged.ge(low(BlockNumber)) if rc.isOK: result = some(rc.value.data.blocks) @@ -216,7 +205,7 @@ proc nextStaged(pool: CtxDataEx): Option[BlockRange] = # Private functions affecting all shared data # ------------------------------------------------------------------------------ -proc globalReset(ctx: CtxRef; backBlocks = maxHeadersFetch): bool = +proc globalReset(ctx: FullCtxRef; backBlocks = maxHeadersFetch): bool = ## Globally flush `pending` and `staged` items and update `unprocessed` ## ranges and set the `unprocessed` back before the best block number/ var topPersistent: BlockNumber @@ -232,32 +221,35 @@ proc globalReset(ctx: CtxRef; backBlocks = maxHeadersFetch): bool = error "Best block header problem", backBlocks, error=($e.name), msg=e.msg return false - ctx.pool.unprocessed.clear() - ctx.pool.staged.clear() - ctx.pool.trusted.clear() - ctx.pool.topPersistent = topPersistent - discard ctx.pool.unprocessed.merge(topPersistent + 1, high(BlockNumber)) + ctx.data.unprocessed.clear() + ctx.data.staged.clear() + ctx.data.trusted.clear() + ctx.data.topPersistent = topPersistent + discard ctx.data.unprocessed.merge(topPersistent + 1, highBlockNumber) true -proc tickerUpdater(ctx: CtxRef): TickerStatsUpdater = +proc tickerUpdater(ctx: FullCtxRef): TickerStatsUpdater = result = proc: TickerStats = let - stagedRange = ctx.pool.nextStaged + stagedRange = ctx.data.nextStaged nextStaged = if stagedRange.isSome: some(stagedRange.get.minPt) else: none(BlockNumber) TickerStats( - topPersistent: ctx.pool.topPersistent, + topPersistent: ctx.data.topPersistent, nextStaged: nextStaged, - nextUnprocessed: ctx.pool.nextUnprocessed, - nStagedQueue: ctx.pool.staged.len, - reOrg: ctx.pool.backtrack.isSome) + nextUnprocessed: ctx.data.nextUnprocessed, + nStagedQueue: ctx.data.staged.len, + reOrg: ctx.data.backtrack.isSome) # ------------------------------------------------------------------------------ # Private functions # ------------------------------------------------------------------------------ -template safeTransport(buddy: BuddyRef; info: static[string]; code: untyped) = +template safeTransport( + buddy: FullBuddyRef; + info: static[string]; + code: untyped) = try: code except TransportError as e: @@ -265,17 +257,18 @@ template safeTransport(buddy: BuddyRef; info: static[string]; code: untyped) = buddy.ctrl.stopped = true -proc getRandomTrustedPeer(buddy: BuddyRef): Result[Peer,void] = +proc getRandomTrustedPeer(buddy: FullBuddyRef): Result[Peer,void] = ## Return random entry from `trusted` peer different from this peer set if ## there are enough ## ## Ackn: nim-eth/eth/p2p/blockchain_sync.nim: `randomTrustedPeer()` let - nPeers = buddy.pool.trusted.len - offInx = if buddy.peer in buddy.pool.trusted: 2 else: 1 + ctx = buddy.ctx + nPeers = ctx.data.trusted.len + offInx = if buddy.peer in ctx.data.trusted: 2 else: 1 if 0 < nPeers: var (walkInx, stopInx) = (0, rand(nPeers - offInx)) - for p in buddy.pool.trusted: + for p in ctx.data.trusted: if p == buddy.peer: continue if walkInx == stopInx: @@ -284,21 +277,22 @@ proc getRandomTrustedPeer(buddy: BuddyRef): Result[Peer,void] = err() -proc newWorkItem(buddy: BuddyRef): Result[WorkItemRef,void] = +proc newWorkItem(buddy: FullBuddyRef): Result[WorkItemRef,void] = ## Fetch the next unprocessed block range and register it as work item. ## ## This function will grab a block range from the `unprocessed` range set, ## ove it and return it as a `WorkItemRef`. The returned range is registered ## in the `pending` list. let + ctx = buddy.ctx peer = buddy.peer - rc = buddy.pool.unprocessed.ge() + rc = ctx.data.unprocessed.ge() if rc.isErr: return err() # no more data for this peer # Check whether there is somthing to do at all - if buddy.local.bestNumber.isNone or - buddy.local.bestNumber.get < rc.value.minPt: + if buddy.data.bestNumber.isNone or + buddy.data.bestNumber.get < rc.value.minPt: return err() # no more data for this peer # Compute interval @@ -306,32 +300,37 @@ proc newWorkItem(buddy: BuddyRef): Result[WorkItemRef,void] = rc.value.minPt, min(rc.value.maxPt, min(rc.value.minPt + maxHeadersFetch - 1, - buddy.local.bestNumber.get))) + buddy.data.bestNumber.get))) - discard buddy.pool.unprocessed.reduce(iv) + discard ctx.data.unprocessed.reduce(iv) return ok(WorkItemRef(blocks: iv)) -proc recycleStaged(buddy: BuddyRef) = +proc recycleStaged(buddy: FullBuddyRef) = ## Flush list of staged items and store the block ranges ## back to the `unprocessed` ranges set ## # using fast traversal - let walk = WorkItemWalkRef.init(buddy.pool.staged) - var rc = walk.first() + let + ctx = buddy.ctx + walk = WorkItemWalkRef.init(ctx.data.staged) + var + rc = walk.first() while rc.isOk: # Store back into `unprocessed` ranges set - discard buddy.pool.unprocessed.merge(rc.value.data) + discard ctx.data.unprocessed.merge(rc.value.data) rc = walk.next() # optional clean up, see comments on the destroy() directive walk.destroy() - buddy.pool.staged.clear() + ctx.data.staged.clear() # ------------------------------------------------------------------------------ # Private `Future` helpers # ------------------------------------------------------------------------------ -proc getBestNumber(buddy: BuddyRef): Future[Result[BlockNumber,void]]{.async.} = +proc getBestNumber( + buddy: FullBuddyRef + ): Future[Result[BlockNumber,void]] {.async.} = ## Get best block number from best block hash. ## ## Ackn: nim-eth/eth/p2p/blockchain_sync.nim: `getBestBlockNumber()` @@ -370,7 +369,7 @@ proc getBestNumber(buddy: BuddyRef): Future[Result[BlockNumber,void]]{.async.} = return err() -proc agreesOnChain(buddy: BuddyRef; other: Peer): Future[bool] {.async.} = +proc agreesOnChain(buddy: FullBuddyRef; other: Peer): Future[bool] {.async.} = ## Returns `true` if one of the peers `buddy.peer` or `other` acknowledges ## existence of the best block of the other peer. ## @@ -417,49 +416,51 @@ proc agreesOnChain(buddy: BuddyRef; other: Peer): Future[bool] {.async.} = # Private functions, worker sub-tasks # ------------------------------------------------------------------------------ -proc initaliseWorker(buddy: BuddyRef): Future[bool] {.async.} = +proc initaliseWorker(buddy: FullBuddyRef): Future[bool] {.async.} = ## Initalise worker. This function must be run in single mode at the ## beginning of running worker peer. ## ## Ackn: nim-eth/eth/p2p/blockchain_sync.nim: `startSyncWithPeer()` ## - let peer = buddy.peer + let + ctx = buddy.ctx + peer = buddy.peer # Delayed clean up batch list - if 0 < buddy.pool.untrusted.len: - trace "Removing untrused peers", peer, count=buddy.pool.untrusted.len - for p in buddy.pool.untrusted: - buddy.pool.trusted.excl p - buddy.pool.untrusted.setLen(0) + if 0 < ctx.data.untrusted.len: + trace "Removing untrused peers", peer, count=ctx.data.untrusted.len + for p in ctx.data.untrusted: + ctx.data.trusted.excl p + ctx.data.untrusted.setLen(0) - if buddy.local.bestNumber.isNone: + if buddy.data.bestNumber.isNone: let rc = await buddy.getBestNumber() # Beware of peer terminating the session right after communicating if rc.isErr or buddy.ctrl.stopped: return false - if rc.value <= buddy.pool.topPersistent: + if rc.value <= ctx.data.topPersistent: buddy.ctrl.zombie = true trace "Useless peer, best number too low", peer, - topPersistent=buddy.pool.topPersistent, bestNumber=rc.value - buddy.local.bestNumber = some(rc.value) + topPersistent=ctx.data.topPersistent, bestNumber=rc.value + buddy.data.bestNumber = some(rc.value) - if minPeersToStartSync <= buddy.pool.trusted.len: + if minPeersToStartSync <= ctx.data.trusted.len: # We have enough trusted peers. Validate new peer against trusted let rc = buddy.getRandomTrustedPeer() if rc.isOK: if await buddy.agreesOnChain(rc.value): # Beware of peer terminating the session if not buddy.ctrl.stopped: - buddy.pool.trusted.incl peer + ctx.data.trusted.incl peer return true # If there are no trusted peers yet, assume this very peer is trusted, # but do not finish initialisation until there are more peers. - elif buddy.pool.trusted.len == 0: + elif ctx.data.trusted.len == 0: trace "Assume initial trusted peer", peer - buddy.pool.trusted.incl peer + ctx.data.trusted.incl peer - elif buddy.pool.trusted.len == 1 and buddy.peer in buddy.pool.trusted: + elif ctx.data.trusted.len == 1 and buddy.peer in ctx.data.trusted: # Ignore degenerate case, note that `trusted.len < minPeersToStartSync` discard @@ -472,7 +473,7 @@ proc initaliseWorker(buddy: BuddyRef): Future[bool] {.async.} = var agreeScore = 0 otherPeer: Peer - for p in buddy.pool.trusted: + for p in ctx.data.trusted: if peer == p: inc agreeScore else: @@ -486,29 +487,34 @@ proc initaliseWorker(buddy: BuddyRef): Future[bool] {.async.} = otherPeer = p # Check for the number of peers that disagree - case buddy.pool.trusted.len - agreeScore + case ctx.data.trusted.len - agreeScore of 0: trace "Peer trusted by score", peer, - trusted=buddy.pool.trusted.len - buddy.pool.trusted.incl peer # best possible outcome + trusted=ctx.data.trusted.len + ctx.data.trusted.incl peer # best possible outcome of 1: trace "Other peer no longer trusted", peer, - otherPeer, trusted=buddy.pool.trusted.len - buddy.pool.trusted.excl otherPeer - buddy.pool.trusted.incl peer + otherPeer, trusted=ctx.data.trusted.len + ctx.data.trusted.excl otherPeer + ctx.data.trusted.incl peer else: trace "Peer not trusted", peer, - trusted=buddy.pool.trusted.len + trusted=ctx.data.trusted.len discard - if minPeersToStartSync <= buddy.pool.trusted.len: + if minPeersToStartSync <= ctx.data.trusted.len: return true -proc fetchHeaders(buddy: BuddyRef; wi: WorkItemRef): Future[bool] {.async.} = +proc fetchHeaders( + buddy: FullBuddyRef; + wi: WorkItemRef + ): Future[bool] {.async.} = ## Get the work item with the least interval and complete it. The function ## returns `true` if bodies were fetched and there were no inconsistencies. - let peer = buddy.peer + let + ctx = buddy.ctx + peer = buddy.peer if 0 < wi.hashes.len: return true @@ -563,7 +569,7 @@ proc fetchHeaders(buddy: BuddyRef; wi: WorkItemRef): Future[bool] {.async.} = wi.headers = hdrResp.get.headers.reversed wi.blocks = BlockRange.new( wi.headers[0].blockNumber, wi.headers[^1].blockNumber) - discard buddy.pool.unprocessed.reduce(wi) + discard ctx.data.unprocessed.reduce(wi) trace "Updated reverse header range", peer, range=($wi.blocks) # Verify start block number @@ -600,13 +606,13 @@ proc fetchHeaders(buddy: BuddyRef; wi: WorkItemRef): Future[bool] {.async.} = let redRng = BlockRange.new( wi.headers[0].blockNumber, wi.headers[^1].blockNumber) trace "Adjusting block range", peer, range=($wi.blocks), reduced=($redRng) - discard buddy.pool.unprocessed.merge(redRng.maxPt + 1, wi.blocks.maxPt) + discard ctx.data.unprocessed.merge(redRng.maxPt + 1, wi.blocks.maxPt) wi.blocks = redRng return true -proc fetchBodies(buddy: BuddyRef; wi: WorkItemRef): Future[bool] {.async.} = +proc fetchBodies(buddy: FullBuddyRef; wi: WorkItemRef): Future[bool] {.async.} = ## Get the work item with the least interval and complete it. The function ## returns `true` if bodies were fetched and there were no inconsistencies. let peer = buddy.peer @@ -646,33 +652,34 @@ proc fetchBodies(buddy: BuddyRef; wi: WorkItemRef): Future[bool] {.async.} = return true -proc stageItem(buddy: BuddyRef; wi: WorkItemRef) = +proc stageItem(buddy: FullBuddyRef; wi: WorkItemRef) = ## Add work item to the list of staged items - let peer = buddy.peer - - let rc = buddy.pool.staged.insert(wi.blocks.minPt) + let + ctx = buddy.ctx + peer = buddy.peer + rc = ctx.data.staged.insert(wi.blocks.minPt) if rc.isOk: rc.value.data = wi # Turn on pool mode if there are too may staged work items queued. # This must only be done when the added work item is not backtracking. - if stagedWorkItemsTrigger < buddy.pool.staged.len and - buddy.pool.backtrack.isNone and + if stagedWorkItemsTrigger < ctx.data.staged.len and + ctx.data.backtrack.isNone and wi.topHash.isNone: buddy.ctx.poolMode = true # The list size is limited. So cut if necessary and recycle back the block # range of the discarded item (tough luck if the current work item is the # one removed from top.) - while maxStagedWorkItems < buddy.pool.staged.len: - let topValue = buddy.pool.staged.le(high(BlockNumber)).value - discard buddy.pool.unprocessed.merge(topValue.data) - discard buddy.pool.staged.delete(topValue.key) + while maxStagedWorkItems < ctx.data.staged.len: + let topValue = ctx.data.staged.le(highBlockNumber).value + discard ctx.data.unprocessed.merge(topValue.data) + discard ctx.data.staged.delete(topValue.key) return # Ooops, duplicates should not exist (but anyway ...) let wj = block: - let rc = buddy.pool.staged.eq(wi.blocks.minPt) + let rc = ctx.data.staged.eq(wi.blocks.minPt) doAssert rc.isOk # Store `wi` and return offending entry let rcData = rc.value.data @@ -685,26 +692,27 @@ proc stageItem(buddy: BuddyRef; wi: WorkItemRef) = block: let rc = wi.blocks - wj.blocks if rc.isOk: - discard buddy.pool.unprocessed.merge(rc.value) + discard ctx.data.unprocessed.merge(rc.value) -proc processStaged(buddy: BuddyRef): bool = +proc processStaged(buddy: FullBuddyRef): bool = ## Fetch a work item from the `staged` queue an process it to be ## stored on the persistent block chain. - let + ctx = buddy.ctx peer = buddy.peer chainDb = buddy.ctx.chain - rc = buddy.pool.staged.ge(low(BlockNumber)) + rc = ctx.data.staged.ge(low(BlockNumber)) + if rc.isErr: # No more items in the database return false let wi = rc.value.data - topPersistent = buddy.pool.topPersistent + topPersistent = ctx.data.topPersistent startNumber = wi.headers[0].blockNumber - stagedRecords = buddy.pool.staged.len + stagedRecords = ctx.data.staged.len # Check whether this record of blocks can be stored, at all if topPersistent + 1 < startNumber: @@ -717,11 +725,11 @@ proc processStaged(buddy: BuddyRef): bool = topPersistent, range=($wi.blocks) # remove from staged DB - discard buddy.pool.staged.delete(wi.blocks.minPt) + discard ctx.data.staged.delete(wi.blocks.minPt) try: if chainDb.persistBlocks(wi.headers, wi.bodies) == ValidationResult.OK: - buddy.pool.topPersistent = wi.blocks.maxPt + ctx.data.topPersistent = wi.blocks.maxPt return true except CatchableError as e: error "Storing persistent blocks failed", peer, range=($wi.blocks), @@ -749,7 +757,7 @@ proc processStaged(buddy: BuddyRef): bool = # the blocks from another peer. trace "Storing persistent blocks failed", peer, range=($wi.blocks) - discard buddy.pool.unprocessed.merge(wi.blocks) + discard ctx.data.unprocessed.merge(wi.blocks) buddy.ctrl.zombie = true return false except CatchableError as e: @@ -758,7 +766,7 @@ proc processStaged(buddy: BuddyRef): bool = # Parent block header problem, so we might be in the middle of a re-org. # Set single mode backtrack following the offending parent hash. - buddy.pool.backtrack = some(parentHash) + ctx.data.backtrack = some(parentHash) buddy.ctrl.multiOk = false if wi.topHash.isNone: @@ -778,42 +786,43 @@ proc processStaged(buddy: BuddyRef): bool = # Public start/stop and admin functions # ------------------------------------------------------------------------------ -proc workerSetup*(ctx: CtxRef; tickerOK: bool): bool = +proc setup*(ctx: FullCtxRef; tickerOK: bool): bool = ## Global set up - ctx.data = CtxDataEx(unprocessed: BlockRangeSetRef.init()) # `pool` extension - ctx.pool.staged.init() + ctx.data.unprocessed = BlockRangeSetRef.init() + ctx.data.staged.init() if tickerOK: - ctx.pool.ticker = Ticker.init(ctx.tickerUpdater) + ctx.data.ticker = TickerRef.init(ctx.tickerUpdater) else: debug "Ticker is disabled" return ctx.globalReset(0) -proc workerRelease*(ctx: CtxRef) = +proc release*(ctx: FullCtxRef) = ## Global clean up - if not ctx.pool.ticker.isNil: - ctx.pool.ticker.stop() + if not ctx.data.ticker.isNil: + ctx.data.ticker.stop() -proc start*(buddy: BuddyRef): bool = +proc start*(buddy: FullBuddyRef): bool = ## Initialise worker peer + let ctx = buddy.ctx if buddy.peer.supports(protocol.eth) and buddy.peer.state(protocol.eth).initialized: - buddy.data = BuddyDataEx.new() # `local` extension - if not buddy.pool.ticker.isNil: - buddy.pool.ticker.startBuddy() + if not ctx.data.ticker.isNil: + ctx.data.ticker.startBuddy() return true -proc stop*(buddy: BuddyRef) = +proc stop*(buddy: FullBuddyRef) = ## Clean up this peer + let ctx = buddy.ctx buddy.ctrl.stopped = true - buddy.pool.untrusted.add buddy.peer - if not buddy.pool.ticker.isNil: - buddy.pool.ticker.stopBuddy() + ctx.data.untrusted.add buddy.peer + if not ctx.data.ticker.isNil: + ctx.data.ticker.stopBuddy() # ------------------------------------------------------------------------------ # Public functions # ------------------------------------------------------------------------------ -proc runSingle*(buddy: BuddyRef) {.async.} = +proc runSingle*(buddy: FullBuddyRef) {.async.} = ## This peer worker is invoked if the peer-local flag `buddy.ctrl.multiOk` ## is set `false` which is the default mode. This flag is updated by the ## worker when deemed appropriate. @@ -825,37 +834,40 @@ proc runSingle*(buddy: BuddyRef) {.async.} = ## ## Note that this function runs in `async` mode. ## - let peer = buddy.peer + let + ctx = buddy.ctx + peer = buddy.peer - if buddy.pool.backtrack.isSome: + if ctx.data.backtrack.isSome: trace "Single run mode, re-org backtracking", peer let wi = WorkItemRef( # This dummy interval can savely merged back without any effect blocks: highBlockRange, # Enable backtrack - topHash: some(buddy.pool.backtrack.get)) + topHash: some(ctx.data.backtrack.get)) # Fetch headers and bodies for the current work item if await buddy.fetchHeaders(wi): if await buddy.fetchBodies(wi): - buddy.pool.backtrack = none(Hash256) + ctx.data.backtrack = none(Hash256) buddy.stageItem(wi) - # Update pool and persistent database (may reset `multiOk`) + # Update persistent database (may reset `multiOk`) buddy.ctrl.multiOk = true - while buddy.processStaged(): - discard + while buddy.processStaged() and not buddy.ctrl.stopped: + # Allow thread switch as `persistBlocks()` might be slow + await sleepAsync(10.milliseconds) return # This work item failed, nothing to do anymore. - discard buddy.pool.unprocessed.merge(wi) + discard ctx.data.unprocessed.merge(wi) buddy.ctrl.zombie = true else: - if buddy.local.bestNumber.isNone: + if buddy.data.bestNumber.isNone: # Only log for the first time, or so trace "Single run mode, initialisation", peer, - trusted=buddy.pool.trusted.len + trusted=ctx.data.trusted.len discard # Initialise/re-initialise this worker @@ -865,7 +877,7 @@ proc runSingle*(buddy: BuddyRef) {.async.} = await sleepAsync(2.seconds) -proc runPool*(buddy: BuddyRef) = +proc runPool*(buddy: FullBuddyRef) = ## Ocne started, the function `runPool()` is called for all worker peers in ## a row (as the body of an iteration.) There will be no other worker peer ## functions activated simultaneously. @@ -877,29 +889,32 @@ proc runPool*(buddy: BuddyRef) = ## ## Note that this function does not run in `async` mode. ## - if buddy.ctx.poolMode: + let ctx = buddy.ctx + if ctx.poolMode: # Mind the gap, fill in if necessary let - topPersistent = buddy.pool.topPersistent + topPersistent = ctx.data.topPersistent covered = min( - buddy.pool.nextUnprocessed.getOrHigh, - buddy.pool.nextStaged.getOrHigh.minPt) + ctx.data.nextUnprocessed.get(highBlockNumber), + ctx.data.nextStaged.get(highBlockRange).minPt) if topPersistent + 1 < covered: - discard buddy.pool.unprocessed.merge(topPersistent + 1, covered - 1) - buddy.ctx.poolMode = false + discard ctx.data.unprocessed.merge(topPersistent + 1, covered - 1) + ctx.poolMode = false -proc runMulti*(buddy: BuddyRef) {.async.} = +proc runMulti*(buddy: FullBuddyRef) {.async.} = ## This peer worker is invoked if the `buddy.ctrl.multiOk` flag is set ## `true` which is typically done after finishing `runSingle()`. This ## instance can be simultaneously active for all peer workers. ## # Fetch work item - let rc = buddy.newWorkItem() + let + ctx = buddy.ctx + rc = buddy.newWorkItem() if rc.isErr: # No way, end of capacity for this peer => re-calibrate buddy.ctrl.multiOk = false - buddy.local.bestNumber = none(BlockNumber) + buddy.data.bestNumber = none(BlockNumber) return let wi = rc.value @@ -908,13 +923,14 @@ proc runMulti*(buddy: BuddyRef) {.async.} = if await buddy.fetchBodies(wi): buddy.stageItem(wi) - # Update pool and persistent database - while buddy.processStaged(): - discard + # Update persistent database + while buddy.processStaged() and not buddy.ctrl.stopped: + # Allow thread switch as `persistBlocks()` might be slow + await sleepAsync(10.milliseconds) return # This work item failed - discard buddy.pool.unprocessed.merge(wi) + discard ctx.data.unprocessed.merge(wi) # ------------------------------------------------------------------------------ # End diff --git a/nimbus/sync/protocol.nim b/nimbus/sync/protocol.nim index edfcbb614..f92f25a61 100644 --- a/nimbus/sync/protocol.nim +++ b/nimbus/sync/protocol.nim @@ -27,4 +27,10 @@ type #eth* = eth67 snap* = snap1 + SnapAccountRange* = accountRangeObj + ## Syntactic sugar, type defined in `snap1` + + SnapTrieNodes* = trieNodesObj + ## Ditto + # End diff --git a/nimbus/sync/protocol/eth66.nim b/nimbus/sync/protocol/eth66.nim index b077231fa..fe1e3ad6b 100644 --- a/nimbus/sync/protocol/eth66.nim +++ b/nimbus/sync/protocol/eth66.nim @@ -40,7 +40,6 @@ import chronos, eth/[common/eth_types, p2p, p2p/private/p2p_types, p2p/blockchain_utils], stew/byteutils, - ../types, ./trace_config logScope: @@ -112,6 +111,16 @@ const trEthSendDelaying* = ">> " & prettyEthProtoName & " Delaying " +func toHex(hash: Hash256): string = + ## Shortcut for `byteutils.toHex(hash.data)` + hash.data.toHex + +func traceStep(request: BlocksRequest): string = + var str = if request.reverse: "-" else: "+" + if request.skip < high(typeof(request.skip)): + return str & $(request.skip + 1) + return static($(high(typeof(request.skip)).u256 + 1)) + p2pProtocol eth66(version = ethVersion, rlpxName = "eth", peerState = PeerState, diff --git a/nimbus/sync/protocol/snap1.nim b/nimbus/sync/protocol/snap1.nim index 665bd2942..247180172 100644 --- a/nimbus/sync/protocol/snap1.nim +++ b/nimbus/sync/protocol/snap1.nim @@ -141,7 +141,6 @@ import nimcrypto/hash, stew/byteutils, ../../constants, - ../snap/path_desc, ./trace_config logScope: @@ -149,13 +148,13 @@ logScope: type SnapAccount* = object - accHash*: NodeTag + accHash*: Hash256 accBody* {.rlpCustomSerialization.}: Account SnapAccountProof* = seq[Blob] SnapStorage* = object - slotHash*: NodeTag + slotHash*: Hash256 slotData*: Blob SnapStorageProof* = seq[Blob] @@ -186,6 +185,55 @@ const # avoids transmitting these hashes in about 90% of accounts. We need to # recognise or set these hashes in `Account` when serialising RLP for `snap`. +proc snapRead*(rlp: var Rlp; T: type Account; strict: static[bool] = false): T + {.gcsafe, raises: [Defect, RlpError]} = + ## RLP decoding for `Account`. The `snap` RLP representation of the account + ## differs from standard `Account` RLP. Empty storage hash and empty code + ## hash are each represented by an RLP zero-length string instead of the + ## full hash. + ## + ## Normally, this read function will silently handle standard encodinig and + ## `snap` enciding. Setting the argument strict as `false` the function will + ## throw an exception if `snap` encoding is violated. + rlp.tryEnterList() + result.nonce = rlp.read(typeof(result.nonce)) + result.balance = rlp.read(typeof(result.balance)) + if rlp.blobLen != 0 or not rlp.isBlob: + result.storageRoot = rlp.read(typeof(result.storageRoot)) + when strict: + if result.storageRoot == BLANK_ROOT_HASH: + raise newException(RlpTypeMismatch, + "BLANK_ROOT_HASH not encoded as empty string in Snap protocol") + else: + rlp.skipElem() + result.storageRoot = BLANK_ROOT_HASH + if rlp.blobLen != 0 or not rlp.isBlob: + result.codeHash = rlp.read(typeof(result.codeHash)) + when strict: + if result.codeHash == EMPTY_SHA3: + raise newException(RlpTypeMismatch, + "EMPTY_SHA3 not encoded as empty string in Snap protocol") + else: + rlp.skipElem() + result.codeHash = EMPTY_SHA3 + +proc snapAppend*(writer: var RlpWriter; account: Account) = + ## RLP encoding for `Account`. The snap RLP representation of the account + ## differs from standard `Account` RLP. Empty storage hash and empty code + ## hash are each represented by an RLP zero-length string instead of the + ## full hash. + writer.startList(4) + writer.append(account.nonce) + writer.append(account.balance) + if account.storageRoot == BLANK_ROOT_HASH: + writer.append("") + else: + writer.append(account.storageRoot) + if account.codeHash == EMPTY_SHA3: + writer.append("") + else: + writer.append(account.codeHash) + proc read(rlp: var Rlp, t: var SnapAccount, T: type Account): T = ## RLP Mixin: decoding for `SnapAccount`. result = rlp.snapRead(T) @@ -202,12 +250,10 @@ p2pProtocol snap1(version = 1, requestResponse: # User message 0x00: GetAccountRange. # Note: `origin` and `limit` differs from the specification to match Geth. - proc getAccountRange(peer: Peer, rootHash: Hash256, - origin: NodeTag, limit: NodeTag, - responseBytes: uint64) = + proc getAccountRange(peer: Peer, rootHash: Hash256, origin: Hash256, + limit: Hash256, responseBytes: uint64) = trace trSnapRecvReceived & "GetAccountRange (0x00)", peer, - accountRange=leafRangePp(origin, limit), - stateRoot=($rootHash), responseBytes + accountRange=(origin,limit), stateRoot=($rootHash), responseBytes trace trSnapSendReplying & "EMPTY AccountRange (0x01)", peer, sent=0 await response.send(@[], @[]) @@ -220,9 +266,8 @@ p2pProtocol snap1(version = 1, # User message 0x02: GetStorageRanges. # Note: `origin` and `limit` differs from the specification to match Geth. proc getStorageRanges(peer: Peer, rootHash: Hash256, - accounts: openArray[NodeTag], - origin: openArray[byte], limit: openArray[byte], - responseBytes: uint64) = + accounts: openArray[Hash256], origin: openArray[byte], + limit: openArray[byte], responseBytes: uint64) = when trSnapTracePacketsOk: var definiteFullRange = ((origin.len == 32 or origin.len == 0) and (limit.len == 32 or limit.len == 0)) @@ -286,7 +331,7 @@ p2pProtocol snap1(version = 1, # User message 0x06: GetTrieNodes. requestResponse: proc getTrieNodes(peer: Peer, rootHash: Hash256, - paths: openArray[PathSegment], responseBytes: uint64) = + paths: openArray[seq[Blob]], responseBytes: uint64) = trace trSnapRecvReceived & "GetTrieNodes (0x06)", peer, nodePaths=paths.len, stateRoot=($rootHash), responseBytes diff --git a/nimbus/sync/snap.nim b/nimbus/sync/snap.nim index 9be594522..3af6e9999 100644 --- a/nimbus/sync/snap.nim +++ b/nimbus/sync/snap.nim @@ -1,5 +1,4 @@ -# Nimbus - New sync approach - A fusion of snap, trie, beam and other methods -# +# Nimbus # Copyright (c) 2021 Status Research & Development GmbH # Licensed under either of # * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or @@ -10,13 +9,12 @@ # except according to those terms. import - std/hashes, + eth/[common/eth_types, p2p], chronicles, chronos, - eth/[common/eth_types, p2p, p2p/peer_pool, p2p/private/p2p_types], - stew/keyed_queue, - "."/[protocol, types], - ./snap/worker + ../p2p/chain, + ./snap/[worker, worker_desc], + "."/[sync_desc, sync_sched, protocol] {.push raises: [Defect].} @@ -24,134 +22,54 @@ logScope: topics = "snap-sync" type - SnapSyncRef* = ref object of Worker - chain: AbstractChainDB - buddies: KeyedQueue[Peer,WorkerBuddy] ## LRU cache with worker descriptors - pool: PeerPool ## for starting the system + SnapSyncRef* = RunnerSyncRef[CtxData,BuddyData] # ------------------------------------------------------------------------------ -# Private helpers +# Virtual methods/interface, `mixin` functions # ------------------------------------------------------------------------------ -proc nsCtx(sp: WorkerBuddy): SnapSyncRef = - sp.ns.SnapSyncRef +proc runSetup(ctx: SnapCtxRef; ticker: bool): bool = + worker.setup(ctx,ticker) -proc hash(peer: Peer): Hash = - ## Needed for `buddies` table key comparison - hash(peer.remote.id) +proc runRelease(ctx: SnapCtxRef) = + worker.release(ctx) -# ------------------------------------------------------------------------------ -# Private functions -# ------------------------------------------------------------------------------ +proc runStart(buddy: SnapBuddyRef): bool = + worker.start(buddy) -proc workerLoop(sp: WorkerBuddy) {.async.} = - let ns = sp.nsCtx - trace "Starting peer worker", peer=sp, - peers=ns.pool.len, workers=ns.buddies.len, maxWorkers=ns.buddiesMax +proc runStop(buddy: SnapBuddyRef) = + worker.stop(buddy) - # Do something, work a bit - await sp.workerExec +proc runPool(buddy: SnapBuddyRef) = + worker.runPool(buddy) - # Continue until stopped - while not sp.ctrl.stopped: - # Rotate connection table so the most used entry is at the end - discard sp.nsCtx.buddies.lruFetch(sp.peer) +proc runSingle(buddy: SnapBuddyRef) {.async.} = + await worker.runSingle(buddy) - let delayMs = if sp.workerLockedOk: 1000 else: 50 - await sleepAsync(chronos.milliseconds(delayMs)) - - # Do something, work a bit - await sp.workerExec - - trace "Peer worker done", peer=sp, ctrlState=sp.ctrl.state, - peers=ns.pool.len, workers=ns.buddies.len, maxWorkers=ns.buddiesMax - - -proc onPeerConnected(ns: SnapSyncRef, peer: Peer) = - let sp = WorkerBuddy.new(ns, peer) - - # Check for known entry (which should not exist.) - if ns.buddies.hasKey(peer): - trace "Ignoring already registered peer!", peer, - peers=ns.pool.len, workers=ns.buddies.len, maxWorkers=ns.buddiesMax - return - - # Initialise worker for this peer - if not sp.workerStart(): - trace "Ignoring useless peer", peer, - peers=ns.pool.len, workers=ns.buddies.len, maxWorkers=ns.buddiesMax - sp.ctrl.zombie = true - return - - # Check for table overflow. An overflow should not happen if the table is - # as large as the peer connection table. - if ns.buddiesMax <= ns.buddies.len: - let leastPeer = ns.buddies.shift.value.data - if leastPeer.ctrl.zombie: - trace "Dequeuing zombie peer", leastPeer, - peers=ns.pool.len, workers=ns.buddies.len, maxWorkers=ns.buddiesMax - discard - else: - trace "Peer table full! Dequeuing least used entry", leastPeer, - peers=ns.pool.len, workers=ns.buddies.len, maxWorkers=ns.buddiesMax - leastPeer.workerStop() - leastPeer.ctrl.zombie = true - - # Add peer entry - discard ns.buddies.lruAppend(sp.peer, sp, ns.buddiesMax) - - # Run worker - asyncSpawn sp.workerLoop() - - -proc onPeerDisconnected(ns: SnapSyncRef, peer: Peer) = - let rc = ns.buddies.eq(peer) - if rc.isErr: - debug "Disconnected from unregistered peer", peer, - peers=ns.pool.len, workers=ns.buddies.len, maxWorkers=ns.buddiesMax - return - let sp = rc.value - if sp.ctrl.zombie: - trace "Disconnected zombie peer", peer, - peers=ns.pool.len, workers=ns.buddies.len, maxWorkers=ns.buddiesMax - else: - sp.workerStop() - ns.buddies.del(peer) - trace "Disconnected peer", peer, - peers=ns.pool.len, workers=ns.buddies.len, maxWorkers=ns.buddiesMax +proc runMulti(buddy: SnapBuddyRef) {.async.} = + await worker.runMulti(buddy) # ------------------------------------------------------------------------------ # Public functions # ------------------------------------------------------------------------------ -proc init*(T: type SnapSyncRef; ethNode: EthereumNode; maxPeers: int): T = - ## Constructor +proc init*( + T: type SnapSyncRef; + ethNode: EthereumNode; + chain: Chain; + rng: ref HmacDrbgContext; + maxPeers: int; + enableTicker = false): T = new result - let size = max(1,maxPeers) - result.chain = ethNode.chain - result.buddies.init(size) - result.buddiesMax = size - result.pool = ethNode.peerPool + result.initSync(ethNode, maxPeers, enableTicker) + result.ctx.chain = chain # explicitely override + result.ctx.data.rng = rng proc start*(ctx: SnapSyncRef) = - ## Set up syncing. This call should come early. - var po = PeerObserver( - onPeerConnected: - proc(p: Peer) {.gcsafe.} = - ctx.onPeerConnected(p), - onPeerDisconnected: - proc(p: Peer) {.gcsafe.} = - ctx.onPeerDisconnected(p)) - - # Initialise sub-systems - ctx.workerSetup(ctx.chain) - po.setProtocol eth - ctx.pool.addObserver(ctx, po) + doAssert ctx.startSync() proc stop*(ctx: SnapSyncRef) = - ## Stop syncing - ctx.pool.delObserver(ctx) - ctx.workerRelease() + ctx.stopSync() # ------------------------------------------------------------------------------ # End diff --git a/nimbus/sync/snap/ChangeLog.md b/nimbus/sync/snap/ChangeLog.md deleted file mode 100644 index 7135cdf45..000000000 --- a/nimbus/sync/snap/ChangeLog.md +++ /dev/null @@ -1,83 +0,0 @@ -# Collected change log from Jamie's snap branch squash merge - -The comments are collected in chronological order, oldest first (as opposed to -squash merge order which is oldest last.) - -If a similar comment is found in a source file it was deleted here. - - -## Sync: Chain head: Promote peer chain head updates to debug level - -This way, you can see peer chain head updates at `--log-level:DEBUG` without -being flooded by trace messages. - -These occur about once every 15 seconds from each good peer. - - -## Sync: Chain head: Rate limit "blocked overlapping" error states - -Under some conditions when a peer is not responding (but stays connected), -these messages happen continuously. Don't output them and don't waste CPU -trying. - - -## Sync: Set and update `syncStateRoot` for each peer - -State syncing requires the `stateRoot` value of the selected block to sync to. - -The chain head tracker selects a block and uses `block.stateRoot`. State sync -reads that value to sync to. It can change at any time, but that's ok, the -state sync algorithm is designed around that idea. - -Aside from getting an initial `stateRoot`, the regular updates are essential -because state sync is so slow. - -On Mainnet, it is normal for the initial selected block to become too old -before state sync is complete, and then peers stop providing data in their -replies. The solution is for `stateRoot` to be updated by the chain head -tracker so it's always recent enough. (On Goerli and a fast peer we can fetch -the whole state just in time without this.) - -There are a number of issues with the simple implementation here: - -- The selected `stateRoot` block shouldn't be the most recent canonical head, - because it is prone to change due to small reorgs. It should be a more stable - block choice, slightly further back in time. - - However, any block close to the head is reasonably harmless during the state - "snap" phase. Small block differences cause a small state delta, which are - patched automatically during "heal" traversals. - -- During the state "heal" phase, `stateRoot` should not be updated on every - block change, because it disrupts the "heal" traversal when this happens. - - It should be kept the same for longer, but not too long because the `snap/1` - protocol does not provide state older than 128 blocks ago. - - So during "heal", `stateRoot` should be updated roughly every N blocks where - N is close to 128, except when the heal is disrupted due to chain reorgs - taking place or other loss of available state from the peer. - -- During the state "heal" phase, `stateRoot` must be coordinated among all - the peers. This is because "heal" converges a patchwork of states from - different times into a unified point-in-time whole state, so that execution - can proceed using entirely local data from there. - - -## Sync: Add `genesisStateRoot` for state syncing - -State syncing requires the `stateRoot` value of the selected block to sync to. -Normally the chain head tracker selects a block and uses `block.stateRoot`. - -However, in some cases in test environments, the chain head tracker finds the -sync block is 0, the genesis block, without receiving that block from a peer. -Of course this only happens when connecting to peers that are on block 0 -themselves, but it can happen and must be handled. - -Perhaps we should not run state sync on block 0, and instead the local trie. -But to get the correct "flat" or "snap sync" style representation that requires -special code. - -In order to exercise the state sync code and see how peers behave when block 0 -is selected, and avoid special code, use the genesis `stateRoot` found locally, -and sync that state from peers like any other. diff --git a/nimbus/sync/snap/path_desc.nim b/nimbus/sync/snap/path_desc.nim deleted file mode 100644 index 9a81b2744..000000000 --- a/nimbus/sync/snap/path_desc.nim +++ /dev/null @@ -1,404 +0,0 @@ -# Nimbus - Types, data structures and shared utilities used in network sync -# -# Copyright (c) 2018-2021 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/[math, sequtils, strutils, hashes], - eth/common/eth_types, - nimcrypto/keccak, - stew/[byteutils, interval_set], - stint, - ../../constants, - ../types - -{.push raises: [Defect].} - -type - NodeTag* = ##\ - ## Trie leaf item, account hash etc. - distinct UInt256 - - LeafRange* = ##\ - ## Interval `[minPt,maxPt]` of` NodeTag` elements, can be managed in an - ## `IntervalSet` data type. - Interval[NodeTag,UInt256] - - LeafRangeSet* = ##\ - ## Managed structure to handle non-adjacent `LeafRange` intervals - IntervalSetRef[NodeTag,UInt256] - - PathSegment* = object - ## Path prefix or trailer for an interior node in a hexary trie. See also - ## the implementation of `NibblesSeq` from `eth/trie/nibbles` for a more - ## general implementation. - bytes: seq[byte] ## + at most 32 bytes (aka 64 nibbles) - - PathSegmentError = enum - isNoError = 0 - isTooLongEvenLength ## More than 64 nibbles (even number) - isTooLongOddLength ## More than 63 nibbles (odd number) - isUnknownType ## Unknown encoduing type - -# ------------------------------------------------------------------------------ -# Public helpers -# ------------------------------------------------------------------------------ - -proc to*(nid: NodeTag; T: type Hash256): T = - result.data = nid.UInt256.toBytesBE - -proc to*(nid: NodeTag; T: type NodeHash): T = - nid.to(Hash256).T - -proc to*(h: Hash256; T: type NodeTag): T = - UInt256.fromBytesBE(h.data).T - -proc to*(nh: NodeHash; T: type NodeTag): T = - nh.Hash256.to(T) - -proc to*(n: SomeUnsignedInt|UInt256; T: type NodeTag): T = - n.u256.T - -# ------------------------------------------------------------------------------ -# Public constructors -# ------------------------------------------------------------------------------ - -proc new*(T: type NodeHash; ps: PathSegment): T = - ## Import `PathSegment` argument into a `LeafTtemData`. Missing nibbles on the - ## right will be zero padded. - if (ps.bytes[0] and 0x10) == 0: - for n in 1 ..< ps.bytes.len: - result.Hash256.data[n-1] = ps.bytes[n] - else: - for n in 0 ..< ps.bytes.len: - result.Hash256.data[n] = (ps.bytes[n] shl 4) or (ps.bytes[n+1] shr 4) - -proc new*(T: type NodeTag; ps: PathSegment): T = - ## Import `PathSegment` argument into a `LeafTtem`. Missing nibbles on the - ## right will be zero padded. - NodeHash.new(ps).to(NodeTag) - -proc init*(nh: var NodeHash; data: openArray[byte]): bool = - ## Import argument `data` into `nh` which must have length either `32` or `0`. - ## The latter case is equivalent to an all zero byte array of size `32`. - if data.len == 32: - for n in 0 ..< 32: - nh.Hash256.data[n] = data[n] - return true - elif data.len == 0: - nh.reset - return true - -proc init*(nt: var NodeTag; data: openArray[byte]): bool = - ## Similar to `init(li: var NodeTag; ps: PathSegment)` - var h: NodeHash - if h.init(data): - nt = h.to(NodeTag) - return true - -proc init*(ps: var PathSegment; data: openArray[byte]): bool = - ## Import argument `data` into `ps` which must be a valid path as found - ## in a trie extension or leaf node starting with: - ## * 0x00, or 0x20: followed by at most 64 nibbles (i.e. by 32 bytes max), - ## Here, data path is made up of the at most 32 pairs of nibbles. - ## * 0x1x, or 0x3x: followed by at most 62 nibbles (31 bytes max). Here the - ## data path value starts with the `x` followed by the at most 62 pairs of - ## nibbles. - if 0 < data.len: - # Check first byte for marker - if ((data[0] and 0xdf) == 0x00 and data.len <= 33) or # right nibble 0 - ((data[0] and 0xd0) == 0x10 and data.len <= 32): # right nibble 1st dgt - ps.bytes = data.toSeq - return true - -proc new*(T: type PathSegment; tag: NodeTag; isLeaf = false): T = - ## Create `PathSegment` from `NodeTag`. If the `isLeaf` argument is set, the - ## path segment is marked as a leaf node (trie prefix' 0x20'). - result.bytes = @[0.byte] & tag.to(Hash256).data.toSeq - -# ------------------------------------------------------------------------------ -# Public `PathSegment` functions -# ------------------------------------------------------------------------------ - -proc verify*(ps: PathSegment): Result[void,PathSegmentError] = - ## Check `ip` for consistency - if ps.bytes.len == 0: - return ok() - if (ps.bytes[0] and 0xdf) == 0: - if 33 < ps.bytes.len: - return err(isTooLongEvenLength) - elif (ps.bytes[0] and 0xd0) == 0x10: - if 32 < ps.bytes.len: - return err(isTooLongOddLength) - else: - return err(isUnknownType) - ok() - -proc len*(ps: PathSegment): int = - ## Returns the number of nibbles in the range 0..64. - if ps.bytes.len == 0: - 0 - elif (ps.bytes[0] and 0x10) == 0: - 2 * ps.bytes.len - 2 - else: - 2 * ps.bytes.len - 1 - -proc setLen*(ps: var PathSegment; newLen: int) = - ## Truncate or extend the length (i.e. the number of nibbles) of the argument - ## `ip` to `newLen` bertwwn 0..63. When extending, new nibbles are zero - ## initialised. - ## This function throws an assertion defect if the `newLen` argument is - ## outside the range 0..64. - doAssert 0 <= newLen and newLen <= 64 - if ps.bytes.len == 0: - ps.bytes = @[0.byte] - if (ps.bytes[0] and 0x10) == 0: - if (newLen and 1) == 0: # both, old and new lengths are even - ps.bytes.setLen(1 + (newLen shr 1)) - else: # new length odd, need to shift nibbles - let newBytesLen = (newLen + 1) shr 1 - ps.bytes[0] = ps.bytes[0] or 0x10 - if 1 < ps.bytes.len: - ps.bytes[0] = ps.bytes[0] or (ps.bytes[1] shr 4) - for n in 1 ..< min(ps.bytes.len-1, newBytesLen): - ps.bytes[n] = (ps.bytes[n] shl 4) or (ps.bytes[n+1] shr 4) - ps.bytes.setLen(newBytesLen) - else: - if (newLen and 1) == 1: # both, old and new lengths are odd - ps.bytes.setLen((newLen + 1) shr 1) - else: # new even length => shift nibbles right - let oldBytesLen = ps.bytes.len - ps.bytes.setLen((newLen shr 1) + 1) - for n in countDown(min(ps.bytes.len-1,oldBytesLen),1): - ps.bytes[n] = (ps.bytes[n-1] shl 4) or (ps.bytes[n] shr 4) - ps.bytes[0] = ps.bytes[0] and 0xd0 - -proc `[]`*(ps: PathSegment; nibbleInx: int): int = - ## Extract the nibble (aka hex digit) value at the argument position index - ## `nibbleInx`. If the position index `nibbleInx` does not relate to a valid - ## nibble position, `0` is returned - ## - ## This function throws an assertion defect if the `nibbleInx` is outside - ## the range 0..63. - doAssert 0 <= nibbleInx and nibbleInx < 64 - if ps.bytes.len == 0: - result = 0 - elif (ps.bytes[0] and 0x10) == 0: - let byteInx = (nibbleInx shr 1) + 1 - if (nibbleInx and 1) == 0: - result = ps.bytes[byteInx].int shr 4 - else: - result = ps.bytes[byteInx].int and 0x0f - else: - let byteInx = (nibbleInx + 1) shr 1 - if (nibbleInx and 1) == 0: - result = ps.bytes[byteInx].int and 0x0f - else: - result = ps.bytes[byteInx].int shr 4 - -proc `[]=`*(ps: var PathSegment; nibbleInx: int; value: int) = - ## Assign a nibble (aka hex) value `value` at position `nibbleInx`. If the - ## length of the argument `ip` was smaller than the `nibbleInx`, the length - ## will be extended to include that nibble. - ## - ## This function throws an assertion defect if the `nibbleInx` is outside - ## the range 0..63, or if `value` is outside 0..15. - doAssert 0 <= nibbleInx and nibbleInx < 64 - doAssert 0 <= value and value < 16 - if ps.len <= nibbleInx: - if ps.bytes.len == 0: - ps.bytes = @[0.byte] - ps.setLen(nibbleInx + 1) - if (ps.bytes[0] and 0x10) == 0: - let byteInx = (nibbleInx shr 1) + 1 - if (nibbleInx and 1) == 0: - ps.bytes[byteInx] = (value.uint8 shl 4) or (ps.bytes[byteInx] and 0x0f) - else: - ps.bytes[byteInx] = (ps.bytes[byteInx] and 0xf0) or value.uint8 - else: - let byteInx = (nibbleInx + 1) shr 1 - if (nibbleInx and 1) == 0: - ps.bytes[byteInx] = (ps.bytes[byteInx] and 0xf0) or value.uint8 - else: - ps.bytes[byteInx] = (value.uint8 shl 4) or (ps.bytes[byteInx] and 0x0f) - -proc `$`*(ps: PathSegment): string = - $ps.len & "#" & ps.bytes.mapIt(it.toHex(2)).join.toLowerAscii - -# ------------------------------------------------------------------------------ -# Public rlp support -# ------------------------------------------------------------------------------ - -proc read*(rlp: var Rlp, T: type NodeTag): T - {.gcsafe, raises: [Defect,RlpError]} = - rlp.read(Hash256).to(T) - -proc append*(writer: var RlpWriter, nid: NodeTag) = - writer.append(nid.to(Hash256)) - -# ------------- - -proc snapRead*(rlp: var Rlp; T: type Account; strict: static[bool] = false): T - {.gcsafe, raises: [Defect, RlpError]} = - ## RLP decoding for `Account`. The `snap` RLP representation of the account - ## differs from standard `Account` RLP. Empty storage hash and empty code - ## hash are each represented by an RLP zero-length string instead of the - ## full hash. - ## - ## Normally, this read function will silently handle standard encodinig and - ## `snap` enciding. Setting the argument strict as `false` the function will - ## throw an exception if `snap` encoding is violated. - rlp.tryEnterList() - result.nonce = rlp.read(typeof(result.nonce)) - result.balance = rlp.read(typeof(result.balance)) - if rlp.blobLen != 0 or not rlp.isBlob: - result.storageRoot = rlp.read(typeof(result.storageRoot)) - when strict: - if result.storageRoot == BLANK_ROOT_HASH: - raise newException(RlpTypeMismatch, - "BLANK_ROOT_HASH not encoded as empty string in Snap protocol") - else: - rlp.skipElem() - result.storageRoot = BLANK_ROOT_HASH - if rlp.blobLen != 0 or not rlp.isBlob: - result.codeHash = rlp.read(typeof(result.codeHash)) - when strict: - if result.codeHash == EMPTY_SHA3: - raise newException(RlpTypeMismatch, - "EMPTY_SHA3 not encoded as empty string in Snap protocol") - else: - rlp.skipElem() - result.codeHash = EMPTY_SHA3 - -proc snapAppend*(writer: var RlpWriter; account: Account) = - ## RLP encoding for `Account`. The snap RLP representation of the account - ## differs from standard `Account` RLP. Empty storage hash and empty code - ## hash are each represented by an RLP zero-length string instead of the - ## full hash. - writer.startList(4) - writer.append(account.nonce) - writer.append(account.balance) - if account.storageRoot == BLANK_ROOT_HASH: - writer.append("") - else: - writer.append(account.storageRoot) - if account.codeHash == EMPTY_SHA3: - writer.append("") - else: - writer.append(account.codeHash) - -# ------------- - -proc compactRead*(rlp: var Rlp, T: type PathSegment): T - {.gcsafe, raises: [Defect,RlpError]} = - ## Read compact encoded path segment - rlp.tryEnterList() - let - path = rlp.read(array[32, byte]) - length = rlp.read(byte) - if 64 < length: - raise newException( - MalformedRlpError, "More the most 64 nibbles for PathSegment") - if (length and 1) == 0: - # initalise as even extension - result.bytes.setLen(1 + (length shr 1)) - for n in 1 ..< result.bytes.len: - result.bytes[n] = path[n-1] - else: - # initalise as odd extension - result.bytes.setLen((length + 1) shr 1) - result.bytes[0] = 0x10 or (path[0] shl 4) - for n in 1 ..< result.bytes.len: - result.bytes[n] = (path[n-1] shl 4) or (path[n] shr 4) - -proc compactAppend*(writer: var RlpWriter, ps: PathSegment) = - ## Append compact encoded path segment - var path: array[32, byte] - if (ps.bytes[0] and 0x10) == 0: - for n in 1 ..< ps.bytes.len: - path[n-1] = ps.bytes[n] - else: - for n in 1 ..< ps.bytes.len: - path[n-1] = (ps.bytes[n-1] shl 4) or (ps.bytes[n] shr 4) - path[ps.bytes.len-1] = ps.bytes[^1] shl 4 - writer.startList(2) - writer.append(path) - writer.append(ps.len.byte) - -# ------------- - -proc dbRead*(rlp: var Rlp, T: type PathSegment): T - {.gcsafe, raises: [Defect,RlpError]} = - ## Read as stored in the database - result.bytes = rlp.read(Blob) - -proc dbAppend*(writer: var RlpWriter, ps: PathSegment) = - ## Append in database record format - writer.append(ps.bytes) - -# ------------------------------------------------------------------------------ -# Public `NodeTag` and `LeafRange` functions -# ------------------------------------------------------------------------------ - -proc u256*(lp: NodeTag): UInt256 = lp.UInt256 -proc low*(T: type NodeTag): T = low(UInt256).T -proc high*(T: type NodeTag): T = high(UInt256).T - -proc `+`*(a: NodeTag; b: UInt256): NodeTag = (a.u256+b).NodeTag -proc `-`*(a: NodeTag; b: UInt256): NodeTag = (a.u256-b).NodeTag -proc `-`*(a, b: NodeTag): UInt256 = (a.u256 - b.u256) - -proc `==`*(a, b: NodeTag): bool = a.u256 == b.u256 -proc `<=`*(a, b: NodeTag): bool = a.u256 <= b.u256 -proc `<`*(a, b: NodeTag): bool = a.u256 < b.u256 - -proc hash*(a: NodeTag): Hash = - ## Mixin for `Table` or `keyedQueue` - a.to(Hash256).data.hash - -proc digestTo*(data: Blob; T: type NodeTag): T = - ## Hash the `data` argument - keccak256.digest(data).to(T) - -proc freeFactor*(lrs: LeafRangeSet): float = - ## Free factor, ie. `#items-free / 2^256` to be used in statistics - if 0 < lrs.total: - ((high(NodeTag) - lrs.total).u256 + 1).to(float) / (2.0^256) - elif lrs.chunks == 0: - 1.0 - else: - 0.0 - -# Printing & pretty printing -proc `$`*(nt: NodeTag): string = - if nt == high(NodeTag): - "high(NodeTag)" - elif nt == 0.u256.NodeTag: - "0" - else: - nt.to(Hash256).data.toHex - -proc leafRangePp*(a, b: NodeTag): string = - ## Needed for macro generated DSL files like `snap.nim` because the - ## `distinct` flavour of `NodeTag` is discarded there. - result = "[" & $a - if a != b: - result &= ',' & $b - result &= "]" - -proc `$`*(a, b: NodeTag): string = - ## Prettyfied prototype - leafRangePp(a,b) - -proc `$`*(iv: LeafRange): string = - leafRangePp(iv.minPt, iv.maxPt) - -# ------------------------------------------------------------------------------ -# End -# ------------------------------------------------------------------------------ diff --git a/nimbus/sync/snap/range_desc.nim b/nimbus/sync/snap/range_desc.nim new file mode 100644 index 000000000..4a02546ae --- /dev/null +++ b/nimbus/sync/snap/range_desc.nim @@ -0,0 +1,153 @@ +# Nimbus - Types, data structures and shared utilities used in network sync +# +# Copyright (c) 2018-2021 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/[math, hashes], + eth/common/eth_types, + nimcrypto/keccak, + stew/[byteutils, interval_set], + stint, + ../../constants, + ../types + +{.push raises: [Defect].} + +type + NodeTag* = ##\ + ## Trie leaf item, account hash etc. + distinct UInt256 + + LeafRange* = ##\ + ## Interval `[minPt,maxPt]` of` NodeTag` elements, can be managed in an + ## `IntervalSet` data type. + Interval[NodeTag,UInt256] + + LeafRangeSet* = ##\ + ## Managed structure to handle non-adjacent `LeafRange` intervals + IntervalSetRef[NodeTag,UInt256] + +# ------------------------------------------------------------------------------ +# Public helpers +# ------------------------------------------------------------------------------ + +proc to*(nid: NodeTag; T: type Hash256): T = + ## Convert to serialised equivalent + result.data = nid.UInt256.toBytesBE + +proc to*(nid: NodeTag; T: type NodeHash): T = + ## Syntactic sugar + nid.to(Hash256).T + +proc to*(h: Hash256; T: type NodeTag): T = + ## Convert from serialised equivalent + UInt256.fromBytesBE(h.data).T + +proc to*(nh: NodeHash; T: type NodeTag): T = + ## Syntactic sugar + nh.Hash256.to(T) + +proc to*(n: SomeUnsignedInt|UInt256; T: type NodeTag): T = + ## Syntactic sugar + n.u256.T + +# ------------------------------------------------------------------------------ +# Public constructors +# ------------------------------------------------------------------------------ + +proc init*(nh: var NodeHash; data: openArray[byte]): bool = + ## Import argument `data` into `nh` which must have length either `32` or `0`. + ## The latter case is equivalent to an all zero byte array of size `32`. + if data.len == 32: + for n in 0 ..< 32: + nh.Hash256.data[n] = data[n] + return true + elif data.len == 0: + nh.reset + return true + +proc init*(nt: var NodeTag; data: openArray[byte]): bool = + ## Similar to `init(nh: var NodeHash; .)`. + var h: NodeHash + if h.init(data): + nt = h.to(NodeTag) + return true + +# ------------------------------------------------------------------------------ +# Public rlp support +# ------------------------------------------------------------------------------ + +proc read*(rlp: var Rlp, T: type NodeTag): T + {.gcsafe, raises: [Defect,RlpError]} = + rlp.read(Hash256).to(T) + +proc append*(writer: var RlpWriter, nid: NodeTag) = + writer.append(nid.to(Hash256)) + +# ------------------------------------------------------------------------------ +# Public `NodeTag` and `LeafRange` functions +# ------------------------------------------------------------------------------ + +proc u256*(lp: NodeTag): UInt256 = lp.UInt256 +proc low*(T: type NodeTag): T = low(UInt256).T +proc high*(T: type NodeTag): T = high(UInt256).T + +proc `+`*(a: NodeTag; b: UInt256): NodeTag = (a.u256+b).NodeTag +proc `-`*(a: NodeTag; b: UInt256): NodeTag = (a.u256-b).NodeTag +proc `-`*(a, b: NodeTag): UInt256 = (a.u256 - b.u256) + +proc `==`*(a, b: NodeTag): bool = a.u256 == b.u256 +proc `<=`*(a, b: NodeTag): bool = a.u256 <= b.u256 +proc `<`*(a, b: NodeTag): bool = a.u256 < b.u256 + +proc hash*(a: NodeTag): Hash = + ## Mixin for `Table` or `keyedQueue` + a.to(Hash256).data.hash + +proc digestTo*(data: Blob; T: type NodeTag): T = + ## Hash the `data` argument + keccak256.digest(data).to(T) + +proc freeFactor*(lrs: LeafRangeSet): float = + ## Free factor, ie. `#items-free / 2^256` to be used in statistics + if 0 < lrs.total: + ((high(NodeTag) - lrs.total).u256 + 1).to(float) / (2.0^256) + elif lrs.chunks == 0: + 1.0 + else: + 0.0 + +# Printing & pretty printing +proc `$`*(nt: NodeTag): string = + if nt == high(NodeTag): + "high(NodeTag)" + elif nt == 0.u256.NodeTag: + "0" + else: + nt.to(Hash256).data.toHex + +proc leafRangePp*(a, b: NodeTag): string = + ## Needed for macro generated DSL files like `snap.nim` because the + ## `distinct` flavour of `NodeTag` is discarded there. + result = "[" & $a + if a != b: + result &= ',' & $b + result &= "]" + +proc `$`*(a, b: NodeTag): string = + ## Prettyfied prototype + leafRangePp(a,b) + +proc `$`*(iv: LeafRange): string = + leafRangePp(iv.minPt, iv.maxPt) + +# ------------------------------------------------------------------------------ +# End +# ------------------------------------------------------------------------------ diff --git a/nimbus/sync/snap/worker.nim b/nimbus/sync/snap/worker.nim index 0d77e7775..ca8b28cc1 100644 --- a/nimbus/sync/snap/worker.nim +++ b/nimbus/sync/snap/worker.nim @@ -1,4 +1,4 @@ -# Nimbus - Rapidly converge on and track the canonical chain head of each peer +# Nimbus # # Copyright (c) 2021 Status Research & Development GmbH # Licensed under either of @@ -9,652 +9,221 @@ # at your option. This file may not be copied, modified, or distributed # except according to those terms. -## This module fetches and tracks the canonical chain head of each connected -## peer. (Or in future, each peer we care about; we won't poll them all so -## often.) -## -## This is for when we aren't sure of the block number of a peer's canonical -## chain head. Most of the time, after finding which block, it quietly polls -## to track small updates to the "best" block number and hash of each peer. -## -## But sometimes that can get out of step. If there has been a deeper reorg -## than our tracking window, or a burst of more than a few new blocks, network -## delays, downtime, or the peer is itself syncing. Perhaps we stopped Nimbus -## and restarted a while later, e.g. suspending a laptop or Control-Z. Then -## this will catch up. It is even possible that the best hash the peer gave us -## in the `Status` handshake has disappeared by the time we query for the -## corresponding block number, so we start at zero. -## -## The steps here perform a robust and efficient O(log N) search to rapidly -## converge on the new best block if it's moved out of the polling window no -## matter where it starts, confirm the peer's canonical chain head boundary, -## then track the peer's chain head in real-time by polling. The method is -## robust to peer state changes at any time. -## -## The purpose is to: -## -## - Help with finding a peer common chain prefix ("fast sync pivot") in a -## consistent, fast and explicit way. -## -## - Catch up quickly after any long pauses of network downtime, program not -## running, or deep chain reorgs. -## -## - Be able to display real-time peer states, so they are less mysterious. -## -## - Tell the beam/snap/trie sync processes when to start and what blocks to -## fetch, and keep those fetchers in the head-adjacent window of the -## ever-changing chain. -## -## - Help the sync process bootstrap usefully when we only have one peer, -## speculatively fetching and validating what data we can before we have more -## peers to corroborate the consensus. -## -## - Help detect consensus failures in the network. -## -## We cannot assume a peer's canonical chain stays the same or only gains new -## blocks from one query to the next. There can be reorgs, including deep -## reorgs. When a reorg happens, the best block number can decrease if the new -## canonical chain is shorter than the old one, and the best block hash we -## previously knew can become unavailable on the peer. So we must detect when -## the current best block disappears and be able to reduce block number. - import - std/bitops, + std/[hashes, math, options, sets], chronicles, chronos, - eth/[common/eth_types, p2p, p2p/private/p2p_types], - "../.."/[constants, p2p/chain/chain_desc], - ".."/[protocol, types], - ./worker/[worker_desc, fetch] - -{.push raises: [Defect].} - -export - worker_desc + eth/[common/eth_types, p2p], + stew/[interval_set, keyed_queue], + ".."/[protocol, sync_desc], + ./worker/[accounts_db, fetch_accounts, pivot, ticker], + "."/[range_desc, worker_desc] logScope: - topics = "snap-worker" - -const - syncLockedMinimumReply = 8 - ## Minimum number of headers we assume any peers will send if they have - ## them in contiguous ascending queries. Fewer than this confirms we have - ## found the peer's canonical chain head boundary. Must be at least 2, and - ## at least `syncLockedQueryOverlap+2` to stay `SyncLocked` when the chain - ## extends. Should not be large as that would be stretching assumptions - ## about peer implementations. 8 is chosen as it allows 3-deep extensions - ## and 3-deep reorgs to be followed in a single round trip. - - syncLockedQueryOverlap = 4 - ## Number of headers to re-query on each poll when `SyncLocked` so that we - ## get small reorg updates in one round trip. Must be no more than - ## `syncLockedMinimumReply-1`, no more than `syncLockedMinimumReply-2` to - ## stay `SyncLocked` when the chain extends, and not too large to avoid - ## excessive duplicate fetching. 4 is chosen as it allows 3-deep reorgs - ## to be followed in single round trip. - - syncLockedQuerySize = 192 - ## Query size when polling `SyncLocked`. Must be at least - ## `syncLockedMinimumReply`. Large is fine, if we get a large reply the - ## values are almost always useful. - - huntQuerySize = 16 - ## Query size when hunting for canonical head boundary. Small is good - ## because we don't want to keep most of the headers at hunt time. - - huntForwardExpandShift = 4 - ## Expansion factor during `HuntForward` exponential search. - ## 16 is chosen for rapid convergence when bootstrapping or catching up. - - huntBackwardExpandShift = 1 - ## Expansion factor during `HuntBackward` exponential search. - ## 2 is chosen for better convergence when tracking a chain reorg. - -type - WorkerMode = enum - ## The current state of tracking the peer's canonical chain head. - ## `bestBlockNumber` is only valid when this is `SyncLocked`. - SyncLocked - SyncOnlyHash - HuntForward - HuntBackward - HuntRange - HuntRangeFinal - - WorkerHuntEx = ref object of WorkerBase - ## Peer canonical chain head ("best block") search state. - syncMode: WorkerMode ## Action mode - startedFetch: bool ## Start download once, only - lowNumber: BlockNumber ## Recent lowest known block number. - highNumber: BlockNumber ## Recent highest known block number. - bestNumber: BlockNumber - bestHash: BlockHash - step: uint - -static: - doAssert syncLockedMinimumReply >= 2 - doAssert syncLockedMinimumReply >= syncLockedQueryOverlap + 2 - doAssert syncLockedQuerySize <= maxHeadersFetch - doAssert huntQuerySize >= 1 and huntQuerySize <= maxHeadersFetch - doAssert huntForwardExpandShift >= 1 and huntForwardExpandShift <= 8 - doAssert huntBackwardExpandShift >= 1 and huntBackwardExpandShift <= 8 - - # Make sure that request/response wire protocol messages are id-tracked and - # would not overlap (no multi-protocol legacy support) - doAssert 66 <= protocol.ethVersion + topics = "snap-sync" # ------------------------------------------------------------------------------ # Private helpers # ------------------------------------------------------------------------------ -proc hunt(sp: WorkerBuddy): WorkerHuntEx = - sp.workerBase.WorkerHuntEx +proc hash(h: Hash256): Hash = + ## Mixin for `Table` or `keyedQueue` + h.data.hash -proc `hunt=`(sp: WorkerBuddy; value: WorkerHuntEx) = - sp.workerBase = value - -proc new(T: type WorkerHuntEx; syncMode: WorkerMode): T = - T(syncMode: syncMode, - lowNumber: 0.toBlockNumber.BlockNumber, - highNumber: high(BlockNumber).BlockNumber, # maximum uncertainty range. - bestNumber: 0.toBlockNumber.BlockNumber, - bestHash: ZERO_HASH256.BlockHash, # whatever - step: 0u) - -# ------------------------------------------------------------------------------ -# Private logging helpers -# ------------------------------------------------------------------------------ - -proc traceSyncLocked(sp: WorkerBuddy, number: BlockNumber, hash: BlockHash) = - ## Trace messages when peer canonical head is confirmed or updated. - let - bestBlock = sp.ns.pp(hash, number) - peer = $sp - if sp.hunt.syncMode != SyncLocked: - debug "Now tracking chain head of peer", peer, bestBlock - elif number > sp.hunt.bestNumber: - if number == sp.hunt.bestNumber + 1: - debug "Peer chain head advanced one block", peer, - advance=1, bestBlock - else: - debug "Peer chain head advanced some blocks", peer, - advance=(sp.hunt.bestNumber - number), bestBlock - elif number < sp.hunt.bestNumber or hash != sp.hunt.bestHash: - debug "Peer chain head reorg detected", peer, - advance=(sp.hunt.bestNumber - number), bestBlock - -# proc peerSyncChainTrace(sp: WorkerBuddy) = -# ## To be called after `peerSyncChainRequest` has updated state. -# case sp.hunt.syncMode: -# of SyncLocked: -# trace "SyncLocked", -# bestBlock = sp.ns.pp(sp.hunt.bestHash, sp.hunt.bestNumber) -# of SyncOnlyHash: -# trace "OnlyHash", -# bestBlock = sp.ns.pp(sp.hunt.bestHash, sp.hunt.bestNumber) -# of HuntForward: -# template highMax(n: BlockNumber): string = -# if n == high(BlockNumber): "max" else: $n -# trace "HuntForward", -# low=sp.hunt.lowNumber, high=highMax(sp.hunt.highNumber), -# step=sp.hunt.step -# of HuntBackward: -# trace "HuntBackward", -# low=sp.hunt.lowNumber, high=sp.hunt.highNumber, step=sp.hunt.step -# of HuntRange: -# trace "HuntRange", -# low=sp.hunt.lowNumber, high=sp.hunt.highNumber, step=sp.hunt.step -# of HuntRangeFinal: -# trace "HuntRangeFinal", -# low=sp.hunt.lowNumber, high=sp.hunt.highNumber, step=1 +proc meanStdDev(sum, sqSum: float; length: int): (float,float) = + if 0 < length: + result[0] = sum / length.float + result[1] = sqrt(sqSum / length.float - result[0] * result[0]) # ------------------------------------------------------------------------------ # Private functions # ------------------------------------------------------------------------------ -proc setSyncLocked(sp: WorkerBuddy, number: BlockNumber, hash: BlockHash) = - ## Actions to take when peer canonical head is confirmed or updated. - sp.traceSyncLocked(number, hash) - sp.hunt.bestNumber = number - sp.hunt.bestHash = hash - sp.hunt.syncMode = SyncLocked +proc rndNodeTag(buddy: SnapBuddyRef): NodeTag = + ## Create random node tag + let + ctx = buddy.ctx + peer = buddy.peer + var data: array[32,byte] + ctx.data.rng[].generate(data) + UInt256.fromBytesBE(data).NodeTag -proc clearSyncStateRoot(sp: WorkerBuddy) = - if sp.ctrl.stateRoot.isSome: - debug "Stopping state sync from this peer", peer=sp - sp.ctrl.stateRoot = none(TrieHash) -proc lockSyncStateAndFetch( - sp: WorkerBuddy, - number: BlockNumber, - hash: BlockHash, - stateRoot: TrieHash) = - sp.setSyncLocked(number, hash) - - let thisBlock = sp.ns.pp(hash, number) - if sp.ctrl.stateRoot.isNone: - debug "Starting state sync from this peer", peer=sp, - thisBlock, stateRoot - elif sp.ctrl.stateRoot.unsafeGet != stateRoot: - trace "Adjusting state sync root from this peer", peer=sp, - thisBlock, stateRoot - - sp.ctrl.stateRoot = some(stateRoot) - - if not sp.hunt.startedFetch: - sp.hunt.startedFetch = true - trace "Starting to download block state", peer=sp, - thisBlock, stateRoot - asyncSpawn sp.fetch() - -proc setHuntBackward(sp: WorkerBuddy, lowestAbsent: BlockNumber) = - ## Start exponential search mode backward due to new uncertainty. - sp.hunt.syncMode = HuntBackward - sp.hunt.step = 0 - # Block zero is always present. - sp.hunt.lowNumber = 0.toBlockNumber - # Zero `lowestAbsent` is never correct, but an incorrect peer could send it. - sp.hunt.highNumber = if lowestAbsent > 0: lowestAbsent else: 1.toBlockNumber - sp.clearSyncStateRoot() - -proc setHuntForward(sp: WorkerBuddy, highestPresent: BlockNumber) = - ## Start exponential search mode forward due to new uncertainty. - sp.hunt.syncMode = HuntForward - sp.hunt.step = 0 - sp.hunt.lowNumber = highestPresent - sp.hunt.highNumber = high(BlockNumber) - sp.clearSyncStateRoot() - -proc updateHuntAbsent(sp: WorkerBuddy, lowestAbsent: BlockNumber) = - ## Converge uncertainty range backward. - if lowestAbsent < sp.hunt.highNumber: - sp.hunt.highNumber = lowestAbsent - # If uncertainty range has moved outside the search window, change to hunt - # backward to block zero. Note that empty uncertainty range is allowed - # (empty range is `hunt.lowNumber + 1 == hunt.highNumber`). - if sp.hunt.highNumber <= sp.hunt.lowNumber: - sp.setHuntBackward(lowestAbsent) - sp.clearSyncStateRoot() - -proc updateHuntPresent(sp: WorkerBuddy, highestPresent: BlockNumber) = - ## Converge uncertainty range forward. - if highestPresent > sp.hunt.lowNumber: - sp.hunt.lowNumber = highestPresent - # If uncertainty range has moved outside the search window, change to hunt - # forward to no upper limit. Note that empty uncertainty range is allowed - # (empty range is `hunt.lowNumber + 1 == hunt.highNumber`). - if sp.hunt.lowNumber >= sp.hunt.highNumber: - sp.setHuntForward(highestPresent) - sp.clearSyncStateRoot() - -# ------------------------------------------------------------------------------ -# Private functions, assemble request -# ------------------------------------------------------------------------------ - -proc peerSyncChainRequest(sp: WorkerBuddy): BlocksRequest = - ## Choose `GetBlockHeaders` parameters when hunting or following the canonical - ## chain of a peer. - if sp.hunt.syncMode == SyncLocked: - # Stable and locked. This is just checking for changes including reorgs. - # `sp.hunt.bestNumber` was recently the head of the peer's canonical - # chain. We must include this block number to detect when the canonical - # chain gets shorter versus no change. - result.startBlock.number = - if sp.hunt.bestNumber <= syncLockedQueryOverlap: - # Every peer should send genesis for block 0, so don't ask for it. - # `peerSyncChainEmptyReply` has logic to handle this reply as if it - # was for block 0. Aside from saving bytes, this is more robust if - # some client doesn't do genesis reply correctly. - 1.toBlockNumber - else: - min(sp.hunt.bestNumber - syncLockedQueryOverlap.toBlockNumber, - high(BlockNumber) - (syncLockedQuerySize - 1).toBlockNumber) - result.maxResults = syncLockedQuerySize +proc setPivotEnv(buddy: SnapBuddyRef; header: BlockHeader) = + ## Activate environment for state root implied by `header` argument + let + ctx = buddy.ctx + key = header.stateRoot + rc = ctx.data.pivotTable.lruFetch(key) + if rc.isOk: + ctx.data.pivotEnv = rc.value return - if sp.hunt.syncMode == SyncOnlyHash: - # We only have the hash of the recent head of the peer's canonical chain. - # Like `SyncLocked`, query more than one item to detect when the - # canonical chain gets shorter, no change or longer. - result.startBlock = sp.hunt.bestHash.to(HashOrNum) - result.maxResults = syncLockedQuerySize - return + let env = SnapPivotRef( + stateHeader: header, + pivotAccount: buddy.rndNodeTag, + availAccounts: LeafRangeSet.init()) + # Pre-filled with the largest possible interval + discard env.availAccounts.merge(low(NodeTag),high(NodeTag)) - # Searching for the peers's canonical head. An ascending query is always - # used, regardless of search direction. This is because a descending query - # (`reverse = true` and `maxResults > 1`) is useless for searching: Either - # `startBlock` is present, in which case the extra descending results - # contribute no more information about the canonical head boundary, or - # `startBlock` is absent in which case there are zero results. It's not - # defined in the `eth` specification that there must be zero results (in - # principle peers could return the lower numbered blocks), but in practice - # peers stop at the first absent block in the sequence from `startBlock`. - # - # Guaranteeing O(log N) time convergence in all scenarios requires some - # properties to be true in both exponential search (expanding) and - # quasi-binary search (converging in a range). The most important is that - # the gap to `startBlock` after `hunt.lowNumber` and also before - # `hunt.highNumber` are proportional to the query step, where the query step - # is `hunt.step` exponentially expanding each round, or `maxStep` - # approximately evenly distributed in the range. - # - # `hunt.lowNumber+1` must not be used consistently as the start, even with a - # large enough query step size, as that will sometimes take O(N) to converge - # in both the exponential and quasi-binary searches. (Ending at - # `hunt.highNumber-1` is fine if `huntQuerySize > 1`. This asymmetry is - # due to ascending queries (see earlier comment), and non-empty truncated - # query reply being proof of presence before the truncation point, but not - # proof of absence after it. A reply can be truncated just because the peer - # decides to.) - # - # The proportional gap requirement is why we divide by query size here, - # instead of stretching to fit more strictly with `(range-1)/(size-1)`. + # Statistics + ctx.data.pivotCount.inc - const huntFinalSize = max(2, huntQuerySize) - var maxStep = 0u - - let fullRangeClamped = - if sp.hunt.highNumber <= sp.hunt.lowNumber: 0u - else: min(high(uint).toBlockNumber, - sp.hunt.highNumber - sp.hunt.lowNumber).truncate(uint) - 1 - - if fullRangeClamped >= huntFinalSize: # `HuntRangeFinal` condition. - maxStep = if huntQuerySize == 1: - fullRangeClamped - elif (huntQuerySize and (huntQuerySize-1)) == 0: - fullRangeClamped shr fastLog2(huntQuerySize) - else: - fullRangeClamped div huntQuerySize - doAssert huntFinalSize >= huntQuerySize - doAssert maxStep >= 1 # Ensured by the above assertion. - - # Check for exponential search (expanding). Iterate `hunt.step`. O(log N) - # requires `startBlock` to be offset from `hunt.lowNumber`/`hunt.highNumber`. - if sp.hunt.syncMode in {HuntForward, HuntBackward} and - fullRangeClamped >= huntFinalSize: - let forward = sp.hunt.syncMode == HuntForward - let expandShift = if forward: huntForwardExpandShift - else: huntBackwardExpandShift - # Switches to range search when this condition is no longer true. - if sp.hunt.step < maxStep shr expandShift: - # The `if` above means the next line cannot overflow. - sp.hunt.step = if sp.hunt.step > 0: sp.hunt.step shl expandShift else: 1 - # Satisfy the O(log N) convergence conditions. - result.startBlock.number = - if forward: sp.hunt.lowNumber + sp.hunt.step.toBlockNumber - else: sp.hunt.highNumber - (sp.hunt.step * huntQuerySize).toBlockNumber - result.maxResults = huntQuerySize - result.skip = sp.hunt.step - 1 - return - - # For tracing/display. - sp.hunt.step = maxStep - sp.hunt.syncMode = HuntRange - if maxStep > 0: - # Quasi-binary search (converging in a range). O(log N) requires - # `startBlock` to satisfy the constraints described above, with the - # proportionality from both ends of the range. The optimal information - # gathering position is tricky and doesn't make much difference, so don't - # bother. We'll centre the query in the range. - var offset = fullRangeClamped - maxStep * (huntQuerySize-1) - # Rounding must bias towards end to ensure `offset >= 1` after this. - offset -= offset shr 1 - result.startBlock.number = sp.hunt.lowNumber + offset.toBlockNumber - result.maxResults = huntQuerySize - result.skip = maxStep - 1 - else: - # Small range, final step. At `fullRange == 0` we must query at least one - # block before and after the range to confirm the canonical head boundary, - # or find it has moved. This ensures progress without getting stuck. When - # `fullRange` is small this is also beneficial, to get `SyncLocked` in one - # round trip from hereand it simplifies the other search branches below. - # Ideally the query is similar to `SyncLocked`, enough to get `SyncLocked` - # in one round trip, and accommodate a small reorg or extension. - const afterSoftMax = syncLockedMinimumReply - syncLockedQueryOverlap - const beforeHardMax = syncLockedQueryOverlap - let extra = huntFinalSize - fullRangeClamped - var before = (extra + 1) shr 1 - before = max(before + afterSoftMax, extra) - afterSoftMax - before = min(before, beforeHardMax) - # See `SyncLocked` case. - result.startBlock.number = - if sp.hunt.bestNumber <= before.toBlockNumber: 1.toBlockNumber - else: min(sp.hunt.bestNumber - before.toBlockNumber, - high(BlockNumber) - (huntFinalSize - 1).toBlockNumber) - result.maxResults = huntFinalSize - sp.hunt.syncMode = HuntRangeFinal - -# ------------------------------------------------------------------------------ -# Private functions, reply handling -# ------------------------------------------------------------------------------ - -proc peerSyncChainEmptyReply( - sp: WorkerBuddy, - request: BlocksRequest) = - ## Handle empty `GetBlockHeaders` reply. This means `request.startBlock` is - ## absent on the peer. If it was `SyncLocked` there must have been a reorg - ## and the previous canonical chain head has disappeared. If hunting, this - ## updates the range of uncertainty. - - # Treat empty response to a request starting from block 1 as equivalent to - # length 1 starting from block 0 in `peerSyncChainNonEmptyReply`. We treat - # every peer as if it would send genesis for block 0, without asking for it. - if request.skip == 0 and - not request.reverse and - not request.startBlock.isHash and - request.startBlock.number == 1.toBlockNumber: - sp.lockSyncStateAndFetch( - 0.toBlockNumber, - sp.peer.network.chain.genesisHash.BlockHash, - sp.peer.network.chain.Chain.genesisStateRoot.TrieHash) - return - - if sp.hunt.syncMode in {SyncLocked, SyncOnlyHash}: - inc sp.stats.ok.reorgDetected - trace "Peer reorg detected, best block disappeared", peer=sp, - startBlock=request.startBlock - - let lowestAbsent = request.startBlock.number - case sp.hunt.syncMode: - of SyncLocked: - # If this message doesn't change our knowledge, ignore it. - if lowestAbsent > sp.hunt.bestNumber: - return - # Due to a reorg, peer's canonical head has lower block number, outside - # our tracking window. Sync lock is no longer valid. Switch to hunt - # backward to find the new canonical head. - sp.setHuntBackward(lowestAbsent) - of SyncOnlyHash: - # Due to a reorg, peer doesn't have the block hash it originally gave us. - # Switch to hunt forward from block zero to find the canonical head. - sp.setHuntForward(0.toBlockNumber) - of HuntForward, HuntBackward, HuntRange, HuntRangeFinal: - # Update the hunt range. - sp.updateHuntAbsent(lowestAbsent) - - # Update best block number. It is invalid except when `SyncLocked`, but - # still useful as a hint of what we knew recently, for example in displays. - if lowestAbsent <= sp.hunt.bestNumber: - sp.hunt.bestNumber = - if lowestAbsent == 0.toBlockNumber: lowestAbsent - else: lowestAbsent - 1.toBlockNumber - sp.hunt.bestHash = default(typeof(sp.hunt.bestHash)) - sp.ns.seen(sp.hunt.bestHash,sp.hunt.bestNumber) + ctx.data.pivotEnv = ctx.data.pivotTable.lruAppend(key, env, ctx.buddiesMax) + # ----- + if ctx.data.proofDumpOk: + let peer = buddy.peer + trace "Snap proofs dump enabled", peer + ctx.data.proofDumpOk = false + env.proofDumpOk = true + #env.pivotAccount = 0.to(NodeTag) -proc peerSyncChainNonEmptyReply( - sp: WorkerBuddy, - request: BlocksRequest, - headers: openArray[BlockHeader]) = - ## Handle non-empty `GetBlockHeaders` reply. This means `request.startBlock` - ## is present on the peer and in its canonical chain (unless the request was - ## made with a hash). If it's a short, contiguous, ascending order reply, it - ## reveals the abrupt transition at the end of the chain and we have learned - ## or reconfirmed the real-time head block. If hunting, this updates the - ## range of uncertainty. +proc updatePivotEnv(buddy: SnapBuddyRef): bool = + ## Update global state root environment from local `pivotHeader`. Choose the + ## latest block number. Returns `true` if the environment was changed + if buddy.data.pivotHeader.isSome: + let + ctx = buddy.ctx + newStateNumber = buddy.data.pivotHeader.unsafeGet.blockNumber + stateNumber = if ctx.data.pivotEnv.isNil: 0.toBlockNumber + else: ctx.data.pivotEnv.stateHeader.blockNumber + if stateNumber + maxPivotBlockWindow < newStateNumber: + buddy.setPivotEnv(buddy.data.pivotHeader.get) + return true - let len = headers.len - let highestIndex = if request.reverse: 0 else: len - 1 - # We assume a short enough reply means we've learned the peer's canonical - # head, because it would have replied with another header if not at the head. - # This is not justified when the request used a general hash, because the - # peer doesn't have to reply with its canonical chain in that case, except it - # is still justified if the hash was the known canonical head, which is - # the case in a `SyncOnlyHash` request. - if len < syncLockedMinimumReply and - request.skip == 0 and not request.reverse and - len.uint < request.maxResults: - sp.lockSyncStateAndFetch( - headers[highestIndex].blockNumber, - headers[highestIndex].blockHash.BlockHash, - headers[highestIndex].stateRoot.TrieHash) - return +proc tickerUpdate*(ctx: SnapCtxRef): TickerStatsUpdater = + result = proc: TickerStats = + var + aSum, aSqSum, uSum, uSqSum: float + count = 0 + for kvp in ctx.data.pivotTable.nextPairs: - # Be careful, this number is from externally supplied data and arithmetic - # in the upward direction could overflow. - let highestPresent = headers[highestIndex].blockNumber + # Accounts mean & variance + let aLen = kvp.data.nAccounts.float + if 0 < aLen: + count.inc + aSum += aLen + aSqSum += aLen * aLen - # A reply that isn't short enough for the canonical head criterion above - # tells us headers up to some number, but it doesn't tell us if there are - # more after it in the peer's canonical chain. We have to request more - # headers to find out. - case sp.hunt.syncMode: - of SyncLocked: - # If this message doesn't change our knowledge, ignore it. - if highestPresent <= sp.hunt.bestNumber: - return - # Sync lock is no longer valid as we don't have confirmed canonical head. - # Switch to hunt forward to find the new canonical head. - sp.setHuntForward(highestPresent) - of SyncOnlyHash: - # As `SyncLocked` but without the block number check. - sp.setHuntForward(highestPresent) - of HuntForward, HuntBackward, HuntRange, HuntRangeFinal: - # Update the hunt range. - sp.updateHuntPresent(highestPresent) + # Fill utilisation mean & variance + let fill = kvp.data.availAccounts.freeFactor + uSum += fill + uSqSum += fill * fill - # Update best block number. It is invalid except when `SyncLocked`, but - # still useful as a hint of what we knew recently, for example in displays. - if highestPresent > sp.hunt.bestNumber: - sp.hunt.bestNumber = highestPresent - sp.hunt.bestHash = headers[highestIndex].blockHash.BlockHash - sp.ns.seen(sp.hunt.bestHash,sp.hunt.bestNumber) + let + tabLen = ctx.data.pivotTable.len + pivotBlock = if ctx.data.pivotEnv.isNil: none(BlockNumber) + else: some(ctx.data.pivotEnv.stateHeader.blockNumber) + TickerStats( + pivotBlock: pivotBlock, + activeQueues: tabLen, + flushedQueues: ctx.data.pivotCount.int64 - tabLen, + accounts: meanStdDev(aSum, aSqSum, count), + fillFactor: meanStdDev(uSum, uSqSum, count)) # ------------------------------------------------------------------------------ # Public start/stop and admin functions # ------------------------------------------------------------------------------ -proc workerSetup*(ns: Worker; chainDb: AbstractChainDB) = +proc setup*(ctx: SnapCtxRef; tickerOK: bool): bool = ## Global set up - ns.fetchSetup(chainDb) + ctx.data.accountRangeMax = high(UInt256) div ctx.buddiesMax.u256 + ctx.data.accountsDb = AccountsDbRef.init(ctx.chain.getTrieDB) + if tickerOK: + ctx.data.ticker = TickerRef.init(ctx.tickerUpdate) + else: + trace "Ticker is disabled" + # ---- + if snapAccountsDumpEnable: + doAssert ctx.data.proofDumpFile.open("./dump-stream.out", fmWrite) + ctx.data.proofDumpOk = true + # ---- + true -proc workerRelease*(ns: Worker) = +proc release*(ctx: SnapCtxRef) = ## Global clean up - ns.fetchRelease() + if not ctx.data.ticker.isNil: + ctx.data.ticker.stop() + ctx.data.ticker = nil -proc workerStart*(sp: WorkerBuddy): bool = - ## Initialise `WorkerBuddy` to support `workerBlockHeaders()` calls - if sp.peer.supports(protocol.snap) and - sp.peer.supports(protocol.eth) and - sp.peer.state(protocol.eth).initialized: - - sp.ctrl.init(running = true) - - # Initialise data retrieval - sp.fetchStart() - - # Link in hunt descriptor - sp.hunt = WorkerHuntEx.new(HuntForward) - - # We know the hash but not the block number. - sp.hunt.bestHash = sp.peer.state(protocol.eth).bestBlockHash.BlockHash - # TODO: Temporarily disabled because it's useful to test the worker. - # sp.syncMode = SyncOnlyHash +proc start*(buddy: SnapBuddyRef): bool = + ## Initialise worker peer + let + ctx = buddy.ctx + peer = buddy.peer + if peer.supports(protocol.snap) and + peer.supports(protocol.eth) and + peer.state(protocol.eth).initialized: + buddy.pivotStart() + if not ctx.data.ticker.isNil: + ctx.data.ticker.startBuddy() return true -proc workerStop*(sp: WorkerBuddy) = +proc stop*(buddy: SnapBuddyRef) = ## Clean up this peer - if not sp.ctrl.stopped: - sp.ctrl.stopped = true - sp.fetchStop() - -proc workerLockedOk*(sp: WorkerBuddy): bool = - sp.hunt.syncMode == SyncLocked + let + ctx = buddy.ctx + peer = buddy.peer + buddy.ctrl.stopped = true + buddy.pivotStop() + if not ctx.data.ticker.isNil: + ctx.data.ticker.stopBuddy() # ------------------------------------------------------------------------------ # Public functions # ------------------------------------------------------------------------------ -proc workerExec*(sp: WorkerBuddy) {.async.} = - ## Query a peer to update our knowledge of its canonical chain and its best - ## block, which is its canonical chain head. This can be called at any time - ## after a peer has negotiated the connection. +proc runSingle*(buddy: SnapBuddyRef) {.async.} = + ## This peer worker is invoked if the peer-local flag `buddy.ctrl.multiOk` + ## is set `false` which is the default mode. This flag is updated by the + ## worker when deemed appropriate. + ## * For all workers, there can be only one `runSingle()` function active + ## simultaneously for all worker peers. + ## * There will be no `runMulti()` function active for the same worker peer + ## simultaneously + ## * There will be no `runPool()` iterator active simultaneously. ## - ## This function is called in an exponential then binary search style - ## during initial sync to find the canonical head, real-time polling - ## afterwards to check for updates. + ## Note that this function runs in `async` mode. ## - ## All replies to this query are part of the peer's canonical chain at the - ## time the peer sends them. + buddy.ctrl.multiOk = true - let request = sp.peerSyncChainRequest - trace trEthSendSendingGetBlockHeaders, peer=sp, - count=request.maxResults, - startBlock=sp.ns.pp(request.startBlock), step=request.traceStep +proc runPool*(buddy: SnapBuddyRef) = + ## Ocne started, the function `runPool()` is called for all worker peers in + ## a row (as the body of an iteration.) There will be no other worker peer + ## functions activated simultaneously. + ## + ## This procedure is started if the global flag `buddy.ctx.poolMode` is set + ## `true` (default is `false`.) It is the responsibility of the `runPool()` + ## instance to reset the flag `buddy.ctx.poolMode`, typically at the first + ## peer instance as the number of active instances is unknown to `runPool()`. + ## + ## Note that this function does not run in `async` mode. + ## + discard - inc sp.stats.ok.getBlockHeaders - var reply: Option[protocol.blockHeadersObj] - try: - reply = await sp.peer.getBlockHeaders(request) - except CatchableError as e: - trace trEthRecvError & "waiting for GetBlockHeaders reply", peer=sp, - error=e.msg - inc sp.stats.major.networkErrors - sp.workerStop() - return - if reply.isNone: - trace trEthRecvTimeoutWaiting & "for GetBlockHeaders reply", peer=sp - # TODO: Should disconnect? - inc sp.stats.minor.timeoutBlockHeaders - return +proc runMulti*(buddy: SnapBuddyRef) {.async.} = + ## This peer worker is invoked if the `buddy.ctrl.multiOk` flag is set + ## `true` which is typically done after finishing `runSingle()`. This + ## instance can be simultaneously active for all peer workers. + ## + let + ctx = buddy.ctx + peer = buddy.peer - let nHeaders = reply.get.headers.len - if nHeaders == 0: - trace trEthRecvReceivedBlockHeaders, peer=sp, - got=0, requested=request.maxResults - else: - trace trEthRecvReceivedBlockHeaders, peer=sp, - got=nHeaders, requested=request.maxResults, - firstBlock=reply.get.headers[0].blockNumber, - lastBlock=reply.get.headers[^1].blockNumber + if buddy.data.pivotHeader.isNone: - if request.maxResults.int < nHeaders: - trace trEthRecvProtocolViolation & "excess headers in BlockHeaders message", - peer=sp, got=nHeaders, requested=request.maxResults - # TODO: Should disconnect. - inc sp.stats.major.excessBlockHeaders - return + await buddy.pivotExec() - if 0 < nHeaders: - # TODO: Check this is not copying the `headers`. - sp.peerSyncChainNonEmptyReply(request, reply.get.headers) - else: - sp.peerSyncChainEmptyReply(request) + if not buddy.updatePivotEnv(): + return -# ------------------------------------------------------------------------------ -# Debugging -# ------------------------------------------------------------------------------ - -proc huntPp*(sn: WorkerBuddy): string = - let hx = sn.hunt - result &= "(mode=" & $hx.syncMode - result &= ",num=(" & hx.lowNumber.pp & "," & hx.highNumber.pp & ")" - result &= ",best=(" & hx.bestNumber.pp & "," & hx.bestHash.pp & ")" - result &= ",step=" & $hx.step - result &= ")" + if await buddy.fetchAccounts(): + buddy.ctrl.multiOk = false + buddy.data.pivotHeader = none(BlockHeader) # ------------------------------------------------------------------------------ # End diff --git a/nimbus/sync/snap/worker/accounts_db.nim b/nimbus/sync/snap/worker/accounts_db.nim new file mode 100644 index 000000000..43af821bf --- /dev/null +++ b/nimbus/sync/snap/worker/accounts_db.nim @@ -0,0 +1,1335 @@ +# +# Copyright (c) 2021 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, hashes, options, sequtils, sets, strutils, strformat, tables], + chronos, + eth/[common/eth_types, p2p, rlp], + eth/trie/[db, nibbles, trie_defs], + nimcrypto/keccak, + stew/byteutils, + stint, + "../../.."/[db/storage_types, constants], + "../.."/[protocol, types], + ../range_desc + +{.push raises: [Defect].} + +logScope: + topics = "snap-proof" + +const + BasicChainTrieEnabled = false # proof-of-concept code, currently unused + BasicChainTrieDebugging = false + + RepairTreeDebugging = false + + +type + AccountsDbError* = enum + NothingSerious = 0 + AccountSmallerThanBase + AccountsNotSrictlyIncreasing + AccountRepairBlocked + Rlp2Or17ListEntries + RlpBlobExpected + RlpBranchLinkExpected + RlpEncoding + RlpExtPathEncoding + RlpNonEmptyBlobExpected + BoundaryProofFailed + + ByteArray32* = + array[32,byte] + + ByteArray33 = + array[33,byte] + + NodeKey = ## Internal DB record reference type + distinct ByteArray32 + + RepairKey = ## Internal DB record, `byte & NodeKey` + distinct ByteArray33 + + RNodeKind = enum + Branch + Extension + Leaf + + RNodeState = enum + Static = 0 ## Inserted as proof record + Locked ## Like `Static`, only added on-the-fly + Mutable ## Open for modification + + RNodeRef = ref object + ## For building a temporary repair tree + state: RNodeState ## `Static` if added as proof data + case kind: RNodeKind + of Leaf: + lPfx: NibblesSeq ## Portion of path segment + lData: Blob + of Extension: + ePfx: NibblesSeq ## Portion of path segment + eLink: RepairKey ## Single down link + of Branch: + bLink: array[16,RepairKey] ## Down links + bData: Blob + + RPathStep = object + ## For constructing tree traversal `seq[RPathStep]` path + key: RepairKey ## Tree label, node hash + node: RNodeRef ## Referes to data record + nibble: int8 ## Branch node selector (if any) + + RPathXStep = object + ## Extended `RPathStep` needed for `NodeKey` assignmant + pos: int ## Some position into `seq[RPathStep]` + step: RPathStep ## Modified copy of an `RPathStep` + canLock: bool ## Can set `Locked` state + + RPath = object + path: seq[RPathStep] + tail: NibblesSeq ## Stands for non completed leaf path + + RAccount = object + ## Temporarily stashed account data. Proper account records have non-empty + ## payload. Records with empty payload are lower boundary records. + tag: NodeTag ## Equivalent to account hash + key: RepairKey ## Leaf hash into hexary repair table + payload: Blob ## Data payload + + RepairTreeDB = object + tab: Table[RepairKey,RNodeRef] ## Repair table + acc: seq[RAccount] ## Accounts to appprove of + repairKeyGen: uint64 ## Unique tmp key generator + + AccountsDbRef* = ref object + db: TrieDatabaseRef ## General database + + AccountsDbSessionRef* = ref object + #dbTx: DbTransaction ## TBD + keyMap: Table[RepairKey,uint] ## For debugging only (will go away) + base: AccountsDbRef ## Back reference to common parameters + rootKey: NodeKey ## Current root node + peer: Peer ## For log messages + rnDB: RepairTreeDB ## Repair database + +const + EmptyBlob = seq[byte].default + EmptyNibbleRange = EmptyBlob.initNibbleRange + +static: + # Not that there is no doubt about this ... + doAssert NodeKey.default.ByteArray32.initNibbleRange.len == 64 + +# ------------------------------------------------------------------------------ +# Private helpers +# ------------------------------------------------------------------------------ + +proc to(tag: NodeTag; T: type NodeKey): T = + tag.UInt256.toBytesBE.T + +proc to(key: NodeKey; T: type UInt256): T = + T.fromBytesBE(key.ByteArray32) + +proc to(key: NodeKey; T: type NodeTag): T = + key.to(UInt256).T + +proc to(h: Hash256; T: type NodeKey): T = + h.data.T + +proc to(key: NodeKey; T: type NibblesSeq): T = + key.ByteArray32.initNibbleRange + +proc to(key: NodeKey; T: type RepairKey): T = + (addr result.ByteArray33[1]).copyMem(unsafeAddr key.ByteArray32[0], 32) + +proc to(tag: NodeTag; T: type RepairKey): T = + tag.to(NodeKey).to(RepairKey) + +proc isZero[T: NodeTag|NodeKey|RepairKey](a: T): bool = + a == T.default + +proc `==`(a, b: NodeKey): bool = + a.ByteArray32 == b.ByteArray32 + +proc `==`(a, b: RepairKey): bool = + a.ByteArray33 == b.ByteArray33 + +proc hash(a: NodeKey): Hash = + a.ByteArray32.hash + +proc hash(a: RepairKey): Hash = + a.ByteArray33.hash + +proc digestTo(data: Blob; T: type NodeKey): T = + keccak256.digest(data).data.T + +proc isNodeKey(a: RepairKey): bool = + a.ByteArray33[0] == 0 + +proc newRepairKey(ps: AccountsDbSessionRef): RepairKey = + ps.rnDB.repairKeyGen.inc + var src = ps.rnDB.repairKeyGen.toBytesBE + (addr result.ByteArray33[25]).copyMem(addr src[0], 8) + result.ByteArray33[0] = 1 + +proc init(key: var NodeKey; data: openArray[byte]): bool = + key.reset + if data.len <= 32: + if 0 < data.len: + let trg = addr key.ByteArray32[32 - data.len] + trg.copyMem(unsafeAddr data[0], data.len) + return true + +proc dup(node: RNodeRef): RNodeRef = + new result + result[] = node[] + +proc convertTo(data: openArray[byte]; T: type NodeKey): T = + discard result.init(data) + +proc convertTo(key: RepairKey; T: type NodeKey): T = + if key.isNodeKey: + discard result.init(key.ByteArray33[1 .. 32]) + +proc convertTo(node: RNodeRef; T: type Blob): T = + var writer = initRlpWriter() + + proc appendOk(writer: var RlpWriter; key: RepairKey): bool = + if key.isZero: + writer.append(EmptyBlob) + elif key.isNodeKey: + var hash: Hash256 + (addr hash.data[0]).copyMem(unsafeAddr key.ByteArray33[1], 32) + writer.append(hash) + else: + return false + true + + case node.kind: + of Branch: + writer.startList(17) + for n in 0 ..< 16: + if not writer.appendOk(node.bLink[n]): + return # empty `Blob` + writer.append(node.bData) + of Extension: + writer.startList(2) + writer.append(node.ePfx.hexPrefixEncode(isleaf = false)) + if not writer.appendOk(node.eLink): + return # empty `Blob` + of Leaf: + writer.startList(2) + writer.append(node.lPfx.hexPrefixEncode(isleaf = true)) + writer.append(node.lData) + + writer.finish() + + +template noKeyError(info: static[string]; code: untyped) = + try: + code + except KeyError as e: + raiseAssert "Not possible (" & info & "): " & e.msg + +# ------------------------------------------------------------------------------ +# Private getters & setters +# ------------------------------------------------------------------------------ + +proc xPfx(node: RNodeRef): NibblesSeq = + case node.kind: + of Leaf: + return node.lPfx + of Extension: + return node.ePfx + of Branch: + doAssert node.kind != Branch # Ooops + +proc `xPfx=`(node: RNodeRef, val: NibblesSeq) = + case node.kind: + of Leaf: + node.lPfx = val + of Extension: + node.ePfx = val + of Branch: + doAssert node.kind != Branch # Ooops + +proc xData(node: RNodeRef): Blob = + case node.kind: + of Branch: + return node.bData + of Leaf: + return node.lData + of Extension: + doAssert node.kind != Extension # Ooops + +proc `xData=`(node: RNodeRef; val: Blob) = + case node.kind: + of Branch: + node.bData = val + of Leaf: + node.lData = val + of Extension: + doAssert node.kind != Extension # Ooops + +# ------------------------------------------------------------------------------ +# Private debugging helpers +# ------------------------------------------------------------------------------ + +template noPpError(info: static[string]; code: untyped) = + try: + code + except ValueError as e: + raiseAssert "Inconveivable (" & info & "): " & e.msg + except KeyError as e: + raiseAssert "Not possible (" & info & "): " & e.msg + +proc pp(s: string; hex = false): string = + if hex: + let n = (s.len + 1) div 2 + (if s.len < 20: s else: s[0 .. 5] & ".." & s[s.len-8 .. s.len-1]) & + "[" & (if 0 < n: "#" & $n else: "") & "]" + elif s.len <= 30: + s + else: + (if (s.len and 1) == 0: s[0 ..< 8] else: "0" & s[0 ..< 7]) & + "..(" & $s.len & ").." & s[s.len-16 ..< s.len] + +proc pp(a: Hash256; collapse = true): string = + if not collapse: + a.data.mapIt(it.toHex(2)).join.toLowerAscii + elif a == emptyRlpHash: + "emptyRlpHash" + elif a == blankStringHash: + "blankStringHash" + else: + a.data.mapIt(it.toHex(2)).join[56 .. 63].toLowerAscii + +proc pp(a: NodeKey; collapse = true): string = + Hash256(data: a.ByteArray32).pp(collapse) + +# --------- + +proc toKey(a: RepairKey; ps: AccountsDbSessionRef): uint = + if not a.isZero: + noPpError("pp(RepairKey)"): + if not ps.keyMap.hasKey(a): + ps.keyMap[a] = ps.keyMap.len.uint + 1 + result = ps.keyMap[a] + +proc toKey(a: NodeKey; ps: AccountsDbSessionRef): uint = + a.to(RepairKey).toKey(ps) + +proc toKey(a: NodeTag; ps: AccountsDbSessionRef): uint = + a.to(NodeKey).toKey(ps) + + +proc pp(a: NodeKey; ps: AccountsDbSessionRef): string = + if a.isZero: "ø" else:"$" & $a.toKey(ps) + +proc pp(a: RepairKey; ps: AccountsDbSessionRef): string = + if a.isZero: "ø" elif a.isNodeKey: "$" & $a.toKey(ps) else: "¶" & $a.toKey(ps) + +proc pp(a: NodeTag; ps: AccountsDbSessionRef): string = + a.to(NodeKey).pp(ps) + +# --------- + +proc pp(q: openArray[byte]; noHash = false): string = + if q.len == 32 and not noHash: + var a: array[32,byte] + for n in 0..31: a[n] = q[n] + ($Hash256(data: a)).pp + else: + q.toSeq.mapIt(it.toHex(2)).join.toLowerAscii.pp(hex = true) + +proc pp(blob: Blob): string = + blob.mapIt(it.toHex(2)).join + +proc pp(a: Account): string = + noPpError("pp(Account)"): + result = &"({a.nonce},{a.balance},{a.storageRoot},{a.codeHash})" + +proc pp(sa: SnapAccount): string = + "(" & $sa.accHash & "," & sa.accBody.pp & ")" + +proc pp(al: seq[SnapAccount]): string = + result = " @[" + noPpError("pp(seq[SnapAccount])"): + for n,rec in al: + result &= &"| # <{n}>| {rec.pp}," + if 10 < result.len: + result[^1] = ']' + else: + result &= "]" + +proc pp(blobs: seq[Blob]): string = + result = " @[" + noPpError("pp(seq[Blob])"): + for n,rec in blobs: + result &= "| # <" & $n & ">| \"" & rec.pp & "\".hexToSeqByte," + if 10 < result.len: + result[^1] = ']' + else: + result &= "]" + +proc pp(branch: array[17,Blob]; ps: AccountsDbSessionRef): string = + result = "[" + noPpError("pp(array[17,Blob])"): + for a in 0 .. 15: + result &= branch[a].convertTo(NodeKey).pp(ps) & "," + result &= branch[16].pp & "]" + +proc pp(branch: array[16,RepairKey]; ps: AccountsDbSessionRef): string = + result = "[" + noPpError("pp(array[17,Blob])"): + for a in 0 .. 15: + result &= branch[a].pp(ps) & "," + result[^1] = ']' + +proc pp(hs: seq[NodeKey]; ps: AccountsDbSessionRef): string = + "<" & hs.mapIt(it.pp(ps)).join(",") & ">" + +proc pp(hs: HashSet[NodeKey]; ps: AccountsDbSessionRef): string = + "{" & + toSeq(hs.items).mapIt(it.toKey(ps)).sorted.mapIt("$" & $it).join(",") & "}" + +proc pp(w: NibblesSeq): string = + $w + +proc pp(n: RNodeRef; ps: AccountsDbSessionRef): string = + proc ppStr(blob: Blob): string = + if blob.len == 0: "" else: blob.pp.pp(hex = true) + noPpError("pp(RNodeRef)"): + let so = n.state.ord + case n.kind: + of Leaf: + result = ["l","ł","L"][so] & &"({n.lPfx.pp},{n.lData.ppStr})" + of Extension: + result = ["e","€","E"][so] & &"({n.ePfx.pp},{n.eLink.pp(ps)})" + of Branch: + result = ["b","þ","B"][so] & &"({n.bLink.pp(ps)},{n.bData.ppStr})" + +proc pp(w: RPathStep; ps: AccountsDbSessionRef): string = + noPpError("pp(seq[(NodeKey,RNodeRef)])"): + let nibble = if 0 <= w.nibble: &"{w.nibble:x}" else: "ø" + result = &"({w.key.pp(ps)},{nibble},{w.node.pp(ps)})" + +proc pp(w: openArray[RPathStep]; ps: AccountsDbSessionRef; indent = 4): string = + let pfx = "\n" & " ".repeat(indent) + noPpError("pp(seq[(NodeKey,RNodeRef)])"): + result = w.toSeq.mapIt(it.pp(ps)).join(pfx) + +proc pp(w: RPath; ps: AccountsDbSessionRef; indent = 4): string = + let pfx = "\n" & " ".repeat(indent) + noPpError("pp(RPath)"): + result = w.path.pp(ps,indent) & &"{pfx}({w.tail.pp})" + +proc pp(w: RPathXStep; ps: AccountsDbSessionRef): string = + noPpError("pp(seq[(int,RPathStep)])"): + let y = if w.canLock: "lockOk" else: "noLock" + result = &"({w.pos},{y},{w.step.pp(ps)})" + +proc pp(w: seq[RPathXStep]; ps: AccountsDbSessionRef; indent = 4): string = + let pfx = "\n" & " ".repeat(indent) + noPpError("pp(seq[RPathXStep])"): + result = w.mapIt(it.pp(ps)).join(pfx) + +# ------------------------------------------------------------------------------ +# Private functions +# ------------------------------------------------------------------------------ + +# Example trie from https://eth.wiki/en/fundamentals/patricia-tree +# +# lookup data: +# "do": "verb" +# "dog": "puppy" +# "dodge": "coin" +# "horse": "stallion" +# +# trie DB: +# root: [16 A] +# A: [* * * * B * * * [20+"orse" "stallion"] * * * * * * * *] +# B: [00+"o" D] +# D: [* * * * * * E * * * * * * * * * "verb"] +# E: [17 [* * * * * * [35 "coin"] * * * * * * * * * "puppy"]] +# +# with first nibble of two-column rows: +# hex bits | node type length +# ---------+------------------ +# 0 0000 | extension even +# 1 0001 | extension odd +# 2 0010 | leaf even +# 3 0011 | leaf odd +# +# and key path: +# "do": 6 4 6 f +# "dog": 6 4 6 f 6 7 +# "dodge": 6 4 6 f 6 7 6 5 +# "horse": 6 8 6 f 7 2 7 3 6 5 +# + +proc hexaryImport( + ps: AccountsDbSessionRef; + recData: Blob + ): Result[void,AccountsDbError] + {.gcsafe, raises: [Defect, RlpError].} = + ## Decode a single trie item for adding to the table and add it to the + ## database. Branch and exrension record links are collected. + let + nodeKey = recData.digestTo(NodeKey) + repairKey = nodeKey.to(RepairKey) # for repair table + var + rlp = recData.rlpFromBytes + blobs = newSeq[Blob](2) # temporary, cache + links: array[16,RepairKey] # reconstruct branch node + blob16: Blob # reconstruct branch node + top = 0 # count entries + rNode: RNodeRef # repair tree node + + # Collect lists of either 2 or 17 blob entries. + for w in rlp.items: + case top + of 0, 1: + if not w.isBlob: + return err(RlpBlobExpected) + blobs[top] = rlp.read(Blob) + of 2 .. 15: + var key: NodeKey + if not key.init(rlp.read(Blob)): + return err(RlpBranchLinkExpected) + # Update ref pool + links[top] = key.to(RepairKey) + of 16: + if not w.isBlob: + return err(RlpBlobExpected) + blob16 = rlp.read(Blob) + else: + return err(Rlp2Or17ListEntries) + top.inc + + # Verify extension data + case top + of 2: + if blobs[0].len == 0: + return err(RlpNonEmptyBlobExpected) + let (isLeaf, pathSegment) = hexPrefixDecode blobs[0] + if isLeaf: + rNode = RNodeRef( + kind: Leaf, + lPfx: pathSegment, + lData: blobs[1]) + else: + var key: NodeKey + if not key.init(blobs[1]): + return err(RlpExtPathEncoding) + # Update ref pool + rNode = RNodeRef( + kind: Extension, + ePfx: pathSegment, + eLink: key.to(RepairKey)) + of 17: + for n in [0,1]: + var key: NodeKey + if not key.init(blobs[n]): + return err(RlpBranchLinkExpected) + # Update ref pool + links[n] = key.to(RepairKey) + rNode = RNodeRef( + kind: Branch, + bLink: links, + bData: blob16) + else: + discard + + # Add to repair database + ps.rnDB.tab[repairKey] = rNode + + # Add to hexary trie database -- disabled, using bulk import later + #ps.base.db.put(nodeKey.ByteArray32, recData) + + when RepairTreeDebugging: + # Rebuild blob from repair record + let nodeBlob = rNode.convertTo(Blob) + if nodeBlob != recData: + echo "*** hexaryImport oops:", + " kind=", rNode.kind, + " key=", repairKey.pp(ps), + " nodeBlob=", nodeBlob.pp, + " recData=", recData.pp + doAssert nodeBlob == recData + + ok() + +# ------------------------------------------------------------------------------ +# Private functions, repair tree action helpers +# ------------------------------------------------------------------------------ + +proc rTreeExtendLeaf( + ps: AccountsDbSessionRef; + rPath: RPath; + key: RepairKey + ): RPath = + ## Append a `Leaf` node to a `Branch` node (see `rTreeExtend()`.) + if 0 < rPath.tail.len: + let + nibble = rPath.path[^1].nibble + leaf = RNodeRef( + state: Mutable, + kind: Leaf, + lPfx: rPath.tail) + ps.rnDB.tab[key] = leaf + if not key.isNodeKey: + rPath.path[^1].node.bLink[nibble] = key + return RPath( + path: rPath.path & RPathStep(key: key, node: leaf, nibble: -1), + tail: EmptyNibbleRange) + +proc rTreeExtendLeaf( + ps: AccountsDbSessionRef; + rPath: RPath; + key: RepairKey; + node: RNodeRef + ): RPath = + ## Register `node` and append/link a `Leaf` node to a `Branch` node (see + ## `rTreeExtend()`.) + if 1 < rPath.tail.len and node.state == Mutable: + let + nibble = rPath.tail[0].int8 + xStep = RPathStep(key: key, node: node, nibble: nibble) + xPath = RPath(path: rPath.path & xStep, tail: rPath.tail.slice(1)) + return ps.rTreeExtendLeaf(xPath, ps.newRepairKey()) + + +proc rTreeSplitNode( + ps: AccountsDbSessionRef; + rPath: RPath; + key: RepairKey; + node: RNodeRef + ): RPath = + ## Replace `Leaf` or `Extension` node in tuple `(key,node)` by parts (see + ## `rTreeExtend()`): + ## + ## left(Extension) -> middle(Branch) -> right(Extension or Leaf) + ## ^ ^ + ## | | + ## added-to-path added-to-path + ## + ## where either `left()` or `right()` extensions might be missing. + ## + let + nibbles = node.xPfx + lLen = rPath.tail.sharedPrefixLen(nibbles) + if nibbles.len == 0 or rPath.tail.len <= lLen: + return # Ooops (^^^^^ otherwise `rPath` was not the longest) + var + mKey = key + let + mNibble = nibbles[lLen] # exists as `lLen < tail.len` + rPfx = nibbles.slice(lLen + 1) # might be empty OK + + result = rPath + + # Insert node (if any): left(Extension) + if 0 < lLen: + let lNode = RNodeRef( + state: Mutable, + kind: Extension, + ePfx: result.tail.slice(0,lLen), + eLink: ps.newRepairKey()) + ps.rnDB.tab[key] = lNode + result.path.add RPathStep(key: key, node: lNode, nibble: -1) + result.tail = result.tail.slice(lLen) + mKey = lNode.eLink + + # Insert node: middle(Branch) + let mNode = RNodeRef( + state: Mutable, + kind: Branch) + ps.rnDB.tab[mKey] = mNode + result.path.add RPathStep(key: mKey, node: mNode, nibble: -1) # no nibble yet + + # Insert node (if any): right(Extension) -- not to be registered in `rPath` + if 0 < rPfx.len: + let rKey = ps.newRepairKey() + # Re-use argument node + mNode.bLink[mNibble] = rKey + ps.rnDB.tab[rKey] = node + node.xPfx = rPfx + # Otherwise merge argument node + elif node.kind == Extension: + mNode.bLink[mNibble] = node.eLink + else: + # Oops, does it make sense, at all? + mNode.bData = node.lData + +# ------------------------------------------------------------------------------ +# Private functions, repair tree actions +# ------------------------------------------------------------------------------ + +proc rTreeFollow(nodeKey: NodeKey; ps: AccountsDbSessionRef): RPath = + ## Compute logest possible path matching the `nodeKey` nibbles. + result.tail = nodeKey.to(NibblesSeq) + noKeyError("rTreeFollow"): + var key = ps.rootKey.to(RepairKey) + while ps.rnDB.tab.hasKey(key) and 0 < result.tail.len: + let node = ps.rnDB.tab[key] + case node.kind: + of Leaf: + if result.tail.len == result.tail.sharedPrefixLen(node.lPfx): + # Bingo, got full path + result.path.add RPathStep(key: key, node: node, nibble: -1) + result.tail = EmptyNibbleRange + return + of Branch: + let nibble = result.tail[0].int8 + if node.bLink[nibble].isZero: + return + result.path.add RPathStep(key: key, node: node, nibble: nibble) + result.tail = result.tail.slice(1) + key = node.bLink[nibble] + of Extension: + if node.ePfx.len != result.tail.sharedPrefixLen(node.ePfx): + return + result.path.add RPathStep(key: key, node: node, nibble: -1) + result.tail = result.tail.slice(node.ePfx.len) + key = node.eLink + +proc rTreeFollow(nodeTag: NodeTag; ps: AccountsDbSessionRef): RPath = + ## Variant of `rTreeFollow()` + nodeTag.to(NodeKey).rTreeFollow(ps) + + +proc rTreeInterpolate(rPath: RPath; ps: AccountsDbSessionRef): RPath = + ## Extend path, add missing nodes to tree. The last node added will be + ## a `Leaf` node if this function succeeds. + ## + ## The function assumed that the `RPath` argument is the longest possible + ## as just constructed by `rTreeFollow()` + if 0 < rPath.path.len and 0 < rPath.tail.len: + noKeyError("rTreeExtend"): + let step = rPath.path[^1] + case step.node.kind: + of Branch: + # Now, the slot must not be empty. An empty slot would lead to a + # rejection of this record as last valid step, contrary to the + # assumption `path` is the longest one. + if step.nibble < 0: + return # sanitary check failed + let key = step.node.bLink[step.nibble] + if key.isZero: + return # sanitary check failed + + # Case: unused slot => add leaf record + if not ps.rnDB.tab.hasKey(key): + return ps.rTreeExtendLeaf(rPath, key) + + # So a `child` node exits but it is something that could not be used to + # extend the argument `path` which is assumed the longest possible one. + let child = ps.rnDB.tab[key] + case child.kind: + of Branch: + # So a `Leaf` node can be linked into the `child` branch + return ps.rTreeExtendLeaf(rPath, key, child) + + # Need to split the right `grandChild` in `child -> grandChild` + # into parts: + # + # left(Extension) -> middle(Branch) + # | | + # | +-----> right(Extension or Leaf) ... + # +---------> new Leaf record + # + # where either `left()` or `right()` extensions might be missing + of Extension, Leaf: + var xPath = ps.rTreeSplitNode(rPath, key, child) + if 0 < xPath.path.len: + # Append `Leaf` node + xPath.path[^1].nibble = xPath.tail[0].int8 + xPath.tail = xPath.tail.slice(1) + return ps.rTreeExtendLeaf(xPath, ps.newRepairKey()) + of Leaf: + return # Oops + of Extension: + let key = step.node.eLink + + var child: RNodeRef + if ps.rnDB.tab.hasKey(key): + child = ps.rnDB.tab[key] + # `Extension` can only be followed by a `Branch` node + if child.kind != Branch: + return + else: + # Case: unused slot => add `Branch` and `Leaf` record + child = RNodeRef( + state: Mutable, + kind: Branch) + ps.rnDB.tab[key] = child + + # So a `Leaf` node can be linked into the `child` branch + return ps.rTreeExtendLeaf(rPath, key, child) + + +proc rTreeInterpolate( + rPath: RPath; + ps: AccountsDbSessionRef; + payload: Blob + ): RPath = + ## Variant of `rTreeExtend()` which completes a `Leaf` record. + result = rPath.rTreeInterpolate(ps) + if 0 < result.path.len and result.tail.len == 0: + let node = result.path[^1].node + if node.kind != Extension and node.state == Mutable: + node.xData = payload + + +proc rTreeUpdateKeys(rPath: RPath; ps: AccountsDbSessionRef): Result[void,int] = + ## The argument `rPath` is assumed to organise database nodes as + ## + ## root -> ... -> () -> () -> ... -> () -> () ... + ## |-------------| |------------| |------ + ## static nodes locked nodes mutable nodes + ## + ## Where + ## * Static nodes are read-only nodes provided by the proof database + ## * Locked nodes are added read-only nodes that satisfy the proof condition + ## * Mutable nodes are incomplete nodes + ## + ## Then update nodes from the right end and set all the mutable nodes + ## locked if possible. + var + rTop = rPath.path.len + stack: seq[RPathXStep] + + if 0 < rTop and + rPath.path[^1].node.state == Mutable and + rPath.path[0].node.state != Mutable: + + # Set `Leaf` entry + let leafNode = rPath.path[^1].node.dup + stack.add RPathXStep( + pos: rTop - 1, + canLock: true, + step: RPathStep( + node: leafNode, + key: leafNode.convertTo(Blob).digestTo(NodeKey).to(RepairKey), + nibble: -1)) + + while true: + rTop.dec + + # Update parent node (note that `2 <= rPath.path.len`) + let + thisKey = stack[^1].step.key + preStep = rPath.path[rTop-1] + preNibble = preStep.nibble + + # End reached + if preStep.node.state != Mutable: + + # Verify the tail matches + var key = RepairKey.default + case preStep.node.kind: + of Branch: + key = preStep.node.bLink[preNibble] + of Extension: + key = preStep.node.eLink + of Leaf: + discard + if key != thisKey: + return err(rTop-1) + + when RepairTreeDebugging: + echo "*** rTreeUpdateKeys", + " rPath\n ", rPath.pp(ps), + "\n stack\n ", stack.pp(ps) + + # Ok, replace database records by stack entries + var lockOk = true + for n in countDown(stack.len-1,0): + let item = stack[n] + ps.rnDB.tab.del(rPath.path[item.pos].key) + ps.rnDB.tab[item.step.key] = item.step.node + if lockOk: + if item.canLock: + item.step.node.state = Locked + else: + lockOk = false + if not lockOk: + return err(rTop-1) # repeat + break # Done ok() + + stack.add RPathXStep( + pos: rTop - 1, + step: RPathStep( + node: preStep.node.dup, # (!) + nibble: preNibble, + key: preStep.key)) + + case stack[^1].step.node.kind: + of Branch: + stack[^1].step.node.bLink[preNibble] = thisKey + # Check whether all keys are proper, non-temporary keys + stack[^1].canLock = true + for n in 0 ..< 16: + if not stack[^1].step.node.bLink[n].isNodeKey: + stack[^1].canLock = false + break + of Extension: + stack[^1].step.node.eLink = thisKey + stack[^1].canLock = thisKey.isNodeKey + of Leaf: + return err(rTop-1) + + # Must not overwrite a non-temprary key + if stack[^1].canLock: + stack[^1].step.key = + stack[^1].step.node.convertTo(Blob).digestTo(NodeKey).to(RepairKey) + + ok() + +# ------------------------------------------------------------------------------ +# Private walk along hexary trie records +# ------------------------------------------------------------------------------ + +when BasicChainTrieEnabled: + proc hexaryFollow( + ps: AccountsDbSessionRef; + root: NodeKey; + path: NibblesSeq + ): (int, bool, Blob) + {.gcsafe, raises: [Defect,RlpError]} = + ## Returns the number of matching digits/nibbles from the argument `path` + ## found in the proofs trie. + let + nNibbles = path.len + var + inPath = path + recKey = root.ByteArray32.toSeq + leafBlob: Blob + emptyRef = false + + when BasicChainTrieDebugging: + trace "follow", rootKey=root.pp(ps), path + + while true: + let value = ps.base.db.get(recKey) + if value.len == 0: + break + + var nodeRlp = rlpFromBytes value + case nodeRlp.listLen: + of 2: + let + (isLeaf, pathSegment) = hexPrefixDecode nodeRlp.listElem(0).toBytes + sharedNibbles = inPath.sharedPrefixLen(pathSegment) + fullPath = sharedNibbles == pathSegment.len + inPathLen = inPath.len + inPath = inPath.slice(sharedNibbles) + + # Leaf node + if isLeaf: + let leafMode = sharedNibbles == inPathLen + if fullPath and leafMode: + leafBlob = nodeRlp.listElem(1).toBytes + when BasicChainTrieDebugging: + let nibblesLeft = inPathLen - sharedNibbles + trace "follow leaf", + fullPath, leafMode, sharedNibbles, nibblesLeft, + pathSegment, newPath=inPath + break + + # Extension node + if fullPath: + let branch = nodeRlp.listElem(1) + if branch.isEmpty: + when BasicChainTrieDebugging: + trace "follow extension", newKey="n/a" + emptyRef = true + break + recKey = branch.toBytes + when BasicChainTrieDebugging: + trace "follow extension", + newKey=recKey.convertTo(NodeKey).pp(ps), newPath=inPath + else: + when BasicChainTrieDebugging: + trace "follow extension", + fullPath, sharedNibbles, pathSegment, + inPathLen, newPath=inPath + break + + of 17: + # Branch node + if inPath.len == 0: + leafBlob = nodeRlp.listElem(1).toBytes + break + let + inx = inPath[0].int + branch = nodeRlp.listElem(inx) + if branch.isEmpty: + when BasicChainTrieDebugging: + trace "follow branch", newKey="n/a" + emptyRef = true + break + inPath = inPath.slice(1) + recKey = branch.toBytes + when BasicChainTrieDebugging: + trace "follow branch", + newKey=recKey.convertTo(NodeKey).pp(ps), inx, newPath=inPath + + else: + when BasicChainTrieDebugging: + trace "follow oops", + nColumns = nodeRlp.listLen + break + + # end while + + let pathLen = nNibbles - inPath.len + + when BasicChainTrieDebugging: + trace "follow done", + recKey, emptyRef, pathLen, leafSize=leafBlob.len + + (pathLen, emptyRef, leafBlob) + + + proc hexaryFollow( + ps: AccountsDbSessionRef; + root: NodeKey; + path: NodeKey + ): (int, bool, Blob) + {.gcsafe, raises: [Defect,RlpError]} = + ## Variant of `hexaryFollow()` + ps.hexaryFollow(root, path.to(NibblesSeq)) + +# ------------------------------------------------------------------------------ +# Public constructor +# ------------------------------------------------------------------------------ + +proc init*( + T: type AccountsDbRef; + db: TrieDatabaseRef + ): T = + ## Main object constructor + T(db: db) + +proc init*( + T: type AccountsDbSessionRef; + pv: AccountsDbRef; + root: Hash256; + peer: Peer = nil + ): T = + ## Start a new session, do some actions an then discard the session + ## descriptor (probably after commiting data.) + AccountsDbSessionRef(base: pv, peer: peer, rootKey: root.to(NodeKey)) + +# ------------------------------------------------------------------------------ +# Public functions, session related +# ------------------------------------------------------------------------------ + +proc merge*( + ps: AccountsDbSessionRef; + proof: SnapAccountProof + ): Result[void,AccountsDbError] + {.gcsafe, raises: [Defect, RlpError].} = + ## Import account proof records (as received with the snap message + ## `AccountRange`) into the hexary trie of the repair database. These hexary + ## trie records can be extended to a full trie at a later stage and used for + ## validating account data. + for n,rlpRec in proof: + let rc = ps.hexaryImport(rlpRec) + if rc.isErr: + trace "merge(SnapAccountProof)", peer=ps.peer, + proofs=ps.rnDB.tab.len, accounts=ps.rnDB.acc.len, error=rc.error + return err(rc.error) + + ok() + + +proc merge*( + ps: AccountsDbSessionRef; + base: NodeTag; + acc: seq[SnapAccount]; + ): Result[void,AccountsDbError] + {.gcsafe, raises: [Defect, RlpError].} = + ## Import account records (as received with the snap message `AccountRange`) + ## into the accounts list of the repair database. The accounts, together + ## with some hexary trie records for proof can be used for validating + ## the argument account data. + ## + if acc.len != 0: + #if ps.rnDB.acc.len == 0 or ps.rnDB.acc[^1].tag <= base: + # return ps.mergeImpl(base, acc) + + let + prependOk = 0 < ps.rnDB.acc.len and base < ps.rnDB.acc[^1].tag + saveLen = ps.rnDB.acc.len + accTag0 = acc[0].accHash.to(NodeTag) + + # For error logging + (peer, proofs, accounts) = (ps.peer, ps.rnDB.tab.len, ps.rnDB.acc.len) + + var + error = NothingSerious + saveQ: seq[RAccount] + if prependOk: + # Prepend `acc` argument before `ps.rnDB.acc` + saveQ = ps.rnDB.acc + + block collectAccounts: + # Verify lower bound + if acc[0].accHash.to(NodeTag) < base: + error = AccountSmallerThanBase + trace "merge(seq[SnapAccount])", peer, proofs, base, accounts, error + break collectAccounts + + # Add base for the records (no payload). Note that the assumption + # holds: `ps.rnDB.acc[^1].tag <= base` + if base < accTag0: + ps.rnDB.acc.add RAccount(tag: base) + + # Check for the case that accounts are appended + elif 0 < ps.rnDB.acc.len and accTag0 <= ps.rnDB.acc[^1].tag: + error = AccountsNotSrictlyIncreasing + trace "merge(seq[SnapAccount])", peer, proofs, base, accounts, error + break collectAccounts + + # Add first account + ps.rnDB.acc.add RAccount(tag: accTag0, payload: acc[0].accBody.encode) + + # Veify & add other accounts + for n in 1 ..< acc.len: + let nodeTag = acc[n].accHash.to(NodeTag) + + if nodeTag <= ps.rnDB.acc[^1].tag: + # Recover accounts list and return error + ps.rnDB.acc.setLen(saveLen) + + error = AccountsNotSrictlyIncreasing + trace "merge(seq[SnapAccount])", peer, proofs, base, accounts, error + break collectAccounts + + ps.rnDB.acc.add RAccount(tag: nodeTag, payload: acc[n].accBody.encode) + + # End block `collectAccounts` + + if prependOk: + if error == NothingSerious: + ps.rnDB.acc = ps.rnDB.acc & saveQ + else: + ps.rnDB.acc = saveQ + + if error != NothingSerious: + return err(error) + + ok() + + +proc interpolate*(ps: AccountsDbSessionRef): Result[void,AccountsDbError] = + ## Verifiy accounts by interpolating the collected accounts on the hexary + ## trie of the repair database. If all accounts can be represented in the + ## hexary trie, they are vonsidered validated. + ## + ## Note: + ## This function temporary and proof-of-concept. for production purposes, + ## it must be replaced by the new facility of the upcoming re-factored + ## database layer. + ## + # Walk top down and insert/complete missing account access nodes + for n in countDown(ps.rnDB.acc.len-1,0): + let acc = ps.rnDB.acc[n] + if acc.payload.len != 0: + let rPath = acc.tag.rTreeFollow(ps) + var repairKey = acc.key + if repairKey.isZero and 0 < rPath.path.len and rPath.tail.len == 0: + repairKey = rPath.path[^1].key + ps.rnDB.acc[n].key = repairKey + if repairKey.isZero: + let + update = rPath.rTreeInterpolate(ps, acc.payload) + final = acc.tag.rTreeFollow(ps) + if update != final: + return err(AccountRepairBlocked) + ps.rnDB.acc[n].key = rPath.path[^1].key + + # Replace temporary repair keys by proper hash based node keys. + var reVisit: seq[NodeTag] + for n in countDown(ps.rnDB.acc.len-1,0): + let acc = ps.rnDB.acc[n] + if not acc.key.isZero: + let rPath = acc.tag.rTreeFollow(ps) + if rPath.path[^1].node.state == Mutable: + let rc = rPath.rTreeUpdateKeys(ps) + if rc.isErr: + reVisit.add acc.tag + + while 0 < reVisit.len: + var again: seq[NodeTag] + for nodeTag in reVisit: + let rc = nodeTag.rTreeFollow(ps).rTreeUpdateKeys(ps) + if rc.isErr: + again.add nodeTag + if reVisit.len <= again.len: + return err(BoundaryProofFailed) + reVisit = again + + ok() + +proc nHexaryRecords*(ps: AccountsDbSessionRef): int = + ## Number of hexary record entries in the session database. + ps.rnDB.tab.len + +proc nAccountRecords*(ps: AccountsDbSessionRef): int = + ## Number of account records in the session database. This number includes + ## lower bound entries (which are not accoiunts, strictly speaking.) + ps.rnDB.acc.len + +# ------------------------------------------------------------------------------ +# Public functions +# ------------------------------------------------------------------------------ + +proc importAccounts*( + pv: AccountsDbRef; + peer: Peer, ## for log messages + root: Hash256; ## state root + base: NodeTag; ## before or at first account entry in `data` + data: SnapAccountRange + ): Result[void,AccountsDbError] = + ## Validate and accounts and proofs (as received with the snap message + ## `AccountRange`). This function combines the functionality of the `merge()` + ## and the `interpolate()` functions. + ## + ## At a later stage, that function also will bulk-import the accounts into + ## the block chain database + ## + ## Note that the `peer` argument is for log messages, only. + let ps = AccountsDbSessionRef.init(pv, root, peer) + try: + block: + let rc = ps.merge(data.proof) + if rc.isErr: + return err(rc.error) + block: + let rc = ps.merge(base, data.accounts) + if rc.isErr: + return err(rc.error) + except RlpError: + return err(RlpEncoding) + + block: + ## Note: + ## `interpolate()` is a temporary proof-of-concept function. For + ## production purposes, it must be replaced by the new facility of + ## the upcoming re-factored database layer. + let rc = ps.interpolate() + if rc.isErr: + return err(rc.error) + + # TODO: bulk import + # ... + + trace "Accounts and proofs ok", peer, root=root.data.toHex, + proof=data.proof.len, base, accounts=data.accounts.len + ok() + +# ------------------------------------------------------------------------------ +# Debugging +# ------------------------------------------------------------------------------ + +proc assignPrettyKeys*(ps: AccountsDbSessionRef) = + ## Prepare foe pretty pringing/debugging. Run early enough this function + ## sets the root key to `"$"`, for instance. + noPpError("validate(1)"): + # Make keys assigned in pretty order for printing + var keysList = toSeq(ps.rnDB.tab.keys) + let rootKey = ps.rootKey.to(RepairKey) + discard rootKey.toKey(ps) + if ps.rnDB.tab.hasKey(rootKey): + keysList = @[rootKey] & keysList + for key in keysList: + let node = ps.rnDB.tab[key] + discard key.toKey(ps) + case node.kind: + of Branch: (for w in node.bLink: discard w.toKey(ps)) + of Extension: discard node.eLink.toKey(ps) + of Leaf: discard + +proc dumpPath*(ps: AccountsDbSessionRef; key: NodeTag): seq[string] = + ## Pretty print helper compiling the path into the repair tree for the + ## argument `key`. + let rPath = key.rTreeFollow(ps) + rPath.path.mapIt(it.pp(ps)) & @["(" & rPath.tail.pp & ")"] + +proc dumpProofsDB*(ps: AccountsDbSessionRef): seq[string] = + ## Dump the entries from the repair tree. + var accu = @[(0u, "($0" & "," & ps.rootKey.pp(ps) & ")")] + for key,node in ps.rnDB.tab.pairs: + accu.add (key.toKey(ps), "(" & key.pp(ps) & "," & node.pp(ps) & ")") + proc cmpIt(x, y: (uint,string)): int = + cmp(x[0],y[0]) + result = accu.sorted(cmpIt).mapIt(it[1]) + +# --------- + +proc dumpRoot*(root: Hash256; name = "snapRoot*"): string = + noPpError("dumpRoot"): + result = "import\n" + result &= " eth/common/eth_types,\n" + result &= " nimcrypto/hash,\n" + result &= " stew/byteutils\n\n" + result &= "const\n" + result &= &" {name} =\n" + result &= &" \"{root.pp(false)}\".toDigest\n" + +proc dumpSnapAccountRange*( + base: NodeTag; + data: SnapAccountRange; + name = "snapData*" + ): string = + noPpError("dumpSnapAccountRange"): + result = &" {name} = (" + result &= &"\n \"{base.to(Hash256).pp(false)}\".toDigest," + result &= "\n @[" + let accPfx = "\n " + for n in 0 ..< data.accounts.len: + let + hash = data.accounts[n].accHash + body = data.accounts[n].accBody + if 0 < n: + result &= accPfx + result &= &"# <{n}>" + result &= &"{accPfx}(\"{hash.pp(false)}\".toDigest," + result &= &"{accPfx} {body.nonce}u64," + result &= &"{accPfx} \"{body.balance}\".parse(Uint256)," + result &= &"{accPfx} \"{body.storageRoot.pp(false)}\".toDigest," + result &= &"{accPfx} \"{body.codehash.pp(false)}\".toDigest)," + if result[^1] == ',': + result[^1] = ']' + else: + result &= "]" + result &= ",\n @[" + let blobPfx = "\n " + for n in 0 ..< data.proof.len: + let blob = data.proof[n] + if 0 < n: + result &= blobPfx + result &= &"# <{n}>" + result &= &"{blobPfx}\"{blob.pp}\".hexToSeqByte," + if result[^1] == ',': + result[^1] = ']' + else: + result &= "]" + result &= ")\n" + +# ------------------------------------------------------------------------------ +# End +# ------------------------------------------------------------------------------ diff --git a/nimbus/sync/snap/worker/fetch.nim b/nimbus/sync/snap/worker/fetch.nim deleted file mode 100644 index f94100d50..000000000 --- a/nimbus/sync/snap/worker/fetch.nim +++ /dev/null @@ -1,339 +0,0 @@ -# Nimbus - Fetch account and storage states from peers efficiently -# -# Copyright (c) 2021 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/math, - chronos, - eth/[common/eth_types, p2p], - nimcrypto/keccak, - stew/[interval_set, keyed_queue], - stint, - ../../types, - ../path_desc, - ./fetch/[fetch_accounts, proof_db], - "."/[ticker, worker_desc] - -{.push raises: [Defect].} - -type - FetchEx = ref object of WorkerFetchBase - accTab: AccLruCache ## Global worker data - quCount: uint64 ## Count visited roots - lastPivot: NodeTag ## Used for calculating pivots - accRangeMaxLen: UInt256 ## Maximap interval length, high(u256)/#peers - pdb: ProofDb ## Proof processing - - AccTabEntryRef = ref object - ## Global worker table - avail: LeafRangeSet ## Accounts to visit (organised as ranges) - pivot: NodeTag ## Where to to start fetching from - base: WorkerFetchBase ## Back reference (`FetchEx` not working, here) - - AccLruCache = - KeyedQueue[TrieHash,AccTabEntryRef] - -logScope: - topics = "snap-fetch" - -const - accRangeMaxLen = ##\ - ## ask for that many accounts at once (not the range is sparse) - (high(NodeTag) - low(NodeTag)) div 1000 - - pivotAccIncrement = ##\ - ## increment when `lastPivot` would stay put - 10_000_000.u256 - -# ------------------------------------------------------------------------------ -# Private helpers -# ------------------------------------------------------------------------------ - -proc `==`(a, b: AccTabEntryRef): bool = - ## Just to make things clear, should be default action anyway - cast[pointer](a) == cast[pointer](b) - -proc fetchEx(ns: Worker): FetchEx = - ## Getter - ns.fetchBase.FetchEx - -proc fetchEx(sp: WorkerBuddy): FetchEx = - ## Getter - sp.ns.fetchEx - -proc withMaxLen(atb: AccTabEntryRef; iv: LeafRange): LeafRange = - ## Reduce accounts interval to maximal size - let maxlen = atb.base.FetchEx.accRangeMaxLen - if 0 < iv.len and iv.len <= maxLen: - iv - else: - LeafRange.new(iv.minPt, iv.minPt + maxLen - 1.u256) - -# ------------------------------------------------------------------------------ -# Private functions -# ------------------------------------------------------------------------------ - -proc getAccTab(sp: WorkerBuddy; key: TrieHash): AccTabEntryRef = - ## Fetch LRU table item, create a new one if missing. - # fetch existing table (if any) - block: - let rc = sp.fetchEx.accTab.lruFetch(key) - if rc.isOk: - # Item was moved to the end of queue - return rc.value - - # Calculate some new start address for the range fetcher - while true: - # Derive pivot from last interval set in table - let rc = sp.fetchEx.accTab.last - if rc.isErr: - break # no more => stop - # Check last interval - let blkRc = rc.value.data.avail.le() # rightmost interval - if blkRc.isErr: - # Delete useless interval set, repeat - sp.fetchEx.accTab.del(rc.value.key) - continue - # use increasing `pivot` values - if sp.fetchEx.lastPivot < blkRc.value.minPt: - sp.ns.fetchEx.lastPivot = blkRc.value.minPt - break - if sp.fetchEx.lastPivot < high(NodeTag) - pivotAccIncrement: - sp.fetchEx.lastPivot = sp.ns.fetchEx.lastPivot + pivotAccIncrement - break - # Otherwise start at 0 - sp.fetchEx.lastPivot = 0.to(NodeTag) - break - - let accRange = AccTabEntryRef( - pivot: sp.fetchEx.lastPivot, - avail: LeafRangeSet.init(), - base: sp.fetchEx) - - trace "New accounts list for syncing", - peer=sp, stateRoot=key, pivot=sp.fetchEx.lastPivot - - # Statistics - sp.fetchEx.quCount.inc - - # Pre-filled with the largest possible interval - discard accRange.avail.merge(low(NodeTag),high(NodeTag)) - - # Append and curb LRU table as needed - return sp.fetchEx.accTab.lruAppend(key, accRange, sp.ns.buddiesMax) - - -proc sameAccTab(sp: WorkerBuddy; key: TrieHash; accTab: AccTabEntryRef): bool = - ## Verify that account list entry has not changed. - let rc = sp.fetchEx.accTab.eq(key) - if rc.isErr: - return accTab.isNil - if not accTab.isNil: - return accTab == rc.value - - -proc fetchAccRange(atb: AccTabEntryRef): Result[LeafRange,void] = - ## Fetch an interval from the account range list. Use the `atb.pivot` value - ## as a start entry to fetch data from, wrapping around if necessary. - block: - # Check whether there the start point is in the middle of an interval - let rc = atb.avail.le(atb.pivot) - if rc.isOk: - if atb.pivot <= rc.value.maxPt: - let iv = LeafRange.new(atb.pivot, rc.value.maxPt) - discard atb.avail.reduce(iv) - return ok(iv) - - block: - # Take the next interval to the right - let rc = atb.avail.ge(atb.pivot) - if rc.isOk: - let iv = atb.withMaxLen(rc.value) - discard atb.avail.reduce(iv) - return ok(iv) - - # Otherwise wrap around - let rc = atb.avail.ge() - if rc.isOk: - let iv = atb.withMaxLen(rc.value) - discard atb.avail.reduce(iv) - return ok(iv) - - err() - - -proc putAccRange(atb: AccTabEntryRef; iv: LeafRange) = - discard atb.avail.merge(iv) - -proc putAccRange(atb: AccTabEntryRef; a, b: NodeTag) = - discard atb.avail.merge(a, b) - -proc haveAccRange(atb: AccTabEntryRef): bool = - 0 < atb.avail.chunks - - -proc meanStdDev(sum, sqSum: float; length: int): (float,float) = - if 0 < length: - result[0] = sum / length.float - result[1] = sqrt(sqSum / length.float - result[0] * result[0]) - -proc tickerStats(ns: Worker): TickerStats {.gcsafe.} = - var aSum, aSqSum, uSum, uSqSum: float - for kvp in ns.fetchEx.accTab.nextPairs: - - # Accounts mean & variance - let aLen = ns.fetchEx.pdb.accountsLen(kvp.key).float - aSum += aLen - aSqSum += aLen * aLen - - # Fill utilisation mean & variance - let fill = kvp.data.avail.freeFactor - uSum += fill - uSqSum += fill * fill - - result.activeQueues = ns.fetchEx.accTab.len - result.flushedQueues = ns.fetchEx.quCount.int64 - result.activeQueues - result.accounts = meanStdDev(aSum, aSqSum, result.activeQueues) - result.fillFactor = meanStdDev(uSum, uSqSum, result.activeQueues) - -# ------------------------------------------------------------------------------ -# Public start/stop and admin functions -# ------------------------------------------------------------------------------ - -proc fetchSetup*(ns: Worker; chainDb: AbstractChainDB) = - ## Global set up - ns.fetchBase = FetchEx() - ns.fetchEx.accTab.init(ns.buddiesMax) - ns.fetchEx.accRangeMaxLen = high(UInt256) div ns.buddiesMax.u256 - ns.fetchEx.pdb.init(chainDb.getTrieDB) - ns.tickerSetup(cb = tickerStats) - -proc fetchRelease*(ns: Worker) = - ## Global clean up - ns.tickerRelease() - ns.fetchBase = nil - -proc fetchStart*(sp: WorkerBuddy) = - ## Initialise fetching for particular peer - discard - -proc fetchStop*(sp: WorkerBuddy) = - ## Clean up for this peer - discard - -# ------------------------------------------------------------------------------ -# Public functions -# ------------------------------------------------------------------------------ - -proc fetch*(sp: WorkerBuddy) {.async.} = - ## Concurrently fetch account data. The data are fetched from `sp.peer` where - ## `sp` is the argument descriptor. Currently, accounts data are fetched but - ## not further processed (i.e. discarded.) - ## - ## The accounts requested depend on - ## * the currrent state root `sp.ctrl.stateRoot`, - ## * an account list `accTab(stateRoot)` depending on the current state root. - ## - ## The account list keeps track of account ranges already requested. It is - ## shared among all instances of `fetch()` (sharing the same `ds` - ## descriptor.) So the accounts requested for a shared accounts list are - ## mutually exclusive. - ## - ## Currently the accounts list to retrieve by `accTab()` is implemented as - ## follows. - ## * For each state root there is a separate accounts list. - ## * If the state root changes and there is no account list yet, create a - ## new one. - ## * Account ranges are fetched from an accoiunts list with increasing values - ## starting at a (typically positive) `pivot` value. The fetch wraps around - ## when the highest values are exhausted. This `pivot` value is increased - ## with each new accounts list (derived from the last used accounts list.) - ## * Accounts list are kept in a LRU table and automatically cleared. The - ## size of the LRU table is set to `sp.ns.buddiesMax`, the maximal number - ## of workers or peers. - - trace "Fetching from peer", peer=sp, ctrlState=sp.ctrl.state - sp.tickerStartPeer() - - while not sp.ctrl.stopped: - - # We need a state root and an access range list (depending on state root) - if sp.ctrl.stateRoot.isNone: - trace "Currently no state root", peer=sp - # Wait for a new state root - while not sp.ctrl.stopped and - sp.ctrl.stateRoot.isNone: - await sleepAsync(5.seconds) - continue - - # Ok, here is the `stateRoot`, tentatively try the access range list - let - stateRoot = sp.ctrl.stateRoot.get - accTab = sp.getAccTab(stateRoot) - - if not accTab.haveAccRange(): - trace "Currently no account ranges", peer=sp - # Account ranges exhausted, wait for a new state root - while not sp.ctrl.stopped and - sp.ctrl.stateRoot.isSome and - stateRoot == sp.ctrl.stateRoot.get and - sp.sameAccTab(stateRoot, accTab) and - not accTab.haveAccRange(): - await sleepAsync(5.seconds) - continue - - # Get a range of accounts to fetch from - let iv = block: - let rc = accTab.fetchAccRange() - if rc.isErr: - continue - rc.value - - # Fetch data for this range delegated to `fetchAccounts()` - let dd = block: - let rc = await sp.fetchAccounts(stateRoot, iv) - if rc.isErr: - accTab.putAccRange(iv) # fail => interval back to pool - case rc.error: - of NetworkProblem, MissingProof, AccountsMinTooSmall, - AccountsMaxTooLarge: - # Mark this peer dead, i.e. avoid fetching from this peer for a while - sp.stats.major.networkErrors.inc() - sp.ctrl.zombie = true - of NothingSerious: - discard - of NoAccountsForStateRoot: - # One could wait for a new state root but this may result in a - # temporary standstill if all `fetch()` instances do the same. So - # waiting for a while here might be preferable in the hope that the - # situation changes at the peer. - await sleepAsync(5.seconds) - continue - rc.value - - # Register consumed accounts range - if dd.consumed < iv.len: - # return some unused range - accTab.putAccRange(iv.minPt + dd.consumed.u256, iv.maxPt) - - # Process data - block: - let rc = sp.ns.fetchEx.pdb.mergeProved(stateRoot, iv.minPt, dd.data) - if rc.isErr: - discard # ?? - - # while end - - trace "Done syncing for this peer", peer=sp, ctrlState=sp.ctrl.state - sp.tickerStopPeer() - -# ------------------------------------------------------------------------------ -# End -# ------------------------------------------------------------------------------ diff --git a/nimbus/sync/snap/worker/fetch/proof_db.nim b/nimbus/sync/snap/worker/fetch/proof_db.nim deleted file mode 100644 index f53b6fd42..000000000 --- a/nimbus/sync/snap/worker/fetch/proof_db.nim +++ /dev/null @@ -1,780 +0,0 @@ -# Nimbus - Fetch account and storage states from peers by snapshot traversal -# -# Copyright (c) 2021 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, hashes, options, sequtils, sets, strutils, strformat, tables], - chronos, - eth/[common/eth_types, p2p, rlp, trie/db], - nimcrypto/keccak, - stew/[byteutils, interval_set], - stint, - ../../../../db/storage_types, - "../../.."/[protocol, types], - ../../path_desc, - ../worker_desc - -{.push raises: [Defect].} - -logScope: - topics = "snap-proof" - -const - RowColumnParserDump = false - NibbleFollowDump = false # true - -type - ProofError* = enum - RlpEncoding - RlpBlobExpected - RlpNonEmptyBlobExpected - RlpBranchLinkExpected - RlpListExpected - Rlp2Or17ListEntries - RlpExtPathEncoding - RlpLeafPathEncoding - RlpRecTypeError - ImpossibleKeyError - RowUnreferenced - AccountSmallerThanBase - AccountsNotSrictlyIncreasing - LastAccountProofFailed - MissingMergeBeginDirective - StateRootDiffers - - ProofRecType = enum - Branch, - Extension, - Leaf - - StatusRec = object - nAccounts: int - nProofs: int - - AccountRec = ##\ - ## Account entry record - distinct Account - - ProofRec = object - ## Proofs entry record - case kind: ProofRecType - of Branch: - vertex: array[16,NodeTag] - value: Blob # name starts with a `v` as in vertex - of Extension: - extend: PathSegment - follow: NodeTag - of Leaf: - path: PathSegment - payload: Blob # name starts with a `p` as in path - - ProofKvp = object - key: NodeTag - data: Option[ProofRec] - - ProofDb* = object - keyMap: Table[NodeTag,uint] ## For debugging only - - rootTag: NodeTag ## Current root node - rootHash: TrieHash ## Root node as hash - stat: StatusRec ## table statistics - - db: TrieDatabaseRef ## general database - dbTx: DbTransaction ## Rollback state capture - - newAccs: seq[(NodeTag,NodeTag)] ## New accounts group: (base,last) - newProofs: seq[NodeTag] ## Newly added proofs records - refPool: HashSet[NodeTag] ## New proofs references recs - -# ------------------------------------------------------------------------------ -# Private helpers -# ------------------------------------------------------------------------------ - -template noRlpError(info: static[string]; code: untyped) = - try: - code - except RlpError as e: - raiseAssert "Inconveivable (" & info & "): " & e.msg - -proc read(rlp: var Rlp; T: type ProofRec): T = - ## RLP mixin - noRlpError("read(ProofRec)"): - result.kind = rlp.read(typeof result.kind) - rlp.tryEnterList() - case result.kind: - of Branch: - result.vertex = rlp.read(typeof result.vertex) - result.value = rlp.read(typeof result.value) - of Extension: - result.extend = rlp.dbRead(typeof result.extend) - result.follow = rlp.read(typeof result.follow) - of Leaf: - result.path = rlp.dbRead(typeof result.path) - result.payload = rlp.read(typeof result.payload) - -proc append(writer: var RlpWriter; rec: ProofRec) = - ## RLP mixin - append(writer, rec.kind) - startList(writer, 2) - case rec.kind: - of Branch: - append(writer, rec.vertex) - append(writer, rec.value) - of Extension: - dbAppend(writer, rec.extend) - append(writer, rec.follow) - of Leaf: - dbAppend(writer, rec.path) - append(writer, rec.payload) - -proc to(w: TrieHash; T: type NodeTag): T = - ## Syntactic sugar - w.Hash256.to(T) - -proc to(w: AccountRec; T: type Account): T = - ## Syntactic sugar - w.T - -proc to(w: Account; T: type AccountRec): T = - ## Syntactic sugar - w.T - - -func nibble(a: array[32,byte]; inx: int): int = - let byteInx = inx shr 1 - if byteInx < 32: - if (inx and 1) == 0: - result = (a[byteInx] shr 4).int - else: - result = (a[byteInx] and 15).int - -proc clearJournal(pv: var ProofDb) = - pv.newAccs.setLen(0) - pv.newProofs.setLen(0) - pv.refPool.clear - -# ------------------------------------------------------------------------------ -# Private debugging helpers -# ------------------------------------------------------------------------------ - -import - ../../../../constants - -template noPpError(info: static[string]; code: untyped) = - try: - code - except ValueError as e: - raiseAssert "Inconveivable (" & info & "): " & e.msg - except KeyError as e: - raiseAssert "Not possible (" & info & "): " & e.msg - -proc pp(s: string; hex = false): string = - if hex: - let n = (s.len + 1) div 2 - (if s.len < 20: s else: s[0 .. 5] & ".." & s[s.len-8 .. s.len-1]) & - "[" & (if 0 < n: "#" & $n else: "") & "]" - elif s.len <= 30: - s - else: - (if (s.len and 1) == 0: s[0 ..< 8] else: "0" & s[0 ..< 7]) & - "..(" & $s.len & ").." & s[s.len-16 ..< s.len] - -proc pp(a: Hash256; collapse = true): string = - if not collapse: - a.data.mapIt(it.toHex(2)).join.toLowerAscii - elif a == ZERO_HASH256: - "ZERO_HASH256" - elif a == BLANK_ROOT_HASH: - "BLANK_ROOT_HASH" - elif a == EMPTY_UNCLE_HASH: - "EMPTY_UNCLE_HASH" - elif a == EMPTY_SHA3: - "EMPTY_SHA3" - elif a == ZERO_HASH256: - "ZERO_HASH256" - else: - a.data.mapIt(it.toHex(2)).join[56 .. 63].toLowerAscii - -proc pp(a: NodeHash|TrieHash; collapse = true): string = - a.Hash256.pp(collapse) - -proc pp(a: NodeTag; collapse = true): string = - a.to(Hash256).pp(collapse) - -proc toKey(a: NodeTag; pv: var ProofDb): uint = - noPpError("pp(NodeTag)"): - if not pv.keyMap.hasKey(a): - pv.keyMap[a] = pv.keyMap.len.uint + 1 - result = pv.keyMap[a] - -proc pp(a: NodeTag; pv: var ProofDb): string = - $a.toKey(pv) - -proc pp(q: openArray[byte]; noHash = false): string = - if q.len == 32 and not noHash: - var a: array[32,byte] - for n in 0..31: a[n] = q[n] - ($Hash256(data: a)).pp - else: - q.toSeq.mapIt(it.toHex(2)).join.toLowerAscii.pp(hex = true) - -proc pp(blob: Blob): string = - blob.mapIt(it.toHex(2)).join - -proc pp(a: Account): string = - noPpError("pp(Account)"): - result = &"({a.nonce},{a.balance},{a.storageRoot},{a.codeHash})" - -proc pp(sa: SnapAccount): string = - "(" & $sa.accHash & "," & sa.accBody.pp & ")" - -proc pp(al: seq[SnapAccount]): string = - result = " @[" - noPpError("pp(seq[SnapAccount])"): - for n,rec in al: - result &= &"| # <{n}>| {rec.pp}," - if 10 < result.len: - result[^1] = ']' - else: - result &= "]" - -proc pp(blobs: seq[Blob]): string = - result = " @[" - noPpError("pp(seq[Blob])"): - for n,rec in blobs: - result &= "| # <" & $n & ">| \"" & rec.pp & "\".hexToSeqByte," - if 10 < result.len: - result[^1] = ']' - else: - result &= "]" - -proc pp(hs: seq[NodeTag]; pv: var ProofDb): string = - "<" & hs.mapIt(it.pp(pv)).join(",") & ">" - -proc pp(hs: HashSet[NodeTag]; pv: var ProofDb): string = - "{" & toSeq(hs.items).mapIt(it.toKey(pv)).sorted.mapIt($it).join(",") & "}" - -proc pp(rec: ProofRec; pv: var ProofDb): string = - noPpError("pp(ProofRec)"): - case rec.kind: - of Branch: result &= - "b(" & rec.vertex.mapIt(it.pp(pv)).join(",") & "," & - rec.value.pp.pp(true) & ")" - of Leaf: result &= - "l(" & ($rec.path).pp(true) & "," & rec.payload.pp.pp(true) & ")" - of Extension: result &= - "x(" & ($rec.extend).pp(true) & "," & rec.follow.pp(pv) & ")" - -proc pp(rec: Option[ProofRec]; pv: var ProofDb): string = - if rec.isSome: - rec.get.pp(pv) - else: - "n/a" - -proc pp(q: seq[ProofKvp]; pv: var ProofDb): string = - result="@[" - for kvp in q: - result &= "(" & kvp.key.pp(pv) & "," & kvp.data.pp(pv) & ")," - if q.len == 0: - result &= "]" - else: - result[^1] = ']' - -# ------------------------------------------------------------------------------ -# Private functions -# ------------------------------------------------------------------------------ - -template mkProofKey(pv: ProofDb; tag: NodeTag): openArray[byte] = - tag.to(Hash256).snapSyncProofKey.toOpenArray - -proc getProofsRec(pv: ProofDb; tag: NodeTag): Result[ProofRec,void] = - let recData = pv.db.get(pv.mkProofKey(tag)) - if 0 < recData.len: - return ok(recData.decode(ProofRec)) - err() - -proc hasProofsRec(pv: ProofDb; tag: NodeTag): bool = - pv.db.contains(pv.mkProofKey(tag)) - -proc collectRefs(pv: var ProofDb; rec: ProofRec) = - case rec.kind: - of Branch: - for v in rec.vertex: - pv.refPool.incl v - of Extension: - pv.refPool.incl rec.follow - of Leaf: - discard - -proc collectRefs(pv: var ProofDb; tag: NodeTag) = - let rc = pv.getProofsRec(tag) - if rc.isOk: - pv.collectRefs(rc.value) - -proc addProofsRec(pv: var ProofDb; tag: NodeTag; rec: ProofRec) = - #debug "addProofsRec", size=pv.nProofs, tag=tag.pp(pv), rec=rec.pp(pv) - if not pv.hasProofsRec(tag): - pv.db.put(pv.mkProofKey(tag), rlp.encode(rec)) - pv.stat.nProofs.inc - pv.newProofs.add tag # to be committed - # Always add references, the rec might have been added earlier outside - # the current transaction. - pv.collectRefs(rec) - -# ----------- - -template mkAccKey(pv: ProofDb; tag: NodeTag): openArray[byte] = - snapSyncAccountKey(tag.to(Hash256), pv.rootHash.Hash256).toOpenArray - -proc hasAccountRec(pv: ProofDb; tag: NodeTag): bool = - pv.db.contains(pv.mkAccKey(tag)) - -proc getAccountRec(pv: ProofDb; tag: NodeTag): Result[AccountRec,void] = - let rec = pv.db.get(pv.mkAccKey(tag)) - if 0 < rec.len: - noRlpError("read(AccountRec)"): - return ok(rec.decode(Account).to(AccountRec)) - err() - -proc addAccountRec(pv: var ProofDb; tag: NodeTag; rec: AccountRec) = - if not pv.hasAccountRec(tag): - pv.db.put(pv.mkAccKey(tag), rlp.encode(rec.to(Account))) - pv.stat.nAccounts.inc - -# ----------- - -template mkStatusKey(pv: ProofDb; root: TrieHash): openArray[byte] = - snapSyncStatusKey(root.Hash256).toOpenArray - -proc hasStatusRec(pv: ProofDb; root: TrieHash): bool = - pv.db.contains(pv.mkStatusKey(root)) - -proc getStatusRec(pv: ProofDb; root: TrieHash): Result[StatusRec,void] = - let rec = pv.db.get(pv.mkStatusKey(root)) - if 0 < rec.len: - noRlpError("getStatusRec"): - return ok(rec.decode(StatusRec)) - err() - -proc useStatusRec(pv: ProofDb; root: TrieHash): StatusRec = - let rec = pv.db.get(pv.mkStatusKey(root)) - if 0 < rec.len: - noRlpError("findStatusRec"): - return rec.decode(StatusRec) - -proc putStatusRec(pv: ProofDb; root: TrieHash; rec: StatusRec) = - pv.db.put(pv.mkStatusKey(root), rlp.encode(rec)) - -# Example trie from https://eth.wiki/en/fundamentals/patricia-tree -# -# lookup data: -# "do": "verb" -# "dog": "puppy" -# "dodge": "coin" -# "horse": "stallion" -# -# trie DB: -# root: [16 A] -# A: [* * * * B * * * [20+"orse" "stallion"] * * * * * * * *] -# B: [00+"o" D] -# D: [* * * * * * E * * * * * * * * * "verb"] -# E: [17 [* * * * * * [35 "coin"] * * * * * * * * * "puppy"]] -# -# with first nibble of two-column rows: -# hex bits | node type length -# ---------+------------------ -# 0 0000 | extension even -# 1 0001 | extension odd -# 2 0010 | leaf even -# 3 0011 | leaf odd -# -# and key path: -# "do": 6 4 6 f -# "dog": 6 4 6 f 6 7 -# "dodge": 6 4 6 f 6 7 6 5 -# "horse": 6 8 6 f 7 2 7 3 6 5 -# - -proc parse(pv: ProofDb; rlpData: Blob): Result[ProofKvp,ProofError] - {.gcsafe, raises: [Defect, RlpError].} = - ## Decode a single trie item for adding to the table - - let recTag = rlpData.digestTo(NodeTag) - when RowColumnParserDump: - debug "Rlp column parser", recTag - if pv.hasProofsRec(recTag): - # No need to do this rec again - return ok(ProofKvp(key: recTag, data: none(ProofRec))) - - var - # Inut data - rlp = rlpData.rlpFromBytes - - # Result data - blobs = newSeq[Blob](2) # temporary, cache - rec = ProofRec(kind: Branch) # part of output, default type - top = 0 # count entries - - # Collect lists of either 2 or 17 blob entries. - for w in rlp.items: - when RowColumnParserDump: - debug "Rlp column parser", col=top, data=w.toBytes.pp - case top - of 0, 1: - if not w.isBlob: - return err(RlpBlobExpected) - blobs[top] = rlp.read(Blob) - of 2 .. 15: - if not rec.vertex[top].init(rlp.read(Blob)): - return err(RlpBranchLinkExpected) - of 16: - if not w.isBlob: - return err(RlpBlobExpected) - rec.value = rlp.read(Blob) - else: - return err(Rlp2Or17ListEntries) - top.inc - - when RowColumnParserDump: - debug "Rlp column parser done collecting columns", col=top - - # Assemble collected data - case top: - of 2: - if blobs[0].len == 0: - return err(RlpNonEmptyBlobExpected) - case blobs[0][0] shr 4: - of 0, 1: - rec.kind = Extension - if not (rec.extend.init(blobs[0]) and rec.follow.init(blobs[1])): - return err(RlpExtPathEncoding) - of 2, 3: - rec.kind = Leaf - if not rec.path.init(blobs[0]): - return err(RlpLeafPathEncoding) - rec.payload = blobs[1] - else: - return err(RlpRecTypeError) - of 17: - # Branch entry, complete the first two vertices - for n,blob in blobs: - if not rec.vertex[n].init(blob): - return err(RlpBranchLinkExpected) - else: - return err(Rlp2Or17ListEntries) - - ok(ProofKvp(key: recTag, data: some(rec))) - - -proc parse(pv: var ProofDb; proof: SnapAccountProof): Result[void,ProofError] = - ## Decode a list of RLP encoded trie entries and add it to the rec pool - try: - for n,rlpRec in proof: - when RowColumnParserDump: - debug "Rlp rec parser", rec=n, data=rec.pp - - let kvp = block: - let rc = pv.parse(rlpRec) - if rc.isErr: - return err(rc.error) - rc.value - - if kvp.data.isNone: # avoids dups, stoll collects references - pv.collectRefs(kvp.key) - else: - pv.addProofsRec(kvp.key, kvp.data.get) - except RlpError: - return err(RlpEncoding) - except KeyError: - return err(ImpossibleKeyError) - - ok() - -proc follow(pv: ProofDb; path: NodeTag): (int, Blob) = - ## Returns the number of matching digits/nibbles from the argument `tag` - ## found in the proofs trie. - var - inTop = 0 - inPath = path.UInt256.toBytesBE - recTag = pv.rootTag - leafBlob: Blob - - when NibbleFollowDump: - trace "follow", root=pv.rootTag, path - - noRlpError("follow"): - block loop: - while true: - - let rec = block: - let rc = pv.getProofsRec(recTag) - if rc.isErr: - break loop - rc.value - - let recType = rec.kind - case recType: - of Branch: - let - nibble = inPath.nibble(inTop) - newTag = rec.vertex[nibble] - when NibbleFollowDump: - trace "follow branch", recType, recTag, inTop, nibble, newTag - recTag = newTag - - of Leaf: - for n in 0 ..< rec.path.len: - if rec.path[n] != inPath.nibble(inTop + n): - inTop += n - when NibbleFollowDump: - let tail = rec.path - trace "follow leaf failed", recType, recTag, tail - break loop - inTop += rec.path.len - leafBlob = rec.payload - when NibbleFollowDump: - trace "follow leaf", recType, recTag, inTop, done=true - break loop - - of Extension: - for n in 0 ..< rec.extend.len: - if rec.extend[n] != inPath.nibble(inTop + n): - inTop += n - when NibbleFollowDump: - let tail = rec.extend - trace "follow extension failed", recType, recTag, tail - break loop - inTop += rec.extend.len - let newTag = rec.follow - when NibbleFollowDump: - trace "follow extension", recType, recTag, inTop, newTag - recTag = newTag - - # end case - inTop.inc - - # end while - inTop.dec - - when NibbleFollowDump: - trace "follow done", tag, inTop - - (inTop, leafBlob) - -# ------------------------------------------------------------------------------ -# Public constructor -# ------------------------------------------------------------------------------ - -proc init*(pv: var ProofDb; db: TrieDatabaseRef) = - pv = ProofDb(db: db) - -# ------------------------------------------------------------------------------ -# Public functions, transaction frame -# ------------------------------------------------------------------------------ - -proc isMergeTx*(pv: ProofDb): bool = - ## The function returns `true` exactly if a merge transaction was initialised - ## with `mergeBegin()`. - not pv.dbTx.isNil - -proc mergeBegin*(pv: var ProofDb; root: TrieHash): bool = - ## Prepare the system for accepting data input unless there is an open - ## transaction, already. The function returns `true` if - ## * There was no transaction initialised, yet - ## * There is an open transaction for the same state root argument `root` - ## In all other cases, `false` is returned. - if pv.dbTx.isNil: - # Update state root - pv.rootTag = root.to(NodeTag) - pv.rootHash = root - # Fetch status record for this `root` - pv.stat = pv.useStatusRec(root) - # New DB transaction - pv.dbTx = pv.db.beginTransaction - return true - # Make sure that the state roots are the same - pv.rootHash == root - -proc mergeCommit*(pv: var ProofDb): bool = - ## Accept merges and clear rollback journal if there was a transaction - ## initialised with `mergeBegin()`. If successful, `true` is returned, and - ## `false` otherwise. - if not pv.dbTx.isNil: - pv.dbTx.commit - pv.dbTx = nil - pv.clearJournal() - pv.putStatusRec(pv.rootHash, pv.stat) # persistent new status for this root - return true - -proc mergeRollback*(pv: var ProofDb): bool = - ## Rewind discaring merges and clear rollback journal if there was a - ## transaction initialised with `mergeBegin()`. If successful, `true` is - ## returned, and `false` otherwise. - if not pv.dbTx.isNil: - pv.dbTx.rollback - pv.dbTx = nil - # restore previous status for this root - pv.stat = pv.useStatusRec(pv.rootHash) - pv.clearJournal() - return true - -proc merge*( - pv: var ProofDb; - proofs: SnapAccountProof - ): Result[void,ProofError] = - ## Merge account proofs (as received with the snap message `AccountRange`) - ## into the database. A rollback journal is maintained so that this operation - ## can be reverted. - if pv.dbTx.isNil: - return err(MissingMergeBeginDirective) - let rc = pv.parse(proofs) - if rc.isErr: - trace "Merge() proof failed", proofs=proofs.len, error=rc.error - return err(rc.error) - ok() - -proc merge*( - pv: var ProofDb; - base: NodeTag; - acc: seq[SnapAccount] - ): Result[void,ProofError] = - ## Merge accounts (as received with the snap message `AccountRange`) into - ## the database. A rollback journal is maintained so that this operation - ## can be reverted. - if pv.dbTx.isNil: - return err(MissingMergeBeginDirective) - if acc.len != 0: - # Verify lower bound - if acc[0].accHash < base: - return err(AccountSmallerThanBase) - # Verify strictly increasing account hashes - for n in 1 ..< acc.len: - if acc[n].accHash <= acc[n-1].accHash: - return err(AccountsNotSrictlyIncreasing) - # Add to database - for sa in acc: - pv.addAccountRec(sa.accHash, sa.accBody.to(AccountRec)) - # Stash boundary values, needed for later boundary proof - pv.newAccs.add (base, acc[^1].accHash) - ok() - -proc mergeValidate*(pv: ProofDb): Result[void,ProofError] = - ## Verify non-commited accounts and proofs: - ## * The prosfs entries must all be referenced from within the rollback - ## journal - ## * For each group of accounts, the base `NodeTag` must be found in the - ## proof database with a partial path of length ??? - ## * The last entry in a group of accounts must habe the `accBody` in the - ## proof database - if pv.dbTx.isNil: - return err(MissingMergeBeginDirective) - - # Make sure that all recs are referenced - if 0 < pv.newProofs.len: - #debug "Ref check",refPool=pv.refPool.pp(pv),newProofs=pv.newProofs.pp(pv) - for tag in pv.newProofs: - if tag notin pv.refPool and tag != pv.rootTag: - #debug "Unreferenced proofs rec", tag, tag=tag.pp(pv) - return err(RowUnreferenced) - - ## verify accounts - for (baseTag,accTag) in pv.newAccs: - - # Validate increasing accounts - - # Base and last account must be in database - let - nBaseDgts = pv.follow(baseTag)[0] - (nAccDgts, accData) = pv.follow(accTag) - - # Verify account base - # ... - - # Verify last account - if nAccDgts == 64: - let rc = pv.getAccountRec(accTag) - if rc.isOk: - noRlpError("validate(Account)"): - if accData.decode(Account) == rc.value.to(Account): - continue - - # This account list did not verify - return err(LastAccountProofFailed) - - ok() - -# ------------------------------------------------------------------------------ -# Public functions -# ------------------------------------------------------------------------------ - -proc mergeProved*( - pv: var ProofDb; - root: TrieHash; - base: NodeTag; - data: WorkerAccountRange - ): Result[void,ProofError] = - ## Validate and merge accounts and proofs (as received with the snap message - ## `AccountRange`) into the database. Any open transaction initialised with - ## `mergeBegin()` is continued ans finished. - if not pv.mergeBegin(root): - return err(StateRootDiffers) - - block: - let rc = pv.merge(data.proof) - if rc.isErr: - trace "Merge proofs failed", - proof=data.proof.len, error=rc.error - discard pv.mergeRollback() - return err(rc.error) - block: - let rc = pv.merge(base, data.accounts) - if rc.isErr: - trace "Merge accounts failed", - accounts=data.accounts.len, error=rc.error - discard pv.mergeRollback() - return err(rc.error) - block: - let rc = pv.mergeValidate() - if rc.isErr: - trace "Proofs or accounts do not valdate", - accounts=data.accounts.len, error=rc.error - discard pv.mergeRollback() - return err(rc.error) - - #trace "Merge accounts and proofs ok", - # root=pv.rootTag, base=base, accounts=data.accounts.pp, proof=data.proof.pp - discard pv.mergeCommit() - ok() - -proc proofsLen*(pv: ProofDb; root: TrieHash): int = - ## Number of entries in the proofs table for the argument state root `root`. - if pv.rootHash == root: - pv.stat.nProofs - else: - pv.useStatusRec(root).nProofs - -proc accountsLen*(pv: ProofDb; root: TrieHash): int = - ## Number of entries in the accounts table for the argument state root `root`. - if pv.rootHash == root: - pv.stat.nAccounts - else: - pv.useStatusRec(root).nAccounts - -proc journalLen*(pv: ProofDb): (bool,int,int,int) = - ## Size of the current rollback journal: - ## * oepn transaction, see `mergeBegin()` - ## * number of added recs - ## * number of added references implied by recs - ## * number of added accounts - (not pv.dbTx.isNil, pv.newProofs.len, pv.refPool.len, pv.newAccs.len) - -# ------------------------------------------------------------------------------ -# End -# ------------------------------------------------------------------------------ diff --git a/nimbus/sync/snap/worker/fetch_accounts.nim b/nimbus/sync/snap/worker/fetch_accounts.nim new file mode 100644 index 000000000..5983a5da9 --- /dev/null +++ b/nimbus/sync/snap/worker/fetch_accounts.nim @@ -0,0 +1,193 @@ +# Nimbus - Fetch account and storage states from peers efficiently +# +# Copyright (c) 2021 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 + chronicles, + chronos, + eth/[common/eth_types, p2p], + nimcrypto/keccak, + stew/[interval_set, keyed_queue], + stint, + ../../sync_desc, + ".."/[range_desc, worker_desc], + "."/[accounts_db, get_account_range] + +{.push raises: [Defect].} + +logScope: + topics = "snap-fetch" + +const + accRangeMaxLen = ##\ + ## Ask for that many accounts at once (not the range is sparse) + (high(NodeTag) - low(NodeTag)) div 1000 + + maxTimeoutErrors = ##\ + ## maximal number of non-resonses accepted in a row + 2 + +# ------------------------------------------------------------------------------ +# Private helpers +# ------------------------------------------------------------------------------ + +proc withMaxLen(buddy: SnapBuddyRef; iv: LeafRange): LeafRange = + ## Reduce accounts interval to maximal size + let maxlen = + if buddy.ctx.data.pivotEnv.proofDumpOk: snapAccountsDumpRange + else: buddy.ctx.data.accountRangeMax + if 0 < iv.len and iv.len <= maxLen: + iv + else: + LeafRange.new(iv.minPt, iv.minPt + (maxLen - 1.u256)) + +# ------------------------------------------------------------------------------ +# Private functions +# ------------------------------------------------------------------------------ + +proc getUnprocessed(buddy: SnapBuddyRef): Result[LeafRange,void] = + ## Fetch an interval from the account range list. Use the `pivotAccount` + ## value as a start entry to fetch data from, wrapping around if necessary. + let + ctx = buddy.ctx + env = ctx.data.pivotEnv + pivotPt = env.pivotAccount + + block: + # Take the next interval to the right (aka ge) `pivotPt` + let rc = env.availAccounts.ge(pivotPt) + if rc.isOk: + let iv = buddy.withMaxLen(rc.value) + discard env.availAccounts.reduce(iv) + return ok(iv) + + block: + # Check whether the `pivotPt` is in the middle of an interval + let rc = env.availAccounts.envelope(pivotPt) + if rc.isOk: + let iv = buddy.withMaxLen(LeafRange.new(pivotPt, rc.value.maxPt)) + discard env.availAccounts.reduce(iv) + return ok(iv) + + block: + # Otherwise wrap around + let rc = env.availAccounts.ge() + if rc.isOk: + let iv = buddy.withMaxLen(rc.value) + discard env.availAccounts.reduce(iv) + return ok(iv) + + err() + +proc putUnprocessed(buddy: SnapBuddyRef; iv: LeafRange) = + discard buddy.ctx.data.pivotEnv.availAccounts.merge(iv) + +proc delUnprocessed(buddy: SnapBuddyRef; iv: LeafRange) = + discard buddy.ctx.data.pivotEnv.availAccounts.reduce(iv) + +# ------------------------------------------------------------------------------ +# Public functions +# ------------------------------------------------------------------------------ + +proc fetchAccounts*(buddy: SnapBuddyRef): Future[bool] {.async.} = + ## Fetch accounts data and store them in the database. The function returns + ## `true` if there are no more unprocessed accounts. + ## + let + ctx = buddy.ctx + peer = buddy.peer + env = ctx.data.pivotEnv + stateRoot = env.stateHeader.stateRoot + + # Get a range of accounts to fetch from + let iv = block: + let rc = buddy.getUnprocessed() + if rc.isErr: + trace "No more unprocessed accounts", peer, stateRoot + return true + rc.value + + # Fetch data for this range delegated to `fetchAccounts()` + let dd = block: + let rc = await buddy.getAccountRange(stateRoot, iv) + if rc.isErr: + buddy.putUnprocessed(iv) # fail => interval back to pool + case rc.error: + of ResponseTimeout: + if maxTimeoutErrors <= buddy.data.errors.nTimeouts: + # Mark this peer dead, i.e. avoid fetching from this peer for a while + buddy.ctrl.zombie = true + else: + buddy.data.errors.nTimeouts.inc + await sleepAsync(5.seconds) + of NetworkProblem, MissingProof, AccountsMinTooSmall, AccountsMaxTooLarge: + # Mark this peer dead, i.e. avoid fetching from this peer for a while + buddy.data.stats.major.networkErrors.inc() + buddy.ctrl.zombie = true + of GetAccountRangeError.NothingSerious: + discard + of NoAccountsForStateRoot: + # Mark this peer dead, i.e. avoid fetching from this peer for a while + buddy.ctrl.zombie = true + return + rc.value + + # Reset error counts + buddy.data.errors.nTimeouts = 0 + + + # Process accounts data + let + nAccounts = dd.data.accounts.len + rc = ctx.data.accountsDb.importAccounts(peer, stateRoot, iv.minPt, dd.data) + if rc.isErr: + # TODO: Prevent deadlock in case there is a problem with the approval + # data which is not in production state, yet. + trace "Import failed, restoring unprocessed accounts", peer, stateRoot, + range=dd.consumed, nAccounts, error=rc.error + + # Just try another peer + buddy.ctrl.zombie = true + else: + # Statistics + env.nAccounts.inc(nAccounts) + + # Register consumed and bulk-imported (well, not yet) accounts range + let rx = iv - dd.consumed + if rx.isOk: + # Return some unused range + buddy.putUnprocessed(rx.value) + else: + # The processed interval might be a bit larger + let ry = dd.consumed - iv + if ry.isOk: + # Remove from unprocessed data. If it is not unprocessed, anymore then + # it was double processed which if ok. + buddy.delUnprocessed(ry.value) + + # ---- + # For dumping data ready to be used in unit tests + if env.proofDumpOk: + var fd = ctx.data.proofDumpFile + if env.proofDumpInx == 0: + fd.write dumpRoot(stateRoot) + fd.write "\n" + if rc.isErr: + fd.write " # Error: base=" & $iv.minPt & " msg=" & $rc.error & "\n" + fd.write dumpSnapAccountRange( + iv.minPt, dd.data, "snapProofData" & $env.proofDumpInx & "*") + fd.flushFile + env.proofDumpInx.inc + if snapAccountsDumpMax <= env.proofDumpInx: + env.proofDumpOk = false + +# ------------------------------------------------------------------------------ +# End +# ------------------------------------------------------------------------------ diff --git a/nimbus/sync/snap/worker/fetch/fetch_accounts.nim b/nimbus/sync/snap/worker/get_account_range.nim similarity index 65% rename from nimbus/sync/snap/worker/fetch/fetch_accounts.nim rename to nimbus/sync/snap/worker/get_account_range.nim index 1c51c8ee3..016001019 100644 --- a/nimbus/sync/snap/worker/fetch/fetch_accounts.nim +++ b/nimbus/sync/snap/worker/get_account_range.nim @@ -16,50 +16,46 @@ import chronos, eth/[common/eth_types, p2p], - nimcrypto/keccak, stew/interval_set, - "../../.."/[protocol, protocol/trace_config, types], - ../../path_desc, - ../worker_desc + "../.."/[protocol, protocol/trace_config], + ".."/[range_desc, worker_desc] {.push raises: [Defect].} logScope: topics = "snap-fetch" -const - snapRequestBytesLimit = 2 * 1024 * 1024 - ## Soft bytes limit to request in `snap` protocol calls. - type - FetchError* = enum - NothingSerious, - MissingProof, - AccountsMinTooSmall, - AccountsMaxTooLarge, - NoAccountsForStateRoot, + GetAccountRangeError* = enum + NothingSerious + MissingProof + AccountsMinTooSmall + AccountsMaxTooLarge + NoAccountsForStateRoot NetworkProblem + ResponseTimeout - FetchAccounts* = object - consumed*: UInt256 ## Leftmost accounts used from argument range - data*: WorkerAccountRange ## reply data + GetAccountRange* = object + consumed*: LeafRange ## Real accounts interval covered + data*: SnapAccountRange ## reply data # ------------------------------------------------------------------------------ # Private functions # ------------------------------------------------------------------------------ -proc getAccountRange( - sp: WorkerBuddy; - root: TrieHash; +proc getAccountRangeReq( + buddy: SnapBuddyRef; + root: Hash256; iv: LeafRange - ): Future[Result[Option[WorkerAccountRange],void]] {.async.} = + ): Future[Result[Option[SnapAccountRange],void]] {.async.} = + let + peer = buddy.peer try: - let reply = await sp.peer.getAccountRange( - root.to(Hash256), iv.minPt, iv.maxPt, snapRequestBytesLimit) + let reply = await peer.getAccountRange( + root, iv.minPt.to(Hash256), iv.maxPt.to(Hash256), snapRequestBytesLimit) return ok(reply) - except CatchableError as e: - trace trSnapRecvError & "waiting for reply to GetAccountRange", peer=sp, + trace trSnapRecvError & "waiting for GetAccountRange reply", peer, error=e.msg return err() @@ -67,25 +63,27 @@ proc getAccountRange( # Public functions # ------------------------------------------------------------------------------ -proc fetchAccounts*( - peer: WorkerBuddy; - stateRoot: TrieHash; +proc getAccountRange*( + buddy: SnapBuddyRef; + stateRoot: Hash256; iv: LeafRange - ): Future[Result[FetchAccounts,FetchError]] {.async.} = + ): Future[Result[GetAccountRange,GetAccountRangeError]] {.async.} = ## Fetch data using the `snap#` protocol, returns the range covered. + let + peer = buddy.peer if trSnapTracePacketsOk: trace trSnapSendSending & "GetAccountRange", peer, accRange=iv, stateRoot, bytesLimit=snapRequestBytesLimit var dd = block: - let rc = await peer.getAccountRange(stateRoot, iv) + let rc = await buddy.getAccountRangeReq(stateRoot, iv) if rc.isErr: return err(NetworkProblem) if rc.value.isNone: trace trSnapRecvTimeoutWaiting & "for reply to GetAccountRange", peer - return err(NothingSerious) - FetchAccounts( - consumed: iv.len, + return err(ResponseTimeout) + GetAccountRange( + consumed: iv, data: rc.value.get) let @@ -105,20 +103,20 @@ proc fetchAccounts*( # any) account after limitHash must be provided. if nProof == 0: # Maybe try another peer - trace trSnapRecvReceived & "EMPTY AccountRange reply", peer, + trace trSnapRecvReceived & "empty AccountRange", peer, nAccounts, nProof, accRange="n/a", reqRange=iv, stateRoot return err(NoAccountsForStateRoot) # So there is no data, otherwise an account beyond the interval end # `iv.maxPt` would have been returned. - trace trSnapRecvReceived & "END AccountRange message", peer, - nAccounts, nProof, accRange=LeafRange.new(iv.minPt, high(NodeTag)), - reqRange=iv, stateRoot - dd.consumed = high(NodeTag) - iv.minPt + dd.consumed = LeafRange.new(iv.minPt, high(NodeTag)) + trace trSnapRecvReceived & "terminal AccountRange", peer, + nAccounts, nProof, accRange=dd.consumed, reqRange=iv, stateRoot return ok(dd) - let (accMinPt, accMaxPt) = - (dd.data.accounts[0].accHash, dd.data.accounts[^1].accHash) + let (accMinPt, accMaxPt) = ( + dd.data.accounts[0].accHash.to(NodeTag), + dd.data.accounts[^1].accHash.to(NodeTag)) if nProof == 0: # github.com/ethereum/devp2p/blob/master/caps/snap.md#accountrange-0x01 @@ -129,11 +127,10 @@ proc fetchAccounts*( # situation for storage slots, this clause keeps the behavior the same # across both. if 0.to(NodeTag) < iv.minPt: - trace trSnapRecvProtocolViolation & "missing proof in AccountRange", peer, + trace trSnapRecvProtocolViolation & "proof-less AccountRange", peer, nAccounts, nProof, accRange=LeafRange.new(iv.minPt, accMaxPt), reqRange=iv, stateRoot return err(MissingProof) - # TODO: How do I know that the full accounts list is correct? if accMinPt < iv.minPt: # Not allowed @@ -150,16 +147,18 @@ proc fetchAccounts*( # * [..] If no accounts exist between startingHash and limitHash, then the # first (if any) account after limitHash must be provided. if 1 < nAccounts: - trace trSnapRecvProtocolViolation & "max too large in AccountRange", peer, - nAccounts, nProof, accRange=LeafRange.new(iv.minPt, accMaxPt), - reqRange=iv, stateRoot - return err(AccountsMaxTooLarge) + # Geth always seems to allow the last account to be larger than the + # limit (seen with Geth/v1.10.18-unstable-4b309c70-20220517.) + if iv.maxPt < dd.data.accounts[^2].accHash.to(NodeTag): + # The segcond largest should not excceed the top one requested. + trace trSnapRecvProtocolViolation & "AccountRange top exceeded", peer, + nAccounts, nProof, accRange=LeafRange.new(iv.minPt, accMaxPt), + reqRange=iv, stateRoot + return err(AccountsMaxTooLarge) - trace trSnapRecvReceived & "AccountRange message", peer, - nAccounts, nProof, accRange=LeafRange.new(iv.minPt, accMaxPt), - reqRange=iv, stateRoot - - dd.consumed = (accMaxPt - iv.minPt) + 1 + dd.consumed = LeafRange.new(iv.minPt, accMaxPt) + trace trSnapRecvReceived & "AccountRange", peer, + nAccounts, nProof, accRange=dd.consumed, reqRange=iv, stateRoot return ok(dd) # ------------------------------------------------------------------------------ diff --git a/nimbus/sync/snap/worker/pivot.nim b/nimbus/sync/snap/worker/pivot.nim new file mode 100644 index 000000000..d1f7a8147 --- /dev/null +++ b/nimbus/sync/snap/worker/pivot.nim @@ -0,0 +1,616 @@ +# Nimbus - Rapidly converge on and track the canonical chain head of each peer +# +# Copyright (c) 2021 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. + +## This module fetches and tracks the canonical chain head of each connected +## peer. (Or in future, each peer we care about; we won't poll them all so +## often.) +## +## This is for when we aren't sure of the block number of a peer's canonical +## chain head. Most of the time, after finding which block, it quietly polls +## to track small updates to the "best" block number and hash of each peer. +## +## But sometimes that can get out of step. If there has been a deeper reorg +## than our tracking window, or a burst of more than a few new blocks, network +## delays, downtime, or the peer is itself syncing. Perhaps we stopped Nimbus +## and restarted a while later, e.g. suspending a laptop or Control-Z. Then +## this will catch up. It is even possible that the best hash the peer gave us +## in the `Status` handshake has disappeared by the time we query for the +## corresponding block number, so we start at zero. +## +## The steps here perform a robust and efficient O(log N) search to rapidly +## converge on the new best block if it's moved out of the polling window no +## matter where it starts, confirm the peer's canonical chain head boundary, +## then track the peer's chain head in real-time by polling. The method is +## robust to peer state changes at any time. +## +## The purpose is to: +## +## - Help with finding a peer common chain prefix ("fast sync pivot") in a +## consistent, fast and explicit way. +## +## - Catch up quickly after any long pauses of network downtime, program not +## running, or deep chain reorgs. +## +## - Be able to display real-time peer states, so they are less mysterious. +## +## - Tell the beam/snap/trie sync processes when to start and what blocks to +## fetch, and keep those fetchers in the head-adjacent window of the +## ever-changing chain. +## +## - Help the sync process bootstrap usefully when we only have one peer, +## speculatively fetching and validating what data we can before we have more +## peers to corroborate the consensus. +## +## - Help detect consensus failures in the network. +## +## We cannot assume a peer's canonical chain stays the same or only gains new +## blocks from one query to the next. There can be reorgs, including deep +## reorgs. When a reorg happens, the best block number can decrease if the new +## canonical chain is shorter than the old one, and the best block hash we +## previously knew can become unavailable on the peer. So we must detect when +## the current best block disappears and be able to reduce block number. + +import + std/bitops, + chronicles, + chronos, + eth/[common/eth_types, p2p, p2p/private/p2p_types], + "../../.."/[constants, genesis, p2p/chain/chain_desc], + "../.."/[protocol, sync_desc, types], + ../worker_desc + +{.push raises: [Defect].} + +export + worker_desc + +logScope: + topics = "snap-pivot" + +const + syncLockedMinimumReply = 8 + ## Minimum number of headers we assume any peers will send if they have + ## them in contiguous ascending queries. Fewer than this confirms we have + ## found the peer's canonical chain head boundary. Must be at least 2, and + ## at least `syncLockedQueryOverlap+2` to stay `SyncLocked` when the chain + ## extends. Should not be large as that would be stretching assumptions + ## about peer implementations. 8 is chosen as it allows 3-deep extensions + ## and 3-deep reorgs to be followed in a single round trip. + + syncLockedQueryOverlap = 4 + ## Number of headers to re-query on each poll when `SyncLocked` so that we + ## get small reorg updates in one round trip. Must be no more than + ## `syncLockedMinimumReply-1`, no more than `syncLockedMinimumReply-2` to + ## stay `SyncLocked` when the chain extends, and not too large to avoid + ## excessive duplicate fetching. 4 is chosen as it allows 3-deep reorgs + ## to be followed in single round trip. + + syncLockedQuerySize = 192 + ## Query size when polling `SyncLocked`. Must be at least + ## `syncLockedMinimumReply`. Large is fine, if we get a large reply the + ## values are almost always useful. + + huntQuerySize = 16 + ## Query size when hunting for canonical head boundary. Small is good + ## because we don't want to keep most of the headers at hunt time. + + huntForwardExpandShift = 4 + ## Expansion factor during `HuntForward` exponential search. + ## 16 is chosen for rapid convergence when bootstrapping or catching up. + + huntBackwardExpandShift = 1 + ## Expansion factor during `HuntBackward` exponential search. + ## 2 is chosen for better convergence when tracking a chain reorg. + +type + WorkerMode = enum + ## The current state of tracking the peer's canonical chain head. + ## `bestBlockNumber` is only valid when this is `SyncLocked`. + SyncLocked + SyncOnlyHash + HuntForward + HuntBackward + HuntRange + HuntRangeFinal + + WorkerHuntEx = ref object of WorkerBase + ## Peer canonical chain head ("best block") search state. + syncMode: WorkerMode ## Action mode + lowNumber: BlockNumber ## Recent lowest known block number. + highNumber: BlockNumber ## Recent highest known block number. + bestNumber: BlockNumber + bestHash: BlockHash + step: uint + +static: + doAssert syncLockedMinimumReply >= 2 + doAssert syncLockedMinimumReply >= syncLockedQueryOverlap + 2 + doAssert syncLockedQuerySize <= maxHeadersFetch + doAssert huntQuerySize >= 1 and huntQuerySize <= maxHeadersFetch + doAssert huntForwardExpandShift >= 1 and huntForwardExpandShift <= 8 + doAssert huntBackwardExpandShift >= 1 and huntBackwardExpandShift <= 8 + + # Make sure that request/response wire protocol messages are id-tracked and + # would not overlap (no multi-protocol legacy support) + doAssert 66 <= protocol.ethVersion + +# ------------------------------------------------------------------------------ +# Private helpers +# ------------------------------------------------------------------------------ + +proc hunt(buddy: SnapBuddyRef): WorkerHuntEx = + buddy.data.workerBase.WorkerHuntEx + +proc `hunt=`(buddy: SnapBuddyRef; value: WorkerHuntEx) = + buddy.data.workerBase = value + +proc new(T: type WorkerHuntEx; syncMode: WorkerMode): T = + T(syncMode: syncMode, + lowNumber: 0.toBlockNumber.BlockNumber, + highNumber: high(BlockNumber).BlockNumber, # maximum uncertainty range. + bestNumber: 0.toBlockNumber.BlockNumber, + bestHash: ZERO_HASH256.BlockHash, # whatever + step: 0u) + +# ------------------------------------------------------------------------------ +# Private logging helpers +# ------------------------------------------------------------------------------ + +proc traceSyncLocked(buddy: SnapBuddyRef, num: BlockNumber, hash: BlockHash) = + ## Trace messages when peer canonical head is confirmed or updated. + let + ctx = buddy.ctx + peer = buddy.peer + bestBlock = ctx.pp(hash, num) + if buddy.hunt.syncMode != SyncLocked: + debug "Now tracking chain head of peer", peer, + bestBlock + elif num > buddy.hunt.bestNumber: + if num == buddy.hunt.bestNumber + 1: + debug "Peer chain head advanced one block", peer, + advance=1, bestBlock + else: + debug "Peer chain head advanced some blocks", peer, + advance=(buddy.hunt.bestNumber - num), bestBlock + elif num < buddy.hunt.bestNumber or hash != buddy.hunt.bestHash: + debug "Peer chain head reorg detected", peer, + advance=(buddy.hunt.bestNumber - num), bestBlock + +# ------------------------------------------------------------------------------ +# Private functions +# ------------------------------------------------------------------------------ + +proc clearSyncStateRoot(buddy: SnapBuddyRef) = + if buddy.data.pivotHeader.isSome: + debug "Stopping state sync from this peer", peer=buddy.peer + buddy.data.pivotHeader = none(BlockHeader) + +proc lockSyncStateAndFetch(buddy: SnapBuddyRef; header: BlockHeader) = + let + ctx = buddy.ctx + peer = buddy.peer + stateRoot = header.stateRoot + hash = header.blockHash.BlockHash + thisBlock = ctx.pp(hash, header.blockNumber) + + buddy.traceSyncLocked(header.blockNumber, hash) + buddy.hunt.bestNumber = header.blockNumber + buddy.hunt.bestHash = hash + buddy.hunt.syncMode = SyncLocked + + if buddy.data.pivotHeader.isNone: + debug "Starting state sync from this peer", peer, thisBlock, stateRoot + elif buddy.data.pivotHeader.unsafeGet.stateRoot != stateRoot: + trace "Adjusting state sync root from this peer", peer, thisBlock, stateRoot + + buddy.data.pivotHeader = some(header) + +proc setHuntBackward(buddy: SnapBuddyRef, lowestAbsent: BlockNumber) = + ## Start exponential search mode backward due to new uncertainty. + buddy.hunt.syncMode = HuntBackward + buddy.hunt.step = 0 + # Block zero is always present. + buddy.hunt.lowNumber = 0.toBlockNumber + # Zero `lowestAbsent` is never correct, but an incorrect peer could send it. + buddy.hunt.highNumber = + if lowestAbsent > 0: lowestAbsent + else: 1.toBlockNumber + buddy.clearSyncStateRoot() + +proc setHuntForward(buddy: SnapBuddyRef, highestPresent: BlockNumber) = + ## Start exponential search mode forward due to new uncertainty. + buddy.hunt.syncMode = HuntForward + buddy.hunt.step = 0 + buddy.hunt.lowNumber = highestPresent + buddy.hunt.highNumber = high(BlockNumber) + buddy.clearSyncStateRoot() + +proc updateHuntAbsent(buddy: SnapBuddyRef, lowestAbsent: BlockNumber) = + ## Converge uncertainty range backward. + if lowestAbsent < buddy.hunt.highNumber: + buddy.hunt.highNumber = lowestAbsent + # If uncertainty range has moved outside the search window, change to hunt + # backward to block zero. Note that empty uncertainty range is allowed + # (empty range is `hunt.lowNumber + 1 == hunt.highNumber`). + if buddy.hunt.highNumber <= buddy.hunt.lowNumber: + buddy.setHuntBackward(lowestAbsent) + buddy.clearSyncStateRoot() + +proc updateHuntPresent(buddy: SnapBuddyRef, highestPresent: BlockNumber) = + ## Converge uncertainty range forward. + if highestPresent > buddy.hunt.lowNumber: + buddy.hunt.lowNumber = highestPresent + # If uncertainty range has moved outside the search window, change to hunt + # forward to no upper limit. Note that empty uncertainty range is allowed + # (empty range is `hunt.lowNumber + 1 == hunt.highNumber`). + if buddy.hunt.lowNumber >= buddy.hunt.highNumber: + buddy.setHuntForward(highestPresent) + buddy.clearSyncStateRoot() + +# ------------------------------------------------------------------------------ +# Private functions, assemble request +# ------------------------------------------------------------------------------ + +proc peerSyncChainRequest(buddy: SnapBuddyRef): BlocksRequest = + ## Choose `GetBlockHeaders` parameters when hunting or following the canonical + ## chain of a peer. + if buddy.hunt.syncMode == SyncLocked: + # Stable and locked. This is just checking for changes including reorgs. + # `buddy.hunt.bestNumber` was recently the head of the peer's canonical + # chain. We must include this block number to detect when the canonical + # chain gets shorter versus no change. + result.startBlock.number = + if buddy.hunt.bestNumber <= syncLockedQueryOverlap: + # Every peer should send genesis for block 0, so don't ask for it. + # `peerSyncChainEmptyReply` has logic to handle this reply as if it + # was for block 0. Aside from saving bytes, this is more robust if + # some client doesn't do genesis reply correctly. + 1.toBlockNumber + else: + min(buddy.hunt.bestNumber - syncLockedQueryOverlap.toBlockNumber, + high(BlockNumber) - (syncLockedQuerySize - 1).toBlockNumber) + result.maxResults = syncLockedQuerySize + return + + if buddy.hunt.syncMode == SyncOnlyHash: + # We only have the hash of the recent head of the peer's canonical chain. + # Like `SyncLocked`, query more than one item to detect when the + # canonical chain gets shorter, no change or longer. + result.startBlock = buddy.hunt.bestHash.to(HashOrNum) + result.maxResults = syncLockedQuerySize + return + + # Searching for the peers's canonical head. An ascending query is always + # used, regardless of search direction. This is because a descending query + # (`reverse = true` and `maxResults > 1`) is useless for searching: Either + # `startBlock` is present, in which case the extra descending results + # contribute no more information about the canonical head boundary, or + # `startBlock` is absent in which case there are zero results. It's not + # defined in the `eth` specification that there must be zero results (in + # principle peers could return the lower numbered blocks), but in practice + # peers stop at the first absent block in the sequence from `startBlock`. + # + # Guaranteeing O(log N) time convergence in all scenarios requires some + # properties to be true in both exponential search (expanding) and + # quasi-binary search (converging in a range). The most important is that + # the gap to `startBlock` after `hunt.lowNumber` and also before + # `hunt.highNumber` are proportional to the query step, where the query step + # is `hunt.step` exponentially expanding each round, or `maxStep` + # approximately evenly distributed in the range. + # + # `hunt.lowNumber+1` must not be used consistently as the start, even with a + # large enough query step size, as that will sometimes take O(N) to converge + # in both the exponential and quasi-binary searches. (Ending at + # `hunt.highNumber-1` is fine if `huntQuerySize > 1`. This asymmetry is + # due to ascending queries (see earlier comment), and non-empty truncated + # query reply being proof of presence before the truncation point, but not + # proof of absence after it. A reply can be truncated just because the peer + # decides to.) + # + # The proportional gap requirement is why we divide by query size here, + # instead of stretching to fit more strictly with `(range-1)/(size-1)`. + + const huntFinalSize = max(2, huntQuerySize) + var maxStep = 0u + + let fullRangeClamped = + if buddy.hunt.highNumber <= buddy.hunt.lowNumber: 0u + else: min(high(uint).toBlockNumber, + buddy.hunt.highNumber - buddy.hunt.lowNumber).truncate(uint) - 1 + + if fullRangeClamped >= huntFinalSize: # `HuntRangeFinal` condition. + maxStep = if huntQuerySize == 1: + fullRangeClamped + elif (huntQuerySize and (huntQuerySize-1)) == 0: + fullRangeClamped shr fastLog2(huntQuerySize) + else: + fullRangeClamped div huntQuerySize + doAssert huntFinalSize >= huntQuerySize + doAssert maxStep >= 1 # Ensured by the above assertion. + + # Check for exponential search (expanding). Iterate `hunt.step`. O(log N) + # requires `startBlock` to be offset from `hunt.lowNumber`/`hunt.highNumber`. + if buddy.hunt.syncMode in {HuntForward, HuntBackward} and + fullRangeClamped >= huntFinalSize: + let forward = buddy.hunt.syncMode == HuntForward + let expandShift = if forward: huntForwardExpandShift + else: huntBackwardExpandShift + # Switches to range search when this condition is no longer true. + if buddy.hunt.step < maxStep shr expandShift: + # The `if` above means the next line cannot overflow. + buddy.hunt.step = if buddy.hunt.step > 0: buddy.hunt.step shl expandShift + else: 1 + # Satisfy the O(log N) convergence conditions. + result.startBlock.number = + if forward: buddy.hunt.lowNumber + buddy.hunt.step.toBlockNumber + else: buddy.hunt.highNumber - + (buddy.hunt.step * huntQuerySize).toBlockNumber + result.maxResults = huntQuerySize + result.skip = buddy.hunt.step - 1 + return + + # For tracing/display. + buddy.hunt.step = maxStep + buddy.hunt.syncMode = HuntRange + if maxStep > 0: + # Quasi-binary search (converging in a range). O(log N) requires + # `startBlock` to satisfy the constraints described above, with the + # proportionality from both ends of the range. The optimal information + # gathering position is tricky and doesn't make much difference, so don't + # bother. We'll centre the query in the range. + var offset = fullRangeClamped - maxStep * (huntQuerySize-1) + # Rounding must bias towards end to ensure `offset >= 1` after this. + offset -= offset shr 1 + result.startBlock.number = buddy.hunt.lowNumber + offset.toBlockNumber + result.maxResults = huntQuerySize + result.skip = maxStep - 1 + else: + # Small range, final step. At `fullRange == 0` we must query at least one + # block before and after the range to confirm the canonical head boundary, + # or find it has moved. This ensures progress without getting stuck. When + # `fullRange` is small this is also beneficial, to get `SyncLocked` in one + # round trip from hereand it simplifies the other search branches below. + # Ideally the query is similar to `SyncLocked`, enough to get `SyncLocked` + # in one round trip, and accommodate a small reorg or extension. + const afterSoftMax = syncLockedMinimumReply - syncLockedQueryOverlap + const beforeHardMax = syncLockedQueryOverlap + let extra = huntFinalSize - fullRangeClamped + var before = (extra + 1) shr 1 + before = max(before + afterSoftMax, extra) - afterSoftMax + before = min(before, beforeHardMax) + # See `SyncLocked` case. + result.startBlock.number = + if buddy.hunt.bestNumber <= before.toBlockNumber: 1.toBlockNumber + else: min(buddy.hunt.bestNumber - before.toBlockNumber, + high(BlockNumber) - (huntFinalSize - 1).toBlockNumber) + result.maxResults = huntFinalSize + buddy.hunt.syncMode = HuntRangeFinal + +# ------------------------------------------------------------------------------ +# Private functions, reply handling +# ------------------------------------------------------------------------------ + +proc peerSyncChainEmptyReply(buddy: SnapBuddyRef; request: BlocksRequest) = + ## Handle empty `GetBlockHeaders` reply. This means `request.startBlock` is + ## absent on the peer. If it was `SyncLocked` there must have been a reorg + ## and the previous canonical chain head has disappeared. If hunting, this + ## updates the range of uncertainty. + let + ctx = buddy.ctx + peer = buddy.peer + + # Treat empty response to a request starting from block 1 as equivalent to + # length 1 starting from block 0 in `peerSyncChainNonEmptyReply`. We treat + # every peer as if it would send genesis for block 0, without asking for it. + if request.skip == 0 and + not request.reverse and + not request.startBlock.isHash and + request.startBlock.number == 1.toBlockNumber: + try: + buddy.lockSyncStateAndFetch(ctx.chain.Chain.db.toGenesisHeader) + except RlpError as e: + raiseAssert "Gensis/chain problem (" & $e.name & "): " & e.msg + return + + if buddy.hunt.syncMode in {SyncLocked, SyncOnlyHash}: + inc buddy.data.stats.ok.reorgDetected + trace "Peer reorg detected, best block disappeared", peer, + startBlock=request.startBlock + + let lowestAbsent = request.startBlock.number + case buddy.hunt.syncMode: + of SyncLocked: + # If this message doesn't change our knowledge, ignore it. + if lowestAbsent > buddy.hunt.bestNumber: + return + # Due to a reorg, peer's canonical head has lower block number, outside + # our tracking window. Sync lock is no longer valid. Switch to hunt + # backward to find the new canonical head. + buddy.setHuntBackward(lowestAbsent) + of SyncOnlyHash: + # Due to a reorg, peer doesn't have the block hash it originally gave us. + # Switch to hunt forward from block zero to find the canonical head. + buddy.setHuntForward(0.toBlockNumber) + of HuntForward, HuntBackward, HuntRange, HuntRangeFinal: + # Update the hunt range. + buddy.updateHuntAbsent(lowestAbsent) + + # Update best block number. It is invalid except when `SyncLocked`, but + # still useful as a hint of what we knew recently, for example in displays. + if lowestAbsent <= buddy.hunt.bestNumber: + buddy.hunt.bestNumber = + if lowestAbsent == 0.toBlockNumber: lowestAbsent + else: lowestAbsent - 1.toBlockNumber + buddy.hunt.bestHash = default(typeof(buddy.hunt.bestHash)) + ctx.seen(buddy.hunt.bestHash,buddy.hunt.bestNumber) + + +proc peerSyncChainNonEmptyReply( + buddy: SnapBuddyRef; + request: BlocksRequest; + headers: openArray[BlockHeader]) = + ## Handle non-empty `GetBlockHeaders` reply. This means `request.startBlock` + ## is present on the peer and in its canonical chain (unless the request was + ## made with a hash). If it's a short, contiguous, ascending order reply, it + ## reveals the abrupt transition at the end of the chain and we have learned + ## or reconfirmed the real-time head block. If hunting, this updates the + ## range of uncertainty. + + let + ctx = buddy.ctx + len = headers.len + highestIndex = if request.reverse: 0 else: len - 1 + + # We assume a short enough reply means we've learned the peer's canonical + # head, because it would have replied with another header if not at the head. + # This is not justified when the request used a general hash, because the + # peer doesn't have to reply with its canonical chain in that case, except it + # is still justified if the hash was the known canonical head, which is + # the case in a `SyncOnlyHash` request. + if len < syncLockedMinimumReply and + request.skip == 0 and not request.reverse and + len.uint < request.maxResults: + buddy.lockSyncStateAndFetch(headers[highestIndex]) + return + + # Be careful, this number is from externally supplied data and arithmetic + # in the upward direction could overflow. + let highestPresent = headers[highestIndex].blockNumber + + # A reply that isn't short enough for the canonical head criterion above + # tells us headers up to some number, but it doesn't tell us if there are + # more after it in the peer's canonical chain. We have to request more + # headers to find out. + case buddy.hunt.syncMode: + of SyncLocked: + # If this message doesn't change our knowledge, ignore it. + if highestPresent <= buddy.hunt.bestNumber: + return + # Sync lock is no longer valid as we don't have confirmed canonical head. + # Switch to hunt forward to find the new canonical head. + buddy.setHuntForward(highestPresent) + of SyncOnlyHash: + # As `SyncLocked` but without the block number check. + buddy.setHuntForward(highestPresent) + of HuntForward, HuntBackward, HuntRange, HuntRangeFinal: + # Update the hunt range. + buddy.updateHuntPresent(highestPresent) + + # Update best block number. It is invalid except when `SyncLocked`, but + # still useful as a hint of what we knew recently, for example in displays. + if highestPresent > buddy.hunt.bestNumber: + buddy.hunt.bestNumber = highestPresent + buddy.hunt.bestHash = headers[highestIndex].blockHash.BlockHash + ctx.seen(buddy.hunt.bestHash,buddy.hunt.bestNumber) + +# ------------------------------------------------------------------------------ +# Public start/stop and admin functions +# ------------------------------------------------------------------------------ + +proc pivotStart*(buddy: SnapBuddyRef) = + ## Setup state root hunter + buddy.hunt = WorkerHuntEx.new(HuntForward) + + # We know the hash but not the block number. + buddy.hunt.bestHash = buddy.peer.state(protocol.eth).bestBlockHash.BlockHash + + # TODO: Temporarily disabled because it's useful to test the worker. + # buddy.syncMode = SyncOnlyHash + +proc pivotStop*(buddy: SnapBuddyRef) = + ## Clean up this peer + discard + +# ------------------------------------------------------------------------------ +# Public functions +# ------------------------------------------------------------------------------ + +proc pivotExec*(buddy: SnapBuddyRef) {.async.} = + ## Query a peer to update our knowledge of its canonical chain and its best + ## block, which is its canonical chain head. This can be called at any time + ## after a peer has negotiated the connection. + ## + ## This function is called in an exponential then binary search style + ## during initial sync to find the canonical head, real-time polling + ## afterwards to check for updates. + ## + ## All replies to this query are part of the peer's canonical chain at the + ## time the peer sends them. + let + peer = buddy.peer + ctx = buddy.ctx + + trace "Starting pivotExec()", peer + + let + request = buddy.peerSyncChainRequest + + trace trEthSendSendingGetBlockHeaders, peer, + count=request.maxResults, + startBlock=ctx.pp(request.startBlock), step=request.traceStep + + inc buddy.data.stats.ok.getBlockHeaders + var reply: Option[protocol.blockHeadersObj] + try: + reply = await peer.getBlockHeaders(request) + except CatchableError as e: + trace trEthRecvError & "waiting for GetBlockHeaders reply", peer, + error=e.msg + inc buddy.data.stats.major.networkErrors + buddy.pivotStop() + return + + if reply.isNone: + trace trEthRecvTimeoutWaiting & "for GetBlockHeaders reply", peer + # TODO: Should disconnect? + inc buddy.data.stats.minor.timeoutBlockHeaders + return + + let nHeaders = reply.get.headers.len + if nHeaders == 0: + trace trEthRecvReceivedBlockHeaders, peer, + got=0, requested=request.maxResults + else: + trace trEthRecvReceivedBlockHeaders, peer, + got=nHeaders, requested=request.maxResults, + firstBlock=reply.get.headers[0].blockNumber, + lastBlock=reply.get.headers[^1].blockNumber + + if request.maxResults.int < nHeaders: + trace trEthRecvProtocolViolation & "excess headers in BlockHeaders message", + peer, got=nHeaders, requested=request.maxResults + # TODO: Should disconnect. + inc buddy.data.stats.major.excessBlockHeaders + return + + if 0 < nHeaders: + # TODO: Check this is not copying the `headers`. + buddy.peerSyncChainNonEmptyReply(request, reply.get.headers) + else: + buddy.peerSyncChainEmptyReply(request) + + trace "Done pivotExec()", peer + +# ------------------------------------------------------------------------------ +# Debugging +# ------------------------------------------------------------------------------ + +proc huntPp*(buddy: SnapBuddyRef): string = + let hx = buddy.hunt + result &= "(mode=" & $hx.syncMode + result &= ",num=(" & hx.lowNumber.pp & "," & hx.highNumber.pp & ")" + result &= ",best=(" & hx.bestNumber.pp & "," & hx.bestHash.pp & ")" + result &= ",step=" & $hx.step + result &= ")" + +# ------------------------------------------------------------------------------ +# End +# ------------------------------------------------------------------------------ diff --git a/nimbus/sync/snap/worker/ticker.nim b/nimbus/sync/snap/worker/ticker.nim index 42d9134dc..09e0e8833 100644 --- a/nimbus/sync/snap/worker/ticker.nim +++ b/nimbus/sync/snap/worker/ticker.nim @@ -16,8 +16,7 @@ import eth/[common/eth_types, p2p], stint, ../../../utils/prettify, - ../../timer_helper, - ./worker_desc + ../../timer_helper {.push raises: [Defect].} @@ -26,25 +25,28 @@ logScope: type TickerStats* = object + pivotBlock*: Option[BlockNumber] accounts*: (float,float) ## mean and standard deviation fillFactor*: (float,float) ## mean and standard deviation activeQueues*: int flushedQueues*: int64 TickerStatsUpdater* = - proc(ns: Worker): TickerStats {.gcsafe, raises: [Defect].} + proc: TickerStats {.gcsafe, raises: [Defect].} - TickerEx = ref object of WorkerTickerBase + TickerRef* = ref object ## Account fetching state that is shared among all peers. - ns: Worker - peersActive: int - statsCb: TickerStatsUpdater - logTicker: TimerCallback - tick: uint64 # more than 5*10^11y before wrap when ticking every sec + nBuddies: int + lastStats: TickerStats + lastTick: uint64 + statsCb: TickerStatsUpdater + logTicker: TimerCallback + tick: uint64 # more than 5*10^11y before wrap when ticking every sec const - defaultTickerStartDelay = 100.milliseconds + tickerStartDelay = 100.milliseconds tickerLogInterval = 1.seconds + tickerLogSuppressMax = 100 # ------------------------------------------------------------------------------ # Private functions: ticking log messages @@ -56,92 +58,81 @@ template noFmtError(info: static[string]; code: untyped) = except ValueError as e: raiseAssert "Inconveivable (" & info & "): " & e.msg -proc setLogTicker(sf: TickerEx; at: Moment) {.gcsafe.} +proc setLogTicker(t: TickerRef; at: Moment) {.gcsafe.} -proc runLogTicker(sf: TickerEx) {.gcsafe.} = - var - avAccounts = "" - avUtilisation = "" - let - tick = sf.tick.toSI - peers = sf.peersActive +proc runLogTicker(t: TickerRef) {.gcsafe.} = + let data = t.statsCb() - y = sf.statsCb(sf.ns) - queues = y.activeQueues - flushed = y.flushedQueues - mem = getTotalMem().uint.toSI + if data != t.lastStats or + t.lastTick + tickerLogSuppressMax < t.tick: + t.lastStats = data + t.lastTick = t.tick + var + avAccounts = "" + avUtilisation = "" + pivot = "n/a" + let + flushed = data.flushedQueues - noFmtError("runLogTicker"): - avAccounts = (&"{(y.accounts[0]+0.5).int64}({(y.accounts[1]+0.5).int64})") - avUtilisation = &"{y.fillFactor[0]*100.0:.2f}%({y.fillFactor[1]*100.0:.2f}%)" + buddies = t.nBuddies + tick = t.tick.toSI + mem = getTotalMem().uint.toSI - info "Sync queue average statistics", - tick, peers, queues, avAccounts, avUtilisation, flushed, mem + noFmtError("runLogTicker"): + if data.pivotBlock.isSome: + pivot = &"#{data.pivotBlock.get}({data.activeQueues})" + avAccounts = + &"{(data.accounts[0]+0.5).int64}({(data.accounts[1]+0.5).int64})" + avUtilisation = + &"{data.fillFactor[0]*100.0:.2f}%({data.fillFactor[1]*100.0:.2f}%)" - sf.tick.inc - sf.setLogTicker(Moment.fromNow(tickerLogInterval)) + info "Snap sync statistics", + tick, buddies, pivot, avAccounts, avUtilisation, flushed, mem -proc setLogTicker(sf: TickerEx; at: Moment) = - if sf.logTicker.isNil: - debug "Sync accounts progress ticker has stopped" - else: - sf.logTicker = safeSetTimer(at, runLogTicker, sf) + t.tick.inc + t.setLogTicker(Moment.fromNow(tickerLogInterval)) + + +proc setLogTicker(t: TickerRef; at: Moment) = + if not t.logTicker.isNil: + t.logTicker = safeSetTimer(at, runLogTicker, t) # ------------------------------------------------------------------------------ -# Private getters/setters +# Public constructor and start/stop functions # ------------------------------------------------------------------------------ -proc tickerEx(ns: Worker): TickerEx = - ## Handy helper - ns.tickerBase.TickerEx +proc init*(T: type TickerRef; cb: TickerStatsUpdater): T = + ## Constructor + T(statsCb: cb) -proc `tickerEx=`(ns: Worker; value: TickerEx) = - ## Handy helper - ns.tickerBase = value - -# ------------------------------------------------------------------------------ -# Public start/stop functions! -# ------------------------------------------------------------------------------ - -proc tickerSetup*(ns: Worker; cb: TickerStatsUpdater) = - ## Global set up - if ns.tickerEx.isNil: - ns.tickerEx = TickerEx(ns: ns) - ns.tickerEx.statsCb = cb - -proc tickerRelease*(ns: Worker) = - ## Global clean up - if not ns.tickerEx.isNil: - ns.tickerEx.logTicker = nil # stop timer - ns.tickerEx = nil # unlink `TickerEx` object - -proc tickerStart*(ns: Worker) = +proc start*(t: TickerRef) = ## Re/start ticker unconditionally - ns.tickerEx.tick = 0 - ns.tickerEx.logTicker = safeSetTimer( - Moment.fromNow(defaultTickerStartDelay), - runLogTicker, - ns.tickerEx) + #debug "Started ticker" + t.logTicker = safeSetTimer(Moment.fromNow(tickerStartDelay), runLogTicker, t) -proc tickerStop*(ns: Worker) = +proc stop*(t: TickerRef) = ## Stop ticker unconditionally - ns.tickerEx.logTicker = nil + t.logTicker = nil + #debug "Stopped ticker" # ------------------------------------------------------------------------------ # Public functions # ------------------------------------------------------------------------------ -proc tickerStartPeer*(sp: WorkerBuddy) = - if sp.ns.tickerEx.peersActive <= 0: - sp.ns.tickerEx.peersActive = 1 - sp.ns.tickerStart() +proc startBuddy*(t: TickerRef) = + ## Increment buddies counter and start ticker unless running. + if t.nBuddies <= 0: + t.nBuddies = 1 + t.start() else: - sp.ns.tickerEx.peersActive.inc + t.nBuddies.inc -proc tickerStopPeer*(sp: WorkerBuddy) = - sp.ns.tickerEx.peersActive.dec - if sp.ns.tickerEx.peersActive <= 0: - sp.ns.tickerStop() +proc stopBuddy*(t: TickerRef) = + ## Decrement buddies counter and stop ticker if there are no more registered + ## buddies. + t.nBuddies.dec + if t.nBuddies <= 0: + t.stop() # ------------------------------------------------------------------------------ # End diff --git a/nimbus/sync/snap/worker/worker_desc.nim b/nimbus/sync/snap/worker/worker_desc.nim deleted file mode 100644 index cf24c1c84..000000000 --- a/nimbus/sync/snap/worker/worker_desc.nim +++ /dev/null @@ -1,237 +0,0 @@ -# Nimbus - New sync approach - A fusion of snap, trie, beam and other methods -# -# Copyright (c) 2021 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/[sequtils, strutils], - eth/[common/eth_types, p2p], - nimcrypto/hash, - stew/[byteutils, keyed_queue], - ../../../constants, - "../.."/[protocol/snap1, types] - -{.push raises: [Defect].} - -const - seenBlocksMax = 500 - ## Internal size of LRU cache (for debugging) - -type - WorkerBase* = ref object of RootObj - ## Stub object, to be inherited in file `worker.nim` - - BuddyStat* = distinct uint - - BuddyRunState = enum - ## Combined state of two boolean values (`stopped`,`stopThisState`) as used - ## in the original source set up (should be double checked and simplified.) - Running ## running, not requested to stop - Stopped ## stopped, stop request - ZombieStop ## abanon/ignore (LRU tab overflow, odd packets) - ZombieRun ## additional zombie state to potentially recover from - - WorkerBuddyStats* = tuple - ## Statistics counters for events associated with this peer. - ## These may be used to recognise errors and select good peers. - ok: tuple[ - reorgDetected: BuddyStat, - getBlockHeaders: BuddyStat, - getNodeData: BuddyStat] - minor: tuple[ - timeoutBlockHeaders: BuddyStat, - unexpectedBlockHash: BuddyStat] - major: tuple[ - networkErrors: BuddyStat, - excessBlockHeaders: BuddyStat, - wrongBlockHeader: BuddyStat] - - WorkerBuddyCtrl* = object - ## Control and state settings - stateRoot*: Option[TrieHash] - ## State root to fetch state for. This changes during sync and is - ## slightly different for each peer. - runState: BuddyRunState - ## Access with getters - - # ------- - - WorkerSeenBlocks = KeyedQueue[array[32,byte],BlockNumber] - ## Temporary for pretty debugging, `BlockHash` keyed lru cache - - WorkerTickerBase* = ref object of RootObj - ## Stub object, to be inherited in file `ticker.nim` - - WorkerFetchBase* = ref object of RootObj - ## Stub object, to be inherited in file `fetch.nim` - - # ------- - - WorkerBuddy* = ref object - ## Non-inheritable peer state tracking descriptor. - ns*: Worker ## Worker descriptor object back reference - peer*: Peer ## Reference to eth p2pProtocol entry - stats*: WorkerBuddyStats ## Statistics counters - ctrl*: WorkerBuddyCtrl ## Control and state settings - - workerBase*: WorkerBase ## Opaque object reference for sub-module - # ... - - Worker* = ref object of RootObj - ## Shared state among all peers of a snap syncing node. Will be - ## amended/inherited into `SnapSyncCtx` by the `snap` module which - ## will also manage a list of `WorkerBuddy` objects. - seenBlock: WorkerSeenBlocks ## Temporary, debugging, pretty logs - - buddiesMax*: int ## Max number of buddies (for LRU caches) - - fetchBase*: WorkerFetchBase ## Opaque object reference - tickerBase*: WorkerTickerBase ## Opaque object reference - - # ------- - - WorkerAccountRange* = accountRangeObj - ## Syntactic sugar, type defined in `snap1` - -# ------------------------------------------------------------------------------ -# Public Constructor -# ------------------------------------------------------------------------------ - -proc new*(T: type WorkerBuddy; ns: Worker; peer: Peer): T = - ## Initial state all default settings. - T(ns: ns, peer: peer) - -proc init*(ctrl: var WorkerBuddyCtrl; running: bool) = - ## Set initial running state `Running` if the argument `running` - ## is `true`. Otherwise the running state is set `stopped`. - ctrl.runState = if running: Running else: Stopped - -# ------------------------------------------------------------------------------ -# Public functions -# ------------------------------------------------------------------------------ - -proc `$`*(sp: WorkerBuddy): string = - $sp.peer - -proc inc(stat: var BuddyStat) {.borrow.} - -# ------------------------------------------------------------------------------ -# Public getters, `BuddyRunState` execution control functions -# ------------------------------------------------------------------------------ - -proc state*(ctrl: WorkerBuddyCtrl): BuddyRunState = - ## Getter (logging only, details of `BuddyRunState` are private) - ctrl.runState - -proc running*(ctrl: WorkerBuddyCtrl): bool = - ## Getter, if `true` if `ctrl.state()` is `Running` - ctrl.runState == Running - -proc stopped*(ctrl: WorkerBuddyCtrl): bool = - ## Getter, if `true`, if `ctrl.state()` is not `Running` - ctrl.runState in {Stopped, ZombieStop, ZombieRun} - -proc zombie*(ctrl: WorkerBuddyCtrl): bool = - ## Getter, `true` if `ctrl.state()` is `Zombie` (i.e. not `running()` and - ## not `stopped()`) - ctrl.runState in {ZombieStop, ZombieRun} - -# ------------------------------------------------------------------------------ -# Public setters, `BuddyRunState` execution control functions -# ------------------------------------------------------------------------------ - -proc `zombie=`*(ctrl: var WorkerBuddyCtrl; value: bool) = - ## Setter - if value: - case ctrl.runState: - of Running: - ctrl.runState = ZombieRun - of Stopped: - ctrl.runState = ZombieStop - else: - discard - else: - case ctrl.runState: - of ZombieRun: - ctrl.runState = Running - of ZombieStop: - ctrl.runState = Stopped - else: - discard - -proc `stopped=`*(ctrl: var WorkerBuddyCtrl; value: bool) = - ## Setter - if value: - case ctrl.runState: - of Running: - ctrl.runState = Stopped - else: - discard - else: - case ctrl.runState: - of Stopped: - ctrl.runState = Running - else: - discard - -# ------------------------------------------------------------------------------ -# Public functions, debugging helpers (will go away eventually) -# ------------------------------------------------------------------------------ - -proc pp*(sn: Worker; bh: BlockHash): string = - ## Pretty printer for debugging - let rc = sn.seenBlock.lruFetch(bh.to(Hash256).data) - if rc.isOk: - return "#" & $rc.value - $bh.to(Hash256).data.toHex - -proc pp*(sn: Worker; bh: BlockHash; bn: BlockNumber): string = - ## Pretty printer for debugging - let rc = sn.seenBlock.lruFetch(bh.to(Hash256).data) - if rc.isOk: - return "#" & $rc.value - "#" & $sn.seenBlock.lruAppend(bh.to(Hash256).data, bn, seenBlocksMax) - -proc pp*(sn: Worker; bhn: HashOrNum): string = - if not bhn.isHash: - return "num(#" & $bhn.number & ")" - let rc = sn.seenBlock.lruFetch(bhn.hash.data) - if rc.isOk: - return "hash(#" & $rc.value & ")" - return "hash(" & $bhn.hash.data.toHex & ")" - -proc seen*(sn: Worker; bh: BlockHash; bn: BlockNumber) = - ## Register for pretty printing - if not sn.seenBlock.lruFetch(bh.to(Hash256).data).isOk: - discard sn.seenBlock.lruAppend(bh.to(Hash256).data, bn, seenBlocksMax) - -proc pp*(a: MDigest[256]; collapse = true): string = - if not collapse: - a.data.mapIt(it.toHex(2)).join.toLowerAscii - elif a == BLANK_ROOT_HASH: - "BLANK_ROOT_HASH" - elif a == EMPTY_UNCLE_HASH: - "EMPTY_UNCLE_HASH" - elif a == EMPTY_SHA3: - "EMPTY_SHA3" - elif a == ZERO_HASH256: - "ZERO_HASH256" - else: - a.data.mapIt(it.toHex(2)).join[56 .. 63].toLowerAscii - -proc pp*(bh: BlockHash): string = - bh.Hash256.pp - -proc pp*(bn: BlockNumber): string = - if bn == high(BlockNumber): "#max" - else: "#" & $bn - -# ------------------------------------------------------------------------------ -# End -# ------------------------------------------------------------------------------ diff --git a/nimbus/sync/snap/worker_desc.nim b/nimbus/sync/snap/worker_desc.nim new file mode 100644 index 000000000..acf538b3c --- /dev/null +++ b/nimbus/sync/snap/worker_desc.nim @@ -0,0 +1,193 @@ +# Nimbus - New sync approach - A fusion of snap, trie, beam and other methods +# +# Copyright (c) 2021 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/[sequtils, strutils], + eth/[common/eth_types, p2p], + nimcrypto, + stew/[byteutils, keyed_queue], + ../../constants, + ".."/[sync_desc, types], + ./worker/[accounts_db, ticker], + ./range_desc + +{.push raises: [Defect].} + +const + snapRequestBytesLimit* = 2 * 1024 * 1024 + ## Soft bytes limit to request in `snap` protocol calls. + + maxPivotBlockWindow* = 500 + ## The maximal depth of two block headers. If the pivot block header + ## (containing the current state root) is more than this many blocks + ## away from a new pivot block header candidate, then the latter one + ## replaces the current block header. + + snapAccountsDumpRangeKiln = (high(UInt256) div 300000) + ## Sample size for a single snap dump on `kiln` (for debugging) + + snapAccountsDumpRange* = snapAccountsDumpRangeKiln + ## Activated size of a data slice if dump is anabled + + snapAccountsDumpMax* = 20 + ## Max number of snap proof dumps (for debugging) + + snapAccountsDumpEnable* = false + ## Enable data dump + + seenBlocksMax = 500 + ## Internal size of LRU cache (for debugging) + +type + WorkerBase* = ref object of RootObj + ## Stub object, to be inherited in file `worker.nim` + + BuddyStat* = distinct uint + + SnapBuddyStats* = tuple + ## Statistics counters for events associated with this peer. + ## These may be used to recognise errors and select good peers. + ok: tuple[ + reorgDetected: BuddyStat, + getBlockHeaders: BuddyStat, + getNodeData: BuddyStat] + minor: tuple[ + timeoutBlockHeaders: BuddyStat, + unexpectedBlockHash: BuddyStat] + major: tuple[ + networkErrors: BuddyStat, + excessBlockHeaders: BuddyStat, + wrongBlockHeader: BuddyStat] + + SnapBuddyErrors* = tuple + ## particular error counters so connections will not be cut immediately + ## after a particular error. + nTimeouts: uint + + # ------- + + WorkerSeenBlocks = KeyedQueue[array[32,byte],BlockNumber] + ## Temporary for pretty debugging, `BlockHash` keyed lru cache + + WorkerTickerBase* = ref object of RootObj + ## Stub object, to be inherited in file `ticker.nim` + + WorkerFetchBase* = ref object of RootObj + ## Stub object, to be inherited in file `fetch.nim` + + WorkerFetchEnvBase* = ref object of RootObj + ## Stub object, to be inherited in file `fetch.nim` + + SnapPivotRef* = ref object + ## Stub object, cache for particular snap data environment + stateHeader*: BlockHeader ## Pivot state, containg state root + pivotAccount*: NodeTag ## Random account + availAccounts*: LeafRangeSet ## Accounts to fetch (organised as ranges) + nAccounts*: uint64 ## Number of accounts imported + # fetchEnv*: WorkerFetchEnvBase ## Opaque object reference + # --- + proofDumpOk*: bool + proofDumpInx*: int + + SnapPivotTable* = ##\ + ## LRU table, indexed by state root + KeyedQueue[Hash256,SnapPivotRef] + + BuddyData* = object + ## Local descriptor data extension + stats*: SnapBuddyStats ## Statistics counters + errors*: SnapBuddyErrors ## For error handling + pivotHeader*: Option[BlockHeader] ## For pivot state hunter + workerBase*: WorkerBase ## Opaque object reference for sub-module + + CtxData* = object + ## Globally shared data extension + seenBlock: WorkerSeenBlocks ## Temporary, debugging, pretty logs + rng*: ref HmacDrbgContext ## Random generator + ticker*: TickerRef ## Ticker, logger + pivotTable*: SnapPivotTable ## Per state root environment + pivotCount*: uint64 ## Total of all created tab entries + pivotEnv*: SnapPivotRef ## Environment containing state root + accountRangeMax*: UInt256 ## Maximal length, high(u256)/#peers + accountsDb*: AccountsDbRef ## Proof processing for accounts + # --- + proofDumpOk*: bool + proofDumpFile*: File + + SnapBuddyRef* = ##\ + ## Extended worker peer descriptor + BuddyRef[CtxData,BuddyData] + + SnapCtxRef* = ##\ + ## Extended global descriptor + CtxRef[CtxData] + +# ------------------------------------------------------------------------------ +# Public functions +# ------------------------------------------------------------------------------ + +proc inc(stat: var BuddyStat) {.borrow.} + +# ------------------------------------------------------------------------------ +# Public functions, debugging helpers (will go away eventually) +# ------------------------------------------------------------------------------ + +proc pp*(ctx: SnapCtxRef; bh: BlockHash): string = + ## Pretty printer for debugging + let rc = ctx.data.seenBlock.lruFetch(bh.to(Hash256).data) + if rc.isOk: + return "#" & $rc.value + "%" & $bh.to(Hash256).data.toHex + +proc pp*(ctx: SnapCtxRef; bh: BlockHash; bn: BlockNumber): string = + ## Pretty printer for debugging + let rc = ctx.data.seenBlock.lruFetch(bh.to(Hash256).data) + if rc.isOk: + return "#" & $rc.value + "#" & $ctx.data.seenBlock.lruAppend(bh.to(Hash256).data, bn, seenBlocksMax) + +proc pp*(ctx: SnapCtxRef; bhn: HashOrNum): string = + if not bhn.isHash: + return "#" & $bhn.number + let rc = ctx.data.seenBlock.lruFetch(bhn.hash.data) + if rc.isOk: + return "%" & $rc.value + return "%" & $bhn.hash.data.toHex + +proc seen*(ctx: SnapCtxRef; bh: BlockHash; bn: BlockNumber) = + ## Register for pretty printing + if not ctx.data.seenBlock.lruFetch(bh.to(Hash256).data).isOk: + discard ctx.data.seenBlock.lruAppend(bh.to(Hash256).data, bn, seenBlocksMax) + +proc pp*(a: MDigest[256]; collapse = true): string = + if not collapse: + a.data.mapIt(it.toHex(2)).join.toLowerAscii + elif a == BLANK_ROOT_HASH: + "BLANK_ROOT_HASH" + elif a == EMPTY_UNCLE_HASH: + "EMPTY_UNCLE_HASH" + elif a == EMPTY_SHA3: + "EMPTY_SHA3" + elif a == ZERO_HASH256: + "ZERO_HASH256" + else: + a.data.mapIt(it.toHex(2)).join[56 .. 63].toLowerAscii + +proc pp*(bh: BlockHash): string = + "%" & bh.Hash256.pp + +proc pp*(bn: BlockNumber): string = + if bn == high(BlockNumber): "#high" + else: "#" & $bn + +# ------------------------------------------------------------------------------ +# End +# ------------------------------------------------------------------------------ diff --git a/nimbus/sync/full/full_desc.nim b/nimbus/sync/sync_desc.nim similarity index 64% rename from nimbus/sync/full/full_desc.nim rename to nimbus/sync/sync_desc.nim index 8b7d2f6a0..1491e8903 100644 --- a/nimbus/sync/full/full_desc.nim +++ b/nimbus/sync/sync_desc.nim @@ -1,5 +1,4 @@ -# Nimbus - New sync approach - A fusion of snap, trie, beam and other methods -# +# Nimbus # Copyright (c) 2021 Status Research & Development GmbH # Licensed under either of # * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or @@ -9,63 +8,53 @@ # at your option. This file may not be copied, modified, or distributed # except according to those terms. +## Worker peers scheduler template +## =============================== +## +## Public descriptors + import eth/[common/eth_types, p2p] {.push raises: [Defect].} type - BuddyRunState = enum - ## Combined state of two boolean values (`stopped`,`stopThisState`) as used - ## in the original source set up (should be double checked and simplified.) - Running = 0 ## running, default state - Stopped ## stopped or about stopping - ZombieStop ## abandon/ignore (LRU tab overflow, odd packets) - ZombieRun ## extra zombie state to potentially recover from + BuddyRunState* = enum + Running = 0 ## Running, default state + Stopped ## Stopped or about stopping + ZombieStop ## Abandon/ignore (wait for pushed out of LRU table) + ZombieRun ## Extra zombie state to potentially recover from BuddyCtrl* = object ## Control and state settings runState: BuddyRunState ## Access with getters - multiPeer: bool ## Triggers `runSingle()` mode unless `true` + multiOk*: bool ## Triggers `runSingle()` mode unless `true` - BuddyDataRef* = ref object of RootObj - ## Stub object, to be inherited in file `worker.nim` - - BuddyRef* = ref object - ## Non-inheritable peer state tracking descriptor. - ctx*: CtxRef ## Shared data back reference + BuddyRef*[S,W] = ref object + ## Worker peer state descriptor. + ctx*: CtxRef[S] ## Shared data descriptor back reference peer*: Peer ## Reference to eth p2pProtocol entry ctrl*: BuddyCtrl ## Control and state settings - data*: BuddyDataRef ## Opaque object reference for sub-module + data*: W ## Worker peer specific data - # ----- - - CtxDataRef* = ref object of RootObj - ## Stub object, to be inherited in file `worker.nim` - - CtxRef* = ref object of RootObj - ## Shared state among all syncing peer workers (aka buddies.) This object - ## Will be amended/inherited main module which controls the peer workers. - buddiesMax*: int ## Max number of buddies (for LRU cache, read only) - chain*: AbstractChainDB ## Block chain database (read only reference) + CtxRef*[S] = ref object + ## Shared state among all syncing peer workers (aka buddies.) + buddiesMax*: int ## Max number of buddies + chain*: AbstractChainDB ## Block chain database (no need for `Peer`) poolMode*: bool ## Activate `runPool()` workers if set `true` - data*: CtxDataRef ## Opaque object reference for sub-module + data*: S ## Shared context for all worker peers # ------------------------------------------------------------------------------ # Public functions # ------------------------------------------------------------------------------ -proc `$`*(buddy: BuddyRef): string = - $buddy.peer & "$" & $buddy.ctrl.runState +proc `$`*[S,W](worker: BuddyRef[S,W]): string = + $worker.peer & "$" & $worker.ctrl.runState # ------------------------------------------------------------------------------ # Public getters, `BuddyRunState` execution control functions # ------------------------------------------------------------------------------ -proc multiOk*(ctrl: BuddyCtrl): bool = - ## Getter - ctrl.multiPeer - proc state*(ctrl: BuddyCtrl): BuddyRunState = ## Getter (logging only, details of `BuddyCtrl` are private) ctrl.runState @@ -87,10 +76,6 @@ proc zombie*(ctrl: BuddyCtrl): bool = # Public setters, `BuddyRunState` execution control functions # ------------------------------------------------------------------------------ -proc `multiOk=`*(ctrl: var BuddyCtrl; val: bool) = - ## Setter - ctrl.multiPeer = val - proc `zombie=`*(ctrl: var BuddyCtrl; value: bool) = ## Setter if value: diff --git a/nimbus/sync/sync_sched.nim b/nimbus/sync/sync_sched.nim new file mode 100644 index 000000000..5a8239725 --- /dev/null +++ b/nimbus/sync/sync_sched.nim @@ -0,0 +1,282 @@ +# Nimbus +# Copyright (c) 2021 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. + +## Sync worker peers scheduler template +## ==================================== +## +## Virtual method/interface functions to be provided as `mixin`: +## +## *runSetup(ctx: CtxRef[S]; tickerOK: bool): bool* +## Global set up. This function will be called before any worker peer is +## started. If that function returns `false`, no worker peers will be run. +## +## *runRelease(ctx: CtxRef[S])* +## Global clean up, done with all the worker peers. +## +## +## *runStart(buddy: BuddyRef[S,W]): bool* +## Initialise a new worker peer. +## +## *runStop(buddy: BuddyRef[S,W])* +## Clean up this worker peer. +## +## +## *runPool(buddy: BuddyRef[S,W])* +## Once started, the function `runPool()` is called for all worker peers in +## sequence as the body of an iteration. There will be no other worker peer +## functions activated simultaneously. +## +## This procedure is started if the global flag `buddy.ctx.poolMode` is set +## `true` (default is `false`.) It is the responsibility of the `runPool()` +## instance to reset the flag `buddy.ctx.poolMode`, typically at the first +## peer instance as the number of active instances is unknown to `runPool()`. +## +## Note that this function does not run in `async` mode. +## +## +## *runSingle(buddy: BuddyRef[S,W]) {.async.}* +## This worker peer method is invoked if the peer-local flag +## `buddy.ctrl.multiOk` is set `false` which is the default mode. This flag +## is updated by the worker peer when deemed appropriate. +## * For all workers, there can be only one `runSingle()` function active +## simultaneously for all worker peers. +## * There will be no `runMulti()` function active for the same worker peer +## simultaneously +## * There will be no `runPool()` iterator active simultaneously. +## +## Note that this function runs in `async` mode. +## +## *runMulti(buddy: BuddyRef[S,W]) {.async.}* +## This worker peer method is invoked if the `buddy.ctrl.multiOk` flag is +## set `true` which is typically done after finishing `runSingle()`. This +## instance can be simultaneously active for all worker peers. +## +## +## Additional import files needed when using this template: +## * eth/[common/eth_types, p2p] +## * chronicles +## * chronos +## * stew/[interval_set, sorted_set], +## * "."/[sync_desc, sync_sched, protocol] +## + +import + std/hashes, + chronos, + eth/[common/eth_types, p2p, p2p/peer_pool, p2p/private/p2p_types], + stew/keyed_queue, + ./sync_desc + +{.push raises: [Defect].} + +type + ActiveBuddies[S,W] = ##\ + ## List of active workers, using `Hash(Peer)` rather than `Peer` + KeyedQueue[Hash,RunnerBuddyRef[S,W]] + + RunnerSyncRef*[S,W] = ref object + ## Module descriptor + ctx*: CtxRef[S] ## Shared data + pool: PeerPool ## For starting the system + buddies: ActiveBuddies[S,W] ## LRU cache with worker descriptors + tickerOk: bool ## Ticker logger + singleRunLock: bool ## For worker initialisation + monitorLock: bool ## For worker monitor + activeMulti: int ## Activated runners + + RunnerBuddyRef[S,W] = ref object + ## Per worker peer descriptor + dsc: RunnerSyncRef[S,W] ## Scheduler descriptor + worker: BuddyRef[S,W] ## Worker peer data + +# ------------------------------------------------------------------------------ +# Private helpers +# ------------------------------------------------------------------------------ + +proc hash(peer: Peer): Hash = + ## Needed for `buddies` table key comparison + peer.remote.id.hash + +# ------------------------------------------------------------------------------ +# Private functions +# ------------------------------------------------------------------------------ + +proc workerLoop[S,W](buddy: RunnerBuddyRef[S,W]) {.async.} = + mixin runMulti, runSingle, runPool, runStop + let + dsc = buddy.dsc + ctx = dsc.ctx + worker = buddy.worker + peer = worker.peer + + # Continue until stopped + while not worker.ctrl.stopped: + if dsc.monitorLock: + await sleepAsync(50.milliseconds) + continue + + # Invoke `runPool()` over all buddies if requested + if ctx.poolMode: + # Grab `monitorLock` (was `false` as checked above) and wait until clear + # to run as the only activated instance. + dsc.monitorLock = true + while 0 < dsc.activeMulti: + await sleepAsync(50.milliseconds) + while dsc.singleRunLock: + await sleepAsync(50.milliseconds) + for w in dsc.buddies.nextValues: + worker.runPool() + dsc.monitorLock = false + continue + + # Rotate connection table so the most used entry is at the top/right + # end. So zombies will end up leftish. + discard dsc.buddies.lruFetch(peer.hash) + + # Allow task switch + await sleepAsync(50.milliseconds) + + # Multi mode + if worker.ctrl.multiOk: + if not dsc.singleRunLock: + dsc.activeMulti.inc + # Continue doing something, work a bit + await worker.runMulti() + dsc.activeMulti.dec + continue + + # Single mode as requested. The `multiOk` flag for this worker was just + # found `false` in the pervious clause. + if not dsc.singleRunLock: + # Lock single instance mode and wait for other workers to finish + dsc.singleRunLock = true + while 0 < dsc.activeMulti: + await sleepAsync(50.milliseconds) + # Run single instance and release afterwards + await worker.runSingle() + dsc.singleRunLock = false + + # End while + + # Note that `runStart()` was dispatched in `onPeerConnected()` + worker.runStop() + + +proc onPeerConnected[S,W](dsc: RunnerSyncRef[S,W]; peer: Peer) = + mixin runStart, runStop + # Check for known entry (which should not exist.) + let + maxWorkers = dsc.ctx.buddiesMax + peers = dsc.pool.len + workers = dsc.buddies.len + if dsc.buddies.hasKey(peer.hash): + trace "Reconnecting zombie peer rejected", peer, peers, workers, maxWorkers + return + + # Initialise worker for this peer + let buddy = RunnerBuddyRef[S,W]( + dsc: dsc, + worker: BuddyRef[S,W]( + ctx: dsc.ctx, + peer: peer)) + if not buddy.worker.runStart(): + trace "Ignoring useless peer", peer, peers, workers, maxWorkers + buddy.worker.ctrl.zombie = true + return + + # Check for table overflow. An overflow might happen if there are zombies + # in the table (though preventing them from re-connecting for a while.) + if dsc.ctx.buddiesMax <= workers: + let leastPeer = dsc.buddies.shift.value.data + if leastPeer.worker.ctrl.zombie: + trace "Dequeuing zombie peer", + oldest=leastPeer.worker, peers, workers=dsc.buddies.len, maxWorkers + discard + else: + # This could happen if there are idle entries in the table, i.e. + # somehow hanging runners. + trace "Peer table full! Dequeuing least used entry", + oldest=leastPeer.worker, peers, workers=dsc.buddies.len, maxWorkers + leastPeer.worker.runStop() + leastPeer.worker.ctrl.zombie = true + + # Add peer entry + discard dsc.buddies.lruAppend(peer.hash, buddy, dsc.ctx.buddiesMax) + + trace "Running peer worker", peer, peers, + workers=dsc.buddies.len, maxWorkers + + asyncSpawn buddy.workerLoop() + + +proc onPeerDisconnected[S,W](dsc: RunnerSyncRef[S,W], peer: Peer) = + let + peers = dsc.pool.len + maxWorkers = dsc.ctx.buddiesMax + workers = dsc.buddies.len + rc = dsc.buddies.eq(peer.hash) + if rc.isErr: + debug "Disconnected, unregistered peer", peer, peers, workers, maxWorkers + return + if rc.value.worker.ctrl.zombie: + # Don't disconnect, leave them fall out of the LRU cache. The effect is, + # that reconnecting might be blocked, for a while. + trace "Disconnected, zombie", peer, peers, workers, maxWorkers + else: + rc.value.worker.ctrl.stopped = true # in case it is hanging somewhere + dsc.buddies.del(peer.hash) + trace "Disconnected buddy", peer, peers, workers=dsc.buddies.len, maxWorkers + +# ------------------------------------------------------------------------------ +# Public functions +# ------------------------------------------------------------------------------ + +proc initSync*[S,W]( + dsc: RunnerSyncRef[S,W]; + node: EthereumNode; + slots: int; + noisy = false) = + ## Constructor + # Leave one extra slot so that it can holds a *zombie* even if all slots + # are full. The effect is that a re-connect on the latest zombie will be + # rejected as long as its worker descriptor is registered. + dsc.ctx = CtxRef[S]( + buddiesMax: max(1, slots + 1), + chain: node.chain) + dsc.pool = node.peerPool + dsc.tickerOk = noisy + dsc.buddies.init(dsc.ctx.buddiesMax) + +proc startSync*[S,W](dsc: RunnerSyncRef[S,W]): bool = + ## Set up syncing. This call should come early. + mixin runSetup + # Initialise sub-systems + if dsc.ctx.runSetup(dsc.tickerOk): + var po = PeerObserver( + onPeerConnected: + proc(p: Peer) {.gcsafe.} = + dsc.onPeerConnected(p), + onPeerDisconnected: + proc(p: Peer) {.gcsafe.} = + dsc.onPeerDisconnected(p)) + + po.setProtocol eth + dsc.pool.addObserver(dsc, po) + return true + +proc stopSync*[S,W](dsc: RunnerSyncRef[S,W]) = + ## Stop syncing + mixin runRelease + dsc.pool.delObserver(dsc) + dsc.ctx.runRelease() + +# ------------------------------------------------------------------------------ +# End +# ------------------------------------------------------------------------------ diff --git a/nimbus/sync/timer_helper.nim b/nimbus/sync/timer_helper.nim index ac9ccbf4a..fcbf73a8b 100644 --- a/nimbus/sync/timer_helper.nim +++ b/nimbus/sync/timer_helper.nim @@ -1,5 +1,4 @@ -# Nimbus - Types, data structures and shared utilities used in network sync -# +# Nimbus # Copyright (c) 2018-2021 Status Research & Development GmbH # Licensed under either of # * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or diff --git a/nimbus/sync/types.nim b/nimbus/sync/types.nim index 5950cfb3a..d564ef6f7 100644 --- a/nimbus/sync/types.nim +++ b/nimbus/sync/types.nim @@ -1,5 +1,4 @@ -# Nimbus - Types, data structures and shared utilities used in network sync -# +# Nimbus # Copyright (c) 2018-2021 Status Research & Development GmbH # Licensed under either of # * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or @@ -17,12 +16,6 @@ import {.push raises: [Defect].} type - TxHash* = distinct Hash256 - ## Hash of a transaction. - ## - ## Note that the `ethXX` protocol driver always uses the - ## underlying `Hash256` type which needs to be converted to `TxHash`. - NodeHash* = distinct Hash256 ## Hash of a trie node or other blob carried over `NodeData` account trie ## nodes, storage trie nodes, contract code. @@ -34,46 +27,41 @@ type ## Hash of a block, goes with `BlockNumber`. ## ## Note that the `ethXX` protocol driver always uses the - ## underlying `Hash256` type which needs to be converted to `TxHash`. + ## underlying `Hash256` type which needs to be converted to `BlockHash`. - TrieHash* = distinct Hash256 - ## Hash of a trie root: accounts, storage, receipts or transactions. - ## - ## Note that the `snapXX` protocol driver always uses the underlying - ## `Hash256` type which needs to be converted to `TrieHash`. + SomeDistinctHash256 = + NodeHash | BlockHash # ------------------------------------------------------------------------------ # Public constructors # ------------------------------------------------------------------------------ -proc new*(T: type TxHash): T = Hash256().T -proc new*(T: type NodeHash): T = Hash256().T -proc new*(T: type BlockHash): T = Hash256().T -proc new*(T: type TrieHash): T = Hash256().T +proc new*(T: type SomeDistinctHash256): T = + Hash256().T # ------------------------------------------------------------------------------ # Public (probably non-trivial) type conversions # ------------------------------------------------------------------------------ -proc to*(num: UInt256; T: type float): T = - ## Convert to float - let mantissaLen = 256 - num.leadingZeros - if mantissaLen <= 64: - num.truncate(uint64).T - else: - let exp = mantissaLen - 64 - (num shr exp).truncate(uint64).T * (2.0 ^ exp) - proc to*(num: SomeInteger; T: type float): T = - ## Convert to float + ## Convert to float. Result an d argument are not strictly equivalent. Though + ## sort of `(num.to(float) + 0.5).int == num` might hold in many cases. num.T -proc to*(w: TrieHash|NodeHash|BlockHash|TxHash; T: type Hash256): T = - ## Get rid of `distinct` harness (needed for `snap1` and `eth1` protocol - ## driver access.) +proc to*(longNum: UInt256; T: type float): T = + ## Convert to float (see also comment at `num.to(float)`, above.) + let mantissaLen = 256 - longNum.leadingZeros + if mantissaLen <= 64: + longNum.truncate(uint64).T + else: + let exp = mantissaLen - 64 + (longNum shr exp).truncate(uint64).T * (2.0 ^ exp) + +proc to*(w: SomeDistinctHash256; T: type Hash256): T = + ## Syntactic sugar w.Hash256 -proc to*(w: seq[TrieHash|NodeHash|BlockHash|TxHash]; T: type seq[Hash256]): T = +proc to*(w: seq[SomeDistinctHash256]; T: type seq[Hash256]): T = ## Ditto cast[seq[Hash256]](w) @@ -85,17 +73,22 @@ proc to*(bh: BlockHash; T: type HashOrNum): T = # Public functions # ------------------------------------------------------------------------------ -proc read*(rlp: var Rlp, T: type TrieHash): T +proc read*(rlp: var Rlp, T: type SomeDistinctHash256): T {.gcsafe, raises: [Defect,RlpError]} = ## RLP mixin reader rlp.read(Hash256).T -proc `==`*(a: NodeHash; b: TrieHash): bool = a.Hash256 == b.Hash256 -proc `==`*(a,b: TrieHash): bool {.borrow.} -proc `==`*(a,b: NodeHash): bool {.borrow.} -proc `==`*(a,b: BlockHash): bool {.borrow.} +proc append*(writer: var RlpWriter; h: SomeDistinctHash256) = + ## RLP mixin + append(writer, h.Hash256) -proc hash*(root: TrieHash|NodeHash|BlockHash): Hash = +proc `==`*(a: SomeDistinctHash256; b: Hash256): bool = + a.Hash256 == b + +proc `==`*[T: SomeDistinctHash256](a,b: T): bool = + a.Hash256 == b.Hash256 + +proc hash*(root: SomeDistinctHash256): Hash = ## Mixin for `Table` or `keyedQueue` root.Hash256.data.hash @@ -123,7 +116,7 @@ func toHex*(hash: Hash256): string = ## Shortcut for `byteutils.toHex(hash.data)` hash.data.toHex -func `$`*(h: TrieHash|NodeHash|BlockHash|TxHash): string = +func `$`*(h: SomeDistinctHash256): string = $h.Hash256.data.toHex func `$`*(blob: Blob): string = diff --git a/tests/replay/pp.nim b/tests/replay/pp.nim index f810da13e..b645b5398 100644 --- a/tests/replay/pp.nim +++ b/tests/replay/pp.nim @@ -78,6 +78,9 @@ proc pp*(h: BlockHeader; indent: int): string = proc pp*(g: Genesis; indent: int): string = g.pp("\n" & " ".repeat(max(1,indent))) +proc pp*(a: Account): string = + &"({a.nonce},{a.balance},{a.storageRoot.pp},{a.codeHash.pp})" + # ------------------------------------------------------------------------------ # End # ------------------------------------------------------------------------------ diff --git a/tests/replay/pp_light.nim b/tests/replay/pp_light.nim index b8cb78ea8..60166ed23 100644 --- a/tests/replay/pp_light.nim +++ b/tests/replay/pp_light.nim @@ -15,26 +15,12 @@ import std/[sequtils, strformat, strutils, tables, times], - nimcrypto/hash + nimcrypto/hash, + ../../nimbus/constants export sequtils, strformat, strutils -const - ZeroHash256 = MDigest[256].default - - EmptyUncleHash = ( "1dcc4de8dec75d7aab85b567b6ccd41a" & - "d312451b948a7413f0a142fd40d49347" ).toDigest - - BlankRootHash = ( "56e81f171bcc55a6ff8345e692c0f86e" & - "5b48e01b996cadc001622fb5e363b421" ).toDigest - - EmptySha3 = ( "c5d2460186f7233c927e7db2dcc703c0" & - "e500b653ca82273b7bfad8045d85a470" ).toDigest - - EmptyRlpHash = ( "56e81f171bcc55a6ff8345e692c0f86e" & - "5b48e01b996cadc001622fb5e363b421" ).toDigest - # ------------------------------------------------------------------------------ # Helpers # ------------------------------------------------------------------------------ @@ -103,14 +89,16 @@ proc pp*(q: openArray[int]; itemsPerLine: int; lineSep: string): string = proc pp*(a: MDigest[256]; collapse = true): string = if not collapse: a.data.mapIt(it.toHex(2)).join.toLowerAscii - elif a == EmptyRlpHash: - "emptyRlpHash" - elif a == EmptyUncleHash: - "emptyUncleHash" - elif a == EmptySha3: - "EmptySha3" - elif a == ZeroHash256: - "zeroHash256" + elif a == ZERO_HASH256: + "ZERO_HASH256" + elif a == BLANK_ROOT_HASH: + "BLANK_ROOT_HASH" + elif a == EMPTY_UNCLE_HASH: + "EMPTY_UNCLE_HASH" + elif a == EMPTY_SHA3: + "EMPTY_SHA3" + elif a == ZERO_HASH256: + "ZERO_HASH256" else: a.data.mapIt(it.toHex(2)).join[56 .. 63].toLowerAscii diff --git a/tests/test_sync_snap.nim b/tests/test_sync_snap.nim index 6048cfee5..f08070406 100644 --- a/tests/test_sync_snap.nim +++ b/tests/test_sync_snap.nim @@ -12,18 +12,19 @@ ## Snap sync components tester import - std/[distros, os, random, sequtils, strformat, strutils], + std/[distros, os, sequtils, strformat, strutils], chronicles, - eth/[common/eth_types, trie/db], + eth/[common/eth_types, p2p, rlp, trie/db], stint, stew/results, unittest2, ../nimbus/db/select_backend, - ../nimbus/sync/[types, protocol/snap1], - ../nimbus/sync/snap/path_desc, - ../nimbus/sync/snap/worker/[fetch/proof_db, worker_desc], + ../nimbus/sync/[types, protocol], + ../nimbus/sync/snap/range_desc, + ../nimbus/sync/snap/worker/accounts_db, ./replay/pp, - ./test_sync_snap/accounts_and_proofs + #./test_sync_snap/sample1, + ./test_sync_snap/sample0 const baseDir = [".", "..", ".."/"..", $DirSep] @@ -37,7 +38,7 @@ type TestItem = object ## palatable input format for tests base: NodeTag - data: WorkerAccountRange + data: SnapAccountRange TestDbInstances = array[3,TrieDatabaseRef] @@ -56,7 +57,7 @@ else: let # Forces `check()` to print the error (as opposed when using `isOk()`) - OkProof = Result[void,ProofError].ok() + OkAccDb = Result[void,AccountsDbError].ok() # There was a problem with the Github/CI which results in spurious crashes # when leaving the `runner()` if the persistent BaseChainDB initialisation @@ -75,8 +76,14 @@ proc findFilePath(file: string): Result[string,void] = return ok(path) err() -proc pp(w: TrieHash): string = - pp.pp(w.Hash256) # `pp()` also available from `worker-desc` +proc pp(w: Hash256): string = + pp.pp(w) # `pp()` also available from `worker_desc` + +proc pp(w: NodeTag; collapse = true): string = + pp.pp(w.to(Hash256),collapse) + +proc pp(w: seq[(string,string)]; indent = 4): string = + w.mapIt(&"({it[0]},{it[1]})").join("\n" & " ".repeat(indent)) proc setTraceLevel = discard @@ -97,25 +104,25 @@ proc to(data: seq[TestSample]; T: type seq[TestItem]): T = for r in data: result.add TestItem( base: r.base.to(NodeTag), - data: WorkerAccountRange( + data: SnapAccountRange( proof: r.proofs, accounts: r.accounts.mapIt( SnapAccount( - accHash: it[0].to(NodeTag), + accHash: it[0], accBody: Account( nonce: it[1], balance: it[2], storageRoot: it[3], codeHash: it[4]))))) -proc permute(r: var Rand; qLen: int): seq[int] = - result = (0 ..< qLen).toSeq - let - halfLen = result.len shr 1 - randMax = result.len - halfLen - 1 - for left in 0 ..< halfLen: - let right = halfLen + r.rand(randMax) - result[left].swap(result[right]) +#proc permute(r: var Rand; qLen: int): seq[int] = +# result = (0 ..< qLen).toSeq +# let +# halfLen = result.len shr 1 +# randMax = result.len - halfLen - 1 +# for left in 0 ..< halfLen: +# let right = halfLen + r.rand(randMax) +# result[left].swap(result[right]) proc flushDbDir(s: string) = if s != "": @@ -153,10 +160,11 @@ proc lastTwo(a: openArray[string]): seq[string] = # ------------------------------------------------------------------------------ proc accountsRunner( - noisy = true; persistent: bool; root: TrieHash; data: seq[TestSample]) = + noisy = true; persistent: bool; root: Hash256; data: seq[TestSample]) = let - lst = data.to(seq[TestItem]) - tmpDir = "accounts_and_proofs.nim".findFilePath.value.splitFile.dir + peer = Peer.new + testItemLst = data.to(seq[TestItem]) + tmpDir = "sample0.nim".findFilePath.value.splitFile.dir db = if persistent: tmpDir.testDbs() else: testDbs() dbDir = db.dbDir.split($DirSep).lastTwo.join($DirSep) info = if db.persistent: &"persistent db on \"{dbDir}\"" @@ -168,86 +176,48 @@ proc accountsRunner( suite &"SyncSnap: accounts and proofs for {info}": var - desc: ProofDb - nRows: seq[int] + base: AccountsDbRef + desc: AccountsDbSessionRef - test &"Merging {lst.len} proofs for state root ..{root.pp}": - desc.init(db.inst[0]) - check desc.mergeBegin(root) - for proofs in lst.mapIt(it.data.proof): - check desc.merge(proofs) == OkProof - check desc.mergeValidate == OkProof - nRows.add desc.proofsLen(root) - check 1 < nRows.len # otherwise test makes no sense - check 0 < nRows[^1] + test &"Verifying {testItemLst.len} snap items for state root ..{root.pp}": + base = AccountsDbRef.init(db.inst[0]) + for n,w in testItemLst: + check base.importAccounts(peer, root, w.base, w.data) == OkAccDb - test "Rollback full database": - check desc.mergeRollback() - check desc.proofsLen(root) == 0 - check desc.accountsLen(root) == 0 - check desc.journalLen == (false,0,0,0) + test &"Merging {testItemLst.len} proofs for state root ..{root.pp}": + base = AccountsDbRef.init(db.inst[1]) + desc = AccountsDbSessionRef.init(base, root, peer) + for n,w in testItemLst: + check desc.merge(w.data.proof) == OkAccDb + check desc.merge(w.base, w.data.accounts) == OkAccDb + desc.assignPrettyKeys() # for debugging (if any) + check desc.interpolate() == OkAccDb - test "Merging and committing all except the last": - for n,proofs in lst.mapIt(it.data.proof): - check desc.mergeBegin(root) - check desc.merge(proofs) == OkProof - check nRows[n] == desc.proofsLen(root) - check desc.mergeValidate == OkProof - if n < nRows.len - 1: - check desc.mergeCommit - check nRows[n] == desc.proofsLen(root) - check desc.mergeRollback - check 1 < nRows.len and nRows[^2] == desc.proofsLen(root) - - test &"Merging/committing {lst.len} proofs, transposed rows": - desc.init(db.inst[1]) - check desc.proofsLen(root) == 0 - check desc.journalLen == (false,0,0,0) - var r = initRand(42) - for n,proofs in lst.mapIt(it.data.proof): - let permProof = r.permute(proofs.len).mapIt(proofs[it]) - check desc.mergeBegin(root) - check desc.merge(permProof) == OkProof - check desc.mergeValidate == OkProof - check desc.mergeCommit - check nRows[n] == desc.proofsLen(root) - - test &"Merging {lst.len} proved account groups"& - &" for state root ..{root.pp}": - desc.init(db.inst[2]) - for n,w in lst: - check desc.mergeProved(root, w.base, w.data) == OkProof - check desc.journalLen == (false,0,0,0) - check nRows[n] == desc.proofsLen(root) - check desc.journalLen == (false,0,0,0) - check 1 < nRows.len # otherwise test makes no sense - check 0 < nRows[^1] + # echo ">>> ", desc.dumpProofsDB.join("\n ") # ------------------------------------------------------------------------------ # Main function(s) # ------------------------------------------------------------------------------ proc syncSnapMain*(noisy = defined(debug)) = - noisy.accountsRunner(persistent = true, testRoot.TrieHash, testSamples) + noisy.accountsRunner( + persistent = false, sample0.snapRoot, sample0.snapProofData) when isMainModule: - const noisy = defined(debug) or true - - when true: # false: - # Import additional data from test data repo - import ../../nimbus-eth1-blobs/replay/accounts_and_proofs_ex - else: - const - testRootEx = testRoot - testSamplesEx = newSeq[TestSample]() + const + noisy = defined(debug) or true + test00 = (sample0.snapRoot, @[sample0.snapProofData0]) + test01 = (sample0.snapRoot, sample0.snapProofData) + #test10 = (sample1.snapRoot, @[sample1.snapProofData1]) + #test11 = (sample1.snapRoot, sample1.snapProofData) setTraceLevel() + setErrorLevel() - # Verify sample state roots - doAssert testRoot == testRootEx - - let samplesList = (testSamples & testSamplesEx) - noisy.accountsRunner(persistent = true, testRoot.TrieHash, samplesList) + noisy.accountsRunner(persistent=false, test00[0], test00[1]) + noisy.accountsRunner(persistent=false, test01[0], test01[1]) + #noisy.accountsRunner(persistent=false, test10[0], test10[1]) + #noisy.accountsRunner(persistent=false, test11[0], test11[1]) # ------------------------------------------------------------------------------ # End diff --git a/tests/test_sync_snap/accounts_and_proofs.nim b/tests/test_sync_snap/accounts_and_proofs.nim deleted file mode 100644 index 4198fd618..000000000 --- a/tests/test_sync_snap/accounts_and_proofs.nim +++ /dev/null @@ -1,134 +0,0 @@ -# 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. - -## Collected snap/1 accounts and proofs when fetching accounts - -import - std/[sequtils], - eth/common/eth_types, - nimcrypto/hash, - stew/byteutils, - ../../nimbus/constants - -const - root = - "b538f1067958013728e013b52c3e37eaecf86ddc83fe5f7b4a045e50deb08810".toDigest - - rec0 = ( - ZERO_HASH256, - @[ - # <0> - ("00000013653234c2d78dcdc645c5141e358ef2e590fe5278778ba729ff5ffd95".toDigest, - 1u64, - "7931794000000000".parse(Uint256), - "56E81F171BCC55A6FF8345E692C0F86E5B48E01B996CADC001622FB5E363B421".toDigest, - "C5D2460186F7233C927E7DB2DCC703C0E500B653CA82273B7BFAD8045D85A470".toDigest), - - # <1> - ("0000008c38d769d75c1ad1de6660da51edc10394c11c50ff9a0ca9e8b8b35dc2".toDigest, - 9u64, - "143314596029971".parse(Uint256), - "56E81F171BCC55A6FF8345E692C0F86E5B48E01B996CADC001622FB5E363B421".toDigest, - "C5D2460186F7233C927E7DB2DCC703C0E500B653CA82273B7BFAD8045D85A470".toDigest), - - # <43695> - ("001048467d0933750604fb19cf5dd096f02f60279cc0d9cf03f9b3424a7fb95f".toDigest, - 4u64, - 0.u256, - "56E81F171BCC55A6FF8345E692C0F86E5B48E01B996CADC001622FB5E363B421".toDigest, - "C5D2460186F7233C927E7DB2DCC703C0E500B653CA82273B7BFAD8045D85A470".toDigest)], - @[ - # <0> - "F90211A0FFBB364A8CB0D565F70271627D91A255FB50D751D52A3348B61C9D7B4E98AB28A0FB1ED3251BBD153BFE1415F9946ABFF90C207678BC09EB006C2538D5EE181944A04EC56E4DC562B7C1C3DC3B0D917CE07B4975D9D4459B3F433EAF5D466DA5FF05A0F5283E423E1112E8E50A68D03E339F914F0D18883C4128571B1D14A64F2C9F2DA09414298F9C3AC243DD13F0412CFE069020D4268767E37ADC529D8923312E6519A07978D8ADDBF59DF2D472783308BB735D8BF9FC951FC694E4249B268F0B67CA67A0A17D1D539EEF8747147C2B77281AC355FF16FA42D6941489AB3A11B46D06DD2EA0D7D9CD27EDEEA84EDD53B201DEC05DDB8F6ADD8CDDC77628FFDE9CABBE4F6C1DA03C45D84EEFF0128C6D19BE1D8CAF2797C7332D5E12040B87E1F4E7E17D9D4977A0A8B7AA500844BCA70F76E20BB231291A54CBC71039D183DA4B1FB058FC79FC69A087682429DABD006289751A0EA2C05AA1FD277DA89BF8C7E26DBAEBC774F246A8A0DA0F3AAB84168AF6C0101C0994B881B4FC9EDC4E99E7F28BA90C26F65EE0C819A0A3D9721D23C8B8118B50FAAA40FB3424B8C2BA1B91A2EAC0AAD29868B74B8497A0D1C18AA65CCA65A7118E2C42C429BADE088FC61987B86575145B5A84CA5379A3A0AD509B03FDE185D3ED8CC884A4D0AC03390E7DB8FEC78EC3127DB28CEB670353A0403A13695F15EAAA0588C74282DFF5A9C05BD9039F44336F10BA5590E087043780".hexToSeqByte, - # <1> - "F90211A050B2D95C218D12F40BE4549EE50709E479B19157BA095501AA94293F662DCA7FA00FB68AA15AD8AD8E2773DC61A370AFE82DAB79EDFDEE1A076F9C3C39B90A30B2A0F7E3E89841383EA0264230C6E5F8BB7A9383E31B13D4333F7417AC389C47C368A0ADF864ED54A756828CA1E8B3C0D434C495CAE24FA3547B10D4B037628BEBD1F4A0ADEEBF028C5D866AC5E6F0D91234E599B2606D89FCFD90F3CF332B46A03AB057A065433307CF0FF284D6A3A7E6D0D434BCD3D732757CCCFA1020B21E5F4762BA5EA0FEC203B690FB1AB74055EF240EA0F9A0E5F27AE2FFED6DA9D2B64883AB456EFEA03C4C09C4F72C15A1CE661C60EB7725403337B46D979FEE259AA3BCC6B0AD6161A05E9BE5120BDF2E94D6E64D1DE4CBAFB7C12E830498228A7D12F6CE2461E93990A066B7F2AD805E04708D28CA06AEE29F88700EB51AB38F4BC109DD9F4ABAAC041BA02C2413B49872A483B1F6B97A741B4B8C59040F4F4759AE444643330CD9907F29A08651CEF990EF0A0C5AB52F4FA4AD4265ACB8F7D2358ABE9AC87BC926F04C1B24A0D29829A570067E446DA03CDFFA7203766F8365C65FBF0E23BF3233BB96D6C658A00F68202899DB482FAFF2AAB2635EDB72E43EBD170D4F0554BAF828E970C4DBC7A06D3D6F17ED39FBB36A6065AC34BE5C633F1B8928B5514DEFFD5A0FFA501AF202A0BE7035655FB3801E87902731C9F0041D8CAFBE471B48F0212293ACCD8C40CACC80".hexToSeqByte, - # <2> - "F90211A000F354080583902374DBAD850D0E8E33803412F22C7BA81CBC2778A3B3350761A0809A2CF3A2F87798CE2B4047BB288F17C000307BC7C57FA251CD2C7292596AECA04B40B0EF7E160F09D3DA0EA477C385A49074D35C937A2B74B3F51ABD8A5F9BCAA0F972583DC80407F31517059FCC80E3D31262D8637BB819E94F2D2CD0736A324CA033539BA750904CED59B6B37F3879FDB62AAA850DCF7994DA64DA41B3A9E78850A0B4F5AA77948FC84800F8A9365531A166B56D3D1C5BBC3367554D0C1DC7766811A0FF28D18F41F88909A7CDF60CE8F5B56171B6FFFC97CF299730AC08436AD562B1A0F83235BB3F0C276386895A503BEF8B61F7A8D65D9F6ED4A92C4FD0616E05DE1EA05DC6F966B54000C0B830DF4BB61E47D4D4357822FE4474823DF4862F92602E2AA067D7F3201504A6BC2CF96A010A856CABC4489BEE2F325AB40C6E3ED230898D68A082FCBFA9FCB388E1EC0CC70F14072B3011CACADC212FFB11DCA1A0387064427FA03F4EB0BC6BB0AF1B9AC31A64FB77C6B75F41301DEFBB3803A660E526D7A8D577A01813C0B7A37EBAA16B78E63E23D6E1EF58B9646C572723FCBAF706EFB0958C77A00E112F5A43F599A7858758D3783525C8BC57CFA1BC9D973045363A6091721A28A0879D607834EC77D3D62A5C30BE61615BFB9DAA671DABCC7294C7C3A8633DB6AFA05876CE1DD19DB3F2FCDE622F6C1AF61898481DD6C72BD9273106835A49C5248480".hexToSeqByte, - # <3> - "F90211A062A4606CBB57887CC2C4B9B686DF69A45791F8518E9B62FB6E86130B4B1C6D13A082126F32BE01C3EF18630C686074A2A393B323B8EC3C964705781631358B8E57A08A47D9820D50F48362B4EC12BCBCD759AC42B2F703401B40AA7699964ABA7B40A0C39C5E09856C11DCC6B3739D8846C5406F8FD462EB79095C419F233749A167C8A009D8A1308EBB7522740233D015BA1910584A7A0360BCFAA3C997FFDA7DB648FBA08728CFDBED8454545FAB8D7A587E24CBCA0AA9AF04F883F954399B1859EF91C1A082EE3DB9657745B7147DB490C653358D0E162C4C28F35D7416F7D720EBA69F48A0E301E9D346E2666B8E97D8A3719388B5FCF68B672D0ECEDC6ABACC3F6F906224A03FF691E4BCEB8DD2B604483F1A116FF8EAB64325F5F10AD426B431EDAE7C5ECEA0F92D8622AFA94F9C1A3C147A491897E39C522060C7FA283E21CD5FE23DA2A911A05995EFA81B02A46AD062F21F5C27E01CC16338AACD382DC796FF48D2B06B8A54A024EFE4D36BF0E3DD5587EB85B960051F2AD4F7E4522C770E5A76186F55A0CBF5A0E90C35D8AD9DEEEC60713B687F2830A9A20C705E46442A4EAFF7D867359F9009A0C2F59F608A467ABB02907A45B7446B5F375C660C256E63E20749C98FFD157299A02F08BF5CE0278F3A28A89D18BD44B2712792C362BF755BC545BC3C35E6895BB2A0BB6EDC6F46D9C76AE0CCDEBA9F1C18CA7F0EE94A561350BDAC731364007A525480".hexToSeqByte, - # <4> - "F90211A05A745895FC96B94DB4D0A16D034CF33F38059F2357ED7CB2EA35DB00DD92EF65A03FB18F877D09F9E2021D5EE638E2F2919E24CAEA933ED4AC51818323F7DDC29EA004E520D01462FC4C79A9A74FEE948EC2F36E708575E7AD4CD9C47F6B0B87F519A09CCEB99ADBC31003D903A288D6EE7DF583A4947FB4ADF0F22886E84A86D4D7E5A070D1C2E44D89E9A140F51F5D1C1E9906EF24A3E70A90614B4A8ACB1AEAB35A5CA001831185E3DBBAA2AEB1C18ED3E0F77B41F2E98E09490F399D8D2FAAB92CB3C3A067F57C912E5826406002CAC84732BF505702CA103ACB63A1024ED53F8AAC86C7A05D0D61D6026C41DFCF47FE3B19F277FC9FEBD94E4C2FF0AA12F15C4035B651B4A05CC597DD4F21C32D0EA8831C9CB585310C5B22CA8FAFEA15A4B3F93C2BAAF394A084807A504C68F016A6DBAB22370B77FAB6AD339DD8C6BFBE54BFD137C808D0CDA0ED42536EE6357BB3AA7DDC2A06FBB4E1D8DE2152A657183979151A8D04EFCA2FA078A48BF0F38B4F0972FBD2D876DD685F4FE8CCEFF168F1C6201C973ACEF1C1C8A0DBFAFB4F768292C4B23EB8F6F81CD29D956702E78390F2C4417F0C4E8F6C2A17A0E9B44679701E0B0F2EF944853AEAFB8CF9FFAC1CE6A52159AF74845E46F47125A0E41FC974110569D17E12190B596DE2C0E3C8B3BB451DC4C91154A0C2645D797AA01199389984707B7EC7157340E1D8B1174F5F47CE465FF4F8449F22E28EA56D0A80".hexToSeqByte, - # <5> - "F90211A050C20A151922301F0D1998EE0141A22B7C919BD0D716794EE7E3A5E0EC48DEC8A0AB31DFBEF2AC74B4E501DCE89315A6A89B10F20CBA64F761993A1037418613A7A0BF0D6EE592B2CAA6F302B27999438103809FAF702A2B30E5E1403965EF87B35EA0135D8AFE5EB5D20A1927A91F535BA7684490658EF272C933115BF0BF060CF5E6A0A1EE2F87381EA364E75D651631B5B00E62B1E7E2008458ACF29E0831D7760AFDA040AC269BEA082940F45ED7D813C47E21B930C88EF2B28BF187AE83789EF94BC5A02A03448BD5B58065042A47DB4310F364915C5D4E5FBDF32D292D6FB6BDD0708CA0E0C204B11B2EECD0A4986EEFD3413AD49DDDE082FEE6FAD50AD5C310EB7F22CDA0DA59FED5F92CC206DC2EA7BAD2EA92CC2E9C9FFE5F3A816BBF4EE2E585CE3DCAA073B2EB114312AAB7D2A765DC7923977FB60AF0447ECC7B381F193F68F658A1B7A031045FF0797D0309A4073C19A00172A2737B1D345269149A4AA4A82E6D202B4EA060BEC4F79BB8D0BCAF2B2E362B75845651A2FCC8876A3511D7843CA4E6D18DFDA01D4D7E3E579AA0FBADD67C3B9F09931DB5D733852B36F6201F9D7CF0961E1C17A0AAE7C5916D9FC01F0E7B27543F3991C0B48302A739F83030A951DA296BFCE7B8A0E3519D65CC3E19949E0D25424D5B3BFBD9DF02C84744FD39360471D197570803A0FD9C31C068D34B2701C0CDD3346012D84BB43E982BE5451E98CE50F9DB2D99DE80".hexToSeqByte, - # <6> - "F8B180A00642D4EC5F43A113E2E0C470A87AB859178187341C0BFA059AABBE2384BDFB62808080808080A0E5652B61A18FE7AC7F7C71DE1AE97B8CA1CF742CA89BE4BBE191A2F492C606C5808080A0B203CB3B8CF8825505939914BCBF2B7B7606768E503682093E9B08B99DB3B38AA014C13E3F5D55A90B2C7C654B7C1C34FC65D2268EEC0EB6296B3A8F6199154F4EA0A0CE8026B7C397771620B3B3620E2B0528157DB901C02EDB86BA87DF8DC23E268080".hexToSeqByte, - # <7> - "F90211A0175A0B1AEB1FFC698C61412794315A73B4C360EFA558142223EB50FDFB7C03E1A04AA803B66B8E66D570D504405C088CAFDE3F1568046360FD8AA0A365A4120214A0E6377FAD331E7EDD34F932BFFBDD7B0A3B828BBB7D2D6C73133B6919D9A49E20A0E7C4D8894D251DBDCE60AB039BF2B6B877FC032465CEEA46775BBD436630823CA0B5637ED98AF879C025DF5C7C0969A45BDD4061A891742DA7A5A95EF148A41623A05E301F8CA8969599E168E4C36276D7EA0CE70F88206CE550CBD211C5F011ED88A079C3DE527358AA2F1052BFDDBBCA68434044644E75EDD214D24281D6A0C58752A0086F191941F619D0B54136FD6E2582AB891066C8EB42113065E7E2ADF84FD5C1A01D1F5BE41598FF2FCAF1BA2A5C081120D1D70105DF11FA96729CBED928BBA2DEA07E7F5873A68A712E0D4952A9AE5216169488D12EB318FE4705396C58F1F0C88EA07585C154BFFE63F1600CD996C3551BB63E9ABF02470D9B92287A7746D6F87D30A090DE8B996508F04459B3FC3F75C65FC7F66CD7F0CB6E48D58B9853EC7DBD1F58A0F8D482EE79E5E29B6741861FE044346F6E5EA80BFD8A1378CCC73824A59EBB3EA0FDAD4E6FC2F866901830B8EB6FCD19ABC1AE8028BDC85C5A20D78D002F1F117CA0424A916B37993B3A8EAA67ABC370994D0F8931E2AD450F18FF820BCB5EBC88E3A032FE085815FE7CCA707217A0308A266BF6F63DEDEC466A8F89F7AE3F66876E7080".hexToSeqByte, - # <8> - "F90211A06C58C81EA693CAC268BD2F0733BEB9A2784C75AA20C39B47644BB8A5E2189B27A05945F9ECE092095BD10E7814A81A14DBEDB25342BEABFAA588BFCDAF6444BCA6A007CAC5ABE793C070CE26F2A21CD0F29A573252D2C88051BC3CD58BECEA261EBEA068D6CE58650D53BBFE68284E09749907136BD52EE69332329DC0B86256987290A0BD948923CEB4704B381153432836A6534DC48C5161558B5B4B2747E9618922E9A075244FB6B65AEAC7B016CB659B04F4F144C7A9093175BBEBD844090BF6548240A0932890EF1AE3A79F55162780E5C6B32B7DEE1DA34F3FC2EBEEDDD1E96FCD433FA0E264A2922852C7C489F91DAA2FCFF0C5285A7DA1DD7891A3D9408D74F63B394BA0BF60414930AC9613A6CEF84FEDD860D0878DF5862626F52DDC7D250CDC79D2CEA026EB321595E04C1C43C33A937E4429F9F731CDC1A737FCFD12AACCF445F26748A0FAD955C9809991E80713C556A6AE30D425F36C62BA701DB863DB601341AB9664A0D48C5E648623C5FEF68B95A7761F7CC59B82FFF0231857158CBAB82C0839B846A0F33215586D176E00AA996ACE3C47E9C7517FF4B2D8CFA3AE69A57C5D767AE8C5A02FC3250268C96860E1D52E2391C43BF1EE881987F96730A750E61C6CD91E6870A02E95A4BF0044815926DF4C81B09BE500DCCBBF98CFC9E624BF2E248EF625E2D3A0F346E1C14D8B033A03E6B8BFD8318B0DBACCA7B138B6AE9A72D84E712A52603380".hexToSeqByte, - # <9> - "F90211A0296F209978A24A9984C5E45D10D6B245947D83FA007DC2121C01A39577138663A055ACACB026401BA36C369FD68C335D30A0CCE097B25AD38819097CFE5D704867A031FF3A3298562295E6B44A13F79028A7DF49AB62CDBBC75B6B671B9265254A76A0BCA39714982A3DB01CF2211E5D13863968929DD3858000AA1970A577F14A5E8BA0A3E891D719D60B724951222664EAD2643E8B8A944BAF4EBAACAE702C8E1AEF42A0924AC4E2FC0B05E457C07660FBB4FC693FBE4ACA9F1290460A59057C742EB734A027C4975E9683463489E1BF7311A77689D566FFB7A9A0D9EBC944CFCE4265F2FBA0F7D781A27E6D5ABC32A251BAE37FCC0D87D6A021B3B24B22BF4E2EB184A9C397A0530A2DAD21BDF103E4A70448739D7B4905C5B23D6AC3505DF879960CEF80FAD6A0569BED1BACE3BF2BAF56C6A6EEE8E2D5722C5C3CB8116ECA93CB2F069BB1B2B2A06A040503F573725DAB1A804D4382492CF0E62AFAAC297AEF46D14D231AD07A24A05E08296620CBF5F661D98EC10AF23B81D3C42650657782E291D2EDE6FD0671C9A0A19F098F8D390CCF95A404C19B808C1E73BD637719458E43E3AA1AE667772657A007A61D4524CE417FD7E75A60C87E49D2ABE2B2F84DEB6195DC291E9227CF65E9A07EA8968C14011CD7F7ABE224736C000213124C9A0819B31E689CB8B534EC889CA004802E2FC25D0C7D828D66701727396F54AA6622453214DDA47F89ACA1616FDD80".hexToSeqByte, - # <10> - "F901318080A0A9B034F6DF142723288C01ABC1D11C5836D0584FCEB53D7F61E0216881474224A0B2B840F666627E35696A5E8B7764FD8C232A2AA09B5C30D6C72FD5AB75143DC8A0EAA66386D22922CFDDC505D0E4FB3A5B7F4C0F589C7200E58AE336284FBB6C02A0579C4844BABBC6F1343B8A2F88A497463533D0B8FA6351CF95D8A61B1457997680A0297155BBC073059DC7C59FB9DB094034DF55D6B93EC5FDEECE12806D1AA8EBC080A0C2DF13436F789DB4517236E714733A08D55239E7C72D9E12D657E39AB47553A5A0BF9B5757D3F5BEEF22AF958A4DED7F5D0CAD563DC3DC7C9BD22F7B61B3885BB0A076CE7BBA69CAFCFE416BC453A064610A9946330563312DD6C57923A1EE46FCDC8080A0866DEA9CB87A66E9C92F86A84526298955FE32F35B17B78DB21A28DF55B67E128080".hexToSeqByte, - # <11> - "F8518080A00AB67F3EB5F163D372289AF571D28A0CFE1EA9E5E8C1B12BED10B1F04ADD8163808080A0A43155ADC4D4F7C6F82A3AE1B2F5B0A91C13F8C8B91D2E4833BDCA163196CA2880808080808080808080".hexToSeqByte, - # <12> - "F8669D207D0933750604FB19CF5DD096F02F60279CC0D9CF03F9B3424A7FB95FB846F8440480A056E81F171BCC55A6FF8345E692C0F86E5B48E01B996CADC001622FB5E363B421A0C5D2460186F7233C927E7DB2DCC703C0E500B653CA82273B7BFAD8045D85A470".hexToSeqByte]) - - rec1 = ( - "001048467d0933750604fb19cf5dd096f02f60279cc0d9cf03f9b3424a7fb960".toDigest, - @[ - # <0> - ("00104852da00c6b5afbceb650f30322fc6e4406b508796d325ff4d3ef3a904e5".toDigest, - 1u64, - 4974.u256, - "56E81F171BCC55A6FF8345E692C0F86E5B48E01B996CADC001622FB5E363B421".toDigest, - "C5D2460186F7233C927E7DB2DCC703C0E500B653CA82273B7BFAD8045D85A470".toDigest), - - # <1> - ("00104859ecda2e64d1a459062849603d4ea641c749f0c3bbbf9e9f5faf9c16ba".toDigest, - 13u64, - 0.u256, - "56E81F171BCC55A6FF8345E692C0F86E5B48E01B996CADC001622FB5E363B421".toDigest, - "C5D2460186F7233C927E7DB2DCC703C0E500B653CA82273B7BFAD8045D85A470".toDigest), - - # <43845> - ("0020c98b155f8a165cdab1ec9865e8a96c2acd182a7f590593d48c9ef88b5d29".toDigest, - 1u64, - "1549611000000000".parse(Uint256), - "56E81F171BCC55A6FF8345E692C0F86E5B48E01B996CADC001622FB5E363B421".toDigest, - "C5D2460186F7233C927E7DB2DCC703C0E500B653CA82273B7BFAD8045D85A470".toDigest)], - @[ - # <0> - "F90211A0FFBB364A8CB0D565F70271627D91A255FB50D751D52A3348B61C9D7B4E98AB28A0FB1ED3251BBD153BFE1415F9946ABFF90C207678BC09EB006C2538D5EE181944A04EC56E4DC562B7C1C3DC3B0D917CE07B4975D9D4459B3F433EAF5D466DA5FF05A0F5283E423E1112E8E50A68D03E339F914F0D18883C4128571B1D14A64F2C9F2DA09414298F9C3AC243DD13F0412CFE069020D4268767E37ADC529D8923312E6519A07978D8ADDBF59DF2D472783308BB735D8BF9FC951FC694E4249B268F0B67CA67A0A17D1D539EEF8747147C2B77281AC355FF16FA42D6941489AB3A11B46D06DD2EA0D7D9CD27EDEEA84EDD53B201DEC05DDB8F6ADD8CDDC77628FFDE9CABBE4F6C1DA03C45D84EEFF0128C6D19BE1D8CAF2797C7332D5E12040B87E1F4E7E17D9D4977A0A8B7AA500844BCA70F76E20BB231291A54CBC71039D183DA4B1FB058FC79FC69A087682429DABD006289751A0EA2C05AA1FD277DA89BF8C7E26DBAEBC774F246A8A0DA0F3AAB84168AF6C0101C0994B881B4FC9EDC4E99E7F28BA90C26F65EE0C819A0A3D9721D23C8B8118B50FAAA40FB3424B8C2BA1B91A2EAC0AAD29868B74B8497A0D1C18AA65CCA65A7118E2C42C429BADE088FC61987B86575145B5A84CA5379A3A0AD509B03FDE185D3ED8CC884A4D0AC03390E7DB8FEC78EC3127DB28CEB670353A0403A13695F15EAAA0588C74282DFF5A9C05BD9039F44336F10BA5590E087043780".hexToSeqByte, - # <1> - "F90211A050B2D95C218D12F40BE4549EE50709E479B19157BA095501AA94293F662DCA7FA00FB68AA15AD8AD8E2773DC61A370AFE82DAB79EDFDEE1A076F9C3C39B90A30B2A0F7E3E89841383EA0264230C6E5F8BB7A9383E31B13D4333F7417AC389C47C368A0ADF864ED54A756828CA1E8B3C0D434C495CAE24FA3547B10D4B037628BEBD1F4A0ADEEBF028C5D866AC5E6F0D91234E599B2606D89FCFD90F3CF332B46A03AB057A065433307CF0FF284D6A3A7E6D0D434BCD3D732757CCCFA1020B21E5F4762BA5EA0FEC203B690FB1AB74055EF240EA0F9A0E5F27AE2FFED6DA9D2B64883AB456EFEA03C4C09C4F72C15A1CE661C60EB7725403337B46D979FEE259AA3BCC6B0AD6161A05E9BE5120BDF2E94D6E64D1DE4CBAFB7C12E830498228A7D12F6CE2461E93990A066B7F2AD805E04708D28CA06AEE29F88700EB51AB38F4BC109DD9F4ABAAC041BA02C2413B49872A483B1F6B97A741B4B8C59040F4F4759AE444643330CD9907F29A08651CEF990EF0A0C5AB52F4FA4AD4265ACB8F7D2358ABE9AC87BC926F04C1B24A0D29829A570067E446DA03CDFFA7203766F8365C65FBF0E23BF3233BB96D6C658A00F68202899DB482FAFF2AAB2635EDB72E43EBD170D4F0554BAF828E970C4DBC7A06D3D6F17ED39FBB36A6065AC34BE5C633F1B8928B5514DEFFD5A0FFA501AF202A0BE7035655FB3801E87902731C9F0041D8CAFBE471B48F0212293ACCD8C40CACC80".hexToSeqByte, - # <2> - "F90211A000F354080583902374DBAD850D0E8E33803412F22C7BA81CBC2778A3B3350761A0809A2CF3A2F87798CE2B4047BB288F17C000307BC7C57FA251CD2C7292596AECA04B40B0EF7E160F09D3DA0EA477C385A49074D35C937A2B74B3F51ABD8A5F9BCAA0F972583DC80407F31517059FCC80E3D31262D8637BB819E94F2D2CD0736A324CA033539BA750904CED59B6B37F3879FDB62AAA850DCF7994DA64DA41B3A9E78850A0B4F5AA77948FC84800F8A9365531A166B56D3D1C5BBC3367554D0C1DC7766811A0FF28D18F41F88909A7CDF60CE8F5B56171B6FFFC97CF299730AC08436AD562B1A0F83235BB3F0C276386895A503BEF8B61F7A8D65D9F6ED4A92C4FD0616E05DE1EA05DC6F966B54000C0B830DF4BB61E47D4D4357822FE4474823DF4862F92602E2AA067D7F3201504A6BC2CF96A010A856CABC4489BEE2F325AB40C6E3ED230898D68A082FCBFA9FCB388E1EC0CC70F14072B3011CACADC212FFB11DCA1A0387064427FA03F4EB0BC6BB0AF1B9AC31A64FB77C6B75F41301DEFBB3803A660E526D7A8D577A01813C0B7A37EBAA16B78E63E23D6E1EF58B9646C572723FCBAF706EFB0958C77A00E112F5A43F599A7858758D3783525C8BC57CFA1BC9D973045363A6091721A28A0879D607834EC77D3D62A5C30BE61615BFB9DAA671DABCC7294C7C3A8633DB6AFA05876CE1DD19DB3F2FCDE622F6C1AF61898481DD6C72BD9273106835A49C5248480".hexToSeqByte, - # <3> - "F90211A0175A0B1AEB1FFC698C61412794315A73B4C360EFA558142223EB50FDFB7C03E1A04AA803B66B8E66D570D504405C088CAFDE3F1568046360FD8AA0A365A4120214A0E6377FAD331E7EDD34F932BFFBDD7B0A3B828BBB7D2D6C73133B6919D9A49E20A0E7C4D8894D251DBDCE60AB039BF2B6B877FC032465CEEA46775BBD436630823CA0B5637ED98AF879C025DF5C7C0969A45BDD4061A891742DA7A5A95EF148A41623A05E301F8CA8969599E168E4C36276D7EA0CE70F88206CE550CBD211C5F011ED88A079C3DE527358AA2F1052BFDDBBCA68434044644E75EDD214D24281D6A0C58752A0086F191941F619D0B54136FD6E2582AB891066C8EB42113065E7E2ADF84FD5C1A01D1F5BE41598FF2FCAF1BA2A5C081120D1D70105DF11FA96729CBED928BBA2DEA07E7F5873A68A712E0D4952A9AE5216169488D12EB318FE4705396C58F1F0C88EA07585C154BFFE63F1600CD996C3551BB63E9ABF02470D9B92287A7746D6F87D30A090DE8B996508F04459B3FC3F75C65FC7F66CD7F0CB6E48D58B9853EC7DBD1F58A0F8D482EE79E5E29B6741861FE044346F6E5EA80BFD8A1378CCC73824A59EBB3EA0FDAD4E6FC2F866901830B8EB6FCD19ABC1AE8028BDC85C5A20D78D002F1F117CA0424A916B37993B3A8EAA67ABC370994D0F8931E2AD450F18FF820BCB5EBC88E3A032FE085815FE7CCA707217A0308A266BF6F63DEDEC466A8F89F7AE3F66876E7080".hexToSeqByte, - # <4> - "F90211A06C58C81EA693CAC268BD2F0733BEB9A2784C75AA20C39B47644BB8A5E2189B27A05945F9ECE092095BD10E7814A81A14DBEDB25342BEABFAA588BFCDAF6444BCA6A007CAC5ABE793C070CE26F2A21CD0F29A573252D2C88051BC3CD58BECEA261EBEA068D6CE58650D53BBFE68284E09749907136BD52EE69332329DC0B86256987290A0BD948923CEB4704B381153432836A6534DC48C5161558B5B4B2747E9618922E9A075244FB6B65AEAC7B016CB659B04F4F144C7A9093175BBEBD844090BF6548240A0932890EF1AE3A79F55162780E5C6B32B7DEE1DA34F3FC2EBEEDDD1E96FCD433FA0E264A2922852C7C489F91DAA2FCFF0C5285A7DA1DD7891A3D9408D74F63B394BA0BF60414930AC9613A6CEF84FEDD860D0878DF5862626F52DDC7D250CDC79D2CEA026EB321595E04C1C43C33A937E4429F9F731CDC1A737FCFD12AACCF445F26748A0FAD955C9809991E80713C556A6AE30D425F36C62BA701DB863DB601341AB9664A0D48C5E648623C5FEF68B95A7761F7CC59B82FFF0231857158CBAB82C0839B846A0F33215586D176E00AA996ACE3C47E9C7517FF4B2D8CFA3AE69A57C5D767AE8C5A02FC3250268C96860E1D52E2391C43BF1EE881987F96730A750E61C6CD91E6870A02E95A4BF0044815926DF4C81B09BE500DCCBBF98CFC9E624BF2E248EF625E2D3A0F346E1C14D8B033A03E6B8BFD8318B0DBACCA7B138B6AE9A72D84E712A52603380".hexToSeqByte, - # <5> - "F90211A0296F209978A24A9984C5E45D10D6B245947D83FA007DC2121C01A39577138663A055ACACB026401BA36C369FD68C335D30A0CCE097B25AD38819097CFE5D704867A031FF3A3298562295E6B44A13F79028A7DF49AB62CDBBC75B6B671B9265254A76A0BCA39714982A3DB01CF2211E5D13863968929DD3858000AA1970A577F14A5E8BA0A3E891D719D60B724951222664EAD2643E8B8A944BAF4EBAACAE702C8E1AEF42A0924AC4E2FC0B05E457C07660FBB4FC693FBE4ACA9F1290460A59057C742EB734A027C4975E9683463489E1BF7311A77689D566FFB7A9A0D9EBC944CFCE4265F2FBA0F7D781A27E6D5ABC32A251BAE37FCC0D87D6A021B3B24B22BF4E2EB184A9C397A0530A2DAD21BDF103E4A70448739D7B4905C5B23D6AC3505DF879960CEF80FAD6A0569BED1BACE3BF2BAF56C6A6EEE8E2D5722C5C3CB8116ECA93CB2F069BB1B2B2A06A040503F573725DAB1A804D4382492CF0E62AFAAC297AEF46D14D231AD07A24A05E08296620CBF5F661D98EC10AF23B81D3C42650657782E291D2EDE6FD0671C9A0A19F098F8D390CCF95A404C19B808C1E73BD637719458E43E3AA1AE667772657A007A61D4524CE417FD7E75A60C87E49D2ABE2B2F84DEB6195DC291E9227CF65E9A07EA8968C14011CD7F7ABE224736C000213124C9A0819B31E689CB8B534EC889CA004802E2FC25D0C7D828D66701727396F54AA6622453214DDA47F89ACA1616FDD80".hexToSeqByte, - # <6> - "F901318080A0A9B034F6DF142723288C01ABC1D11C5836D0584FCEB53D7F61E0216881474224A0B2B840F666627E35696A5E8B7764FD8C232A2AA09B5C30D6C72FD5AB75143DC8A0EAA66386D22922CFDDC505D0E4FB3A5B7F4C0F589C7200E58AE336284FBB6C02A0579C4844BABBC6F1343B8A2F88A497463533D0B8FA6351CF95D8A61B1457997680A0297155BBC073059DC7C59FB9DB094034DF55D6B93EC5FDEECE12806D1AA8EBC080A0C2DF13436F789DB4517236E714733A08D55239E7C72D9E12D657E39AB47553A5A0BF9B5757D3F5BEEF22AF958A4DED7F5D0CAD563DC3DC7C9BD22F7B61B3885BB0A076CE7BBA69CAFCFE416BC453A064610A9946330563312DD6C57923A1EE46FCDC8080A0866DEA9CB87A66E9C92F86A84526298955FE32F35B17B78DB21A28DF55B67E128080".hexToSeqByte, - # <7> - "F8518080A00AB67F3EB5F163D372289AF571D28A0CFE1EA9E5E8C1B12BED10B1F04ADD8163808080A0A43155ADC4D4F7C6F82A3AE1B2F5B0A91C13F8C8B91D2E4833BDCA163196CA2880808080808080808080".hexToSeqByte, - # <8> - "F8669D207D0933750604FB19CF5DD096F02F60279CC0D9CF03F9B3424A7FB95FB846F8440480A056E81F171BCC55A6FF8345E692C0F86E5B48E01B996CADC001622FB5E363B421A0C5D2460186F7233C927E7DB2DCC703C0E500B653CA82273B7BFAD8045D85A470".hexToSeqByte, - # <9> - "F90211A0D8DF8DD801321FCCFF6BC008D582698662A15ED4B29431A1CD1CB540169476ACA03B76B2DB6EE17A52E226B2301A7DD2FF21A957909FCD26D81DBA762DF65B678EA015C7B6DB1A993A54976301D2FB6AC31CEB33ED48018E322C8487F2DB3DC264EDA044B53BF21F700419E1A6F5340880311240A356596B095E8D8944D1240513F4DBA0BD2245AC2DD694A1823AC008FC02F2ADB917F5D3B5AF0A02CD8257C25BC4A9E1A033BA8C1554270E029F0FF6D43E4C903E179F5FBE2189BABC4744B7ADAD96178AA037CA87F94F696DE439973B89FE6DCCB39F64C0D95FEE05CC72DADF7C1F4063E0A054B143D6C582F858EDF2301467D3A0A6E11F6BFBA2B93C3F49C5AB9E418AEEAAA0C82405BC0E336A9C8BD8B171E4569086EF20CCA24FA032161D7E38522F7A6E3AA0187182D43F5A0E84692C755ECE4C5763CABFF4B88F8D960D60DF39B4E3ED80C6A0BD00857DCEB2AEFF0B3E6D94204C466B62A2F945DC5BA1C629B27B0149106E18A0865BCFFB1D3C36173CE347878A7E4FBC2DAB1DF7BF9BA80B9F53E06FE00D5461A0973E5E630F4E4FC9E65F6EB23A9B7C516201C9DAB309B29CA33B379B07BA9D29A0CC49BF76F5D3A790F3B7EC2392AA88B30BFF500BF03A17E4AC0D9093DE27A313A0B3ED1849C31A2B81446D87F590707E1A352D3B5431A8281F593FD153B9B75E33A028872B9C072274D6AAD1D3FAAFDD163D393ADB405C7435815E9367B06645941A80".hexToSeqByte, - # <10> - "F90211A0D50F2E39F45A2111D55147674DD3DFE9EF778723B49B17BBB5423338CF899966A0E30B43737727FF3F099B43478F3EB3F6CB58171A4D4914A49E18E4250BE7E17CA0EB0ADC11962D3A32FE947CBC5BD542D2E33BE338869CF2253E6EB4D5ABFFF308A00877B208516D96B6C8943261281568E787B0C4ED71C25F08DABE28B3CFA23311A07CE4931FB0A59EA544536ADD1C9731BF9A6806690CA5DE401304AABC57379E56A0C10B68793F9F0AEF92E0BC9511ADD7F32E64AE71325BE9FBC9A04ABE817C73F1A0E09B62F3EDDB32F66360E3CF098A11FAA5E6BA74FCBA58017B8AB1FBE322DC75A0A3D7CB9D94C9ABDCBE75EDDF9119EF6BA96EA469D4232EC16849C9F0D6A4D920A004CAC15D7CCEBBA9587B95F3656CEDAAD7F1180C63B47A7DCE5CEE3EB1F87655A0AEC2B3F123707530EDC5BB11DEF09AE196F3391DA1F7CD4555B3FB3200843B92A01B87103A5557E37231639416C5D76FD76E3D763C222198D6C554C64CF679F982A0D627D002CC4EE0C2F51E4304B350C16080FEBB3B1BB7267A42D245422C75826FA08F4307EB616ABD9A0FEDA83E3E12B41BDAF6F05215A6A92EE11CF68F830DF944A065D0E5A8EE025D5EFEBB60F767203221D4B7B243743283EA7EB745074098A2A1A04E4B7B9F232A245C7F7D1FD4AA5495DD8A6E27420366E4515A7C1A275764ADC3A0D53C6B29CB990E25619E4A1E4E92A1F35102985E6EC999D040896AFBD7D56AC480".hexToSeqByte, - # <11> - "F90211A04A89D57B9CB42F6E269D51AC8DDA52101DEF5163BFB415E0C7D5D838544905DEA0B9EA602CEB85FEB7F9763B870AD24845DCC4C47DD12D7CB091C9B640CED04166A0B323570ABA3DD34B2AD3042BD7A7E7A10F36BA3007A6C9F81182526960FCB297A0C15BB5B84B2E0E0145566DC26C33992B27D90DDAE3CAE6AD71E4CC6AB8FEC13DA066D2E06E2F9F5E3FCCF972BA9508A4E29316CEB1AD3E643DE4582073E90C9653A0DEA30438B2AB11706FD4EB35A0BE6B4C2E1A1FEB4D6F3C9D2105123FE325AF3AA0A76442E86C30687060E41A70112ED2D4B7F00AFD537D9562BA50F3A6870667B2A037E3C627AB7C7B4EE4BC86754B9C950D4BB992AA5F271CDCB9FDB280CFF74E4FA00673239BAF1BDB51BBC1E504B844C9275E39F998BE78153A6674B002844459EBA0D8C03E5D9B3B5295ADE0C34520CDE8BA36D3D4DDB49EC5B3C1C1A04E1C8213C9A00C7364BE1AB503A7A9017021E915D2AAB82FFA3B58E4FA4EF8A36D1BBAEF035AA0846D08C50C7978D5234C5D77565D4D3386D9FA6BBC0F20F58D726EE4CACA8C73A0C052ED2FF80CD00B598AD906101D2B539F1DA5745E2E39C9D6E51B6AB419A6E4A07817251C528F0D7297E4BB855EB66B767EE817B4D697BDAD59107734C6A13352A0B69712AA6A7D9BDDB4462F71EB5688583B277A13453F5CD37947B4561A7D5F23A0C7EFCD12218C436D3055B9DB0121964412A981BDC224ACDB6D8382B3B433DC0980".hexToSeqByte, - # <12> - "F9017180A00AB394B3794B7909B05FA99943D33B6C650A3EDA8278104EE6A815E63B23F294A00BEC2410E52705BCE45A0E7D536BC1BC720CCEAE09A82F259528EB52249BC7A580A0490DFF989D2CA627D0FFFE5A7624E5D1F93D13D148C8E895A533E637D72DF692A06963AF64D5582FF69C1AED0338830CFD29FEB6CA241D27CF2B003DF3872226AD80A051A0B35BC60C36717801FA70B97EF70F83DC87272C1BB1D8A018422A6881EFEBA0B240366AAF91CBE93688DF2012629A6E322BA7893BC155894912F1298727FC0280A0248DEDB0AE495BBDCC633729DB6755CF75154B487848EE1A2AF41B16651AB159A0F91E534C8C8B4C99673618DF0C8439134C5BE214CA88B31A9AF856F329280512A0740EF53248D750747B8A867C90B824C2D8D0B989BF5FBD9B0F85B1CC7EC8A5D780A02666267A3FF068897BDFE0B8CD363070ADC2A47F2B75245C5D95E2CE5C0BA665A04176569E211DE429567E85394E2851FC1858DB8AEADD2A9FDD76EA580BB62F2F80".hexToSeqByte, - # <13> - "F871808080808080808080A0E27FB04D7FEE1421851667D4958BF753B72531863F37128B1524F79036DA3DBBA0FC79B7B936154EFB48ED672C1C45F7ADE8D90C37C6876CCF0A8E67DAFB42CF57A0362D97C46FED60D536848D8A8F02A0B897606DA3841A83B68A206486B80F508D8080808080".hexToSeqByte, - # <14> - "F86D9D20155F8A165CDAB1EC9865E8A96C2ACD182A7F590593D48C9EF88B5D29B84DF84B018705815CED31EE00A056E81F171BCC55A6FF8345E692C0F86E5B48E01B996CADC001622FB5E363B421A0C5D2460186F7233C927E7DB2DCC703C0E500B653CA82273B7BFAD8045D85A470".hexToSeqByte]) - -# ---------- - -const - testRoot* = root - testSamples* = @[rec0, rec1] - -# End diff --git a/tests/test_sync_snap/sample0.nim b/tests/test_sync_snap/sample0.nim new file mode 100644 index 000000000..2f857e92e --- /dev/null +++ b/tests/test_sync_snap/sample0.nim @@ -0,0 +1,2582 @@ +import + eth/common/eth_types, + nimcrypto/hash, + stew/byteutils + +const + snapRoot* = + "5d6890c1b7fb777ce63bd23466364e150bef22e463f2c029b4e9d3b2bc0812bd".toDigest + + snapProofData0* = ( + "0000000000000000000000000000000000000000000000000000000000000000".toDigest, + @[# <0> + ("000007278d4cf0d6b10e2c70d13386271e21a5c7d5a8d4e1f7cfa6c163cd6794".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <1> + ("00000ac332045cf01659c8e19654cfa81738e84f7f6c9c77e0ce139268776eb0".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <2> + ("00000d3f4ac80120dca415ac598f28acf0df7bb6c3d0a0d5f88e7782336dab46".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <3> + ("00000f1f4fa213f3e0cd9f82c37557262072ec747d8e8cdae6c9d6a7d73567f2".toDigest, + 1u64, + "150551342899000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <4> + ("00000ff313de1ca7692eb49a7b449e1fb42da400a8aa954fd428303ed98a2b39".toDigest, + 0u64, + "1000000000000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <5> + ("00001361504b3ce049e335da34d7ea45e4305ee7452ae37f492a5d5ab0b98489".toDigest, + 1u64, + "0".parse(Uint256), + "a05fc61070f92b55dad49e25f839bbd71226380d5c62bc4e990fef19cda4be62".toDigest, + "dddd87e694fa3f1c846135f277f1220970a319db40a365a9329b8698134c129e".toDigest), + # <6> + ("000017f9e10e803c89dc71c87412e335efd9381885d83d805fcad04398107337".toDigest, + 2u64, + "2873999999705999".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <7> + ("00001a36879948fa9f3b32f94aa504acd26d680d52f946459c55f6842c8eb338".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "7b7e01b1062c9272df150db2cf701f7bd42d0f244ce8d52c8d8cba260b6c7ef4".toDigest), + # <8> + ("00001dc4569f141311de9a40d40de350284a0217fe73f5fb8559027c16e698a1".toDigest, + 1u64, + "0".parse(Uint256), + "e15a1fd5e0a211828faacfe45a6e055e813cac6b394d426668417e6148ebec74".toDigest, + "8a0c9017e6ef5d552ec36e1a772b70d6c066432aacc2c619ac16cca7de079321".toDigest), + # <9> + ("00001f162da1bf7264260384854f9730b959e8217eded6ac29187a818e153256".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d7f276f0e97b8acdbc5c47dfabd9f8927e20326e42215856c8120bb1eb21b838".toDigest), + # <10> + ("00001f89b4c9f074384282f1a9aa60069039e4c2c8ade9d57e63705676afc72c".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <11> + ("000020bff974e2406514f9696a0857712d6f4d9b4ef116d9560972ef9931ae46".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <12> + ("000022b2fcd3ccd817b3cfa7f6f8385024978c162efcc636f7bda3f539bd8f15".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "7b7e01b1062c9272df150db2cf701f7bd42d0f244ce8d52c8d8cba260b6c7ef4".toDigest), + # <13> + ("0000230ed7c461cbabd0d680f91257def0064bc3e73c4ed64f0e4ea5e7bf6b5a".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <14> + ("00002b6b2db87606008b2a07b11944278185e9032c0af9ad4bc39bde53ae1797".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <15> + ("00002dddbd9c6ca98cc67d1458d885839ba5708ac3b6ef5d667fa38a19cff403".toDigest, + 1u64, + "0".parse(Uint256), + "1d40ed4738c1b04df8bd8fd102ccde2f4a62f8803134a01d124da5c2389af5b1".toDigest, + "4e3c65796be34e5a3b67459453315d4367389de0dd041a33c53f2ae90870ed76".toDigest), + # <16> + ("00002e652e8f1faa41ed18b963c5cf65a59ee90979d1c8a9366a51f2c7c589f8".toDigest, + 1u64, + "0".parse(Uint256), + "88f495ca7c1b270b375bf6aa6b219e4fdb520712efa1d907abf9c91b8772f0c6".toDigest, + "8a0c9017e6ef5d552ec36e1a772b70d6c066432aacc2c619ac16cca7de079321".toDigest), + # <17> + ("000034b62901edd9a5e4b3251b2e74ee604d6ed675e28eb55ee71c1bc072d247".toDigest, + 0u64, + "1000000000000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <18> + ("00003c86df0717731604d813ef5f9584d345fad609cf665340ebe3d05e9e249b".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest)], + @[# <0> + "F90211A00F41A498B871B7A732DB8B91A4AD4E013596110F383BF3041F2E818714FB57FBA0203149AB733DABFFB4A075E06DFBAC60A68A88CEDA23AAAE3F82A059E73A0DF4A034A036B6A6B8B596BA29F2C1EB8FAC71F21B8BC72481CA2E7C21733E39DAE8E2A0D533FA53AFE1DEFBA7E2CC6E645523F3AA1FB76651634F9EEF68D063F23FEA31A0A3AEFA0444DE17ADCF431EC4C5F057D799A2AC3A568F4C303E43561A7FCB629CA0033F5BC31420628F447A5ABC3DECF498E6852B486C07ADB3BBD5334556463BD4A0015307556953C623310A1D3A70C58D9465C6EEBBA5547DF67BDCB19CA953B3A1A09FFD78C0F9E3A550DD9D45E4C82C78A437F85640E7F86461E101578E898D3FA5A01D624ED0F8C9C98AA2FCD5D8D0AE1954B1DB998E7C79F9A98601A42F255FFCAEA049BF312A5690ACF97114EEC2F0B7BA8541624339DCC4359FA63DD98D942C7745A08045A2EA09815540255B0F0071311084C58F0A16D89CE46B1A7BD3948DFBC7BDA0F86E5DFF074F176403C0E8388587D213890A5B926AFBB521767C384DC64F321AA01AD0BEC29F16127FF644D87CAF6F4EC6FA7F4EF50FBC51AC4984D496DB2CA99BA04981C605424CD0CE7233D469C85DC60FC8D88E585C3C02B4923B184B8FCDE926A012F41CFB88E684A08727C66FEE65D20D93DB71E8508A1476A61056E967494914A059D005E9864E5F502DAF61049C7A8F862E84F2591AE3394E3066C2648239B7FE80".hexToSeqByte, + # <1> + "F90211A0B5E039DE5CD9F82A1647EE05E212486A2DDB81999B80727B76C3156B40641CA9A02569520DCDB1750D48C7060FEE64B1C16388A098648AE4FEF929D75775EBBE53A0CE915C5DE8826650CC3F49E82B307A98E87359B679965A1A7796BB50298947F4A012C8C06E4EF08F8A47BB81A9F7FDD4279A7BE096E793DC10E217F5C17D1C61D7A0A5F8FF8BADFECACB3AD5EAFF8C4EE750D33DD50A1D1CC5130D027D574F4D1A9EA00E35BB939DD5CF81DC2219FB27BAE31DFE2AEE079FDBAD367619DBDBAD276D81A0C012A282076332BB92BEEDD74CF7EF9CC36875B54D1DAAC81AC0EC9CB60CE5AEA00FB445610AA14436F057BE1BC4DBD86BDBE01490AEEC46059C710797591B7E14A0EB99BB2A00E7CC498ABD0B020276A1790628369314133F35E8089B28D55839AAA0C290D06ADA3FFA0760EDD11D57F0B32F08BF668943CE01CF651B8B29DC63A197A088BD981BF81F4F0C0B37A04542269BE7C24A5D09D455807256344C6A864925E0A08924048D8F415C57A2835DB0B33F92AD157C4173D2FF819CB32D5EF89E7060A1A083DA61DBDC26D3202C343A0168D69B0DDB0572F43C20EDB924809DBA6149BE88A01B640A922A59C6DA8860058354FD78A39D00A46E0CDF92FB6A338ED556BABFD3A01D8ACC7CA60FCD378DE1DD765B680D037620F08485ED84BAD1A5BB82702F80D0A0B00F0533277C292308F9FC4BF5A85027D68F693D70CA92F1D983A32EA7C5478480".hexToSeqByte, + # <2> + "F90211A0D126CFCCD9CC595EE94F46C23D2C203D06BBF3E628C050CC34A1B5C76965A978A0B22839972A330E8CE273187A801F7257E8655A9E728283D41DF57E408A423127A06FB243B4185E550D9CA703A47E786E55B01E47968D53BBA5600865F3E7FF739AA01297B6C416BE295952F78AABCDA3D15F6087F1976194CF881A0A55D2CF8E9B6CA0523DF620C80E8C4B48A0DBB2328226921BE949F61671707A604AA95294EE4E75A0917B77A88A260098D6BBF3600D84BA39F22B59426823A2BE8A8D0771DAFA4DA0A007C73D02889414B827DA606183D4A960AAB396777217792EC55C68D6FF29B52CA079B593B9D554CFCDB265AD20A7AD018EC97B580F62904DC1052B7BD725ACB2E1A06EB993C8B75F317A7F73825D84BFA191E041667B20D0E21D2E9EF7BF311A53ACA048484A38E04A2B0001D0D3D34D731F8149459D08987EF7293331CBCCCFB3D09BA044A3A485D94F02B53EB6BF99842F88101A9E3C42D24577720742690F584B0BCCA058E3BF550CE62F775EAE8B9A49196A78F7F8DA68874420D959925F95C2CCF553A0988D0E99F69E71C24C2F17C69B7AC3F448FD7133D624FB68F148A439048C5B2EA01A48E7BAF56999165D3E2DC392D768EA88B4820D0A4D5BD8781B60A247138B36A0DBF8593863396974608C4D47BFAB51662750421D7D431842AF55740089AA3D4CA029AD034ECF2E8054767808DDF956EE2C89720F633C3B3594881A71BC582B9A0480".hexToSeqByte, + # <3> + "F90211A0C61F01AADE2395B4E17DE4C79353729EFFAD97B46C0E425B518D4D77B75CB713A03C2D55238135DA002B914D80556FFD60890E37705E7A5487B0B52FC26DA29299A07CE74DABF9451C37485200571A867213063A8FED408C6BC72F6D84564E9F8F3EA0514930C58E952176FF4C626CED5913394B5115CA7C81A9B5B543F7858F43DC01A02D93C42DC532FE12D6CCFD735EC1F22FBD7D009D386F79E76B1F0088CB412A30A0860EB915C38B045754025B34FD796CED538071EA6B98307754124A5ABB459326A00720A388A06A78305A61D749A3895319611313C5CDBE5A396E3C30793E18C9A4A0847A9DBC85722DF97F2B46D9D95DDA6C69E6D3FA68A98FCFDCF218B26A6EF727A0C7A6A3ECE732C86186CE38D8329D2242E65D8BCC240E186FF797621B9CD75B2DA05879B4B262967609E2D8ED94D0C358B9C8A6FD1759A4AA0AD836EEA77F9C76D8A0132F0874B2E39B4E357F8654B6B6A88F306E6631FAED6C0DF223F5D18A7CD448A0504A50995DB2FACDAC157BA88A6D73CCB2DEC75FCA6424EBE45381D9DBC82151A0DABD28DEDCF493701D612D87BEE5C008C40A46D8B1A5AA7860C7B388E3026A23A021680406AF0738D3FA04C827AA8735FF8ED2C057103AA909BF84C618F63E08A8A0AD769155BAC8DC43ACC0B2FA3A6D909E8C6FD08F54D3D70452197D64D4105B8DA01E1CA8EF41EBA3606E19ECBF58499208653BAD2891AD190227DB3B8705EEE06F80".hexToSeqByte, + # <4> + "F90211A0BE50E5783F7D3A2065DD822D40084386093B77FC22AD0A685CB0F54CBA532FA9A0202C94F67BE429B2AC3C241BFDA31AE5D5968ECACBB240D7C73F9E0140A3C1A7A0EA3FD58C72BCC61DDCD60FE900248481E227F6863927981C93B0AAEAFDD4E579A0057FDB2295A2FF9DA5CE6F6672752C0EE357D72E911EA4A337333F09D61A0062A031B7107E632B31170005D633D09A15A453F51762CB4EB1BECA62BBFEEC918530A0FC0B5E8C1349494480CDA5958FEB63542C8DAE8CD404D65E1F30BB5B31CB1081A0C1E5E68E12CA7008B0D7D06EB3E1569222909C841DBB187968E2370BE41A7BF4A06321142789FBD6D6EB092019B66B71D80912D7931A311DF05D56485368B05924A0F0E3BB64E9027EFAB77DCA13FE1AED824AEF6998128BD06DF9B77F2FA4822800A08A6282C15897C09165742C5799A9B79A68AD1B834E91667335BA615D7CBB8AC7A023EC2E3D24BA99D14C82FC52E1C97EDE646DF62E5E01AE5BF983003477274F23A06C21AE60204031C2F6C26ACCADF51DE91F438FBA88FF6000648590ED022747ECA0DB12785060A364F3506432E77E210726C266E5918A33F85CE3224A8CBC3848BEA01497748CD1DE0732C2B693534DBF1765A061A8323B30DB781FF6DEDE83D606DAA043E9BC03A4D7AC6F76295983D2FCD7950700C08E0EC995DF57C4F666924A5F7BA05165972EDE7FE8960E187F270D7223CF2B0DC48A2F7AC7973F42C8B71CDC581D80".hexToSeqByte, + # <5> + "F89180808080808080A0A651CFED796C868BFC739665BC1B4F4FD6C29081B6EDF395C7989727D3120D378080A0FFC4EA418257E74963E908BE8B467B939428EE818BA2C0F629B324DA9568BDC68080A002998AD996C73F9902A769F519E71DA3F451847E905B00D40D281639B8402FA980A05723AE4AF3E82865CFFECE7B90A3D82FF73179924B51815EA959C87177D12B1780".hexToSeqByte, + # <6> + "F87180808080A0E6CF1429E6BC79676A42FFC8769E3413B69A6ECDF4CD154D458C14447895E11680808080808080A0EEA840C9EB90383D816E1B2FB54C740497103C58CD1C4743A58184D5155EC7EB80A06FF7A9E39AAE5CE5CADA64C46A0B110B272A9FDFE4ECE0370EBADB174E89DF6A8080".hexToSeqByte, + # <7> + "F8518080808080808080A0C82C3101AA2C10FC5A5C9EF9B553A5DFAEFF22CC09D8C3C21501F6C8059FFA7E8080A0DCAC364B7A3A08FC87A62C5E1688E240B3ECECFA98B9E5DADD9914675A1F75698080808080".hexToSeqByte, + # <8> + "F8669D36DF0717731604D813EF5F9584D345FAD609CF665340EBE3D05E9E249BB846F8440180A056E81F171BCC55A6FF8345E692C0F86E5B48E01B996CADC001622FB5E363B421A0D8990879C8C8E2B683D4F0FC5A76889AEEBA15181AE48F09FBF37ED182286300".hexToSeqByte]) + + snapProofData1* = ( + "000037ec8ec25e6d2c0bafefd59ebbd0b471449f24ac401db5abd74229ff6635".toDigest, + @[# <0> + ("00003c86df0717731604d813ef5f9584d345fad609cf665340ebe3d05e9e249b".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <1> + ("00003cb6762e73b9429f23cef03a81aa593e01716ad694e278e0fba353f847f0".toDigest, + 1u64, + "16466713946000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <2> + ("00003eb1fc35587153210ecf2562f2a59bb0e734ad7e638377859c02bb720f92".toDigest, + 0u64, + "1000000000000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <3> + ("00004570b09de9801840d6bef135e83255b8c2e82358b1d1415ef59fd8e251bf".toDigest, + 106u64, + "2168192000000000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <4> + ("00004dee8840e82f1832dcb733186226641fb6587a19f21662c65102a26e7d45".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <5> + ("00004e8dbd1e09a2f50b6ed201713be1e16c5a615e9e9a6887d670a522f9a64d".toDigest, + 0u64, + "1000000000000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <6> + ("00004ec1ea22f10f4bd13decbccd1615853dd3cb2706d2f6e3497320de1cd6a7".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <7> + ("00005d922e0b861d314b610bd5bb44782d1c2d5d03d36fc061c41375effb2a56".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c00d405db37ea41f54f32347d032c33cc016d69481a1172f4fda978c53d271ce".toDigest), + # <8> + ("000065f13e799cc4d0df85b3650c7e73cfa9af2f39bc8c073b3bd8349b5af1d2".toDigest, + 1u64, + "0".parse(Uint256), + "1d40ed4738c1b04df8bd8fd102ccde2f4a62f8803134a01d124da5c2389af5b1".toDigest, + "4e3c65796be34e5a3b67459453315d4367389de0dd041a33c53f2ae90870ed76".toDigest), + # <9> + ("000069ea881999a3348e337ef56fbfddfd6d0e99bf244e79edde7bf456daaa1c".toDigest, + 1u64, + "47899999217330000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <10> + ("00007354944d3fed14652f3767e32f68df25264bc6804b397b0f4bdf3e760f95".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "b584c17b44e43b80f2360b207e45c2e48ca8761240b7eb8153480a9b6983530d".toDigest)], + @[# <0> + "F90211A00F41A498B871B7A732DB8B91A4AD4E013596110F383BF3041F2E818714FB57FBA0203149AB733DABFFB4A075E06DFBAC60A68A88CEDA23AAAE3F82A059E73A0DF4A034A036B6A6B8B596BA29F2C1EB8FAC71F21B8BC72481CA2E7C21733E39DAE8E2A0D533FA53AFE1DEFBA7E2CC6E645523F3AA1FB76651634F9EEF68D063F23FEA31A0A3AEFA0444DE17ADCF431EC4C5F057D799A2AC3A568F4C303E43561A7FCB629CA0033F5BC31420628F447A5ABC3DECF498E6852B486C07ADB3BBD5334556463BD4A0015307556953C623310A1D3A70C58D9465C6EEBBA5547DF67BDCB19CA953B3A1A09FFD78C0F9E3A550DD9D45E4C82C78A437F85640E7F86461E101578E898D3FA5A01D624ED0F8C9C98AA2FCD5D8D0AE1954B1DB998E7C79F9A98601A42F255FFCAEA049BF312A5690ACF97114EEC2F0B7BA8541624339DCC4359FA63DD98D942C7745A08045A2EA09815540255B0F0071311084C58F0A16D89CE46B1A7BD3948DFBC7BDA0F86E5DFF074F176403C0E8388587D213890A5B926AFBB521767C384DC64F321AA01AD0BEC29F16127FF644D87CAF6F4EC6FA7F4EF50FBC51AC4984D496DB2CA99BA04981C605424CD0CE7233D469C85DC60FC8D88E585C3C02B4923B184B8FCDE926A012F41CFB88E684A08727C66FEE65D20D93DB71E8508A1476A61056E967494914A059D005E9864E5F502DAF61049C7A8F862E84F2591AE3394E3066C2648239B7FE80".hexToSeqByte, + # <1> + "F90211A0B5E039DE5CD9F82A1647EE05E212486A2DDB81999B80727B76C3156B40641CA9A02569520DCDB1750D48C7060FEE64B1C16388A098648AE4FEF929D75775EBBE53A0CE915C5DE8826650CC3F49E82B307A98E87359B679965A1A7796BB50298947F4A012C8C06E4EF08F8A47BB81A9F7FDD4279A7BE096E793DC10E217F5C17D1C61D7A0A5F8FF8BADFECACB3AD5EAFF8C4EE750D33DD50A1D1CC5130D027D574F4D1A9EA00E35BB939DD5CF81DC2219FB27BAE31DFE2AEE079FDBAD367619DBDBAD276D81A0C012A282076332BB92BEEDD74CF7EF9CC36875B54D1DAAC81AC0EC9CB60CE5AEA00FB445610AA14436F057BE1BC4DBD86BDBE01490AEEC46059C710797591B7E14A0EB99BB2A00E7CC498ABD0B020276A1790628369314133F35E8089B28D55839AAA0C290D06ADA3FFA0760EDD11D57F0B32F08BF668943CE01CF651B8B29DC63A197A088BD981BF81F4F0C0B37A04542269BE7C24A5D09D455807256344C6A864925E0A08924048D8F415C57A2835DB0B33F92AD157C4173D2FF819CB32D5EF89E7060A1A083DA61DBDC26D3202C343A0168D69B0DDB0572F43C20EDB924809DBA6149BE88A01B640A922A59C6DA8860058354FD78A39D00A46E0CDF92FB6A338ED556BABFD3A01D8ACC7CA60FCD378DE1DD765B680D037620F08485ED84BAD1A5BB82702F80D0A0B00F0533277C292308F9FC4BF5A85027D68F693D70CA92F1D983A32EA7C5478480".hexToSeqByte, + # <2> + "F90211A0D126CFCCD9CC595EE94F46C23D2C203D06BBF3E628C050CC34A1B5C76965A978A0B22839972A330E8CE273187A801F7257E8655A9E728283D41DF57E408A423127A06FB243B4185E550D9CA703A47E786E55B01E47968D53BBA5600865F3E7FF739AA01297B6C416BE295952F78AABCDA3D15F6087F1976194CF881A0A55D2CF8E9B6CA0523DF620C80E8C4B48A0DBB2328226921BE949F61671707A604AA95294EE4E75A0917B77A88A260098D6BBF3600D84BA39F22B59426823A2BE8A8D0771DAFA4DA0A007C73D02889414B827DA606183D4A960AAB396777217792EC55C68D6FF29B52CA079B593B9D554CFCDB265AD20A7AD018EC97B580F62904DC1052B7BD725ACB2E1A06EB993C8B75F317A7F73825D84BFA191E041667B20D0E21D2E9EF7BF311A53ACA048484A38E04A2B0001D0D3D34D731F8149459D08987EF7293331CBCCCFB3D09BA044A3A485D94F02B53EB6BF99842F88101A9E3C42D24577720742690F584B0BCCA058E3BF550CE62F775EAE8B9A49196A78F7F8DA68874420D959925F95C2CCF553A0988D0E99F69E71C24C2F17C69B7AC3F448FD7133D624FB68F148A439048C5B2EA01A48E7BAF56999165D3E2DC392D768EA88B4820D0A4D5BD8781B60A247138B36A0DBF8593863396974608C4D47BFAB51662750421D7D431842AF55740089AA3D4CA029AD034ECF2E8054767808DDF956EE2C89720F633C3B3594881A71BC582B9A0480".hexToSeqByte, + # <3> + "F90211A0C61F01AADE2395B4E17DE4C79353729EFFAD97B46C0E425B518D4D77B75CB713A03C2D55238135DA002B914D80556FFD60890E37705E7A5487B0B52FC26DA29299A07CE74DABF9451C37485200571A867213063A8FED408C6BC72F6D84564E9F8F3EA0514930C58E952176FF4C626CED5913394B5115CA7C81A9B5B543F7858F43DC01A02D93C42DC532FE12D6CCFD735EC1F22FBD7D009D386F79E76B1F0088CB412A30A0860EB915C38B045754025B34FD796CED538071EA6B98307754124A5ABB459326A00720A388A06A78305A61D749A3895319611313C5CDBE5A396E3C30793E18C9A4A0847A9DBC85722DF97F2B46D9D95DDA6C69E6D3FA68A98FCFDCF218B26A6EF727A0C7A6A3ECE732C86186CE38D8329D2242E65D8BCC240E186FF797621B9CD75B2DA05879B4B262967609E2D8ED94D0C358B9C8A6FD1759A4AA0AD836EEA77F9C76D8A0132F0874B2E39B4E357F8654B6B6A88F306E6631FAED6C0DF223F5D18A7CD448A0504A50995DB2FACDAC157BA88A6D73CCB2DEC75FCA6424EBE45381D9DBC82151A0DABD28DEDCF493701D612D87BEE5C008C40A46D8B1A5AA7860C7B388E3026A23A021680406AF0738D3FA04C827AA8735FF8ED2C057103AA909BF84C618F63E08A8A0AD769155BAC8DC43ACC0B2FA3A6D909E8C6FD08F54D3D70452197D64D4105B8DA01E1CA8EF41EBA3606E19ECBF58499208653BAD2891AD190227DB3B8705EEE06F80".hexToSeqByte, + # <4> + "F90211A0BE50E5783F7D3A2065DD822D40084386093B77FC22AD0A685CB0F54CBA532FA9A0202C94F67BE429B2AC3C241BFDA31AE5D5968ECACBB240D7C73F9E0140A3C1A7A0EA3FD58C72BCC61DDCD60FE900248481E227F6863927981C93B0AAEAFDD4E579A0057FDB2295A2FF9DA5CE6F6672752C0EE357D72E911EA4A337333F09D61A0062A031B7107E632B31170005D633D09A15A453F51762CB4EB1BECA62BBFEEC918530A0FC0B5E8C1349494480CDA5958FEB63542C8DAE8CD404D65E1F30BB5B31CB1081A0C1E5E68E12CA7008B0D7D06EB3E1569222909C841DBB187968E2370BE41A7BF4A06321142789FBD6D6EB092019B66B71D80912D7931A311DF05D56485368B05924A0F0E3BB64E9027EFAB77DCA13FE1AED824AEF6998128BD06DF9B77F2FA4822800A08A6282C15897C09165742C5799A9B79A68AD1B834E91667335BA615D7CBB8AC7A023EC2E3D24BA99D14C82FC52E1C97EDE646DF62E5E01AE5BF983003477274F23A06C21AE60204031C2F6C26ACCADF51DE91F438FBA88FF6000648590ED022747ECA0DB12785060A364F3506432E77E210726C266E5918A33F85CE3224A8CBC3848BEA01497748CD1DE0732C2B693534DBF1765A061A8323B30DB781FF6DEDE83D606DAA043E9BC03A4D7AC6F76295983D2FCD7950700C08E0EC995DF57C4F666924A5F7BA05165972EDE7FE8960E187F270D7223CF2B0DC48A2F7AC7973F42C8B71CDC581D80".hexToSeqByte, + # <5> + "F87180808080A0E6CF1429E6BC79676A42FFC8769E3413B69A6ECDF4CD154D458C14447895E11680808080808080A0EEA840C9EB90383D816E1B2FB54C740497103C58CD1C4743A58184D5155EC7EB80A06FF7A9E39AAE5CE5CADA64C46A0B110B272A9FDFE4ECE0370EBADB174E89DF6A8080".hexToSeqByte, + # <6> + "F851808080A09887DD1822D5EAF36F9FABDC47963EBB061E48A579D9EC0EF15DD24DB1A58C47808080A07C50193E89918FF092A0CAEEA37213283622531EDF6E26A3A4D6613F0942FD20808080808080808080".hexToSeqByte, + # <7> + "F8679E2054944D3FED14652F3767E32F68DF25264BC6804B397B0F4BDF3E760F95B846F8440180A056E81F171BCC55A6FF8345E692C0F86E5B48E01B996CADC001622FB5E363B421A0B584C17B44E43B80F2360B207E45C2E48CA8761240B7EB8153480A9B6983530D".hexToSeqByte]) + + snapProofData2* = ( + "00006fd91d84bcda58175fdfab3d77a168e2893e4958803b6b57ae8453fecc6a".toDigest, + @[# <0> + ("00007354944d3fed14652f3767e32f68df25264bc6804b397b0f4bdf3e760f95".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "b584c17b44e43b80f2360b207e45c2e48ca8761240b7eb8153480a9b6983530d".toDigest), + # <1> + ("000077c4787539dfe5a267bb3e6c95cd1902a27bff8269d92944d0ba15a3f530".toDigest, + 1u64, + "0".parse(Uint256), + "1d40ed4738c1b04df8bd8fd102ccde2f4a62f8803134a01d124da5c2389af5b1".toDigest, + "4e3c65796be34e5a3b67459453315d4367389de0dd041a33c53f2ae90870ed76".toDigest), + # <2> + ("0000858aae30c97db85d751722fae1798e592d7f702c569f33e4a16371784c75".toDigest, + 0u64, + "1000000000000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <3> + ("000089c417a3f3983eef12c44eb1d4486c09efaadab6248539d2c51deb5377c5".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "49bd22636268dcfd5f22e76d6ac63185ee7f2aeb4a38c2b829db950daa1aa7f2".toDigest), + # <4> + ("00009077a2f4fa89209ce250d539a82e771200a8d9dccc89ff81650286b9a6bb".toDigest, + 1u64, + "0".parse(Uint256), + "d9dcb488c63d168168b231c459dd336e15e9184c99c9475121067fd6853f428b".toDigest, + "af974539a8d1b7b250b247757abb3141a15893895d31f783046a40fa0f13088d".toDigest), + # <5> + ("00009684658be020c7ec6980ae2b42445573f84ecfe98cd505a8a3a560687e93".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "5c3a8543510c82bcb5286319d9711fcec14e40a861db6cff137625c30280fde8".toDigest), + # <6> + ("00009b2c07b16b4edc8aa45596838aab8bc06b59ae5081845f5400f7e2b13529".toDigest, + 1u64, + "0".parse(Uint256), + "1d40ed4738c1b04df8bd8fd102ccde2f4a62f8803134a01d124da5c2389af5b1".toDigest, + "4e3c65796be34e5a3b67459453315d4367389de0dd041a33c53f2ae90870ed76".toDigest), + # <7> + ("00009b82f07b6d086a2f1574482af15472d565ea145ff42edfa412a1bd632625".toDigest, + 55u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <8> + ("00009c6a6d0889d1691be52a31eea0defb25ba2636c74b0f4f82f0cabfc452f7".toDigest, + 1u64, + "0".parse(Uint256), + "7f93985e55886230eaa0ed1095fbb75034fdbc58a7f485c9da72ca475ac983fb".toDigest, + "c80ea2acb5134ed19266d28c228ad60c342ab1def4c0a7c865dc58d838d4d5a1".toDigest), + # <9> + ("0000a127dacb158f888fef4f1482b0fb02579c90c87ab79f1bca32dd9d801a16".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "875191faaccab052ffb33984e8ba823d46be31b93a9127447e8d332f8dedaca9".toDigest), + # <10> + ("0000ac99764c7d35bc94665985b2a1f1a9cdd8b09b886b5334eda616963c5b2d".toDigest, + 1u64, + "0".parse(Uint256), + "1d40ed4738c1b04df8bd8fd102ccde2f4a62f8803134a01d124da5c2389af5b1".toDigest, + "4e3c65796be34e5a3b67459453315d4367389de0dd041a33c53f2ae90870ed76".toDigest)], + @[# <0> + "F90211A00F41A498B871B7A732DB8B91A4AD4E013596110F383BF3041F2E818714FB57FBA0203149AB733DABFFB4A075E06DFBAC60A68A88CEDA23AAAE3F82A059E73A0DF4A034A036B6A6B8B596BA29F2C1EB8FAC71F21B8BC72481CA2E7C21733E39DAE8E2A0D533FA53AFE1DEFBA7E2CC6E645523F3AA1FB76651634F9EEF68D063F23FEA31A0A3AEFA0444DE17ADCF431EC4C5F057D799A2AC3A568F4C303E43561A7FCB629CA0033F5BC31420628F447A5ABC3DECF498E6852B486C07ADB3BBD5334556463BD4A0015307556953C623310A1D3A70C58D9465C6EEBBA5547DF67BDCB19CA953B3A1A09FFD78C0F9E3A550DD9D45E4C82C78A437F85640E7F86461E101578E898D3FA5A01D624ED0F8C9C98AA2FCD5D8D0AE1954B1DB998E7C79F9A98601A42F255FFCAEA049BF312A5690ACF97114EEC2F0B7BA8541624339DCC4359FA63DD98D942C7745A08045A2EA09815540255B0F0071311084C58F0A16D89CE46B1A7BD3948DFBC7BDA0F86E5DFF074F176403C0E8388587D213890A5B926AFBB521767C384DC64F321AA01AD0BEC29F16127FF644D87CAF6F4EC6FA7F4EF50FBC51AC4984D496DB2CA99BA04981C605424CD0CE7233D469C85DC60FC8D88E585C3C02B4923B184B8FCDE926A012F41CFB88E684A08727C66FEE65D20D93DB71E8508A1476A61056E967494914A059D005E9864E5F502DAF61049C7A8F862E84F2591AE3394E3066C2648239B7FE80".hexToSeqByte, + # <1> + "F90211A0B5E039DE5CD9F82A1647EE05E212486A2DDB81999B80727B76C3156B40641CA9A02569520DCDB1750D48C7060FEE64B1C16388A098648AE4FEF929D75775EBBE53A0CE915C5DE8826650CC3F49E82B307A98E87359B679965A1A7796BB50298947F4A012C8C06E4EF08F8A47BB81A9F7FDD4279A7BE096E793DC10E217F5C17D1C61D7A0A5F8FF8BADFECACB3AD5EAFF8C4EE750D33DD50A1D1CC5130D027D574F4D1A9EA00E35BB939DD5CF81DC2219FB27BAE31DFE2AEE079FDBAD367619DBDBAD276D81A0C012A282076332BB92BEEDD74CF7EF9CC36875B54D1DAAC81AC0EC9CB60CE5AEA00FB445610AA14436F057BE1BC4DBD86BDBE01490AEEC46059C710797591B7E14A0EB99BB2A00E7CC498ABD0B020276A1790628369314133F35E8089B28D55839AAA0C290D06ADA3FFA0760EDD11D57F0B32F08BF668943CE01CF651B8B29DC63A197A088BD981BF81F4F0C0B37A04542269BE7C24A5D09D455807256344C6A864925E0A08924048D8F415C57A2835DB0B33F92AD157C4173D2FF819CB32D5EF89E7060A1A083DA61DBDC26D3202C343A0168D69B0DDB0572F43C20EDB924809DBA6149BE88A01B640A922A59C6DA8860058354FD78A39D00A46E0CDF92FB6A338ED556BABFD3A01D8ACC7CA60FCD378DE1DD765B680D037620F08485ED84BAD1A5BB82702F80D0A0B00F0533277C292308F9FC4BF5A85027D68F693D70CA92F1D983A32EA7C5478480".hexToSeqByte, + # <2> + "F90211A0D126CFCCD9CC595EE94F46C23D2C203D06BBF3E628C050CC34A1B5C76965A978A0B22839972A330E8CE273187A801F7257E8655A9E728283D41DF57E408A423127A06FB243B4185E550D9CA703A47E786E55B01E47968D53BBA5600865F3E7FF739AA01297B6C416BE295952F78AABCDA3D15F6087F1976194CF881A0A55D2CF8E9B6CA0523DF620C80E8C4B48A0DBB2328226921BE949F61671707A604AA95294EE4E75A0917B77A88A260098D6BBF3600D84BA39F22B59426823A2BE8A8D0771DAFA4DA0A007C73D02889414B827DA606183D4A960AAB396777217792EC55C68D6FF29B52CA079B593B9D554CFCDB265AD20A7AD018EC97B580F62904DC1052B7BD725ACB2E1A06EB993C8B75F317A7F73825D84BFA191E041667B20D0E21D2E9EF7BF311A53ACA048484A38E04A2B0001D0D3D34D731F8149459D08987EF7293331CBCCCFB3D09BA044A3A485D94F02B53EB6BF99842F88101A9E3C42D24577720742690F584B0BCCA058E3BF550CE62F775EAE8B9A49196A78F7F8DA68874420D959925F95C2CCF553A0988D0E99F69E71C24C2F17C69B7AC3F448FD7133D624FB68F148A439048C5B2EA01A48E7BAF56999165D3E2DC392D768EA88B4820D0A4D5BD8781B60A247138B36A0DBF8593863396974608C4D47BFAB51662750421D7D431842AF55740089AA3D4CA029AD034ECF2E8054767808DDF956EE2C89720F633C3B3594881A71BC582B9A0480".hexToSeqByte, + # <3> + "F90211A0C61F01AADE2395B4E17DE4C79353729EFFAD97B46C0E425B518D4D77B75CB713A03C2D55238135DA002B914D80556FFD60890E37705E7A5487B0B52FC26DA29299A07CE74DABF9451C37485200571A867213063A8FED408C6BC72F6D84564E9F8F3EA0514930C58E952176FF4C626CED5913394B5115CA7C81A9B5B543F7858F43DC01A02D93C42DC532FE12D6CCFD735EC1F22FBD7D009D386F79E76B1F0088CB412A30A0860EB915C38B045754025B34FD796CED538071EA6B98307754124A5ABB459326A00720A388A06A78305A61D749A3895319611313C5CDBE5A396E3C30793E18C9A4A0847A9DBC85722DF97F2B46D9D95DDA6C69E6D3FA68A98FCFDCF218B26A6EF727A0C7A6A3ECE732C86186CE38D8329D2242E65D8BCC240E186FF797621B9CD75B2DA05879B4B262967609E2D8ED94D0C358B9C8A6FD1759A4AA0AD836EEA77F9C76D8A0132F0874B2E39B4E357F8654B6B6A88F306E6631FAED6C0DF223F5D18A7CD448A0504A50995DB2FACDAC157BA88A6D73CCB2DEC75FCA6424EBE45381D9DBC82151A0DABD28DEDCF493701D612D87BEE5C008C40A46D8B1A5AA7860C7B388E3026A23A021680406AF0738D3FA04C827AA8735FF8ED2C057103AA909BF84C618F63E08A8A0AD769155BAC8DC43ACC0B2FA3A6D909E8C6FD08F54D3D70452197D64D4105B8DA01E1CA8EF41EBA3606E19ECBF58499208653BAD2891AD190227DB3B8705EEE06F80".hexToSeqByte, + # <4> + "F90211A0BE50E5783F7D3A2065DD822D40084386093B77FC22AD0A685CB0F54CBA532FA9A0202C94F67BE429B2AC3C241BFDA31AE5D5968ECACBB240D7C73F9E0140A3C1A7A0EA3FD58C72BCC61DDCD60FE900248481E227F6863927981C93B0AAEAFDD4E579A0057FDB2295A2FF9DA5CE6F6672752C0EE357D72E911EA4A337333F09D61A0062A031B7107E632B31170005D633D09A15A453F51762CB4EB1BECA62BBFEEC918530A0FC0B5E8C1349494480CDA5958FEB63542C8DAE8CD404D65E1F30BB5B31CB1081A0C1E5E68E12CA7008B0D7D06EB3E1569222909C841DBB187968E2370BE41A7BF4A06321142789FBD6D6EB092019B66B71D80912D7931A311DF05D56485368B05924A0F0E3BB64E9027EFAB77DCA13FE1AED824AEF6998128BD06DF9B77F2FA4822800A08A6282C15897C09165742C5799A9B79A68AD1B834E91667335BA615D7CBB8AC7A023EC2E3D24BA99D14C82FC52E1C97EDE646DF62E5E01AE5BF983003477274F23A06C21AE60204031C2F6C26ACCADF51DE91F438FBA88FF6000648590ED022747ECA0DB12785060A364F3506432E77E210726C266E5918A33F85CE3224A8CBC3848BEA01497748CD1DE0732C2B693534DBF1765A061A8323B30DB781FF6DEDE83D606DAA043E9BC03A4D7AC6F76295983D2FCD7950700C08E0EC995DF57C4F666924A5F7BA05165972EDE7FE8960E187F270D7223CF2B0DC48A2F7AC7973F42C8B71CDC581D80".hexToSeqByte, + # <5> + "F8518080808080A07B679845B251C56D81BA9E78052F572DD37631586A7B355100AF5A18EA5ED9B5808080A0C3F810D5EDD7F007246B45D48C30403E8D8FA4C82854A47E686D7E4B03DC405380808080808080".hexToSeqByte, + # <6> + "F85180A0E96A6780E1CBF4298CC558FF8D1163F5915C2A66213ED4F6C5DC4164744EDFF880808080808080808080A0CA8BCC923F9D4B5DC971615D4555E89F7A1CE97DA196E223FA4991FB1064040D80808080".hexToSeqByte, + # <7> + "F8679E2099764C7D35BC94665985B2A1F1A9CDD8B09B886B5334EDA616963C5B2DB846F8440180A01D40ED4738C1B04DF8BD8FD102CCDE2F4A62F8803134A01D124DA5C2389AF5B1A04E3C65796BE34E5A3B67459453315D4367389DE0DD041A33C53F2AE90870ED76".hexToSeqByte]) + + snapProofData3* = ( + "0000a7c5ac471b4784230fcf80dc33721d53cddd6e04c059210385c67dfe329f".toDigest, + @[# <0> + ("0000ac99764c7d35bc94665985b2a1f1a9cdd8b09b886b5334eda616963c5b2d".toDigest, + 1u64, + "0".parse(Uint256), + "1d40ed4738c1b04df8bd8fd102ccde2f4a62f8803134a01d124da5c2389af5b1".toDigest, + "4e3c65796be34e5a3b67459453315d4367389de0dd041a33c53f2ae90870ed76".toDigest), + # <1> + ("0000b0cf401badb4765f2b784e9f785b15bfe4fc2e77b8a0c86c751d47f0abad".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <2> + ("0000b20dc12ccbfcba3c396d29e3e274895f324cb6fcfbac5b5b5b2c617f4d71".toDigest, + 1u64, + "17972666009000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <3> + ("0000b5e7ff88f0b5f2eb030d5bee5ca4b3373b3dc56acead58743ff1c06bf8b7".toDigest, + 1u64, + "0".parse(Uint256), + "7f3585c795bfd0889215f450db7b9655de300f7843625465327eadaaa93fc5e7".toDigest, + "af974539a8d1b7b250b247757abb3141a15893895d31f783046a40fa0f13088d".toDigest), + # <4> + ("0000ba54e185468676556e2b7d7b6a8407ab6c3619aa8ac9c37c39e7bc8da734".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <5> + ("0000bdac4dc292792028fa4f7574268656fd69ea4a98ff70ecc06e8f5a54473e".toDigest, + 1u64, + "8856704861000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <6> + ("0000c2177663a3e2dbce7f46a5b4ccc3fe1b60ef932577bdf2edc0353baa7c5a".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <7> + ("0000c7268c58440f6d9768fd5a682dfbd162af45b58bcdedfd04f389bdd1626b".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <8> + ("0000c98bd2d172062fd8c2002971df9f30b79f2007b8f77af662ba21518650be".toDigest, + 0u64, + "1000000000000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <9> + ("0000ce9efaa7c38ff13c76303e3177fead4667a6a8a2eaf5a15cfb87cf51f48f".toDigest, + 0u64, + "1000000000000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <10> + ("0000cfd7ca9238a12cd3673f432d179f4334cc7be664336c0f43156e4c82c178".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "875191faaccab052ffb33984e8ba823d46be31b93a9127447e8d332f8dedaca9".toDigest), + # <11> + ("0000d181a707520053d9c6fda09a8bd66a630a0ab32c639aa072b453febce193".toDigest, + 1u64, + "0".parse(Uint256), + "1d40ed4738c1b04df8bd8fd102ccde2f4a62f8803134a01d124da5c2389af5b1".toDigest, + "4e3c65796be34e5a3b67459453315d4367389de0dd041a33c53f2ae90870ed76".toDigest), + # <12> + ("0000d1c9b6575fc2a6ebdcd06675f347f1aa046a204a8a86adda81d09b12c56a".toDigest, + 2u64, + "2947499999853000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <13> + ("0000dc2eed80c31d56c486312b08db0a15d5a5ab72e4feaeea35920bbb052af5".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <14> + ("0000e139b60be7696807fc538c33ec55ae522b6ec0f1931e50838ce77bdcacd3".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "7b7e01b1062c9272df150db2cf701f7bd42d0f244ce8d52c8d8cba260b6c7ef4".toDigest)], + @[# <0> + "F90211A00F41A498B871B7A732DB8B91A4AD4E013596110F383BF3041F2E818714FB57FBA0203149AB733DABFFB4A075E06DFBAC60A68A88CEDA23AAAE3F82A059E73A0DF4A034A036B6A6B8B596BA29F2C1EB8FAC71F21B8BC72481CA2E7C21733E39DAE8E2A0D533FA53AFE1DEFBA7E2CC6E645523F3AA1FB76651634F9EEF68D063F23FEA31A0A3AEFA0444DE17ADCF431EC4C5F057D799A2AC3A568F4C303E43561A7FCB629CA0033F5BC31420628F447A5ABC3DECF498E6852B486C07ADB3BBD5334556463BD4A0015307556953C623310A1D3A70C58D9465C6EEBBA5547DF67BDCB19CA953B3A1A09FFD78C0F9E3A550DD9D45E4C82C78A437F85640E7F86461E101578E898D3FA5A01D624ED0F8C9C98AA2FCD5D8D0AE1954B1DB998E7C79F9A98601A42F255FFCAEA049BF312A5690ACF97114EEC2F0B7BA8541624339DCC4359FA63DD98D942C7745A08045A2EA09815540255B0F0071311084C58F0A16D89CE46B1A7BD3948DFBC7BDA0F86E5DFF074F176403C0E8388587D213890A5B926AFBB521767C384DC64F321AA01AD0BEC29F16127FF644D87CAF6F4EC6FA7F4EF50FBC51AC4984D496DB2CA99BA04981C605424CD0CE7233D469C85DC60FC8D88E585C3C02B4923B184B8FCDE926A012F41CFB88E684A08727C66FEE65D20D93DB71E8508A1476A61056E967494914A059D005E9864E5F502DAF61049C7A8F862E84F2591AE3394E3066C2648239B7FE80".hexToSeqByte, + # <1> + "F90211A0B5E039DE5CD9F82A1647EE05E212486A2DDB81999B80727B76C3156B40641CA9A02569520DCDB1750D48C7060FEE64B1C16388A098648AE4FEF929D75775EBBE53A0CE915C5DE8826650CC3F49E82B307A98E87359B679965A1A7796BB50298947F4A012C8C06E4EF08F8A47BB81A9F7FDD4279A7BE096E793DC10E217F5C17D1C61D7A0A5F8FF8BADFECACB3AD5EAFF8C4EE750D33DD50A1D1CC5130D027D574F4D1A9EA00E35BB939DD5CF81DC2219FB27BAE31DFE2AEE079FDBAD367619DBDBAD276D81A0C012A282076332BB92BEEDD74CF7EF9CC36875B54D1DAAC81AC0EC9CB60CE5AEA00FB445610AA14436F057BE1BC4DBD86BDBE01490AEEC46059C710797591B7E14A0EB99BB2A00E7CC498ABD0B020276A1790628369314133F35E8089B28D55839AAA0C290D06ADA3FFA0760EDD11D57F0B32F08BF668943CE01CF651B8B29DC63A197A088BD981BF81F4F0C0B37A04542269BE7C24A5D09D455807256344C6A864925E0A08924048D8F415C57A2835DB0B33F92AD157C4173D2FF819CB32D5EF89E7060A1A083DA61DBDC26D3202C343A0168D69B0DDB0572F43C20EDB924809DBA6149BE88A01B640A922A59C6DA8860058354FD78A39D00A46E0CDF92FB6A338ED556BABFD3A01D8ACC7CA60FCD378DE1DD765B680D037620F08485ED84BAD1A5BB82702F80D0A0B00F0533277C292308F9FC4BF5A85027D68F693D70CA92F1D983A32EA7C5478480".hexToSeqByte, + # <2> + "F90211A0D126CFCCD9CC595EE94F46C23D2C203D06BBF3E628C050CC34A1B5C76965A978A0B22839972A330E8CE273187A801F7257E8655A9E728283D41DF57E408A423127A06FB243B4185E550D9CA703A47E786E55B01E47968D53BBA5600865F3E7FF739AA01297B6C416BE295952F78AABCDA3D15F6087F1976194CF881A0A55D2CF8E9B6CA0523DF620C80E8C4B48A0DBB2328226921BE949F61671707A604AA95294EE4E75A0917B77A88A260098D6BBF3600D84BA39F22B59426823A2BE8A8D0771DAFA4DA0A007C73D02889414B827DA606183D4A960AAB396777217792EC55C68D6FF29B52CA079B593B9D554CFCDB265AD20A7AD018EC97B580F62904DC1052B7BD725ACB2E1A06EB993C8B75F317A7F73825D84BFA191E041667B20D0E21D2E9EF7BF311A53ACA048484A38E04A2B0001D0D3D34D731F8149459D08987EF7293331CBCCCFB3D09BA044A3A485D94F02B53EB6BF99842F88101A9E3C42D24577720742690F584B0BCCA058E3BF550CE62F775EAE8B9A49196A78F7F8DA68874420D959925F95C2CCF553A0988D0E99F69E71C24C2F17C69B7AC3F448FD7133D624FB68F148A439048C5B2EA01A48E7BAF56999165D3E2DC392D768EA88B4820D0A4D5BD8781B60A247138B36A0DBF8593863396974608C4D47BFAB51662750421D7D431842AF55740089AA3D4CA029AD034ECF2E8054767808DDF956EE2C89720F633C3B3594881A71BC582B9A0480".hexToSeqByte, + # <3> + "F90211A0C61F01AADE2395B4E17DE4C79353729EFFAD97B46C0E425B518D4D77B75CB713A03C2D55238135DA002B914D80556FFD60890E37705E7A5487B0B52FC26DA29299A07CE74DABF9451C37485200571A867213063A8FED408C6BC72F6D84564E9F8F3EA0514930C58E952176FF4C626CED5913394B5115CA7C81A9B5B543F7858F43DC01A02D93C42DC532FE12D6CCFD735EC1F22FBD7D009D386F79E76B1F0088CB412A30A0860EB915C38B045754025B34FD796CED538071EA6B98307754124A5ABB459326A00720A388A06A78305A61D749A3895319611313C5CDBE5A396E3C30793E18C9A4A0847A9DBC85722DF97F2B46D9D95DDA6C69E6D3FA68A98FCFDCF218B26A6EF727A0C7A6A3ECE732C86186CE38D8329D2242E65D8BCC240E186FF797621B9CD75B2DA05879B4B262967609E2D8ED94D0C358B9C8A6FD1759A4AA0AD836EEA77F9C76D8A0132F0874B2E39B4E357F8654B6B6A88F306E6631FAED6C0DF223F5D18A7CD448A0504A50995DB2FACDAC157BA88A6D73CCB2DEC75FCA6424EBE45381D9DBC82151A0DABD28DEDCF493701D612D87BEE5C008C40A46D8B1A5AA7860C7B388E3026A23A021680406AF0738D3FA04C827AA8735FF8ED2C057103AA909BF84C618F63E08A8A0AD769155BAC8DC43ACC0B2FA3A6D909E8C6FD08F54D3D70452197D64D4105B8DA01E1CA8EF41EBA3606E19ECBF58499208653BAD2891AD190227DB3B8705EEE06F80".hexToSeqByte, + # <4> + "F90211A0BE50E5783F7D3A2065DD822D40084386093B77FC22AD0A685CB0F54CBA532FA9A0202C94F67BE429B2AC3C241BFDA31AE5D5968ECACBB240D7C73F9E0140A3C1A7A0EA3FD58C72BCC61DDCD60FE900248481E227F6863927981C93B0AAEAFDD4E579A0057FDB2295A2FF9DA5CE6F6672752C0EE357D72E911EA4A337333F09D61A0062A031B7107E632B31170005D633D09A15A453F51762CB4EB1BECA62BBFEEC918530A0FC0B5E8C1349494480CDA5958FEB63542C8DAE8CD404D65E1F30BB5B31CB1081A0C1E5E68E12CA7008B0D7D06EB3E1569222909C841DBB187968E2370BE41A7BF4A06321142789FBD6D6EB092019B66B71D80912D7931A311DF05D56485368B05924A0F0E3BB64E9027EFAB77DCA13FE1AED824AEF6998128BD06DF9B77F2FA4822800A08A6282C15897C09165742C5799A9B79A68AD1B834E91667335BA615D7CBB8AC7A023EC2E3D24BA99D14C82FC52E1C97EDE646DF62E5E01AE5BF983003477274F23A06C21AE60204031C2F6C26ACCADF51DE91F438FBA88FF6000648590ED022747ECA0DB12785060A364F3506432E77E210726C266E5918A33F85CE3224A8CBC3848BEA01497748CD1DE0732C2B693534DBF1765A061A8323B30DB781FF6DEDE83D606DAA043E9BC03A4D7AC6F76295983D2FCD7950700C08E0EC995DF57C4F666924A5F7BA05165972EDE7FE8960E187F270D7223CF2B0DC48A2F7AC7973F42C8B71CDC581D80".hexToSeqByte, + # <5> + "F85180A0E96A6780E1CBF4298CC558FF8D1163F5915C2A66213ED4F6C5DC4164744EDFF880808080808080808080A0CA8BCC923F9D4B5DC971615D4555E89F7A1CE97DA196E223FA4991FB1064040D80808080".hexToSeqByte, + # <6> + "F87180A0A74ACF3E14D02AA65D03619762FE4784D3E21F47C89CBEB7C617FF2CA69E6700A0B54D3F1EFB72CE9E841AB8068D1649E4CCE0064CDCA2611DCB2952877A26C7B8A036D5EE329856594B3E9AEE0C635985264AE2D8FB84B4BC1A7585379EDB4C63B780808080808080808080808080".hexToSeqByte, + # <7> + "F8679E2039B60BE7696807FC538C33EC55AE522B6EC0F1931E50838CE77BDCACD3B846F8440180A056E81F171BCC55A6FF8345E692C0F86E5B48E01B996CADC001622FB5E363B421A07B7E01B1062C9272DF150DB2CF701F7BD42D0F244CE8D52C8D8CBA260B6C7EF4".hexToSeqByte]) + + snapProofData4* = ( + "0000dfb23b0979b4b02ebfbf567aef42d1c5127c92b10076d6af5d08a7fd98d4".toDigest, + @[# <0> + ("0000e139b60be7696807fc538c33ec55ae522b6ec0f1931e50838ce77bdcacd3".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "7b7e01b1062c9272df150db2cf701f7bd42d0f244ce8d52c8d8cba260b6c7ef4".toDigest), + # <1> + ("0000e2718fb3c18a69cf95b8841e51d4601c086896984ffb0d686e728a1b311d".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <2> + ("0000e3c92282480b30aba8a2498f65e11b1b778e4b118d6f67f4ff377a7c89ef".toDigest, + 0u64, + "1000000000000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <3> + ("0000f2d7650ea90cc3a9ee5a603f8e98bfc52b12d97a2ddd8053d880490a6a63".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "51a18c6f6f98c4ec00f9d9ab8655497698311ead2d4f6cf542fa123641896cf5".toDigest), + # <4> + ("0000f89e96ba7b589074f02039e64e81f9dce3e4ab44e14076117318fd3992fd".toDigest, + 1u64, + "0".parse(Uint256), + "1d40ed4738c1b04df8bd8fd102ccde2f4a62f8803134a01d124da5c2389af5b1".toDigest, + "4e3c65796be34e5a3b67459453315d4367389de0dd041a33c53f2ae90870ed76".toDigest), + # <5> + ("0000f976ce2cee30fb40d2f2075a6eb3d5fdfe303aa2cc74e6b716c3bdcf4a5e".toDigest, + 2u64, + "16277498626705".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <6> + ("0000fbc7f0716279d3985077761952f85011ec505029fbc496d06ab43d76b2f3".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <7> + ("0000fc1cccbc6aaee1589162e1632057760f8b516eb14722e0ac287378487e2b".toDigest, + 0u64, + "1000000000000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <8> + ("0000fe6e6de67b7e244dd7fe2826170f681c31073f91b22ac917be38e5368ba2".toDigest, + 1u64, + "0".parse(Uint256), + "a1927b6c96917d7deba715ed7cba6e672925b5ea60f77205b98552032cf85f4a".toDigest, + "af974539a8d1b7b250b247757abb3141a15893895d31f783046a40fa0f13088d".toDigest), + # <9> + ("000101975019cb966ab499e5def95b36caf4c7b6556d34b130702246e3c04674".toDigest, + 0u64, + "50000000000000000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <10> + ("00010316ac0ffb0a5b65ac33d77d4c4653b10cf40223a444f7d0f10aa3492b57".toDigest, + 0u64, + "1000000000000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <11> + ("00010798b4845fbbd8e5b9d73aba753ecdb803cec4f5482e1b750d57b0291c3a".toDigest, + 1u64, + "0".parse(Uint256), + "1d40ed4738c1b04df8bd8fd102ccde2f4a62f8803134a01d124da5c2389af5b1".toDigest, + "4e3c65796be34e5a3b67459453315d4367389de0dd041a33c53f2ae90870ed76".toDigest), + # <12> + ("00010d2a9570e13fd93bdd5c7b88d8a735f2e33bcb531d79fb36509b6457e389".toDigest, + 1u64, + "0".parse(Uint256), + "1d40ed4738c1b04df8bd8fd102ccde2f4a62f8803134a01d124da5c2389af5b1".toDigest, + "4e3c65796be34e5a3b67459453315d4367389de0dd041a33c53f2ae90870ed76".toDigest), + # <13> + ("00010dce88f17de5a9fb4f76243a2e6b49fd5945e23f7868c7b489bafddbeb56".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <14> + ("00010f53e1f9c596a42100647e397dc245241b831925a1c424bb1e8069cfe213".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <15> + ("0001102a38fa37f83460721288a4fee444b9102ec50145ef9ea4c997188ecb5f".toDigest, + 0u64, + "1000000000000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <16> + ("000113af6937a70fdb23da37baa146882bf032dfd3e8cc45807ee85dd4c34b16".toDigest, + 0u64, + "8782980100000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <17> + ("000116d5c36dc735641de47b3a23dc25ecd46104fa11e4db5e89ef52b3cb7372".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "51a18c6f6f98c4ec00f9d9ab8655497698311ead2d4f6cf542fa123641896cf5".toDigest), + # <18> + ("000119ff8e9e74bf610d2284e36c7e04e28b2275b62e66fbeab69bdb8fcbf754".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "cae7a391a198ba03b1278dedb0e8ea440a27bdc6d7f04fbfd926893ff71341ce".toDigest)], + @[# <0> + "F90211A00F41A498B871B7A732DB8B91A4AD4E013596110F383BF3041F2E818714FB57FBA0203149AB733DABFFB4A075E06DFBAC60A68A88CEDA23AAAE3F82A059E73A0DF4A034A036B6A6B8B596BA29F2C1EB8FAC71F21B8BC72481CA2E7C21733E39DAE8E2A0D533FA53AFE1DEFBA7E2CC6E645523F3AA1FB76651634F9EEF68D063F23FEA31A0A3AEFA0444DE17ADCF431EC4C5F057D799A2AC3A568F4C303E43561A7FCB629CA0033F5BC31420628F447A5ABC3DECF498E6852B486C07ADB3BBD5334556463BD4A0015307556953C623310A1D3A70C58D9465C6EEBBA5547DF67BDCB19CA953B3A1A09FFD78C0F9E3A550DD9D45E4C82C78A437F85640E7F86461E101578E898D3FA5A01D624ED0F8C9C98AA2FCD5D8D0AE1954B1DB998E7C79F9A98601A42F255FFCAEA049BF312A5690ACF97114EEC2F0B7BA8541624339DCC4359FA63DD98D942C7745A08045A2EA09815540255B0F0071311084C58F0A16D89CE46B1A7BD3948DFBC7BDA0F86E5DFF074F176403C0E8388587D213890A5B926AFBB521767C384DC64F321AA01AD0BEC29F16127FF644D87CAF6F4EC6FA7F4EF50FBC51AC4984D496DB2CA99BA04981C605424CD0CE7233D469C85DC60FC8D88E585C3C02B4923B184B8FCDE926A012F41CFB88E684A08727C66FEE65D20D93DB71E8508A1476A61056E967494914A059D005E9864E5F502DAF61049C7A8F862E84F2591AE3394E3066C2648239B7FE80".hexToSeqByte, + # <1> + "F90211A0B5E039DE5CD9F82A1647EE05E212486A2DDB81999B80727B76C3156B40641CA9A02569520DCDB1750D48C7060FEE64B1C16388A098648AE4FEF929D75775EBBE53A0CE915C5DE8826650CC3F49E82B307A98E87359B679965A1A7796BB50298947F4A012C8C06E4EF08F8A47BB81A9F7FDD4279A7BE096E793DC10E217F5C17D1C61D7A0A5F8FF8BADFECACB3AD5EAFF8C4EE750D33DD50A1D1CC5130D027D574F4D1A9EA00E35BB939DD5CF81DC2219FB27BAE31DFE2AEE079FDBAD367619DBDBAD276D81A0C012A282076332BB92BEEDD74CF7EF9CC36875B54D1DAAC81AC0EC9CB60CE5AEA00FB445610AA14436F057BE1BC4DBD86BDBE01490AEEC46059C710797591B7E14A0EB99BB2A00E7CC498ABD0B020276A1790628369314133F35E8089B28D55839AAA0C290D06ADA3FFA0760EDD11D57F0B32F08BF668943CE01CF651B8B29DC63A197A088BD981BF81F4F0C0B37A04542269BE7C24A5D09D455807256344C6A864925E0A08924048D8F415C57A2835DB0B33F92AD157C4173D2FF819CB32D5EF89E7060A1A083DA61DBDC26D3202C343A0168D69B0DDB0572F43C20EDB924809DBA6149BE88A01B640A922A59C6DA8860058354FD78A39D00A46E0CDF92FB6A338ED556BABFD3A01D8ACC7CA60FCD378DE1DD765B680D037620F08485ED84BAD1A5BB82702F80D0A0B00F0533277C292308F9FC4BF5A85027D68F693D70CA92F1D983A32EA7C5478480".hexToSeqByte, + # <2> + "F90211A0D126CFCCD9CC595EE94F46C23D2C203D06BBF3E628C050CC34A1B5C76965A978A0B22839972A330E8CE273187A801F7257E8655A9E728283D41DF57E408A423127A06FB243B4185E550D9CA703A47E786E55B01E47968D53BBA5600865F3E7FF739AA01297B6C416BE295952F78AABCDA3D15F6087F1976194CF881A0A55D2CF8E9B6CA0523DF620C80E8C4B48A0DBB2328226921BE949F61671707A604AA95294EE4E75A0917B77A88A260098D6BBF3600D84BA39F22B59426823A2BE8A8D0771DAFA4DA0A007C73D02889414B827DA606183D4A960AAB396777217792EC55C68D6FF29B52CA079B593B9D554CFCDB265AD20A7AD018EC97B580F62904DC1052B7BD725ACB2E1A06EB993C8B75F317A7F73825D84BFA191E041667B20D0E21D2E9EF7BF311A53ACA048484A38E04A2B0001D0D3D34D731F8149459D08987EF7293331CBCCCFB3D09BA044A3A485D94F02B53EB6BF99842F88101A9E3C42D24577720742690F584B0BCCA058E3BF550CE62F775EAE8B9A49196A78F7F8DA68874420D959925F95C2CCF553A0988D0E99F69E71C24C2F17C69B7AC3F448FD7133D624FB68F148A439048C5B2EA01A48E7BAF56999165D3E2DC392D768EA88B4820D0A4D5BD8781B60A247138B36A0DBF8593863396974608C4D47BFAB51662750421D7D431842AF55740089AA3D4CA029AD034ECF2E8054767808DDF956EE2C89720F633C3B3594881A71BC582B9A0480".hexToSeqByte, + # <3> + "F90211A0C61F01AADE2395B4E17DE4C79353729EFFAD97B46C0E425B518D4D77B75CB713A03C2D55238135DA002B914D80556FFD60890E37705E7A5487B0B52FC26DA29299A07CE74DABF9451C37485200571A867213063A8FED408C6BC72F6D84564E9F8F3EA0514930C58E952176FF4C626CED5913394B5115CA7C81A9B5B543F7858F43DC01A02D93C42DC532FE12D6CCFD735EC1F22FBD7D009D386F79E76B1F0088CB412A30A0860EB915C38B045754025B34FD796CED538071EA6B98307754124A5ABB459326A00720A388A06A78305A61D749A3895319611313C5CDBE5A396E3C30793E18C9A4A0847A9DBC85722DF97F2B46D9D95DDA6C69E6D3FA68A98FCFDCF218B26A6EF727A0C7A6A3ECE732C86186CE38D8329D2242E65D8BCC240E186FF797621B9CD75B2DA05879B4B262967609E2D8ED94D0C358B9C8A6FD1759A4AA0AD836EEA77F9C76D8A0132F0874B2E39B4E357F8654B6B6A88F306E6631FAED6C0DF223F5D18A7CD448A0504A50995DB2FACDAC157BA88A6D73CCB2DEC75FCA6424EBE45381D9DBC82151A0DABD28DEDCF493701D612D87BEE5C008C40A46D8B1A5AA7860C7B388E3026A23A021680406AF0738D3FA04C827AA8735FF8ED2C057103AA909BF84C618F63E08A8A0AD769155BAC8DC43ACC0B2FA3A6D909E8C6FD08F54D3D70452197D64D4105B8DA01E1CA8EF41EBA3606E19ECBF58499208653BAD2891AD190227DB3B8705EEE06F80".hexToSeqByte, + # <4> + "F90211A0BE50E5783F7D3A2065DD822D40084386093B77FC22AD0A685CB0F54CBA532FA9A0202C94F67BE429B2AC3C241BFDA31AE5D5968ECACBB240D7C73F9E0140A3C1A7A0EA3FD58C72BCC61DDCD60FE900248481E227F6863927981C93B0AAEAFDD4E579A0057FDB2295A2FF9DA5CE6F6672752C0EE357D72E911EA4A337333F09D61A0062A031B7107E632B31170005D633D09A15A453F51762CB4EB1BECA62BBFEEC918530A0FC0B5E8C1349494480CDA5958FEB63542C8DAE8CD404D65E1F30BB5B31CB1081A0C1E5E68E12CA7008B0D7D06EB3E1569222909C841DBB187968E2370BE41A7BF4A06321142789FBD6D6EB092019B66B71D80912D7931A311DF05D56485368B05924A0F0E3BB64E9027EFAB77DCA13FE1AED824AEF6998128BD06DF9B77F2FA4822800A08A6282C15897C09165742C5799A9B79A68AD1B834E91667335BA615D7CBB8AC7A023EC2E3D24BA99D14C82FC52E1C97EDE646DF62E5E01AE5BF983003477274F23A06C21AE60204031C2F6C26ACCADF51DE91F438FBA88FF6000648590ED022747ECA0DB12785060A364F3506432E77E210726C266E5918A33F85CE3224A8CBC3848BEA01497748CD1DE0732C2B693534DBF1765A061A8323B30DB781FF6DEDE83D606DAA043E9BC03A4D7AC6F76295983D2FCD7950700C08E0EC995DF57C4F666924A5F7BA05165972EDE7FE8960E187F270D7223CF2B0DC48A2F7AC7973F42C8B71CDC581D80".hexToSeqByte, + # <5> + "F85180A0FBE20522C71165A9CC150DC4C1341A14E7ECCB6C6A69EC5EAC18DD2813FC646C80808080808080808080A0C69BF42152F45BAFBB784733997D950691E7041FA1DD278AFB52EF891CEB2A7580808080".hexToSeqByte, + # <6> + "F90211A04D0B43185B88D09268323C29A0EF25E82DB4BF1E4D8533419DD390D145D2F413A0461FB3802E911FCBAB629D59E9F4603C43AC425A7398F82CB0F20A31F5D326FEA0AA4045C94006E7D21313816F3D4AC125B8D430EF6439950BCC10D57DB6D17F27A06B5A75E3CD1848BF2A4107244111F4C845A4B4355019E21F0E549A26CFAC4466A0B6C5330412A4EA9A616E3B44C6FAD6CACA452A56E10C038D2EC4754B5D89EE4EA0BD8F7BDDD0A6DF94786C9A05CF724126A7F66ADE48D740BE53AD3561882210FCA07CBEE3428088A236BFD8884EBF18E6E858DBC1EAED0DB934A62A55548B0CB6D7A05C72FD737EB2089F2EF92A0D18B53B70F0EA88844ACF378EA9310D593D2CCAD6A05716834FACDD63C05C77A84DCF7205EE2BBA8E56CFACE0E701C578E469000C46A0DC46F8ED6086FFA553BD71C12089046829BD342BE17B4AD583D149F9D87BEDDEA063DA085F6C323ACAACD7779DB176E3DDA500C57A9F0041C9020681C20EE3F211A0634A4CF830FDADAACFB4E1EE0D5B4233A8596C4DD98239CD156F256ED209E6FCA0696B3F62C2CDB6F9E5AB77A0D62C7372B208851E8C8DB4265685B2C6F4390C54A0F4C75EC4D674379A8333B284E45D0FA57C208EEC6A66707828BD46A654E1D33AA0AEAEA8DE7014BBF7A8FF12D124330F4981DE4163B821B1A6A0E1B0B17A373FAAA0ACB27F326E15F8008D8491A7C8C015C340A3DA189171D0158AC880FF2600204E80".hexToSeqByte, + # <7> + "F8B1A0EB37116C54BEA1E8E2A82F56479D3AFBD0B23F5176ADD04CA27E879CFAD489898080A0272B11871626D72E4A106CB55052273C0DD97AC77E71C8AA8F46E71E40D65D1F8080A0ECD1B34FBFE1EB5CCA34BD2284095941C33D27409D8D73666491C563B40A47688080A0DDEDC4223AE055E410B2A27517793134802DAEA6EA59FE2E7C1FDEC18879477AA0597BB25A4B2C06865A2ED6BEC427D678729C5CEF8D5FEF98702695ED899DB499808080808080".hexToSeqByte, + # <8> + "F8679E20FF8E9E74BF610D2284E36C7E04E28B2275B62E66FBEAB69BDB8FCBF754B846F8440180A056E81F171BCC55A6FF8345E692C0F86E5B48E01B996CADC001622FB5E363B421A0CAE7A391A198BA03B1278DEDB0E8EA440A27BDC6D7F04FBFD926893FF71341CE".hexToSeqByte]) + + snapProofData5* = ( + "0001179ec9cbd821dc3a6faf2c19ab138636571bb75d40948c5b344ad1fcff09".toDigest, + @[# <0> + ("000119ff8e9e74bf610d2284e36c7e04e28b2275b62e66fbeab69bdb8fcbf754".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "cae7a391a198ba03b1278dedb0e8ea440a27bdc6d7f04fbfd926893ff71341ce".toDigest), + # <1> + ("00011aa88433e4e99ea324da099c0d36a76d272148c044dd0ba6fab3af6268bb".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "5360608677a431762eb75b72089946bd0b7d6fc378c1c6cd43806d2c099cc692".toDigest), + # <2> + ("000122ddb486ca11290f62013bc155836d3520a0ef33500bb91bc7b85786d665".toDigest, + 1u64, + "0".parse(Uint256), + "cfb6a2a81b223526ad0fa6d4b00106030d2d4ad92216b4eb4b103b1174148c0e".toDigest, + "657344f2a8c03bbc89f917df6c13155b1e6e7a043767f6f86def2e0887930846".toDigest), + # <3> + ("00012447062cbfea2f78aa2b61b614b4b501dc270daa5d81971cbdc3e5840b09".toDigest, + 0u64, + "1000000000000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <4> + ("0001247ab597adc264bb2820fb7f5a820e7e7169bda40f9e5b180835e83180a7".toDigest, + 0u64, + "1000000000000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <5> + ("0001280d37a4acf945731ffb1921fbddb60fc274423b0da803aae9b1b79c59e0".toDigest, + 1u64, + "0".parse(Uint256), + "1d40ed4738c1b04df8bd8fd102ccde2f4a62f8803134a01d124da5c2389af5b1".toDigest, + "4e3c65796be34e5a3b67459453315d4367389de0dd041a33c53f2ae90870ed76".toDigest), + # <6> + ("0001287b69f4d2f1fbd81bed6195fa8d60447d881d5aa054e6c05ca6c95250ec".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <7> + ("00012a2ce44e64f04a1bcfa857088a600104945b2a896c947a9fb503827e2d5e".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <8> + ("00012a5d773d2a725e66065ceae435a0b65ceaade4587fbcc7d5939aa57398c2".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <9> + ("000131ceb4e4470a719e41dd0c40be00274b818aa37a0e9b60db4b9b219fd621".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <10> + ("000135b909ef25a1766f0e42007f708d1c6b50d427db2c3fa53e82dd829ca754".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <11> + ("00013fd0af4b5c21b6534dcca5be208c8ad9ebd49ca8abe1fecd6f8e8b4d099e".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <12> + ("0001419a0ffef3d8841b865e9b8b414f2af3fecbf0df8dec0556343967449729".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "a25093fe75171f153efcf777b023a82d46ca91e8157253de484fd886e16756f7".toDigest), + # <13> + ("00014366a208b7cebba03667cb570443d1e52905414709a5a27d97d3cb8ce560".toDigest, + 0u64, + "1".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <14> + ("000143711056c2b2b78668128493fd9b682625e45ade0d376bbdabaf8c459447".toDigest, + 0u64, + "1000000000000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <15> + ("0001446c3fecb18635bc9683f9183d392e03a491c9307f3fe71d24d7df627b7c".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <16> + ("00014761991ab448e6b17d94e771a19516cafbe4a829853fda428e90d7f899cd".toDigest, + 1u64, + "0".parse(Uint256), + "d41166ebb086cbb788872634cec58e14eb0c0d7fd8ddcddb9be3aca0353c1c83".toDigest, + "8a0c9017e6ef5d552ec36e1a772b70d6c066432aacc2c619ac16cca7de079321".toDigest), + # <17> + ("00014beb66a384ef44c92327af6921f085160a1f55bf793b8e10deb36cf6fcc7".toDigest, + 2u64, + "16277420560675".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <18> + ("00014c88cdd95bc688ee22c7c90fdcffa449ad166f54ab5edadf5f08e3b8c00e".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <19> + ("00014f4e9610978c7e3c9c18442d7a979c6201f8fd47dfb72edb1e3f11770559".toDigest, + 0u64, + "1000000000000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <20> + ("00015462a70a37b6cd4f4ff72fedd4af28b2e1e2f3c62d365ab4c83535d334d2".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest)], + @[# <0> + "F90211A00F41A498B871B7A732DB8B91A4AD4E013596110F383BF3041F2E818714FB57FBA0203149AB733DABFFB4A075E06DFBAC60A68A88CEDA23AAAE3F82A059E73A0DF4A034A036B6A6B8B596BA29F2C1EB8FAC71F21B8BC72481CA2E7C21733E39DAE8E2A0D533FA53AFE1DEFBA7E2CC6E645523F3AA1FB76651634F9EEF68D063F23FEA31A0A3AEFA0444DE17ADCF431EC4C5F057D799A2AC3A568F4C303E43561A7FCB629CA0033F5BC31420628F447A5ABC3DECF498E6852B486C07ADB3BBD5334556463BD4A0015307556953C623310A1D3A70C58D9465C6EEBBA5547DF67BDCB19CA953B3A1A09FFD78C0F9E3A550DD9D45E4C82C78A437F85640E7F86461E101578E898D3FA5A01D624ED0F8C9C98AA2FCD5D8D0AE1954B1DB998E7C79F9A98601A42F255FFCAEA049BF312A5690ACF97114EEC2F0B7BA8541624339DCC4359FA63DD98D942C7745A08045A2EA09815540255B0F0071311084C58F0A16D89CE46B1A7BD3948DFBC7BDA0F86E5DFF074F176403C0E8388587D213890A5B926AFBB521767C384DC64F321AA01AD0BEC29F16127FF644D87CAF6F4EC6FA7F4EF50FBC51AC4984D496DB2CA99BA04981C605424CD0CE7233D469C85DC60FC8D88E585C3C02B4923B184B8FCDE926A012F41CFB88E684A08727C66FEE65D20D93DB71E8508A1476A61056E967494914A059D005E9864E5F502DAF61049C7A8F862E84F2591AE3394E3066C2648239B7FE80".hexToSeqByte, + # <1> + "F90211A0B5E039DE5CD9F82A1647EE05E212486A2DDB81999B80727B76C3156B40641CA9A02569520DCDB1750D48C7060FEE64B1C16388A098648AE4FEF929D75775EBBE53A0CE915C5DE8826650CC3F49E82B307A98E87359B679965A1A7796BB50298947F4A012C8C06E4EF08F8A47BB81A9F7FDD4279A7BE096E793DC10E217F5C17D1C61D7A0A5F8FF8BADFECACB3AD5EAFF8C4EE750D33DD50A1D1CC5130D027D574F4D1A9EA00E35BB939DD5CF81DC2219FB27BAE31DFE2AEE079FDBAD367619DBDBAD276D81A0C012A282076332BB92BEEDD74CF7EF9CC36875B54D1DAAC81AC0EC9CB60CE5AEA00FB445610AA14436F057BE1BC4DBD86BDBE01490AEEC46059C710797591B7E14A0EB99BB2A00E7CC498ABD0B020276A1790628369314133F35E8089B28D55839AAA0C290D06ADA3FFA0760EDD11D57F0B32F08BF668943CE01CF651B8B29DC63A197A088BD981BF81F4F0C0B37A04542269BE7C24A5D09D455807256344C6A864925E0A08924048D8F415C57A2835DB0B33F92AD157C4173D2FF819CB32D5EF89E7060A1A083DA61DBDC26D3202C343A0168D69B0DDB0572F43C20EDB924809DBA6149BE88A01B640A922A59C6DA8860058354FD78A39D00A46E0CDF92FB6A338ED556BABFD3A01D8ACC7CA60FCD378DE1DD765B680D037620F08485ED84BAD1A5BB82702F80D0A0B00F0533277C292308F9FC4BF5A85027D68F693D70CA92F1D983A32EA7C5478480".hexToSeqByte, + # <2> + "F90211A0D126CFCCD9CC595EE94F46C23D2C203D06BBF3E628C050CC34A1B5C76965A978A0B22839972A330E8CE273187A801F7257E8655A9E728283D41DF57E408A423127A06FB243B4185E550D9CA703A47E786E55B01E47968D53BBA5600865F3E7FF739AA01297B6C416BE295952F78AABCDA3D15F6087F1976194CF881A0A55D2CF8E9B6CA0523DF620C80E8C4B48A0DBB2328226921BE949F61671707A604AA95294EE4E75A0917B77A88A260098D6BBF3600D84BA39F22B59426823A2BE8A8D0771DAFA4DA0A007C73D02889414B827DA606183D4A960AAB396777217792EC55C68D6FF29B52CA079B593B9D554CFCDB265AD20A7AD018EC97B580F62904DC1052B7BD725ACB2E1A06EB993C8B75F317A7F73825D84BFA191E041667B20D0E21D2E9EF7BF311A53ACA048484A38E04A2B0001D0D3D34D731F8149459D08987EF7293331CBCCCFB3D09BA044A3A485D94F02B53EB6BF99842F88101A9E3C42D24577720742690F584B0BCCA058E3BF550CE62F775EAE8B9A49196A78F7F8DA68874420D959925F95C2CCF553A0988D0E99F69E71C24C2F17C69B7AC3F448FD7133D624FB68F148A439048C5B2EA01A48E7BAF56999165D3E2DC392D768EA88B4820D0A4D5BD8781B60A247138B36A0DBF8593863396974608C4D47BFAB51662750421D7D431842AF55740089AA3D4CA029AD034ECF2E8054767808DDF956EE2C89720F633C3B3594881A71BC582B9A0480".hexToSeqByte, + # <3> + "F90211A0C61F01AADE2395B4E17DE4C79353729EFFAD97B46C0E425B518D4D77B75CB713A03C2D55238135DA002B914D80556FFD60890E37705E7A5487B0B52FC26DA29299A07CE74DABF9451C37485200571A867213063A8FED408C6BC72F6D84564E9F8F3EA0514930C58E952176FF4C626CED5913394B5115CA7C81A9B5B543F7858F43DC01A02D93C42DC532FE12D6CCFD735EC1F22FBD7D009D386F79E76B1F0088CB412A30A0860EB915C38B045754025B34FD796CED538071EA6B98307754124A5ABB459326A00720A388A06A78305A61D749A3895319611313C5CDBE5A396E3C30793E18C9A4A0847A9DBC85722DF97F2B46D9D95DDA6C69E6D3FA68A98FCFDCF218B26A6EF727A0C7A6A3ECE732C86186CE38D8329D2242E65D8BCC240E186FF797621B9CD75B2DA05879B4B262967609E2D8ED94D0C358B9C8A6FD1759A4AA0AD836EEA77F9C76D8A0132F0874B2E39B4E357F8654B6B6A88F306E6631FAED6C0DF223F5D18A7CD448A0504A50995DB2FACDAC157BA88A6D73CCB2DEC75FCA6424EBE45381D9DBC82151A0DABD28DEDCF493701D612D87BEE5C008C40A46D8B1A5AA7860C7B388E3026A23A021680406AF0738D3FA04C827AA8735FF8ED2C057103AA909BF84C618F63E08A8A0AD769155BAC8DC43ACC0B2FA3A6D909E8C6FD08F54D3D70452197D64D4105B8DA01E1CA8EF41EBA3606E19ECBF58499208653BAD2891AD190227DB3B8705EEE06F80".hexToSeqByte, + # <4> + "F90211A04D0B43185B88D09268323C29A0EF25E82DB4BF1E4D8533419DD390D145D2F413A0461FB3802E911FCBAB629D59E9F4603C43AC425A7398F82CB0F20A31F5D326FEA0AA4045C94006E7D21313816F3D4AC125B8D430EF6439950BCC10D57DB6D17F27A06B5A75E3CD1848BF2A4107244111F4C845A4B4355019E21F0E549A26CFAC4466A0B6C5330412A4EA9A616E3B44C6FAD6CACA452A56E10C038D2EC4754B5D89EE4EA0BD8F7BDDD0A6DF94786C9A05CF724126A7F66ADE48D740BE53AD3561882210FCA07CBEE3428088A236BFD8884EBF18E6E858DBC1EAED0DB934A62A55548B0CB6D7A05C72FD737EB2089F2EF92A0D18B53B70F0EA88844ACF378EA9310D593D2CCAD6A05716834FACDD63C05C77A84DCF7205EE2BBA8E56CFACE0E701C578E469000C46A0DC46F8ED6086FFA553BD71C12089046829BD342BE17B4AD583D149F9D87BEDDEA063DA085F6C323ACAACD7779DB176E3DDA500C57A9F0041C9020681C20EE3F211A0634A4CF830FDADAACFB4E1EE0D5B4233A8596C4DD98239CD156F256ED209E6FCA0696B3F62C2CDB6F9E5AB77A0D62C7372B208851E8C8DB4265685B2C6F4390C54A0F4C75EC4D674379A8333B284E45D0FA57C208EEC6A66707828BD46A654E1D33AA0AEAEA8DE7014BBF7A8FF12D124330F4981DE4163B821B1A6A0E1B0B17A373FAAA0ACB27F326E15F8008D8491A7C8C015C340A3DA189171D0158AC880FF2600204E80".hexToSeqByte, + # <5> + "F8B1A0EB37116C54BEA1E8E2A82F56479D3AFBD0B23F5176ADD04CA27E879CFAD489898080A0272B11871626D72E4A106CB55052273C0DD97AC77E71C8AA8F46E71E40D65D1F8080A0ECD1B34FBFE1EB5CCA34BD2284095941C33D27409D8D73666491C563B40A47688080A0DDEDC4223AE055E410B2A27517793134802DAEA6EA59FE2E7C1FDEC18879477AA0597BB25A4B2C06865A2ED6BEC427D678729C5CEF8D5FEF98702695ED899DB499808080808080".hexToSeqByte, + # <6> + "F8679E3462A70A37B6CD4F4FF72FEDD4AF28B2E1E2F3C62D365AB4C83535D334D2B846F8440180A056E81F171BCC55A6FF8345E692C0F86E5B48E01B996CADC001622FB5E363B421A0D8990879C8C8E2B683D4F0FC5A76889AEEBA15181AE48F09FBF37ED182286300".hexToSeqByte]) + + snapProofData6* = ( + "00014f8b588e368f08461f9f01b866e43aa79bbadc0980b242070b8cfbfc653e".toDigest, + @[# <0> + ("00015462a70a37b6cd4f4ff72fedd4af28b2e1e2f3c62d365ab4c83535d334d2".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <1> + ("0001639adb9a0024f0426e45bc7e96f62e058e15be71b69781a13091466ddad2".toDigest, + 1u64, + "0".parse(Uint256), + "1d40ed4738c1b04df8bd8fd102ccde2f4a62f8803134a01d124da5c2389af5b1".toDigest, + "4e3c65796be34e5a3b67459453315d4367389de0dd041a33c53f2ae90870ed76".toDigest), + # <2> + ("0001650e299fd9297d2952ecf0902d8c7e56d09975ca5dd1c7a6b6347a81c81c".toDigest, + 1u64, + "150551347666000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <3> + ("00016a6960fb521b5eb546e650ba9413e121cf8501cf404a9130abb7b0b4053c".toDigest, + 0u64, + "1000000000000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <4> + ("00016b29fc29367feafffaa9398348dc9c580e90b28b821fc6e78734f0c0b13d".toDigest, + 1u64, + "0".parse(Uint256), + "1d40ed4738c1b04df8bd8fd102ccde2f4a62f8803134a01d124da5c2389af5b1".toDigest, + "4e3c65796be34e5a3b67459453315d4367389de0dd041a33c53f2ae90870ed76".toDigest), + # <5> + ("00016b64488e85aeabd9d793460b0126528804007432ed23949af3593880c197".toDigest, + 1u64, + "0".parse(Uint256), + "1d40ed4738c1b04df8bd8fd102ccde2f4a62f8803134a01d124da5c2389af5b1".toDigest, + "4e3c65796be34e5a3b67459453315d4367389de0dd041a33c53f2ae90870ed76".toDigest), + # <6> + ("0001731478a0d941ef602cb516d3861fec35cb5af3e53c44085cde9dae8efd42".toDigest, + 1u64, + "0".parse(Uint256), + "4b5fc224618445f61a4baefad2f6bc553977d77aa6dc5ad86d523f12bf896db0".toDigest, + "bc85462169ac1d54cf480b7d0dffaffa5bcc0ba17a39c0b879e77b22b4b9f813".toDigest), + # <7> + ("000177bcf69bd53c00900602d09d93677a61a64ed9ee0e15796cc9a2fa83d89b".toDigest, + 1u64, + "0".parse(Uint256), + "61f4fb9dcd3e1392dc5246d11a519b78bd1a73cb09041157b0837939d0793615".toDigest, + "a6c8d835f483582299797f5ed7e396eb614ac1bddf7165dfb0484bf8079e9e30".toDigest), + # <8> + ("000179a932f12758a0390a085171abe15011772ddd1d474e3157cef0f0285a48".toDigest, + 1u64, + "215349999832000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <9> + ("00017a7055ddbff877cbcb6625259a4ab4f869980a7f19dc370c7fc3bbd5bac1".toDigest, + 14u64, + "1045993107487416350".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <10> + ("00017bc3949f7abcdc53164b7d027b98a42b74780a87fa96a136a3df67002b9c".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <11> + ("00017c1808a2451cf3c9864c4643bf635de543a708b3271862d6d362e3a731e7".toDigest, + 1u64, + "0".parse(Uint256), + "740802776108863e5b82e3c77e1e5d1ac5a0110c82f8424930477b31a7c56ae2".toDigest, + "f2dd2603e291b964d61688b403832b83bd3df87a83d9e7e2effae7f7af1c3553".toDigest), + # <12> + ("00017e35ff155883df7ef5f35bcc809ad5a2d62e8fb0c6185cd9fc423b725592".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "f567e0f33909988902d2eb06241ae57078c593cf67d2868793f99ad995300e08".toDigest), + # <13> + ("00017f2e9a62c49046fe00d2acee2a41762ada6d634a43e74ce78ff0deede71e".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "35f6983ef950790afd9635788f7d1f5cac228761c01a3f2b89b1b961d264512f".toDigest), + # <14> + ("000183e3d1be62fb07e61ff998b900442765c22b4cfbf6dd8325e3e5593469ce".toDigest, + 1u64, + "0".parse(Uint256), + "1d40ed4738c1b04df8bd8fd102ccde2f4a62f8803134a01d124da5c2389af5b1".toDigest, + "4e3c65796be34e5a3b67459453315d4367389de0dd041a33c53f2ae90870ed76".toDigest), + # <15> + ("000184e11952c6acfac997d752335b195c650eb4fecd7125402fb2c25a26e303".toDigest, + 1u64, + "0".parse(Uint256), + "eb8ce44b7a575c140849ab68017b54855ba8d80f26ea357c99f8a51651f6a061".toDigest, + "c80ea2acb5134ed19266d28c228ad60c342ab1def4c0a7c865dc58d838d4d5a1".toDigest), + # <16> + ("0001861fb61d61fd0e15793cc910cc61425fd72bc0713dcb7a1a37d5b6cf3325".toDigest, + 1u64, + "47899992244825800".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <17> + ("00018be9fb956e1447cfbfdfa4a6190c6b7a9b0f932c2fd7bce9c9ef8b994f71".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "1865db6af4731e8b7cddb0e04832a5648740449ed5a92f9805f005e9715be536".toDigest)], + @[# <0> + "F90211A00F41A498B871B7A732DB8B91A4AD4E013596110F383BF3041F2E818714FB57FBA0203149AB733DABFFB4A075E06DFBAC60A68A88CEDA23AAAE3F82A059E73A0DF4A034A036B6A6B8B596BA29F2C1EB8FAC71F21B8BC72481CA2E7C21733E39DAE8E2A0D533FA53AFE1DEFBA7E2CC6E645523F3AA1FB76651634F9EEF68D063F23FEA31A0A3AEFA0444DE17ADCF431EC4C5F057D799A2AC3A568F4C303E43561A7FCB629CA0033F5BC31420628F447A5ABC3DECF498E6852B486C07ADB3BBD5334556463BD4A0015307556953C623310A1D3A70C58D9465C6EEBBA5547DF67BDCB19CA953B3A1A09FFD78C0F9E3A550DD9D45E4C82C78A437F85640E7F86461E101578E898D3FA5A01D624ED0F8C9C98AA2FCD5D8D0AE1954B1DB998E7C79F9A98601A42F255FFCAEA049BF312A5690ACF97114EEC2F0B7BA8541624339DCC4359FA63DD98D942C7745A08045A2EA09815540255B0F0071311084C58F0A16D89CE46B1A7BD3948DFBC7BDA0F86E5DFF074F176403C0E8388587D213890A5B926AFBB521767C384DC64F321AA01AD0BEC29F16127FF644D87CAF6F4EC6FA7F4EF50FBC51AC4984D496DB2CA99BA04981C605424CD0CE7233D469C85DC60FC8D88E585C3C02B4923B184B8FCDE926A012F41CFB88E684A08727C66FEE65D20D93DB71E8508A1476A61056E967494914A059D005E9864E5F502DAF61049C7A8F862E84F2591AE3394E3066C2648239B7FE80".hexToSeqByte, + # <1> + "F90211A0B5E039DE5CD9F82A1647EE05E212486A2DDB81999B80727B76C3156B40641CA9A02569520DCDB1750D48C7060FEE64B1C16388A098648AE4FEF929D75775EBBE53A0CE915C5DE8826650CC3F49E82B307A98E87359B679965A1A7796BB50298947F4A012C8C06E4EF08F8A47BB81A9F7FDD4279A7BE096E793DC10E217F5C17D1C61D7A0A5F8FF8BADFECACB3AD5EAFF8C4EE750D33DD50A1D1CC5130D027D574F4D1A9EA00E35BB939DD5CF81DC2219FB27BAE31DFE2AEE079FDBAD367619DBDBAD276D81A0C012A282076332BB92BEEDD74CF7EF9CC36875B54D1DAAC81AC0EC9CB60CE5AEA00FB445610AA14436F057BE1BC4DBD86BDBE01490AEEC46059C710797591B7E14A0EB99BB2A00E7CC498ABD0B020276A1790628369314133F35E8089B28D55839AAA0C290D06ADA3FFA0760EDD11D57F0B32F08BF668943CE01CF651B8B29DC63A197A088BD981BF81F4F0C0B37A04542269BE7C24A5D09D455807256344C6A864925E0A08924048D8F415C57A2835DB0B33F92AD157C4173D2FF819CB32D5EF89E7060A1A083DA61DBDC26D3202C343A0168D69B0DDB0572F43C20EDB924809DBA6149BE88A01B640A922A59C6DA8860058354FD78A39D00A46E0CDF92FB6A338ED556BABFD3A01D8ACC7CA60FCD378DE1DD765B680D037620F08485ED84BAD1A5BB82702F80D0A0B00F0533277C292308F9FC4BF5A85027D68F693D70CA92F1D983A32EA7C5478480".hexToSeqByte, + # <2> + "F90211A0D126CFCCD9CC595EE94F46C23D2C203D06BBF3E628C050CC34A1B5C76965A978A0B22839972A330E8CE273187A801F7257E8655A9E728283D41DF57E408A423127A06FB243B4185E550D9CA703A47E786E55B01E47968D53BBA5600865F3E7FF739AA01297B6C416BE295952F78AABCDA3D15F6087F1976194CF881A0A55D2CF8E9B6CA0523DF620C80E8C4B48A0DBB2328226921BE949F61671707A604AA95294EE4E75A0917B77A88A260098D6BBF3600D84BA39F22B59426823A2BE8A8D0771DAFA4DA0A007C73D02889414B827DA606183D4A960AAB396777217792EC55C68D6FF29B52CA079B593B9D554CFCDB265AD20A7AD018EC97B580F62904DC1052B7BD725ACB2E1A06EB993C8B75F317A7F73825D84BFA191E041667B20D0E21D2E9EF7BF311A53ACA048484A38E04A2B0001D0D3D34D731F8149459D08987EF7293331CBCCCFB3D09BA044A3A485D94F02B53EB6BF99842F88101A9E3C42D24577720742690F584B0BCCA058E3BF550CE62F775EAE8B9A49196A78F7F8DA68874420D959925F95C2CCF553A0988D0E99F69E71C24C2F17C69B7AC3F448FD7133D624FB68F148A439048C5B2EA01A48E7BAF56999165D3E2DC392D768EA88B4820D0A4D5BD8781B60A247138B36A0DBF8593863396974608C4D47BFAB51662750421D7D431842AF55740089AA3D4CA029AD034ECF2E8054767808DDF956EE2C89720F633C3B3594881A71BC582B9A0480".hexToSeqByte, + # <3> + "F90211A0C61F01AADE2395B4E17DE4C79353729EFFAD97B46C0E425B518D4D77B75CB713A03C2D55238135DA002B914D80556FFD60890E37705E7A5487B0B52FC26DA29299A07CE74DABF9451C37485200571A867213063A8FED408C6BC72F6D84564E9F8F3EA0514930C58E952176FF4C626CED5913394B5115CA7C81A9B5B543F7858F43DC01A02D93C42DC532FE12D6CCFD735EC1F22FBD7D009D386F79E76B1F0088CB412A30A0860EB915C38B045754025B34FD796CED538071EA6B98307754124A5ABB459326A00720A388A06A78305A61D749A3895319611313C5CDBE5A396E3C30793E18C9A4A0847A9DBC85722DF97F2B46D9D95DDA6C69E6D3FA68A98FCFDCF218B26A6EF727A0C7A6A3ECE732C86186CE38D8329D2242E65D8BCC240E186FF797621B9CD75B2DA05879B4B262967609E2D8ED94D0C358B9C8A6FD1759A4AA0AD836EEA77F9C76D8A0132F0874B2E39B4E357F8654B6B6A88F306E6631FAED6C0DF223F5D18A7CD448A0504A50995DB2FACDAC157BA88A6D73CCB2DEC75FCA6424EBE45381D9DBC82151A0DABD28DEDCF493701D612D87BEE5C008C40A46D8B1A5AA7860C7B388E3026A23A021680406AF0738D3FA04C827AA8735FF8ED2C057103AA909BF84C618F63E08A8A0AD769155BAC8DC43ACC0B2FA3A6D909E8C6FD08F54D3D70452197D64D4105B8DA01E1CA8EF41EBA3606E19ECBF58499208653BAD2891AD190227DB3B8705EEE06F80".hexToSeqByte, + # <4> + "F90211A04D0B43185B88D09268323C29A0EF25E82DB4BF1E4D8533419DD390D145D2F413A0461FB3802E911FCBAB629D59E9F4603C43AC425A7398F82CB0F20A31F5D326FEA0AA4045C94006E7D21313816F3D4AC125B8D430EF6439950BCC10D57DB6D17F27A06B5A75E3CD1848BF2A4107244111F4C845A4B4355019E21F0E549A26CFAC4466A0B6C5330412A4EA9A616E3B44C6FAD6CACA452A56E10C038D2EC4754B5D89EE4EA0BD8F7BDDD0A6DF94786C9A05CF724126A7F66ADE48D740BE53AD3561882210FCA07CBEE3428088A236BFD8884EBF18E6E858DBC1EAED0DB934A62A55548B0CB6D7A05C72FD737EB2089F2EF92A0D18B53B70F0EA88844ACF378EA9310D593D2CCAD6A05716834FACDD63C05C77A84DCF7205EE2BBA8E56CFACE0E701C578E469000C46A0DC46F8ED6086FFA553BD71C12089046829BD342BE17B4AD583D149F9D87BEDDEA063DA085F6C323ACAACD7779DB176E3DDA500C57A9F0041C9020681C20EE3F211A0634A4CF830FDADAACFB4E1EE0D5B4233A8596C4DD98239CD156F256ED209E6FCA0696B3F62C2CDB6F9E5AB77A0D62C7372B208851E8C8DB4265685B2C6F4390C54A0F4C75EC4D674379A8333B284E45D0FA57C208EEC6A66707828BD46A654E1D33AA0AEAEA8DE7014BBF7A8FF12D124330F4981DE4163B821B1A6A0E1B0B17A373FAAA0ACB27F326E15F8008D8491A7C8C015C340A3DA189171D0158AC880FF2600204E80".hexToSeqByte, + # <5> + "F8F180A07622FE5C0CD5A8BABD7E8D3B2D1E51A3F24F04A7A32FFF2FFA0A8CAB49C3E87580A06EBC0C1A764B07BEC1231D6B4EF52B95063D47BA811B39AF4189EC369107ECC2A0C2DFC77F7CB353B509E92A2BD68EE39EFA8A5E6DA7456A3E7C596B1AB00B5ABB8080A06933965C85F84B45282084B118BB200C94C7BB2524B589504EA0F6EED6A6B709808080A08FF8AAE728561F1811536E2B65D16A2E4B93CCCC2929FC3D9693D3E19E9C9176A0B160A11B8F76AA77009C86912C102F4A5BF8DC86961086304DA6A650F023E79F8080A01EF147FDC253A21B46F5488946EBDC06999FFD8FFE864D2DF9A734B1FFDD3A2C80".hexToSeqByte, + # <6> + "F86C9E204E9610978C7E3C9C18442D7A979C6201F8FD47DFB72EDB1E3F11770559B84BF8498085E8D4A51000A056E81F171BCC55A6FF8345E692C0F86E5B48E01B996CADC001622FB5E363B421A0C5D2460186F7233C927E7DB2DCC703C0E500B653CA82273B7BFAD8045D85A470".hexToSeqByte, + # <7> + "F891808080A0A43C5979F9221E93708B1611109400AFF1C38EBE95C57593E9EA42A529531B2CA0E75BC6A9BD32689E88A2051B3DDEC18BFEB562ABF3460EF19F8AA3CDFF4877F080A0A58693C62A9A86BF869E2214126E93C71E84303354FA224D1AC37D486B8AAD1E80808080A007884B413D12B1185B958265DDDA93F3EBC2FD34D28AD8B9D04880E5DC241A078080808080".hexToSeqByte, + # <8> + "F8679E20E9FB956E1447CFBFDFA4A6190C6B7A9B0F932C2FD7BCE9C9EF8B994F71B846F8440180A056E81F171BCC55A6FF8345E692C0F86E5B48E01B996CADC001622FB5E363B421A01865DB6AF4731E8B7CDDB0E04832A5648740449ED5A92F9805F005E9715BE536".hexToSeqByte]) + + snapProofData7* = ( + "00018777e75094fc3451cf8ed75722b4ef18e05a00b5c0cff7b2e2cf25fbcb73".toDigest, + @[# <0> + ("00018be9fb956e1447cfbfdfa4a6190c6b7a9b0f932c2fd7bce9c9ef8b994f71".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "1865db6af4731e8b7cddb0e04832a5648740449ed5a92f9805f005e9715be536".toDigest), + # <1> + ("000190807da389c1bfb7caefb3354326cd98be5737d20b6a307d7ca4b4727061".toDigest, + 1u64, + "14106965261000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <2> + ("000195a4b660e74b8913ced202e3d2e27d53e6bf2979aadb07ed10da693dbd4f".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "62b4028a53d2d7378c18c69e519cab4b96eebb339fa039d1b0d7eefe5a9c6363".toDigest), + # <3> + ("000195a70bebaba887344fa6dc160b527beb5868a4644b06f684b4f5ce5a3d2f".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "51a18c6f6f98c4ec00f9d9ab8655497698311ead2d4f6cf542fa123641896cf5".toDigest), + # <4> + ("000199b6b1e5ac6f4778cf504233c95e70490eaffce2a29612924b0a04f2ab44".toDigest, + 1u64, + "32091499934000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <5> + ("00019db59bade3091bb4ea9f16e2374de64eb80c71cd3b9527d6f754abfd5057".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "7b7e01b1062c9272df150db2cf701f7bd42d0f244ce8d52c8d8cba260b6c7ef4".toDigest), + # <6> + ("0001a0bd035673870f72c01272c0f13bf64e472b33725c21ca4f5051b39abc11".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <7> + ("0001a2cd97667926858d25475f5cadf93ef44126f6ab99c0f68425154bf5eeec".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "f935747b13ae488350c78881f1c7ed4156d42364e164d5c6b28c44f5079011e3".toDigest), + # <8> + ("0001a3f30fea4b601dacba43975c9bb26306ddaa64687bc46460f2fef163dcad".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "875191faaccab052ffb33984e8ba823d46be31b93a9127447e8d332f8dedaca9".toDigest), + # <9> + ("0001a7d68e58421628afe8ee8e62d21abb1306f91382837b085980bc5d432abf".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "41338751fff7bd292d9fd9ec80be640c8a957715449b410ba06784cd9a1fcb22".toDigest), + # <10> + ("0001a85e6590cca6bbc13cfba5bb8fae5d4f318a99af4624d4025a423d18edcd".toDigest, + 2u64, + "16277128074565".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <11> + ("0001a941be03b1a76f0471cb83609d483c5284cfc82da3d7d1c8958858db3454".toDigest, + 10u64, + "12401801043594354996".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <12> + ("0001ab3b86331058dd66d15349a11ad1d3e3c79c8f29855a4e0992098096ab6e".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <13> + ("0001b2563114789fcf1ae618b8778e7ef2400cc54ada9f24fd070722c31aca21".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <14> + ("0001b6a6d3508eac0053d7f8e0b70d459d99cf41aeb3fa3d20c98b1f456cff4c".toDigest, + 2u64, + "61959175552240".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <15> + ("0001b7553bfd411556a334d73be26c241b45f4fc7b7e337dea8904f207146ec7".toDigest, + 0u64, + "210000000000000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <16> + ("0001b989dc49b9df17e95a9e1e50870aabf88e21b9d4cd4811b5507dad53517c".toDigest, + 11u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <17> + ("0001c06e59a877d5503e036ae43b720ed1f6a9cfa78b51b0a25ea1682c35597e".toDigest, + 1u64, + "0".parse(Uint256), + "3a427292fef8ae0341774b67b29678c6a33e66eb60970f912be4393658a36569".toDigest, + "728cd215210d7fb0e0af1d788f8dacc6ad82e5ee37b3c0a1fff2c997261b3d56".toDigest)], + @[# <0> + "F90211A00F41A498B871B7A732DB8B91A4AD4E013596110F383BF3041F2E818714FB57FBA0203149AB733DABFFB4A075E06DFBAC60A68A88CEDA23AAAE3F82A059E73A0DF4A034A036B6A6B8B596BA29F2C1EB8FAC71F21B8BC72481CA2E7C21733E39DAE8E2A0D533FA53AFE1DEFBA7E2CC6E645523F3AA1FB76651634F9EEF68D063F23FEA31A0A3AEFA0444DE17ADCF431EC4C5F057D799A2AC3A568F4C303E43561A7FCB629CA0033F5BC31420628F447A5ABC3DECF498E6852B486C07ADB3BBD5334556463BD4A0015307556953C623310A1D3A70C58D9465C6EEBBA5547DF67BDCB19CA953B3A1A09FFD78C0F9E3A550DD9D45E4C82C78A437F85640E7F86461E101578E898D3FA5A01D624ED0F8C9C98AA2FCD5D8D0AE1954B1DB998E7C79F9A98601A42F255FFCAEA049BF312A5690ACF97114EEC2F0B7BA8541624339DCC4359FA63DD98D942C7745A08045A2EA09815540255B0F0071311084C58F0A16D89CE46B1A7BD3948DFBC7BDA0F86E5DFF074F176403C0E8388587D213890A5B926AFBB521767C384DC64F321AA01AD0BEC29F16127FF644D87CAF6F4EC6FA7F4EF50FBC51AC4984D496DB2CA99BA04981C605424CD0CE7233D469C85DC60FC8D88E585C3C02B4923B184B8FCDE926A012F41CFB88E684A08727C66FEE65D20D93DB71E8508A1476A61056E967494914A059D005E9864E5F502DAF61049C7A8F862E84F2591AE3394E3066C2648239B7FE80".hexToSeqByte, + # <1> + "F90211A0B5E039DE5CD9F82A1647EE05E212486A2DDB81999B80727B76C3156B40641CA9A02569520DCDB1750D48C7060FEE64B1C16388A098648AE4FEF929D75775EBBE53A0CE915C5DE8826650CC3F49E82B307A98E87359B679965A1A7796BB50298947F4A012C8C06E4EF08F8A47BB81A9F7FDD4279A7BE096E793DC10E217F5C17D1C61D7A0A5F8FF8BADFECACB3AD5EAFF8C4EE750D33DD50A1D1CC5130D027D574F4D1A9EA00E35BB939DD5CF81DC2219FB27BAE31DFE2AEE079FDBAD367619DBDBAD276D81A0C012A282076332BB92BEEDD74CF7EF9CC36875B54D1DAAC81AC0EC9CB60CE5AEA00FB445610AA14436F057BE1BC4DBD86BDBE01490AEEC46059C710797591B7E14A0EB99BB2A00E7CC498ABD0B020276A1790628369314133F35E8089B28D55839AAA0C290D06ADA3FFA0760EDD11D57F0B32F08BF668943CE01CF651B8B29DC63A197A088BD981BF81F4F0C0B37A04542269BE7C24A5D09D455807256344C6A864925E0A08924048D8F415C57A2835DB0B33F92AD157C4173D2FF819CB32D5EF89E7060A1A083DA61DBDC26D3202C343A0168D69B0DDB0572F43C20EDB924809DBA6149BE88A01B640A922A59C6DA8860058354FD78A39D00A46E0CDF92FB6A338ED556BABFD3A01D8ACC7CA60FCD378DE1DD765B680D037620F08485ED84BAD1A5BB82702F80D0A0B00F0533277C292308F9FC4BF5A85027D68F693D70CA92F1D983A32EA7C5478480".hexToSeqByte, + # <2> + "F90211A0D126CFCCD9CC595EE94F46C23D2C203D06BBF3E628C050CC34A1B5C76965A978A0B22839972A330E8CE273187A801F7257E8655A9E728283D41DF57E408A423127A06FB243B4185E550D9CA703A47E786E55B01E47968D53BBA5600865F3E7FF739AA01297B6C416BE295952F78AABCDA3D15F6087F1976194CF881A0A55D2CF8E9B6CA0523DF620C80E8C4B48A0DBB2328226921BE949F61671707A604AA95294EE4E75A0917B77A88A260098D6BBF3600D84BA39F22B59426823A2BE8A8D0771DAFA4DA0A007C73D02889414B827DA606183D4A960AAB396777217792EC55C68D6FF29B52CA079B593B9D554CFCDB265AD20A7AD018EC97B580F62904DC1052B7BD725ACB2E1A06EB993C8B75F317A7F73825D84BFA191E041667B20D0E21D2E9EF7BF311A53ACA048484A38E04A2B0001D0D3D34D731F8149459D08987EF7293331CBCCCFB3D09BA044A3A485D94F02B53EB6BF99842F88101A9E3C42D24577720742690F584B0BCCA058E3BF550CE62F775EAE8B9A49196A78F7F8DA68874420D959925F95C2CCF553A0988D0E99F69E71C24C2F17C69B7AC3F448FD7133D624FB68F148A439048C5B2EA01A48E7BAF56999165D3E2DC392D768EA88B4820D0A4D5BD8781B60A247138B36A0DBF8593863396974608C4D47BFAB51662750421D7D431842AF55740089AA3D4CA029AD034ECF2E8054767808DDF956EE2C89720F633C3B3594881A71BC582B9A0480".hexToSeqByte, + # <3> + "F90211A0C61F01AADE2395B4E17DE4C79353729EFFAD97B46C0E425B518D4D77B75CB713A03C2D55238135DA002B914D80556FFD60890E37705E7A5487B0B52FC26DA29299A07CE74DABF9451C37485200571A867213063A8FED408C6BC72F6D84564E9F8F3EA0514930C58E952176FF4C626CED5913394B5115CA7C81A9B5B543F7858F43DC01A02D93C42DC532FE12D6CCFD735EC1F22FBD7D009D386F79E76B1F0088CB412A30A0860EB915C38B045754025B34FD796CED538071EA6B98307754124A5ABB459326A00720A388A06A78305A61D749A3895319611313C5CDBE5A396E3C30793E18C9A4A0847A9DBC85722DF97F2B46D9D95DDA6C69E6D3FA68A98FCFDCF218B26A6EF727A0C7A6A3ECE732C86186CE38D8329D2242E65D8BCC240E186FF797621B9CD75B2DA05879B4B262967609E2D8ED94D0C358B9C8A6FD1759A4AA0AD836EEA77F9C76D8A0132F0874B2E39B4E357F8654B6B6A88F306E6631FAED6C0DF223F5D18A7CD448A0504A50995DB2FACDAC157BA88A6D73CCB2DEC75FCA6424EBE45381D9DBC82151A0DABD28DEDCF493701D612D87BEE5C008C40A46D8B1A5AA7860C7B388E3026A23A021680406AF0738D3FA04C827AA8735FF8ED2C057103AA909BF84C618F63E08A8A0AD769155BAC8DC43ACC0B2FA3A6D909E8C6FD08F54D3D70452197D64D4105B8DA01E1CA8EF41EBA3606E19ECBF58499208653BAD2891AD190227DB3B8705EEE06F80".hexToSeqByte, + # <4> + "F90211A04D0B43185B88D09268323C29A0EF25E82DB4BF1E4D8533419DD390D145D2F413A0461FB3802E911FCBAB629D59E9F4603C43AC425A7398F82CB0F20A31F5D326FEA0AA4045C94006E7D21313816F3D4AC125B8D430EF6439950BCC10D57DB6D17F27A06B5A75E3CD1848BF2A4107244111F4C845A4B4355019E21F0E549A26CFAC4466A0B6C5330412A4EA9A616E3B44C6FAD6CACA452A56E10C038D2EC4754B5D89EE4EA0BD8F7BDDD0A6DF94786C9A05CF724126A7F66ADE48D740BE53AD3561882210FCA07CBEE3428088A236BFD8884EBF18E6E858DBC1EAED0DB934A62A55548B0CB6D7A05C72FD737EB2089F2EF92A0D18B53B70F0EA88844ACF378EA9310D593D2CCAD6A05716834FACDD63C05C77A84DCF7205EE2BBA8E56CFACE0E701C578E469000C46A0DC46F8ED6086FFA553BD71C12089046829BD342BE17B4AD583D149F9D87BEDDEA063DA085F6C323ACAACD7779DB176E3DDA500C57A9F0041C9020681C20EE3F211A0634A4CF830FDADAACFB4E1EE0D5B4233A8596C4DD98239CD156F256ED209E6FCA0696B3F62C2CDB6F9E5AB77A0D62C7372B208851E8C8DB4265685B2C6F4390C54A0F4C75EC4D674379A8333B284E45D0FA57C208EEC6A66707828BD46A654E1D33AA0AEAEA8DE7014BBF7A8FF12D124330F4981DE4163B821B1A6A0E1B0B17A373FAAA0ACB27F326E15F8008D8491A7C8C015C340A3DA189171D0158AC880FF2600204E80".hexToSeqByte, + # <5> + "F891808080A0A43C5979F9221E93708B1611109400AFF1C38EBE95C57593E9EA42A529531B2CA0E75BC6A9BD32689E88A2051B3DDEC18BFEB562ABF3460EF19F8AA3CDFF4877F080A0A58693C62A9A86BF869E2214126E93C71E84303354FA224D1AC37D486B8AAD1E80808080A007884B413D12B1185B958265DDDA93F3EBC2FD34D28AD8B9D04880E5DC241A078080808080".hexToSeqByte, + # <6> + "F90111A0A0BA84BC6FBDA7B8F39FD36518A66E9179F567DE55BA8CB9B2A307ABDBF68662A0D2D66F1399411B593E54A0640803A0244A6594F4FA80A288F9E140F61B39D774A06C52D3C0FBFA5048A6BAB9785C3FDEB58CDAC6CC5EF0A84864E270AD090FD79FA07D857DD1A93B8D76A3E734C1B7D1C4EB3367AC827DFD7D67211126AEC1391AD7A06FD927DCEFDC76284FFB5929C636A9C5629CF00F8D6C0DDE50D07FBAD885E48680A0832143115A093B2F15B821A0A333F773AFCB772939355764CE7F457A5F5D794980A06054EBF751D931EC2EFDA53BF669CC4E3AF20D2B69079A9934A2A6AA0DC88F58808080A0BEF01492658EFE2DB30CC8BE35841A26D1E03B3CF358343B2E7E8E8ECA7C1BBC80808080".hexToSeqByte, + # <7> + "F8679E206E59A877D5503E036AE43B720ED1F6A9CFA78B51B0A25EA1682C35597EB846F8440180A03A427292FEF8AE0341774B67B29678C6A33E66EB60970F912BE4393658A36569A0728CD215210D7FB0E0AF1D788F8DACC6AD82E5EE37B3C0A1FFF2C997261B3D56".hexToSeqByte]) + + snapProofData8* = ( + "0001bf647612f369605d7f7eacf5de85a38a24f9256200edad5eba114ffb31a8".toDigest, + @[# <0> + ("0001c06e59a877d5503e036ae43b720ed1f6a9cfa78b51b0a25ea1682c35597e".toDigest, + 1u64, + "0".parse(Uint256), + "3a427292fef8ae0341774b67b29678c6a33e66eb60970f912be4393658a36569".toDigest, + "728cd215210d7fb0e0af1d788f8dacc6ad82e5ee37b3c0a1fff2c997261b3d56".toDigest), + # <1> + ("0001c1125d56224eeee4039e8cc5fe7b9524cb37fea1eba50a4298d114f5a808".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <2> + ("0001c240b874011115add6922058ce969d2fbbfaf1dfbaee78a075a7da35368a".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "875191faaccab052ffb33984e8ba823d46be31b93a9127447e8d332f8dedaca9".toDigest), + # <3> + ("0001c3cd5b483deea5d1b079a01bc3d5d78c17fb1b3a846ff8889e46dc9f6e1c".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <4> + ("0001c45b16ecc2599c3d75157774a50340bc83bf2346adf9d8ac892df9cd368b".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <5> + ("0001c6b485c0ce8b5277c4032ba8cf92179a3e5c443d7bb66556a9fcf46c530b".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "7b7e01b1062c9272df150db2cf701f7bd42d0f244ce8d52c8d8cba260b6c7ef4".toDigest), + # <6> + ("0001c81e5cfee4bd2cfc3d0206e97da89ec512c9e178b623564918c2c0c89f41".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "7e67423a439f63f52d39b5aca729657d5a281c576689256e7aed711c7b9bdb61".toDigest), + # <7> + ("0001cc4e0dd967b968c0580163b0407746b0fda73c07b89f785d7d5953398a48".toDigest, + 1u64, + "16678835345000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <8> + ("0001d074e0b5a2deb7fc8e2febb5958da6a15e25d56c26396c8d1697a38053c0".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "f27fd7834c37d24e90b060d669400553b81221e018d0f5f0a40530a97e63c375".toDigest), + # <9> + ("0001d5fb206805a6c4cbf0c5c5a17812b9234db441376dd02823620f2ee3c9a2".toDigest, + 1u64, + "47899999999853000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <10> + ("0001de1918b401de79b34094831151a039ea6f3931074e1c554c1aef6c5a73d6".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <11> + ("0001e0239da1432561b2d832cd208467b087a2fcf83d196036743af072fe5e92".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "2f851dfc3afdb1e1a5e902da81c745607b2aca2e8722031379aaff3e1e7e7cf8".toDigest), + # <12> + ("0001e6c2b441ccd54d74c7678823e26b6ca6ba7a85659cf4537dba0f0d48932b".toDigest, + 0u64, + "1".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <13> + ("0001e70af19680bf20c42c4aac083804d79d646a7de488f4281193882c8af5f0".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "55d296451f428597536fe83995c5c7880f90ff7cc8d89eb903304982d2be74bc".toDigest), + # <14> + ("0001e89e1094bcb30ed494aa87aa07e3111cdf8025d4c6484c1d121b50c0bd1b".toDigest, + 2u64, + "16277498533990".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <15> + ("0001e9551dd914cd3ab347adfc7353a027d11113dc5e03e246fbafb395766c0e".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <16> + ("0001ec7c2d6473ad314ab56c379cb4490c80cf24ee0b30f706cfc583665ad38a".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <17> + ("0001eeb18b7bccddd821316cbaadb8cd33a55dfc043de75d54b5e385a3730e4c".toDigest, + 1u64, + "0".parse(Uint256), + "c1e342b8870c3932e659251de13b6ae97461023672b8e488c1287c3164048618".toDigest, + "8a0c9017e6ef5d552ec36e1a772b70d6c066432aacc2c619ac16cca7de079321".toDigest), + # <18> + ("0001fa8e04d3719cfe710f668d23aaa984c400761ea835807a90d570799d8e67".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest)], + @[# <0> + "F90211A00F41A498B871B7A732DB8B91A4AD4E013596110F383BF3041F2E818714FB57FBA0203149AB733DABFFB4A075E06DFBAC60A68A88CEDA23AAAE3F82A059E73A0DF4A034A036B6A6B8B596BA29F2C1EB8FAC71F21B8BC72481CA2E7C21733E39DAE8E2A0D533FA53AFE1DEFBA7E2CC6E645523F3AA1FB76651634F9EEF68D063F23FEA31A0A3AEFA0444DE17ADCF431EC4C5F057D799A2AC3A568F4C303E43561A7FCB629CA0033F5BC31420628F447A5ABC3DECF498E6852B486C07ADB3BBD5334556463BD4A0015307556953C623310A1D3A70C58D9465C6EEBBA5547DF67BDCB19CA953B3A1A09FFD78C0F9E3A550DD9D45E4C82C78A437F85640E7F86461E101578E898D3FA5A01D624ED0F8C9C98AA2FCD5D8D0AE1954B1DB998E7C79F9A98601A42F255FFCAEA049BF312A5690ACF97114EEC2F0B7BA8541624339DCC4359FA63DD98D942C7745A08045A2EA09815540255B0F0071311084C58F0A16D89CE46B1A7BD3948DFBC7BDA0F86E5DFF074F176403C0E8388587D213890A5B926AFBB521767C384DC64F321AA01AD0BEC29F16127FF644D87CAF6F4EC6FA7F4EF50FBC51AC4984D496DB2CA99BA04981C605424CD0CE7233D469C85DC60FC8D88E585C3C02B4923B184B8FCDE926A012F41CFB88E684A08727C66FEE65D20D93DB71E8508A1476A61056E967494914A059D005E9864E5F502DAF61049C7A8F862E84F2591AE3394E3066C2648239B7FE80".hexToSeqByte, + # <1> + "F90211A0B5E039DE5CD9F82A1647EE05E212486A2DDB81999B80727B76C3156B40641CA9A02569520DCDB1750D48C7060FEE64B1C16388A098648AE4FEF929D75775EBBE53A0CE915C5DE8826650CC3F49E82B307A98E87359B679965A1A7796BB50298947F4A012C8C06E4EF08F8A47BB81A9F7FDD4279A7BE096E793DC10E217F5C17D1C61D7A0A5F8FF8BADFECACB3AD5EAFF8C4EE750D33DD50A1D1CC5130D027D574F4D1A9EA00E35BB939DD5CF81DC2219FB27BAE31DFE2AEE079FDBAD367619DBDBAD276D81A0C012A282076332BB92BEEDD74CF7EF9CC36875B54D1DAAC81AC0EC9CB60CE5AEA00FB445610AA14436F057BE1BC4DBD86BDBE01490AEEC46059C710797591B7E14A0EB99BB2A00E7CC498ABD0B020276A1790628369314133F35E8089B28D55839AAA0C290D06ADA3FFA0760EDD11D57F0B32F08BF668943CE01CF651B8B29DC63A197A088BD981BF81F4F0C0B37A04542269BE7C24A5D09D455807256344C6A864925E0A08924048D8F415C57A2835DB0B33F92AD157C4173D2FF819CB32D5EF89E7060A1A083DA61DBDC26D3202C343A0168D69B0DDB0572F43C20EDB924809DBA6149BE88A01B640A922A59C6DA8860058354FD78A39D00A46E0CDF92FB6A338ED556BABFD3A01D8ACC7CA60FCD378DE1DD765B680D037620F08485ED84BAD1A5BB82702F80D0A0B00F0533277C292308F9FC4BF5A85027D68F693D70CA92F1D983A32EA7C5478480".hexToSeqByte, + # <2> + "F90211A0D126CFCCD9CC595EE94F46C23D2C203D06BBF3E628C050CC34A1B5C76965A978A0B22839972A330E8CE273187A801F7257E8655A9E728283D41DF57E408A423127A06FB243B4185E550D9CA703A47E786E55B01E47968D53BBA5600865F3E7FF739AA01297B6C416BE295952F78AABCDA3D15F6087F1976194CF881A0A55D2CF8E9B6CA0523DF620C80E8C4B48A0DBB2328226921BE949F61671707A604AA95294EE4E75A0917B77A88A260098D6BBF3600D84BA39F22B59426823A2BE8A8D0771DAFA4DA0A007C73D02889414B827DA606183D4A960AAB396777217792EC55C68D6FF29B52CA079B593B9D554CFCDB265AD20A7AD018EC97B580F62904DC1052B7BD725ACB2E1A06EB993C8B75F317A7F73825D84BFA191E041667B20D0E21D2E9EF7BF311A53ACA048484A38E04A2B0001D0D3D34D731F8149459D08987EF7293331CBCCCFB3D09BA044A3A485D94F02B53EB6BF99842F88101A9E3C42D24577720742690F584B0BCCA058E3BF550CE62F775EAE8B9A49196A78F7F8DA68874420D959925F95C2CCF553A0988D0E99F69E71C24C2F17C69B7AC3F448FD7133D624FB68F148A439048C5B2EA01A48E7BAF56999165D3E2DC392D768EA88B4820D0A4D5BD8781B60A247138B36A0DBF8593863396974608C4D47BFAB51662750421D7D431842AF55740089AA3D4CA029AD034ECF2E8054767808DDF956EE2C89720F633C3B3594881A71BC582B9A0480".hexToSeqByte, + # <3> + "F90211A0C61F01AADE2395B4E17DE4C79353729EFFAD97B46C0E425B518D4D77B75CB713A03C2D55238135DA002B914D80556FFD60890E37705E7A5487B0B52FC26DA29299A07CE74DABF9451C37485200571A867213063A8FED408C6BC72F6D84564E9F8F3EA0514930C58E952176FF4C626CED5913394B5115CA7C81A9B5B543F7858F43DC01A02D93C42DC532FE12D6CCFD735EC1F22FBD7D009D386F79E76B1F0088CB412A30A0860EB915C38B045754025B34FD796CED538071EA6B98307754124A5ABB459326A00720A388A06A78305A61D749A3895319611313C5CDBE5A396E3C30793E18C9A4A0847A9DBC85722DF97F2B46D9D95DDA6C69E6D3FA68A98FCFDCF218B26A6EF727A0C7A6A3ECE732C86186CE38D8329D2242E65D8BCC240E186FF797621B9CD75B2DA05879B4B262967609E2D8ED94D0C358B9C8A6FD1759A4AA0AD836EEA77F9C76D8A0132F0874B2E39B4E357F8654B6B6A88F306E6631FAED6C0DF223F5D18A7CD448A0504A50995DB2FACDAC157BA88A6D73CCB2DEC75FCA6424EBE45381D9DBC82151A0DABD28DEDCF493701D612D87BEE5C008C40A46D8B1A5AA7860C7B388E3026A23A021680406AF0738D3FA04C827AA8735FF8ED2C057103AA909BF84C618F63E08A8A0AD769155BAC8DC43ACC0B2FA3A6D909E8C6FD08F54D3D70452197D64D4105B8DA01E1CA8EF41EBA3606E19ECBF58499208653BAD2891AD190227DB3B8705EEE06F80".hexToSeqByte, + # <4> + "F90211A04D0B43185B88D09268323C29A0EF25E82DB4BF1E4D8533419DD390D145D2F413A0461FB3802E911FCBAB629D59E9F4603C43AC425A7398F82CB0F20A31F5D326FEA0AA4045C94006E7D21313816F3D4AC125B8D430EF6439950BCC10D57DB6D17F27A06B5A75E3CD1848BF2A4107244111F4C845A4B4355019E21F0E549A26CFAC4466A0B6C5330412A4EA9A616E3B44C6FAD6CACA452A56E10C038D2EC4754B5D89EE4EA0BD8F7BDDD0A6DF94786C9A05CF724126A7F66ADE48D740BE53AD3561882210FCA07CBEE3428088A236BFD8884EBF18E6E858DBC1EAED0DB934A62A55548B0CB6D7A05C72FD737EB2089F2EF92A0D18B53B70F0EA88844ACF378EA9310D593D2CCAD6A05716834FACDD63C05C77A84DCF7205EE2BBA8E56CFACE0E701C578E469000C46A0DC46F8ED6086FFA553BD71C12089046829BD342BE17B4AD583D149F9D87BEDDEA063DA085F6C323ACAACD7779DB176E3DDA500C57A9F0041C9020681C20EE3F211A0634A4CF830FDADAACFB4E1EE0D5B4233A8596C4DD98239CD156F256ED209E6FCA0696B3F62C2CDB6F9E5AB77A0D62C7372B208851E8C8DB4265685B2C6F4390C54A0F4C75EC4D674379A8333B284E45D0FA57C208EEC6A66707828BD46A654E1D33AA0AEAEA8DE7014BBF7A8FF12D124330F4981DE4163B821B1A6A0E1B0B17A373FAAA0ACB27F326E15F8008D8491A7C8C015C340A3DA189171D0158AC880FF2600204E80".hexToSeqByte, + # <5> + "F8918080A0840809AF09F4F3FE69F9DC657FFC70EDD57AAB73FCE260A30F8A02CE6773FAB1808080A0B26FC24BE88A0DD1AE07F3C5FA24C48389CAEBC7B20FBB53D1EBC291975E5C8DA0BECAFA84D0F2D5EB832E852201E09BF789D913DFF5996B095413E2AF7DCBB85280A0BA4160DD08B1FBF97F12B1A0CEE2F9FA7AA908488FA463ABED2C84D4F768F8B480808080808080".hexToSeqByte, + # <6> + "F85180808080808080808080A06737AAE4DA8E09B04472E1731B2727A39E390D28D1BB544A39CAFD769F47825E80808080A02A398B06DE329FC5D0900308F0D2F8B7A086EB2B97724243C633654DF73419C380".hexToSeqByte, + # <7> + "F8679E208E04D3719CFE710F668D23AAA984C400761EA835807A90D570799D8E67B846F8440180A056E81F171BCC55A6FF8345E692C0F86E5B48E01B996CADC001622FB5E363B421A0D8990879C8C8E2B683D4F0FC5A76889AEEBA15181AE48F09FBF37ED182286300".hexToSeqByte]) + + snapProofData9* = ( + "0001f75104d551d68c692f6e82949a5657fb69984a0e410b630a915379fa97dd".toDigest, + @[# <0> + ("0001fa8e04d3719cfe710f668d23aaa984c400761ea835807a90d570799d8e67".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <1> + ("0001ff735cf03369336036f289faa4bb01b667044cc3e0ff041c3db7147f2365".toDigest, + 1u64, + "0".parse(Uint256), + "1d40ed4738c1b04df8bd8fd102ccde2f4a62f8803134a01d124da5c2389af5b1".toDigest, + "4e3c65796be34e5a3b67459453315d4367389de0dd041a33c53f2ae90870ed76".toDigest), + # <2> + ("00021333166090cc52615df1bb245953eb2466bd9d67d3fcb37354fa660ec194".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <3> + ("000215501088799911655b089a90137feb5db3c0a0f7a6e1a8602745b84ab41a".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "6a2e0df8064ab5e7f8d264a65efdcccf2af975e2ad5028b08004d4585cbb0ef9".toDigest), + # <4> + ("000217a3faa0aea5315d6ecdb9ef46589f907bc2d5f407af21b0aaad36240f14".toDigest, + 1u64, + "47818243958968500".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <5> + ("000217be005cdbcc3ea6deed0c694ab3cb908a231bd0f8437acdd38fd7621cac".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "5af53ff5426a115490bbbdc1d68bf0c185b8afaac22f7a08963851e1ffdf754c".toDigest), + # <6> + ("000217c6bae06ba416bd27fbadbffaa044c98f66a2bad8f3d8fc83335c77a7a8".toDigest, + 1u64, + "0".parse(Uint256), + "7b0d6a72718ddccba299834332305f58ca729db83c23f452365807efab46c37a".toDigest, + "af974539a8d1b7b250b247757abb3141a15893895d31f783046a40fa0f13088d".toDigest), + # <7> + ("00021890cbbd67c832b5dc0a10422d0c78dc412067c6ce694a168e3810f361f3".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <8> + ("00021ac956f0a3c39b4bd8e41edb973bf9c10f8c4f59a1a2df5227cfe73a92ae".toDigest, + 2u64, + "14957692556500".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <9> + ("00021c5cd1dd80c78ec9e6da53922bbbe3b91705bbd8a0cda1f1d157b9cf017f".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <10> + ("000223fe9ca9dc994af730e2e1f2747f83c22a9aea52c169ef18f87658a8b8c1".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <11> + ("00022e9d6a4132122065f96b7e8e14c546735bfd473ff0b795f45ff33c77eb83".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <12> + ("0002355d781cfddabcbf50a6f7c2ee0dbf4f0f1f5f49e97f867983511822efe9".toDigest, + 0u64, + "1000000000000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest)], + @[# <0> + "F90211A00F41A498B871B7A732DB8B91A4AD4E013596110F383BF3041F2E818714FB57FBA0203149AB733DABFFB4A075E06DFBAC60A68A88CEDA23AAAE3F82A059E73A0DF4A034A036B6A6B8B596BA29F2C1EB8FAC71F21B8BC72481CA2E7C21733E39DAE8E2A0D533FA53AFE1DEFBA7E2CC6E645523F3AA1FB76651634F9EEF68D063F23FEA31A0A3AEFA0444DE17ADCF431EC4C5F057D799A2AC3A568F4C303E43561A7FCB629CA0033F5BC31420628F447A5ABC3DECF498E6852B486C07ADB3BBD5334556463BD4A0015307556953C623310A1D3A70C58D9465C6EEBBA5547DF67BDCB19CA953B3A1A09FFD78C0F9E3A550DD9D45E4C82C78A437F85640E7F86461E101578E898D3FA5A01D624ED0F8C9C98AA2FCD5D8D0AE1954B1DB998E7C79F9A98601A42F255FFCAEA049BF312A5690ACF97114EEC2F0B7BA8541624339DCC4359FA63DD98D942C7745A08045A2EA09815540255B0F0071311084C58F0A16D89CE46B1A7BD3948DFBC7BDA0F86E5DFF074F176403C0E8388587D213890A5B926AFBB521767C384DC64F321AA01AD0BEC29F16127FF644D87CAF6F4EC6FA7F4EF50FBC51AC4984D496DB2CA99BA04981C605424CD0CE7233D469C85DC60FC8D88E585C3C02B4923B184B8FCDE926A012F41CFB88E684A08727C66FEE65D20D93DB71E8508A1476A61056E967494914A059D005E9864E5F502DAF61049C7A8F862E84F2591AE3394E3066C2648239B7FE80".hexToSeqByte, + # <1> + "F90211A0B5E039DE5CD9F82A1647EE05E212486A2DDB81999B80727B76C3156B40641CA9A02569520DCDB1750D48C7060FEE64B1C16388A098648AE4FEF929D75775EBBE53A0CE915C5DE8826650CC3F49E82B307A98E87359B679965A1A7796BB50298947F4A012C8C06E4EF08F8A47BB81A9F7FDD4279A7BE096E793DC10E217F5C17D1C61D7A0A5F8FF8BADFECACB3AD5EAFF8C4EE750D33DD50A1D1CC5130D027D574F4D1A9EA00E35BB939DD5CF81DC2219FB27BAE31DFE2AEE079FDBAD367619DBDBAD276D81A0C012A282076332BB92BEEDD74CF7EF9CC36875B54D1DAAC81AC0EC9CB60CE5AEA00FB445610AA14436F057BE1BC4DBD86BDBE01490AEEC46059C710797591B7E14A0EB99BB2A00E7CC498ABD0B020276A1790628369314133F35E8089B28D55839AAA0C290D06ADA3FFA0760EDD11D57F0B32F08BF668943CE01CF651B8B29DC63A197A088BD981BF81F4F0C0B37A04542269BE7C24A5D09D455807256344C6A864925E0A08924048D8F415C57A2835DB0B33F92AD157C4173D2FF819CB32D5EF89E7060A1A083DA61DBDC26D3202C343A0168D69B0DDB0572F43C20EDB924809DBA6149BE88A01B640A922A59C6DA8860058354FD78A39D00A46E0CDF92FB6A338ED556BABFD3A01D8ACC7CA60FCD378DE1DD765B680D037620F08485ED84BAD1A5BB82702F80D0A0B00F0533277C292308F9FC4BF5A85027D68F693D70CA92F1D983A32EA7C5478480".hexToSeqByte, + # <2> + "F90211A0D126CFCCD9CC595EE94F46C23D2C203D06BBF3E628C050CC34A1B5C76965A978A0B22839972A330E8CE273187A801F7257E8655A9E728283D41DF57E408A423127A06FB243B4185E550D9CA703A47E786E55B01E47968D53BBA5600865F3E7FF739AA01297B6C416BE295952F78AABCDA3D15F6087F1976194CF881A0A55D2CF8E9B6CA0523DF620C80E8C4B48A0DBB2328226921BE949F61671707A604AA95294EE4E75A0917B77A88A260098D6BBF3600D84BA39F22B59426823A2BE8A8D0771DAFA4DA0A007C73D02889414B827DA606183D4A960AAB396777217792EC55C68D6FF29B52CA079B593B9D554CFCDB265AD20A7AD018EC97B580F62904DC1052B7BD725ACB2E1A06EB993C8B75F317A7F73825D84BFA191E041667B20D0E21D2E9EF7BF311A53ACA048484A38E04A2B0001D0D3D34D731F8149459D08987EF7293331CBCCCFB3D09BA044A3A485D94F02B53EB6BF99842F88101A9E3C42D24577720742690F584B0BCCA058E3BF550CE62F775EAE8B9A49196A78F7F8DA68874420D959925F95C2CCF553A0988D0E99F69E71C24C2F17C69B7AC3F448FD7133D624FB68F148A439048C5B2EA01A48E7BAF56999165D3E2DC392D768EA88B4820D0A4D5BD8781B60A247138B36A0DBF8593863396974608C4D47BFAB51662750421D7D431842AF55740089AA3D4CA029AD034ECF2E8054767808DDF956EE2C89720F633C3B3594881A71BC582B9A0480".hexToSeqByte, + # <3> + "F90211A0C61F01AADE2395B4E17DE4C79353729EFFAD97B46C0E425B518D4D77B75CB713A03C2D55238135DA002B914D80556FFD60890E37705E7A5487B0B52FC26DA29299A07CE74DABF9451C37485200571A867213063A8FED408C6BC72F6D84564E9F8F3EA0514930C58E952176FF4C626CED5913394B5115CA7C81A9B5B543F7858F43DC01A02D93C42DC532FE12D6CCFD735EC1F22FBD7D009D386F79E76B1F0088CB412A30A0860EB915C38B045754025B34FD796CED538071EA6B98307754124A5ABB459326A00720A388A06A78305A61D749A3895319611313C5CDBE5A396E3C30793E18C9A4A0847A9DBC85722DF97F2B46D9D95DDA6C69E6D3FA68A98FCFDCF218B26A6EF727A0C7A6A3ECE732C86186CE38D8329D2242E65D8BCC240E186FF797621B9CD75B2DA05879B4B262967609E2D8ED94D0C358B9C8A6FD1759A4AA0AD836EEA77F9C76D8A0132F0874B2E39B4E357F8654B6B6A88F306E6631FAED6C0DF223F5D18A7CD448A0504A50995DB2FACDAC157BA88A6D73CCB2DEC75FCA6424EBE45381D9DBC82151A0DABD28DEDCF493701D612D87BEE5C008C40A46D8B1A5AA7860C7B388E3026A23A021680406AF0738D3FA04C827AA8735FF8ED2C057103AA909BF84C618F63E08A8A0AD769155BAC8DC43ACC0B2FA3A6D909E8C6FD08F54D3D70452197D64D4105B8DA01E1CA8EF41EBA3606E19ECBF58499208653BAD2891AD190227DB3B8705EEE06F80".hexToSeqByte, + # <4> + "F90211A04D0B43185B88D09268323C29A0EF25E82DB4BF1E4D8533419DD390D145D2F413A0461FB3802E911FCBAB629D59E9F4603C43AC425A7398F82CB0F20A31F5D326FEA0AA4045C94006E7D21313816F3D4AC125B8D430EF6439950BCC10D57DB6D17F27A06B5A75E3CD1848BF2A4107244111F4C845A4B4355019E21F0E549A26CFAC4466A0B6C5330412A4EA9A616E3B44C6FAD6CACA452A56E10C038D2EC4754B5D89EE4EA0BD8F7BDDD0A6DF94786C9A05CF724126A7F66ADE48D740BE53AD3561882210FCA07CBEE3428088A236BFD8884EBF18E6E858DBC1EAED0DB934A62A55548B0CB6D7A05C72FD737EB2089F2EF92A0D18B53B70F0EA88844ACF378EA9310D593D2CCAD6A05716834FACDD63C05C77A84DCF7205EE2BBA8E56CFACE0E701C578E469000C46A0DC46F8ED6086FFA553BD71C12089046829BD342BE17B4AD583D149F9D87BEDDEA063DA085F6C323ACAACD7779DB176E3DDA500C57A9F0041C9020681C20EE3F211A0634A4CF830FDADAACFB4E1EE0D5B4233A8596C4DD98239CD156F256ED209E6FCA0696B3F62C2CDB6F9E5AB77A0D62C7372B208851E8C8DB4265685B2C6F4390C54A0F4C75EC4D674379A8333B284E45D0FA57C208EEC6A66707828BD46A654E1D33AA0AEAEA8DE7014BBF7A8FF12D124330F4981DE4163B821B1A6A0E1B0B17A373FAAA0ACB27F326E15F8008D8491A7C8C015C340A3DA189171D0158AC880FF2600204E80".hexToSeqByte, + # <5> + "F85180808080808080808080A06737AAE4DA8E09B04472E1731B2727A39E390D28D1BB544A39CAFD769F47825E80808080A02A398B06DE329FC5D0900308F0D2F8B7A086EB2B97724243C633654DF73419C380".hexToSeqByte, + # <6> + "F901F180A00721B77ADBF56E10BCB1CEC4787186A235BD3B3E050FCF7A3DB5F70EE515E296A0C6FF265B9C54AEE8313ECBB12EEB4AD1E8631B128135C4411275A7BF1E6CD1FAA090B44487924813BE85B041C39C0867932478F4CBA60929C13B71394C95FF9897A078F1E269731A75A6EDE38C2685C61AD03F12C6C893C56854850F313AB71ACC03A02580A50379970DDF88A7B0018640558A5471321C406BC6F4B61620F376B4CDCEA0FDC88E5C70E22C83FD378426D777E1CEC63B9FF0C0131D1574BF72665BAB26CDA01779C5AF5F90EEE40D780D37F6139B912049C2B27DD266D58475C0FB63D12CDBA0346066652BA9172AA0CBA886C2CF9057476D1CCF3C93BFD080432BE00BF92B7DA088EE2F226A61012983517A7E5BC372D112D5132FEA45A110443077BDC01F2F64A062CF9B18FDFFD9849C12306A4CCA6CD49B15CA56D11ABE6C47DBE641E64475D5A0304070EEF6C8EDD6A9D47E7B93155D3454180E3FD84274DF4CCDD58838BA3931A0F8DFB8930866D16C68E2EFAC9540104F6338EBA186DCDF47EAE9207A8F3D40CFA045177DA925E3C8BACD2E0D66EE38B740AB57A13706073A12A205511C9AC911E2A07197A582DD6C0C58F9D6BFFFD961E7FBC2D2C9DB0C2B0E2418ABDFE8CD0F8214A08AB4D4F34902A5C3BADB4F08CF7EB8A724A4016E052A388323795B1D9036F25880".hexToSeqByte, + # <7> + "F8718080808080A04BDCBB12C57AE35DA029269D44F034D281BA26AA7E8728C438EAD818D134B72880A0C94BE1E1576F3DEC3BFA5BAC25AE6717A0E3EAB0A2BF670FAC80B90051825E41A0E65D9EC7D4BD250B0DCA625C67CDC0A45F70D145B4A51641BA086FA14588E6728080808080808080".hexToSeqByte, + # <8> + "F8518080808080A03AE805C13F44D47161D6436D0862A19135A0058EB932BB060C54FBB8C37475D18080A02C0ECC32B46D5BED636D725A1897AC887AC67D0672F09F253771EE27DA2AC57B8080808080808080".hexToSeqByte, + # <9> + "F86B9D3D781CFDDABCBF50A6F7C2EE0DBF4F0F1F5F49E97F867983511822EFE9B84BF8498085E8D4A51000A056E81F171BCC55A6FF8345E692C0F86E5B48E01B996CADC001622FB5E363B421A0C5D2460186F7233C927E7DB2DCC703C0E500B653CA82273B7BFAD8045D85A470".hexToSeqByte]) + + snapProofData10* = ( + "00022f3d9397b043b874df5e583356270c6cae376eba812918b66895a3f9fe12".toDigest, + @[# <0> + ("0002355d781cfddabcbf50a6f7c2ee0dbf4f0f1f5f49e97f867983511822efe9".toDigest, + 0u64, + "1000000000000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <1> + ("0002358e1f73a4220e5cf6e608c75fa1311661809ced3847f4d87befd0b94ba7".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <2> + ("00023748f7ecfa90c947e26ffbdf28a76ab15c71ff932d4cfe4d729ffb043b20".toDigest, + 1u64, + "47899999999832000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <3> + ("000238a02b3e2240e2a14e8c896746706ec9977869ff51150568e52aba562c13".toDigest, + 0u64, + "1000000000000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <4> + ("00024209f45752c8e4a5ff0a35335ce23afa9ce18eea715477861fe9749da626".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "e55ef5350de3e968e91862eca584ea626826a221852e42c3f30b51383fd5f7aa".toDigest), + # <5> + ("0002453506c87e5d03ad5635a80803ee442cd16d6edb58538327b9d39893aab3".toDigest, + 1u64, + "0".parse(Uint256), + "8acc342b798979e6f7cea14aeb166a3641199f7fdb35849ae7627fef8642c1e9".toDigest, + "9e60bf1f5b1c512ad6bf123bccf02f1019eb49b82163b4fc92c5e1b27635191c".toDigest), + # <6> + ("00024a3d50f727e3a51f5520a70cb71954564b9e305ef5dd1f22649dfd603fce".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <7> + ("00024b2ca9aa555dc47fe52a6cd2482ad31b548c09aa663c8fa2e29abacc115b".toDigest, + 1u64, + "0".parse(Uint256), + "000b1595ffdd6b61cbf4b5f03e2bec31c421cb51ec7659a929ee7b4be71f51b0".toDigest, + "15b731527a2a29c52e0773354bea62e10f441e5dcdbe5c42bcb448c8d7e6a6eb".toDigest), + # <8> + ("00024e3d648fcbff234eaf39f00017c830ae9ef736f0d129d64373a2b2684284".toDigest, + 1u64, + "155349999664000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <9> + ("0002506bf1d94ba1da9cc331424ca8638dd4d735812d5d662793eb01a4865f87".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "cd2164837eb1849b6d039f15d03b9078413e08644a4c598632af1e3be0d83205".toDigest), + # <10> + ("000250e11b0e47145d2687b2451a45a86e5d8b3bee3f421a24d4e80e811792e8".toDigest, + 1u64, + "0".parse(Uint256), + "1d40ed4738c1b04df8bd8fd102ccde2f4a62f8803134a01d124da5c2389af5b1".toDigest, + "4e3c65796be34e5a3b67459453315d4367389de0dd041a33c53f2ae90870ed76".toDigest), + # <11> + ("0002534f8b1f089c1126f3bf75fb8e6e6d086be1a537af71b2b8eff1424ff8de".toDigest, + 1u64, + "0".parse(Uint256), + "8a4fd0a4931305e3c412c67c955105de74d33a0b8a527d8b7fc92feec9fa6a3f".toDigest, + "8a0c9017e6ef5d552ec36e1a772b70d6c066432aacc2c619ac16cca7de079321".toDigest), + # <12> + ("000254954b7f82be0e0e21fd8977e1fa69b51259f315caf61a1057d3ce5d51f5".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <13> + ("00025558956d9dbebf49d926cdb8f389f8d90640436c3ba0c239fec3a489e37e".toDigest, + 1u64, + "0".parse(Uint256), + "1d40ed4738c1b04df8bd8fd102ccde2f4a62f8803134a01d124da5c2389af5b1".toDigest, + "4e3c65796be34e5a3b67459453315d4367389de0dd041a33c53f2ae90870ed76".toDigest), + # <14> + ("000255c809228ba0fadbdeaeb5cee83e21c6b8d3d28c74d71af34b317fead5ee".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "875191faaccab052ffb33984e8ba823d46be31b93a9127447e8d332f8dedaca9".toDigest), + # <15> + ("00026230f9340c6aa8e581c3f05fa2aec40c7f31080d74f83dd2b618d560a89b".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "2d0030988d6de09bf519a11fd165c3f9080ebddb98fa274703dd7f8bbb53bd16".toDigest), + # <16> + ("00026b3faf91aaf9e6465eb26231bd3305636d8c818f840f668a97ffcd9d7e10".toDigest, + 1u64, + "0".parse(Uint256), + "1d40ed4738c1b04df8bd8fd102ccde2f4a62f8803134a01d124da5c2389af5b1".toDigest, + "4e3c65796be34e5a3b67459453315d4367389de0dd041a33c53f2ae90870ed76".toDigest)], + @[# <0> + "F90211A00F41A498B871B7A732DB8B91A4AD4E013596110F383BF3041F2E818714FB57FBA0203149AB733DABFFB4A075E06DFBAC60A68A88CEDA23AAAE3F82A059E73A0DF4A034A036B6A6B8B596BA29F2C1EB8FAC71F21B8BC72481CA2E7C21733E39DAE8E2A0D533FA53AFE1DEFBA7E2CC6E645523F3AA1FB76651634F9EEF68D063F23FEA31A0A3AEFA0444DE17ADCF431EC4C5F057D799A2AC3A568F4C303E43561A7FCB629CA0033F5BC31420628F447A5ABC3DECF498E6852B486C07ADB3BBD5334556463BD4A0015307556953C623310A1D3A70C58D9465C6EEBBA5547DF67BDCB19CA953B3A1A09FFD78C0F9E3A550DD9D45E4C82C78A437F85640E7F86461E101578E898D3FA5A01D624ED0F8C9C98AA2FCD5D8D0AE1954B1DB998E7C79F9A98601A42F255FFCAEA049BF312A5690ACF97114EEC2F0B7BA8541624339DCC4359FA63DD98D942C7745A08045A2EA09815540255B0F0071311084C58F0A16D89CE46B1A7BD3948DFBC7BDA0F86E5DFF074F176403C0E8388587D213890A5B926AFBB521767C384DC64F321AA01AD0BEC29F16127FF644D87CAF6F4EC6FA7F4EF50FBC51AC4984D496DB2CA99BA04981C605424CD0CE7233D469C85DC60FC8D88E585C3C02B4923B184B8FCDE926A012F41CFB88E684A08727C66FEE65D20D93DB71E8508A1476A61056E967494914A059D005E9864E5F502DAF61049C7A8F862E84F2591AE3394E3066C2648239B7FE80".hexToSeqByte, + # <1> + "F90211A0B5E039DE5CD9F82A1647EE05E212486A2DDB81999B80727B76C3156B40641CA9A02569520DCDB1750D48C7060FEE64B1C16388A098648AE4FEF929D75775EBBE53A0CE915C5DE8826650CC3F49E82B307A98E87359B679965A1A7796BB50298947F4A012C8C06E4EF08F8A47BB81A9F7FDD4279A7BE096E793DC10E217F5C17D1C61D7A0A5F8FF8BADFECACB3AD5EAFF8C4EE750D33DD50A1D1CC5130D027D574F4D1A9EA00E35BB939DD5CF81DC2219FB27BAE31DFE2AEE079FDBAD367619DBDBAD276D81A0C012A282076332BB92BEEDD74CF7EF9CC36875B54D1DAAC81AC0EC9CB60CE5AEA00FB445610AA14436F057BE1BC4DBD86BDBE01490AEEC46059C710797591B7E14A0EB99BB2A00E7CC498ABD0B020276A1790628369314133F35E8089B28D55839AAA0C290D06ADA3FFA0760EDD11D57F0B32F08BF668943CE01CF651B8B29DC63A197A088BD981BF81F4F0C0B37A04542269BE7C24A5D09D455807256344C6A864925E0A08924048D8F415C57A2835DB0B33F92AD157C4173D2FF819CB32D5EF89E7060A1A083DA61DBDC26D3202C343A0168D69B0DDB0572F43C20EDB924809DBA6149BE88A01B640A922A59C6DA8860058354FD78A39D00A46E0CDF92FB6A338ED556BABFD3A01D8ACC7CA60FCD378DE1DD765B680D037620F08485ED84BAD1A5BB82702F80D0A0B00F0533277C292308F9FC4BF5A85027D68F693D70CA92F1D983A32EA7C5478480".hexToSeqByte, + # <2> + "F90211A0D126CFCCD9CC595EE94F46C23D2C203D06BBF3E628C050CC34A1B5C76965A978A0B22839972A330E8CE273187A801F7257E8655A9E728283D41DF57E408A423127A06FB243B4185E550D9CA703A47E786E55B01E47968D53BBA5600865F3E7FF739AA01297B6C416BE295952F78AABCDA3D15F6087F1976194CF881A0A55D2CF8E9B6CA0523DF620C80E8C4B48A0DBB2328226921BE949F61671707A604AA95294EE4E75A0917B77A88A260098D6BBF3600D84BA39F22B59426823A2BE8A8D0771DAFA4DA0A007C73D02889414B827DA606183D4A960AAB396777217792EC55C68D6FF29B52CA079B593B9D554CFCDB265AD20A7AD018EC97B580F62904DC1052B7BD725ACB2E1A06EB993C8B75F317A7F73825D84BFA191E041667B20D0E21D2E9EF7BF311A53ACA048484A38E04A2B0001D0D3D34D731F8149459D08987EF7293331CBCCCFB3D09BA044A3A485D94F02B53EB6BF99842F88101A9E3C42D24577720742690F584B0BCCA058E3BF550CE62F775EAE8B9A49196A78F7F8DA68874420D959925F95C2CCF553A0988D0E99F69E71C24C2F17C69B7AC3F448FD7133D624FB68F148A439048C5B2EA01A48E7BAF56999165D3E2DC392D768EA88B4820D0A4D5BD8781B60A247138B36A0DBF8593863396974608C4D47BFAB51662750421D7D431842AF55740089AA3D4CA029AD034ECF2E8054767808DDF956EE2C89720F633C3B3594881A71BC582B9A0480".hexToSeqByte, + # <3> + "F90211A0C61F01AADE2395B4E17DE4C79353729EFFAD97B46C0E425B518D4D77B75CB713A03C2D55238135DA002B914D80556FFD60890E37705E7A5487B0B52FC26DA29299A07CE74DABF9451C37485200571A867213063A8FED408C6BC72F6D84564E9F8F3EA0514930C58E952176FF4C626CED5913394B5115CA7C81A9B5B543F7858F43DC01A02D93C42DC532FE12D6CCFD735EC1F22FBD7D009D386F79E76B1F0088CB412A30A0860EB915C38B045754025B34FD796CED538071EA6B98307754124A5ABB459326A00720A388A06A78305A61D749A3895319611313C5CDBE5A396E3C30793E18C9A4A0847A9DBC85722DF97F2B46D9D95DDA6C69E6D3FA68A98FCFDCF218B26A6EF727A0C7A6A3ECE732C86186CE38D8329D2242E65D8BCC240E186FF797621B9CD75B2DA05879B4B262967609E2D8ED94D0C358B9C8A6FD1759A4AA0AD836EEA77F9C76D8A0132F0874B2E39B4E357F8654B6B6A88F306E6631FAED6C0DF223F5D18A7CD448A0504A50995DB2FACDAC157BA88A6D73CCB2DEC75FCA6424EBE45381D9DBC82151A0DABD28DEDCF493701D612D87BEE5C008C40A46D8B1A5AA7860C7B388E3026A23A021680406AF0738D3FA04C827AA8735FF8ED2C057103AA909BF84C618F63E08A8A0AD769155BAC8DC43ACC0B2FA3A6D909E8C6FD08F54D3D70452197D64D4105B8DA01E1CA8EF41EBA3606E19ECBF58499208653BAD2891AD190227DB3B8705EEE06F80".hexToSeqByte, + # <4> + "F901F180A00721B77ADBF56E10BCB1CEC4787186A235BD3B3E050FCF7A3DB5F70EE515E296A0C6FF265B9C54AEE8313ECBB12EEB4AD1E8631B128135C4411275A7BF1E6CD1FAA090B44487924813BE85B041C39C0867932478F4CBA60929C13B71394C95FF9897A078F1E269731A75A6EDE38C2685C61AD03F12C6C893C56854850F313AB71ACC03A02580A50379970DDF88A7B0018640558A5471321C406BC6F4B61620F376B4CDCEA0FDC88E5C70E22C83FD378426D777E1CEC63B9FF0C0131D1574BF72665BAB26CDA01779C5AF5F90EEE40D780D37F6139B912049C2B27DD266D58475C0FB63D12CDBA0346066652BA9172AA0CBA886C2CF9057476D1CCF3C93BFD080432BE00BF92B7DA088EE2F226A61012983517A7E5BC372D112D5132FEA45A110443077BDC01F2F64A062CF9B18FDFFD9849C12306A4CCA6CD49B15CA56D11ABE6C47DBE641E64475D5A0304070EEF6C8EDD6A9D47E7B93155D3454180E3FD84274DF4CCDD58838BA3931A0F8DFB8930866D16C68E2EFAC9540104F6338EBA186DCDF47EAE9207A8F3D40CFA045177DA925E3C8BACD2E0D66EE38B740AB57A13706073A12A205511C9AC911E2A07197A582DD6C0C58F9D6BFFFD961E7FBC2D2C9DB0C2B0E2418ABDFE8CD0F8214A08AB4D4F34902A5C3BADB4F08CF7EB8A724A4016E052A388323795B1D9036F25880".hexToSeqByte, + # <5> + "F851808080A0D79FF8ABA0144BD032AA94CDF75D83FCC056F2B82DF08BD544B0CB458F0D70CC80808080808080808080A00C6E4A015FE343A22C9EAD9B57C0D3BAB451B789723F79F6B6593D15644EF6F08080".hexToSeqByte, + # <6> + "F8918080A093F5DE90F6BCE657569ACB11E16085BCAB51279FBCB138785CE3D2CEFC2F8FA08080808080808080A019A7654C59B43B95D7873D41EDF27CCE120D274ACED05B03F6B7DD796F74014580A0188DFE56CDB5890FCB376A7FE8D3AE1A700E41D6B6A646604F24659C278C547580A0C78A3CB7E10BBE786A8543F7C7145902C041F346CFCEC5E6DCBA53330AA2C31E80".hexToSeqByte, + # <7> + "F851808080A02BBC12D86628FE46324B463008CAAD0E5B8CB759CA2F60C84F2C1F02F8F28B7380808080808080808080A005249A66D0423E06467788F2821DDC6961625BC738B5946B6F453164864010AC8080".hexToSeqByte, + # <8> + "F8669D3FAF91AAF9E6465EB26231BD3305636D8C818F840F668A97FFCD9D7E10B846F8440180A01D40ED4738C1B04DF8BD8FD102CCDE2F4A62F8803134A01D124DA5C2389AF5B1A04E3C65796BE34E5A3B67459453315D4367389DE0DD041A33C53F2AE90870ED76".hexToSeqByte]) + + snapProofData11* = ( + "0002672a225a0eb0e4808f4e2dd211f7c0ddf2d69366c146ce623fd7cdf96447".toDigest, + @[# <0> + ("00026b3faf91aaf9e6465eb26231bd3305636d8c818f840f668a97ffcd9d7e10".toDigest, + 1u64, + "0".parse(Uint256), + "1d40ed4738c1b04df8bd8fd102ccde2f4a62f8803134a01d124da5c2389af5b1".toDigest, + "4e3c65796be34e5a3b67459453315d4367389de0dd041a33c53f2ae90870ed76".toDigest), + # <1> + ("00026be8af990c2dca842408de4b83edf0b3dfc55622bd7db4d5e066b56fb869".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <2> + ("00026d03435156b9a1f0919be2d05de74a55dcff345287d5fee46808171ccea2".toDigest, + 0u64, + "1000000000000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <3> + ("00026fef8519d5761288fa5313bbb3edc964961ef5aee1d46f4fb988826d1197".toDigest, + 1u64, + "0".parse(Uint256), + "1d40ed4738c1b04df8bd8fd102ccde2f4a62f8803134a01d124da5c2389af5b1".toDigest, + "4e3c65796be34e5a3b67459453315d4367389de0dd041a33c53f2ae90870ed76".toDigest), + # <4> + ("0002704dac775ce1aa9656aa8dcfddcbf094eb2871516270110b642634c857ce".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <5> + ("000275467133feae794a3cb47b42439dc63561155f3bd4e0a690ec17e9d8dbc8".toDigest, + 1u64, + "0".parse(Uint256), + "1d40ed4738c1b04df8bd8fd102ccde2f4a62f8803134a01d124da5c2389af5b1".toDigest, + "4e3c65796be34e5a3b67459453315d4367389de0dd041a33c53f2ae90870ed76".toDigest), + # <6> + ("0002792a7436a6e950c65eabf9e5bf3206eae24784f5abe70e28a0e3893c4f28".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <7> + ("00027ae7376d391d7a4a0f5d3e0a4b2e8854e9e62a494571ffe13a865fe0ddd2".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "66f2f67ad2a10fb961bdde23805195038083fcb3a1ea4f7e2f024aa4d00ff307".toDigest), + # <8> + ("00028687b691df45ffdb3e53c93897fd9c57319756443b4f417ee51dea9df155".toDigest, + 0u64, + "1000000000000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <9> + ("000286d3b4201c30bb34a3cf1d27e176bd880c0e0b78d411f002764076fd1a2c".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c6ddb709ebd00ab44378e7e16530ac5d95fc13a4a923db9f8dbd5ff974f6d5fd".toDigest), + # <10> + ("0002879dbc836c36c0a8427263e7cca14cd9ae98b2729c9a0721a45c2cea9f45".toDigest, + 1u64, + "49675855933110142".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <11> + ("00028c4c578b6bf32b0ff3475fbf06a88254522b1f26a8592d424975525a2eaa".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "2cac655fc72d0e16b3529fe7470cc5b2ea315b6c1a2cac146a3ba1afda0fa28b".toDigest), + # <12> + ("00028f3cd18b1f91e074613fa2d3897bb1839d0d7fab2e3d828c3ac3eed01657".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <13> + ("000293720dc9e3c75bfbabd641981c33fe779339dd782d00c680b16137254806".toDigest, + 1u64, + "0".parse(Uint256), + "1d40ed4738c1b04df8bd8fd102ccde2f4a62f8803134a01d124da5c2389af5b1".toDigest, + "4e3c65796be34e5a3b67459453315d4367389de0dd041a33c53f2ae90870ed76".toDigest), + # <14> + ("00029658966dc8bfd95d2f1e59b7bcbc398b7ffccb6ce01b6d90e423024c6c66".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d7b2dc5b97e3d9e2bdfce240da44102262b9c9ed7573c77749773bc3507742b0".toDigest), + # <15> + ("000298f980ebfe380fce352f629dece043d61bae1746feac2b34d5f83a58da27".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <16> + ("00029ab4729b9d7f86a324de7aafb32f43d240ae439d27a8d9132a516940d413".toDigest, + 1u64, + "0".parse(Uint256), + "1d40ed4738c1b04df8bd8fd102ccde2f4a62f8803134a01d124da5c2389af5b1".toDigest, + "4e3c65796be34e5a3b67459453315d4367389de0dd041a33c53f2ae90870ed76".toDigest), + # <17> + ("0002a27937fe4a325bb13c30a8d8911659ed91855ea9143af163b515affa1e86".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest)], + @[# <0> + "F90211A00F41A498B871B7A732DB8B91A4AD4E013596110F383BF3041F2E818714FB57FBA0203149AB733DABFFB4A075E06DFBAC60A68A88CEDA23AAAE3F82A059E73A0DF4A034A036B6A6B8B596BA29F2C1EB8FAC71F21B8BC72481CA2E7C21733E39DAE8E2A0D533FA53AFE1DEFBA7E2CC6E645523F3AA1FB76651634F9EEF68D063F23FEA31A0A3AEFA0444DE17ADCF431EC4C5F057D799A2AC3A568F4C303E43561A7FCB629CA0033F5BC31420628F447A5ABC3DECF498E6852B486C07ADB3BBD5334556463BD4A0015307556953C623310A1D3A70C58D9465C6EEBBA5547DF67BDCB19CA953B3A1A09FFD78C0F9E3A550DD9D45E4C82C78A437F85640E7F86461E101578E898D3FA5A01D624ED0F8C9C98AA2FCD5D8D0AE1954B1DB998E7C79F9A98601A42F255FFCAEA049BF312A5690ACF97114EEC2F0B7BA8541624339DCC4359FA63DD98D942C7745A08045A2EA09815540255B0F0071311084C58F0A16D89CE46B1A7BD3948DFBC7BDA0F86E5DFF074F176403C0E8388587D213890A5B926AFBB521767C384DC64F321AA01AD0BEC29F16127FF644D87CAF6F4EC6FA7F4EF50FBC51AC4984D496DB2CA99BA04981C605424CD0CE7233D469C85DC60FC8D88E585C3C02B4923B184B8FCDE926A012F41CFB88E684A08727C66FEE65D20D93DB71E8508A1476A61056E967494914A059D005E9864E5F502DAF61049C7A8F862E84F2591AE3394E3066C2648239B7FE80".hexToSeqByte, + # <1> + "F90211A0B5E039DE5CD9F82A1647EE05E212486A2DDB81999B80727B76C3156B40641CA9A02569520DCDB1750D48C7060FEE64B1C16388A098648AE4FEF929D75775EBBE53A0CE915C5DE8826650CC3F49E82B307A98E87359B679965A1A7796BB50298947F4A012C8C06E4EF08F8A47BB81A9F7FDD4279A7BE096E793DC10E217F5C17D1C61D7A0A5F8FF8BADFECACB3AD5EAFF8C4EE750D33DD50A1D1CC5130D027D574F4D1A9EA00E35BB939DD5CF81DC2219FB27BAE31DFE2AEE079FDBAD367619DBDBAD276D81A0C012A282076332BB92BEEDD74CF7EF9CC36875B54D1DAAC81AC0EC9CB60CE5AEA00FB445610AA14436F057BE1BC4DBD86BDBE01490AEEC46059C710797591B7E14A0EB99BB2A00E7CC498ABD0B020276A1790628369314133F35E8089B28D55839AAA0C290D06ADA3FFA0760EDD11D57F0B32F08BF668943CE01CF651B8B29DC63A197A088BD981BF81F4F0C0B37A04542269BE7C24A5D09D455807256344C6A864925E0A08924048D8F415C57A2835DB0B33F92AD157C4173D2FF819CB32D5EF89E7060A1A083DA61DBDC26D3202C343A0168D69B0DDB0572F43C20EDB924809DBA6149BE88A01B640A922A59C6DA8860058354FD78A39D00A46E0CDF92FB6A338ED556BABFD3A01D8ACC7CA60FCD378DE1DD765B680D037620F08485ED84BAD1A5BB82702F80D0A0B00F0533277C292308F9FC4BF5A85027D68F693D70CA92F1D983A32EA7C5478480".hexToSeqByte, + # <2> + "F90211A0D126CFCCD9CC595EE94F46C23D2C203D06BBF3E628C050CC34A1B5C76965A978A0B22839972A330E8CE273187A801F7257E8655A9E728283D41DF57E408A423127A06FB243B4185E550D9CA703A47E786E55B01E47968D53BBA5600865F3E7FF739AA01297B6C416BE295952F78AABCDA3D15F6087F1976194CF881A0A55D2CF8E9B6CA0523DF620C80E8C4B48A0DBB2328226921BE949F61671707A604AA95294EE4E75A0917B77A88A260098D6BBF3600D84BA39F22B59426823A2BE8A8D0771DAFA4DA0A007C73D02889414B827DA606183D4A960AAB396777217792EC55C68D6FF29B52CA079B593B9D554CFCDB265AD20A7AD018EC97B580F62904DC1052B7BD725ACB2E1A06EB993C8B75F317A7F73825D84BFA191E041667B20D0E21D2E9EF7BF311A53ACA048484A38E04A2B0001D0D3D34D731F8149459D08987EF7293331CBCCCFB3D09BA044A3A485D94F02B53EB6BF99842F88101A9E3C42D24577720742690F584B0BCCA058E3BF550CE62F775EAE8B9A49196A78F7F8DA68874420D959925F95C2CCF553A0988D0E99F69E71C24C2F17C69B7AC3F448FD7133D624FB68F148A439048C5B2EA01A48E7BAF56999165D3E2DC392D768EA88B4820D0A4D5BD8781B60A247138B36A0DBF8593863396974608C4D47BFAB51662750421D7D431842AF55740089AA3D4CA029AD034ECF2E8054767808DDF956EE2C89720F633C3B3594881A71BC582B9A0480".hexToSeqByte, + # <3> + "F90211A0C61F01AADE2395B4E17DE4C79353729EFFAD97B46C0E425B518D4D77B75CB713A03C2D55238135DA002B914D80556FFD60890E37705E7A5487B0B52FC26DA29299A07CE74DABF9451C37485200571A867213063A8FED408C6BC72F6D84564E9F8F3EA0514930C58E952176FF4C626CED5913394B5115CA7C81A9B5B543F7858F43DC01A02D93C42DC532FE12D6CCFD735EC1F22FBD7D009D386F79E76B1F0088CB412A30A0860EB915C38B045754025B34FD796CED538071EA6B98307754124A5ABB459326A00720A388A06A78305A61D749A3895319611313C5CDBE5A396E3C30793E18C9A4A0847A9DBC85722DF97F2B46D9D95DDA6C69E6D3FA68A98FCFDCF218B26A6EF727A0C7A6A3ECE732C86186CE38D8329D2242E65D8BCC240E186FF797621B9CD75B2DA05879B4B262967609E2D8ED94D0C358B9C8A6FD1759A4AA0AD836EEA77F9C76D8A0132F0874B2E39B4E357F8654B6B6A88F306E6631FAED6C0DF223F5D18A7CD448A0504A50995DB2FACDAC157BA88A6D73CCB2DEC75FCA6424EBE45381D9DBC82151A0DABD28DEDCF493701D612D87BEE5C008C40A46D8B1A5AA7860C7B388E3026A23A021680406AF0738D3FA04C827AA8735FF8ED2C057103AA909BF84C618F63E08A8A0AD769155BAC8DC43ACC0B2FA3A6D909E8C6FD08F54D3D70452197D64D4105B8DA01E1CA8EF41EBA3606E19ECBF58499208653BAD2891AD190227DB3B8705EEE06F80".hexToSeqByte, + # <4> + "F901F180A00721B77ADBF56E10BCB1CEC4787186A235BD3B3E050FCF7A3DB5F70EE515E296A0C6FF265B9C54AEE8313ECBB12EEB4AD1E8631B128135C4411275A7BF1E6CD1FAA090B44487924813BE85B041C39C0867932478F4CBA60929C13B71394C95FF9897A078F1E269731A75A6EDE38C2685C61AD03F12C6C893C56854850F313AB71ACC03A02580A50379970DDF88A7B0018640558A5471321C406BC6F4B61620F376B4CDCEA0FDC88E5C70E22C83FD378426D777E1CEC63B9FF0C0131D1574BF72665BAB26CDA01779C5AF5F90EEE40D780D37F6139B912049C2B27DD266D58475C0FB63D12CDBA0346066652BA9172AA0CBA886C2CF9057476D1CCF3C93BFD080432BE00BF92B7DA088EE2F226A61012983517A7E5BC372D112D5132FEA45A110443077BDC01F2F64A062CF9B18FDFFD9849C12306A4CCA6CD49B15CA56D11ABE6C47DBE641E64475D5A0304070EEF6C8EDD6A9D47E7B93155D3454180E3FD84274DF4CCDD58838BA3931A0F8DFB8930866D16C68E2EFAC9540104F6338EBA186DCDF47EAE9207A8F3D40CFA045177DA925E3C8BACD2E0D66EE38B740AB57A13706073A12A205511C9AC911E2A07197A582DD6C0C58F9D6BFFFD961E7FBC2D2C9DB0C2B0E2418ABDFE8CD0F8214A08AB4D4F34902A5C3BADB4F08CF7EB8A724A4016E052A388323795B1D9036F25880".hexToSeqByte, + # <5> + "F8918080A093F5DE90F6BCE657569ACB11E16085BCAB51279FBCB138785CE3D2CEFC2F8FA08080808080808080A019A7654C59B43B95D7873D41EDF27CCE120D274ACED05B03F6B7DD796F74014580A0188DFE56CDB5890FCB376A7FE8D3AE1A700E41D6B6A646604F24659C278C547580A0C78A3CB7E10BBE786A8543F7C7145902C041F346CFCEC5E6DCBA53330AA2C31E80".hexToSeqByte, + # <6> + "F8918080A09B00AD8C4C9E6C75DA0D4314A094F1E17443023734B3431B934EC2FD7255516180808080808080A0E28B99E93ADC3749980667A48380263D1B94CDCE4285777EDD0DA73E50FE09EDA08DEB1C60921CB8587B50A64F9E743F21C8EC2FE9D9749B25DDECD2906CC19FAC80A068EEFFC31BC109B288FE7553F2E22A2A8352E50A8D83028FD82FEF1349FDCE4F808080".hexToSeqByte, + # <7> + "F8679E207937FE4A325BB13C30A8D8911659ED91855EA9143AF163B515AFFA1E86B846F8440180A056E81F171BCC55A6FF8345E692C0F86E5B48E01B996CADC001622FB5E363B421A0D8990879C8C8E2B683D4F0FC5A76889AEEBA15181AE48F09FBF37ED182286300".hexToSeqByte]) + + snapProofData12* = ( + "00029f16b11c6d1e108c3f3e0370cdc8754f3775b8130164840e1719f7f8ca7c".toDigest, + @[# <0> + ("0002a27937fe4a325bb13c30a8d8911659ed91855ea9143af163b515affa1e86".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <1> + ("0002aaaed0240116d3543d5c073fff07551e5586eda706fc4ca8e29b77a1f1fa".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <2> + ("0002abefb79cbb52427317eff78d80d1488c994f45dc1e0097a59bb44bf1cb95".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "51a18c6f6f98c4ec00f9d9ab8655497698311ead2d4f6cf542fa123641896cf5".toDigest), + # <3> + ("0002adf729bc9727e6cccfdcf104ad4a5dcda5af964fcb94bcdad88b72553331".toDigest, + 1u64, + "99999790000000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <4> + ("0002b1fb32a77a990ae6fa4b46432a24dc552e8cccb348f45d6c74734272a557".toDigest, + 1u64, + "0".parse(Uint256), + "1d40ed4738c1b04df8bd8fd102ccde2f4a62f8803134a01d124da5c2389af5b1".toDigest, + "4e3c65796be34e5a3b67459453315d4367389de0dd041a33c53f2ae90870ed76".toDigest), + # <5> + ("0002b28c6553dc4e1f079ccc14de1571caaf4c326e2e401f6bde561e106b0201".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <6> + ("0002b3c3692b2ca6dc28526b38ef858d3af935cdd705ae4eb5ab6ec4fcb24b09".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <7> + ("0002b7aaa888967448a4aa62d4cb9a9b5b2583d180ef61b72a903abfcdf093e5".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <8> + ("0002b9fb68c53e297952bea6f6a60ffc9980b03adc9d3dcb9f443aa7e45e7347".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "7afcb040d782461c779959dbe42e2f4036e1cd33ae06a446f7e7805ebd3a1706".toDigest), + # <9> + ("0002ba07998e1c0cc01a52ba7037699616fddfbfbe0f7789bdf25ded360c48f2".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <10> + ("0002ba5eb8453564eed78b75b1ce9320d7f19c41803cce5686763b1da629bfe1".toDigest, + 1u64, + "47899999999832000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <11> + ("0002bc90c2b0063efd93ed2e85b20d5ccbe260fefad920d4e304094237ffabfd".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <12> + ("0002c4b940bb38b41a14a93fccd76929d8473e6e9b29d34bcdf46c1abe90739b".toDigest, + 1u64, + "0".parse(Uint256), + "1d40ed4738c1b04df8bd8fd102ccde2f4a62f8803134a01d124da5c2389af5b1".toDigest, + "4e3c65796be34e5a3b67459453315d4367389de0dd041a33c53f2ae90870ed76".toDigest), + # <13> + ("0002c55d54f7310bebdda46dcf714245fce00e4880ccd24059dba7a64f7edbf0".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <14> + ("0002c9720cc5495625c900e4356cbd61bdf444a5779c1f4a72db645c65d191e8".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "75f0feaa480d784762abbda505dda8e084272628d1e96a4cfeae2b90960cdf22".toDigest), + # <15> + ("0002cd8bc350d654415ec47c459542ab39ef6060891db2ab8d2e384033753ae9".toDigest, + 1u64, + "155349999832000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <16> + ("0002ce2de3121f3491663ffe4c9c821dd99f753e6947d8c77af5c39d5c933d6e".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <17> + ("0002d742f6c08044b437441d2761a017ebe32e2a08636a27013fff4d2dbc00c0".toDigest, + 0u64, + "1000000000000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest)], + @[# <0> + "F90211A00F41A498B871B7A732DB8B91A4AD4E013596110F383BF3041F2E818714FB57FBA0203149AB733DABFFB4A075E06DFBAC60A68A88CEDA23AAAE3F82A059E73A0DF4A034A036B6A6B8B596BA29F2C1EB8FAC71F21B8BC72481CA2E7C21733E39DAE8E2A0D533FA53AFE1DEFBA7E2CC6E645523F3AA1FB76651634F9EEF68D063F23FEA31A0A3AEFA0444DE17ADCF431EC4C5F057D799A2AC3A568F4C303E43561A7FCB629CA0033F5BC31420628F447A5ABC3DECF498E6852B486C07ADB3BBD5334556463BD4A0015307556953C623310A1D3A70C58D9465C6EEBBA5547DF67BDCB19CA953B3A1A09FFD78C0F9E3A550DD9D45E4C82C78A437F85640E7F86461E101578E898D3FA5A01D624ED0F8C9C98AA2FCD5D8D0AE1954B1DB998E7C79F9A98601A42F255FFCAEA049BF312A5690ACF97114EEC2F0B7BA8541624339DCC4359FA63DD98D942C7745A08045A2EA09815540255B0F0071311084C58F0A16D89CE46B1A7BD3948DFBC7BDA0F86E5DFF074F176403C0E8388587D213890A5B926AFBB521767C384DC64F321AA01AD0BEC29F16127FF644D87CAF6F4EC6FA7F4EF50FBC51AC4984D496DB2CA99BA04981C605424CD0CE7233D469C85DC60FC8D88E585C3C02B4923B184B8FCDE926A012F41CFB88E684A08727C66FEE65D20D93DB71E8508A1476A61056E967494914A059D005E9864E5F502DAF61049C7A8F862E84F2591AE3394E3066C2648239B7FE80".hexToSeqByte, + # <1> + "F90211A0B5E039DE5CD9F82A1647EE05E212486A2DDB81999B80727B76C3156B40641CA9A02569520DCDB1750D48C7060FEE64B1C16388A098648AE4FEF929D75775EBBE53A0CE915C5DE8826650CC3F49E82B307A98E87359B679965A1A7796BB50298947F4A012C8C06E4EF08F8A47BB81A9F7FDD4279A7BE096E793DC10E217F5C17D1C61D7A0A5F8FF8BADFECACB3AD5EAFF8C4EE750D33DD50A1D1CC5130D027D574F4D1A9EA00E35BB939DD5CF81DC2219FB27BAE31DFE2AEE079FDBAD367619DBDBAD276D81A0C012A282076332BB92BEEDD74CF7EF9CC36875B54D1DAAC81AC0EC9CB60CE5AEA00FB445610AA14436F057BE1BC4DBD86BDBE01490AEEC46059C710797591B7E14A0EB99BB2A00E7CC498ABD0B020276A1790628369314133F35E8089B28D55839AAA0C290D06ADA3FFA0760EDD11D57F0B32F08BF668943CE01CF651B8B29DC63A197A088BD981BF81F4F0C0B37A04542269BE7C24A5D09D455807256344C6A864925E0A08924048D8F415C57A2835DB0B33F92AD157C4173D2FF819CB32D5EF89E7060A1A083DA61DBDC26D3202C343A0168D69B0DDB0572F43C20EDB924809DBA6149BE88A01B640A922A59C6DA8860058354FD78A39D00A46E0CDF92FB6A338ED556BABFD3A01D8ACC7CA60FCD378DE1DD765B680D037620F08485ED84BAD1A5BB82702F80D0A0B00F0533277C292308F9FC4BF5A85027D68F693D70CA92F1D983A32EA7C5478480".hexToSeqByte, + # <2> + "F90211A0D126CFCCD9CC595EE94F46C23D2C203D06BBF3E628C050CC34A1B5C76965A978A0B22839972A330E8CE273187A801F7257E8655A9E728283D41DF57E408A423127A06FB243B4185E550D9CA703A47E786E55B01E47968D53BBA5600865F3E7FF739AA01297B6C416BE295952F78AABCDA3D15F6087F1976194CF881A0A55D2CF8E9B6CA0523DF620C80E8C4B48A0DBB2328226921BE949F61671707A604AA95294EE4E75A0917B77A88A260098D6BBF3600D84BA39F22B59426823A2BE8A8D0771DAFA4DA0A007C73D02889414B827DA606183D4A960AAB396777217792EC55C68D6FF29B52CA079B593B9D554CFCDB265AD20A7AD018EC97B580F62904DC1052B7BD725ACB2E1A06EB993C8B75F317A7F73825D84BFA191E041667B20D0E21D2E9EF7BF311A53ACA048484A38E04A2B0001D0D3D34D731F8149459D08987EF7293331CBCCCFB3D09BA044A3A485D94F02B53EB6BF99842F88101A9E3C42D24577720742690F584B0BCCA058E3BF550CE62F775EAE8B9A49196A78F7F8DA68874420D959925F95C2CCF553A0988D0E99F69E71C24C2F17C69B7AC3F448FD7133D624FB68F148A439048C5B2EA01A48E7BAF56999165D3E2DC392D768EA88B4820D0A4D5BD8781B60A247138B36A0DBF8593863396974608C4D47BFAB51662750421D7D431842AF55740089AA3D4CA029AD034ECF2E8054767808DDF956EE2C89720F633C3B3594881A71BC582B9A0480".hexToSeqByte, + # <3> + "F90211A0C61F01AADE2395B4E17DE4C79353729EFFAD97B46C0E425B518D4D77B75CB713A03C2D55238135DA002B914D80556FFD60890E37705E7A5487B0B52FC26DA29299A07CE74DABF9451C37485200571A867213063A8FED408C6BC72F6D84564E9F8F3EA0514930C58E952176FF4C626CED5913394B5115CA7C81A9B5B543F7858F43DC01A02D93C42DC532FE12D6CCFD735EC1F22FBD7D009D386F79E76B1F0088CB412A30A0860EB915C38B045754025B34FD796CED538071EA6B98307754124A5ABB459326A00720A388A06A78305A61D749A3895319611313C5CDBE5A396E3C30793E18C9A4A0847A9DBC85722DF97F2B46D9D95DDA6C69E6D3FA68A98FCFDCF218B26A6EF727A0C7A6A3ECE732C86186CE38D8329D2242E65D8BCC240E186FF797621B9CD75B2DA05879B4B262967609E2D8ED94D0C358B9C8A6FD1759A4AA0AD836EEA77F9C76D8A0132F0874B2E39B4E357F8654B6B6A88F306E6631FAED6C0DF223F5D18A7CD448A0504A50995DB2FACDAC157BA88A6D73CCB2DEC75FCA6424EBE45381D9DBC82151A0DABD28DEDCF493701D612D87BEE5C008C40A46D8B1A5AA7860C7B388E3026A23A021680406AF0738D3FA04C827AA8735FF8ED2C057103AA909BF84C618F63E08A8A0AD769155BAC8DC43ACC0B2FA3A6D909E8C6FD08F54D3D70452197D64D4105B8DA01E1CA8EF41EBA3606E19ECBF58499208653BAD2891AD190227DB3B8705EEE06F80".hexToSeqByte, + # <4> + "F901F180A00721B77ADBF56E10BCB1CEC4787186A235BD3B3E050FCF7A3DB5F70EE515E296A0C6FF265B9C54AEE8313ECBB12EEB4AD1E8631B128135C4411275A7BF1E6CD1FAA090B44487924813BE85B041C39C0867932478F4CBA60929C13B71394C95FF9897A078F1E269731A75A6EDE38C2685C61AD03F12C6C893C56854850F313AB71ACC03A02580A50379970DDF88A7B0018640558A5471321C406BC6F4B61620F376B4CDCEA0FDC88E5C70E22C83FD378426D777E1CEC63B9FF0C0131D1574BF72665BAB26CDA01779C5AF5F90EEE40D780D37F6139B912049C2B27DD266D58475C0FB63D12CDBA0346066652BA9172AA0CBA886C2CF9057476D1CCF3C93BFD080432BE00BF92B7DA088EE2F226A61012983517A7E5BC372D112D5132FEA45A110443077BDC01F2F64A062CF9B18FDFFD9849C12306A4CCA6CD49B15CA56D11ABE6C47DBE641E64475D5A0304070EEF6C8EDD6A9D47E7B93155D3454180E3FD84274DF4CCDD58838BA3931A0F8DFB8930866D16C68E2EFAC9540104F6338EBA186DCDF47EAE9207A8F3D40CFA045177DA925E3C8BACD2E0D66EE38B740AB57A13706073A12A205511C9AC911E2A07197A582DD6C0C58F9D6BFFFD961E7FBC2D2C9DB0C2B0E2418ABDFE8CD0F8214A08AB4D4F34902A5C3BADB4F08CF7EB8A724A4016E052A388323795B1D9036F25880".hexToSeqByte, + # <5> + "F891808080A0D81E277429403B49A095AFE80EDC942565A688EB80C5F97ED9891FD0D051CAF68080A05D5D2FF5290A73C4EEA3C066729B8119991C42F44185692E03546564F5117A5180A0B7447EE77382ED51DA2170043D8BC027190BCE45BBB3D8B658F02A3564CC8AFD80A0EA3650D7F3DD8F14FD898F57EBA52F14DD2F49C5258769415DD4D49D2F400327808080808080".hexToSeqByte, + # <6> + "F85180808080808080A0D5B9D7161EE1893A9A55170FA7277A9E16538B26ECC1FFFB9BBE05A846BF09F1A0135D15A732FC74324E53CF1F65AA91418318AB6639788A5A7D9141B76307D36C8080808080808080".hexToSeqByte, + # <7> + "F86C9E2042F6C08044B437441D2761A017EBE32E2A08636A27013FFF4D2DBC00C0B84BF8498085E8D4A51000A056E81F171BCC55A6FF8345E692C0F86E5B48E01B996CADC001622FB5E363B421A0C5D2460186F7233C927E7DB2DCC703C0E500B653CA82273B7BFAD8045D85A470".hexToSeqByte]) + + snapProofData13* = ( + "0002d7033fdecb8b3c97ef2dd90f899929c07c14dcbf418239b9ee5c21f830b1".toDigest, + @[# <0> + ("0002d742f6c08044b437441d2761a017ebe32e2a08636a27013fff4d2dbc00c0".toDigest, + 0u64, + "1000000000000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <1> + ("0002d8234fca7169615082bffa556e5073a8c86e486c294706a8fb62074df2ba".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <2> + ("0002e21f8823a453f98ca97dc8f78f00c2dde552f4deb9da2e41cbedb436f3bf".toDigest, + 1u64, + "0".parse(Uint256), + "1d40ed4738c1b04df8bd8fd102ccde2f4a62f8803134a01d124da5c2389af5b1".toDigest, + "4e3c65796be34e5a3b67459453315d4367389de0dd041a33c53f2ae90870ed76".toDigest), + # <3> + ("0002e3567c07122a707ea1a45cafeb365312bc11693fe4a3bf45baee562d06f0".toDigest, + 2u64, + "2968499999853000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <4> + ("0002e5c06c004ffe677876456c393a99cabdc441b14fa9fc1c6c08d93c83938d".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <5> + ("0002e7e97672da44ce1d9801bd86035ad9a65cf81a5a9fba1986ff6f9b61d721".toDigest, + 0u64, + "1000000000000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <6> + ("0002e9977618451a6e4d99aa1a5cd05822d45517975bae292b4f2dabe9fa90a1".toDigest, + 0u64, + "1000000000000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <7> + ("0002f37507064c7160ee868fe0d079c28980cfc7a49a703782ffb06bb106f336".toDigest, + 0u64, + "1000000000000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <8> + ("0002f5a9a43e1e0078d7f8f3e9ceac49ef34db3aa0b8412339f36566fc1a0383".toDigest, + 0u64, + "1".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <9> + ("0002f6cdd50270504f173fa6117147206d49ac5f6d458b41600eeb46362a6051".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <10> + ("0002f6ce5fd9fd49cc33e81a7558421b82969378a5da3f369161bbf8a57698e8".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <11> + ("0002f7f5cf760bea70cb5102c9fc994e250f7b8970f831239ed354d42012182a".toDigest, + 1u64, + "0".parse(Uint256), + "1ab997a3be3478a862bbe700ca833c958407794c40bf482b7b4a307b03d50bd0".toDigest, + "71ec9566f1a545b6235c40cdbea993f80ba79f36ca4f77037b2092bd089f9091".toDigest), + # <12> + ("0002fc0b7d1debbdfc0e62fcc6b1bbd04fcba1f4515eb418489a79607f4311d0".toDigest, + 1u64, + "0".parse(Uint256), + "4f85b30e477e9a5d6882f7f26699f2914c28824c745bb4d3daeaf2cdc38bf131".toDigest, + "8a0c9017e6ef5d552ec36e1a772b70d6c066432aacc2c619ac16cca7de079321".toDigest), + # <13> + ("0002fe14f59762784e8c4ac8298885280807128a3879da4d3867e76ef1874262".toDigest, + 205u64, + "100019500000000000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <14> + ("000301cd3ce0ae08f19405570b9584ca14f6a432b6beef719f77a8c6f8d01a6e".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <15> + ("00030219d8ac850176cef5449de6472923db97b47149af1c8391cd439e89bd85".toDigest, + 127u64, + "1506785389285092736".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <16> + ("0003038b7cde218e2f4d9f94d44350988991f67851c152c96e3d0c143b5e3265".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <17> + ("000306a9988cf7937c8ad241404e1388d58402a963d977b4332b5d76f54254bb".toDigest, + 0u64, + "1000000000000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <18> + ("000308fdf289e773fcbb79d69a48b3bf1fd62175c0289849af624c3eef35d138".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <19> + ("0003091856ca8ab976107b257f040d6f991f1058fea254a5a298f069f3f2f7ed".toDigest, + 2u64, + "2947499999853000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <20> + ("000309de7d55aa234dc4fdcac3d0d155fc589b3fa09f44e8f54e4a0ebb40969e".toDigest, + 1u64, + "0".parse(Uint256), + "1d40ed4738c1b04df8bd8fd102ccde2f4a62f8803134a01d124da5c2389af5b1".toDigest, + "4e3c65796be34e5a3b67459453315d4367389de0dd041a33c53f2ae90870ed76".toDigest), + # <21> + ("00030c6165750be39efdf62036b6aba37e1ec5c39336e7bb94de225f3e273a8a".toDigest, + 1u64, + "0".parse(Uint256), + "af516c5906db7f0cedb5559403704b14e983dca1a3aa15804be77a77a2f400a2".toDigest, + "8a0c9017e6ef5d552ec36e1a772b70d6c066432aacc2c619ac16cca7de079321".toDigest), + # <22> + ("00030d039b12c7e72bd265f1ef583cfd472f7d0ec13903cea991950658c6fef1".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "b3a7f5455edb25bdc00e37dca924a97f80c20adc14700acd313bc093b710192f".toDigest), + # <23> + ("00030fccb83f959973e0aa1f0b86b4f42f279b3a9da7e24dd1ffb950ee02be1d".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest)], + @[# <0> + "F90211A00F41A498B871B7A732DB8B91A4AD4E013596110F383BF3041F2E818714FB57FBA0203149AB733DABFFB4A075E06DFBAC60A68A88CEDA23AAAE3F82A059E73A0DF4A034A036B6A6B8B596BA29F2C1EB8FAC71F21B8BC72481CA2E7C21733E39DAE8E2A0D533FA53AFE1DEFBA7E2CC6E645523F3AA1FB76651634F9EEF68D063F23FEA31A0A3AEFA0444DE17ADCF431EC4C5F057D799A2AC3A568F4C303E43561A7FCB629CA0033F5BC31420628F447A5ABC3DECF498E6852B486C07ADB3BBD5334556463BD4A0015307556953C623310A1D3A70C58D9465C6EEBBA5547DF67BDCB19CA953B3A1A09FFD78C0F9E3A550DD9D45E4C82C78A437F85640E7F86461E101578E898D3FA5A01D624ED0F8C9C98AA2FCD5D8D0AE1954B1DB998E7C79F9A98601A42F255FFCAEA049BF312A5690ACF97114EEC2F0B7BA8541624339DCC4359FA63DD98D942C7745A08045A2EA09815540255B0F0071311084C58F0A16D89CE46B1A7BD3948DFBC7BDA0F86E5DFF074F176403C0E8388587D213890A5B926AFBB521767C384DC64F321AA01AD0BEC29F16127FF644D87CAF6F4EC6FA7F4EF50FBC51AC4984D496DB2CA99BA04981C605424CD0CE7233D469C85DC60FC8D88E585C3C02B4923B184B8FCDE926A012F41CFB88E684A08727C66FEE65D20D93DB71E8508A1476A61056E967494914A059D005E9864E5F502DAF61049C7A8F862E84F2591AE3394E3066C2648239B7FE80".hexToSeqByte, + # <1> + "F90211A0B5E039DE5CD9F82A1647EE05E212486A2DDB81999B80727B76C3156B40641CA9A02569520DCDB1750D48C7060FEE64B1C16388A098648AE4FEF929D75775EBBE53A0CE915C5DE8826650CC3F49E82B307A98E87359B679965A1A7796BB50298947F4A012C8C06E4EF08F8A47BB81A9F7FDD4279A7BE096E793DC10E217F5C17D1C61D7A0A5F8FF8BADFECACB3AD5EAFF8C4EE750D33DD50A1D1CC5130D027D574F4D1A9EA00E35BB939DD5CF81DC2219FB27BAE31DFE2AEE079FDBAD367619DBDBAD276D81A0C012A282076332BB92BEEDD74CF7EF9CC36875B54D1DAAC81AC0EC9CB60CE5AEA00FB445610AA14436F057BE1BC4DBD86BDBE01490AEEC46059C710797591B7E14A0EB99BB2A00E7CC498ABD0B020276A1790628369314133F35E8089B28D55839AAA0C290D06ADA3FFA0760EDD11D57F0B32F08BF668943CE01CF651B8B29DC63A197A088BD981BF81F4F0C0B37A04542269BE7C24A5D09D455807256344C6A864925E0A08924048D8F415C57A2835DB0B33F92AD157C4173D2FF819CB32D5EF89E7060A1A083DA61DBDC26D3202C343A0168D69B0DDB0572F43C20EDB924809DBA6149BE88A01B640A922A59C6DA8860058354FD78A39D00A46E0CDF92FB6A338ED556BABFD3A01D8ACC7CA60FCD378DE1DD765B680D037620F08485ED84BAD1A5BB82702F80D0A0B00F0533277C292308F9FC4BF5A85027D68F693D70CA92F1D983A32EA7C5478480".hexToSeqByte, + # <2> + "F90211A0D126CFCCD9CC595EE94F46C23D2C203D06BBF3E628C050CC34A1B5C76965A978A0B22839972A330E8CE273187A801F7257E8655A9E728283D41DF57E408A423127A06FB243B4185E550D9CA703A47E786E55B01E47968D53BBA5600865F3E7FF739AA01297B6C416BE295952F78AABCDA3D15F6087F1976194CF881A0A55D2CF8E9B6CA0523DF620C80E8C4B48A0DBB2328226921BE949F61671707A604AA95294EE4E75A0917B77A88A260098D6BBF3600D84BA39F22B59426823A2BE8A8D0771DAFA4DA0A007C73D02889414B827DA606183D4A960AAB396777217792EC55C68D6FF29B52CA079B593B9D554CFCDB265AD20A7AD018EC97B580F62904DC1052B7BD725ACB2E1A06EB993C8B75F317A7F73825D84BFA191E041667B20D0E21D2E9EF7BF311A53ACA048484A38E04A2B0001D0D3D34D731F8149459D08987EF7293331CBCCCFB3D09BA044A3A485D94F02B53EB6BF99842F88101A9E3C42D24577720742690F584B0BCCA058E3BF550CE62F775EAE8B9A49196A78F7F8DA68874420D959925F95C2CCF553A0988D0E99F69E71C24C2F17C69B7AC3F448FD7133D624FB68F148A439048C5B2EA01A48E7BAF56999165D3E2DC392D768EA88B4820D0A4D5BD8781B60A247138B36A0DBF8593863396974608C4D47BFAB51662750421D7D431842AF55740089AA3D4CA029AD034ECF2E8054767808DDF956EE2C89720F633C3B3594881A71BC582B9A0480".hexToSeqByte, + # <3> + "F90211A0C61F01AADE2395B4E17DE4C79353729EFFAD97B46C0E425B518D4D77B75CB713A03C2D55238135DA002B914D80556FFD60890E37705E7A5487B0B52FC26DA29299A07CE74DABF9451C37485200571A867213063A8FED408C6BC72F6D84564E9F8F3EA0514930C58E952176FF4C626CED5913394B5115CA7C81A9B5B543F7858F43DC01A02D93C42DC532FE12D6CCFD735EC1F22FBD7D009D386F79E76B1F0088CB412A30A0860EB915C38B045754025B34FD796CED538071EA6B98307754124A5ABB459326A00720A388A06A78305A61D749A3895319611313C5CDBE5A396E3C30793E18C9A4A0847A9DBC85722DF97F2B46D9D95DDA6C69E6D3FA68A98FCFDCF218B26A6EF727A0C7A6A3ECE732C86186CE38D8329D2242E65D8BCC240E186FF797621B9CD75B2DA05879B4B262967609E2D8ED94D0C358B9C8A6FD1759A4AA0AD836EEA77F9C76D8A0132F0874B2E39B4E357F8654B6B6A88F306E6631FAED6C0DF223F5D18A7CD448A0504A50995DB2FACDAC157BA88A6D73CCB2DEC75FCA6424EBE45381D9DBC82151A0DABD28DEDCF493701D612D87BEE5C008C40A46D8B1A5AA7860C7B388E3026A23A021680406AF0738D3FA04C827AA8735FF8ED2C057103AA909BF84C618F63E08A8A0AD769155BAC8DC43ACC0B2FA3A6D909E8C6FD08F54D3D70452197D64D4105B8DA01E1CA8EF41EBA3606E19ECBF58499208653BAD2891AD190227DB3B8705EEE06F80".hexToSeqByte, + # <4> + "F901F180A00721B77ADBF56E10BCB1CEC4787186A235BD3B3E050FCF7A3DB5F70EE515E296A0C6FF265B9C54AEE8313ECBB12EEB4AD1E8631B128135C4411275A7BF1E6CD1FAA090B44487924813BE85B041C39C0867932478F4CBA60929C13B71394C95FF9897A078F1E269731A75A6EDE38C2685C61AD03F12C6C893C56854850F313AB71ACC03A02580A50379970DDF88A7B0018640558A5471321C406BC6F4B61620F376B4CDCEA0FDC88E5C70E22C83FD378426D777E1CEC63B9FF0C0131D1574BF72665BAB26CDA01779C5AF5F90EEE40D780D37F6139B912049C2B27DD266D58475C0FB63D12CDBA0346066652BA9172AA0CBA886C2CF9057476D1CCF3C93BFD080432BE00BF92B7DA088EE2F226A61012983517A7E5BC372D112D5132FEA45A110443077BDC01F2F64A062CF9B18FDFFD9849C12306A4CCA6CD49B15CA56D11ABE6C47DBE641E64475D5A0304070EEF6C8EDD6A9D47E7B93155D3454180E3FD84274DF4CCDD58838BA3931A0F8DFB8930866D16C68E2EFAC9540104F6338EBA186DCDF47EAE9207A8F3D40CFA045177DA925E3C8BACD2E0D66EE38B740AB57A13706073A12A205511C9AC911E2A07197A582DD6C0C58F9D6BFFFD961E7FBC2D2C9DB0C2B0E2418ABDFE8CD0F8214A08AB4D4F34902A5C3BADB4F08CF7EB8A724A4016E052A388323795B1D9036F25880".hexToSeqByte, + # <5> + "F85180808080808080A0D5B9D7161EE1893A9A55170FA7277A9E16538B26ECC1FFFB9BBE05A846BF09F1A0135D15A732FC74324E53CF1F65AA91418318AB6639788A5A7D9141B76307D36C8080808080808080".hexToSeqByte, + # <6> + "F86C9E2042F6C08044B437441D2761A017EBE32E2A08636A27013FFF4D2DBC00C0B84BF8498085E8D4A51000A056E81F171BCC55A6FF8345E692C0F86E5B48E01B996CADC001622FB5E363B421A0C5D2460186F7233C927E7DB2DCC703C0E500B653CA82273B7BFAD8045D85A470".hexToSeqByte, + # <7> + "F90211A006595B4D3D6123ED45BA88365835D6FBACCB558F8DD33E77E77C9C8BF3DB5CFBA016BB7E4E3DD1FA72F83E564B820C1BCF6C4A4941ED982D47EBCB3BC047CD96AEA0696C7E771243751B5DEA642303DFCC7E1D7983E421D1E6D549F788C343296677A00B7A9FFC945648DEE1D5C10841A754DD393B0114C4E66B7224A8F65CF7D49561A0CA584D0FF992715E1397828FAA48B97BF72D3F60C1DA547791C2AE3A53C2CEC6A04B4638241287B07F85653E4D00EAB8297801960F755DB3D862F20340ED79C4E5A0BC79FEB1C69017D5980E1088972DC5B5AA925099558710F7278A9126C9C91F64A0BA218DE6CC5FF3E23C47CBC266E800C3673766F8EF7E674DA6A9E68B4C0D5AB8A02347B99A7624BCD7DA963389C45F3298D5F4170E0987A530BB1EA34B439AA3C5A088E0648DD88CBD9942917E3BFB4F37711149138642B1C94FF316CFCCCCACF216A05D60A2E9EF66F2AA23614DFF64A1BAC7C5EE1BC45F37BFBF0EBC102666D295F4A04B8CE06E5CFD9CC4002C90CFA8F3B74F4B75B33845768924544035B88D2DD11AA0FC94D998FD40EACD71AE5AA674F363BA779E757E5F9EC6BE4213E6E39E3C1F21A021A54C5361CFF0F6348BA965E1C45C819FC2D6ADB09021A4DFE936555D4E67E7A0CAB0D8B570FC0798B92CABE9614D5706E0EB100AB8C0CD25E3100BC408F7859AA08C70BEBE028D7400128AC1D3A38F4F1EA91B7978840C333B5381926532B3C97E80".hexToSeqByte, + # <8> + "F9013180A0E4E0C70AD95A0463091EC50F9484789831962CFB82F3FDFAC19022CAD57EE3D6A0D9B0458F2A967F2A9A7E944093364B17436DC1FF1A85F6154A89163FFB39D7D5A02F936EAE7EEA94F87BEA071897120B5C349598AF39A44B1B6247F36C72D1993D8080A0DDD388E37F7F097913577952EF64BC7C4C5E44037353D2844BB957423E27F64280A00D61A5FFD19AD36C4549304D5090D4E3336250C309BEEA98C60EDA76072534BAA041B5D1B27BB81232EC801192BCEC4D623ED32137D214FABC0381750E2ADC68C38080A0CE83C77E972F4D50E00D3726EF4CBAACB28176FCF4575165663314388AB67B1CA0D22BD945DAEDC22C86F7D1237CED6A4884AD4B0E15E7447624210B624CA8B05B80A0F446369450B573D124E6A2E89C02A9F71785509D5D7774753CEF73B3F8EEFB3B80".hexToSeqByte, + # <9> + "F8679E20CCB83F959973E0AA1F0B86B4F42F279B3A9DA7E24DD1FFB950EE02BE1DB846F8440180A056E81F171BCC55A6FF8345E692C0F86E5B48E01B996CADC001622FB5E363B421A0D8990879C8C8E2B683D4F0FC5A76889AEEBA15181AE48F09FBF37ED182286300".hexToSeqByte]) + + snapProofData14* = ( + "00030eefcea129f868a39f1daeae4569de31c0b4016b819fef65c59e4bf796e6".toDigest, + @[# <0> + ("00030fccb83f959973e0aa1f0b86b4f42f279b3a9da7e24dd1ffb950ee02be1d".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <1> + ("0003118073feb9889db0d24866fdf669ed3bf3bee4672df203607c0529c50b8c".toDigest, + 1u64, + "0".parse(Uint256), + "1d7f75936498f9811cb760ae8d37afe09c78ac5b0cdd32110fb350598aa9aecd".toDigest, + "8a0c9017e6ef5d552ec36e1a772b70d6c066432aacc2c619ac16cca7de079321".toDigest), + # <2> + ("00031235dbf5ebb81b0a8038d01b2bd736eb8fc0eb3efb8ab8a9cd98571bd457".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "ed1b5e1ebfbe958e0312a3d18c3089f896e5a317355de92e70579003f9fba271".toDigest), + # <3> + ("000314102fd34abfdd339c4f9c448fac3be9b1d1e76a4b243c469e059a3059b4".toDigest, + 1u64, + "21498710489000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <4> + ("000319f9e34672b2b7a29d9d9e2cf8e1ead54766af82d8873c5660a0debcf723".toDigest, + 0u64, + "1000000000000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <5> + ("0003214598191fd987f19ea3d13860921722bd154b73d05b9528e70e3a3dea97".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "6cd52a29362fafa93a10d4aff6ea2db32a555d36a74d55ab6b98a4e89c9462cb".toDigest), + # <6> + ("0003219768479c0b13dcaa7ca461801d6d5966f7f36891bcf4024e0e1d6e3322".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <7> + ("000327190c02caa1c084ec6fa508a534bdfc1b78e28856809b88907311588264".toDigest, + 1u64, + "0".parse(Uint256), + "2301463d118f2c93dca0a3152d6cf4d1438ec901ff3415c58dc406e5e2b34eee".toDigest, + "c80ea2acb5134ed19266d28c228ad60c342ab1def4c0a7c865dc58d838d4d5a1".toDigest), + # <8> + ("00032ab4b74dfb1ece764c7158f85be77a522bac7deceba896a29bbfbb8c34fe".toDigest, + 1u64, + "0".parse(Uint256), + "ed0ddb3b925a1c3a01b1c10059698683f9e62a64ea5eed4bd53dc1e920c669ab".toDigest, + "293ada5dad77fa661d8c2ef10738be3f50e1ed6125da168e1b46be3a5a0d94d6".toDigest), + # <9> + ("00032d4a54969c596849bdf25206b843c62cc592372def5e478ef1f83b8ee020".toDigest, + 1u64, + "18795912587000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <10> + ("000330de409058025f89920098312eb173e6147e407320845554f0590fa7e878".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "81f3c8ccd403fed8d0ee26dcc7d71ff1d3ba8e585db28f0bc5eb91710211ce70".toDigest), + # <11> + ("000332433610917a9343c377d6c7f0dfbf9cdd29623bf76679b36472f87585cb".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <12> + ("00033422b251aecb765be24417c9649e87826d6522fba907e45c4b4d7151d95c".toDigest, + 1u64, + "149809423200972000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <13> + ("00033501d3b63f1fcf99d62a9e3892c212c9458caa4651ddbaa47daf5835d81a".toDigest, + 0u64, + "1000000000000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <14> + ("0003372193b5494ca60dd4f7cadb9fc1edd5549ddb180299b534ae6b63217427".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <15> + ("000337e50b73cc13f4212960125132a128e809b425625fb1cc246bfe409dfea5".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <16> + ("0003393dc9cdb1a6571839e384bb0e4d918fec3e7894355ba183494f79b5ab63".toDigest, + 1u64, + "0".parse(Uint256), + "99bb8d257bf6405a21740c87e74944de531efa6ac53355899ffae1280f2bcb3b".toDigest, + "e06100e0e0cca170f4109d1d7ced0cbd2a413962f7362da0e6b22e7b07328e02".toDigest), + # <17> + ("00033a323cf07c3005d41fc93a443d89c7b8e12c14fa25c5c4d77b8512f4bf1f".toDigest, + 1u64, + "0".parse(Uint256), + "78438349e2f1493243429b2a31636aeef1c0aae66ef9a9c8241d16c14d3a0b24".toDigest, + "f2c1b49193da604b41c3fc67a848a63ffe4732ffcf4769079c5d43be4b8b9121".toDigest), + # <18> + ("00033b6fca125e4981e7570a154d83c3db9483051e74804cdf7dcd3d59783bc1".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "348a96acfc62c4fc217759c0c879a1c8eb02c002ca67927004c902e17b337d82".toDigest), + # <19> + ("00033cc422f79f509467539867f1e4f9fa6bd89403c5d5e09511413592662ef0".toDigest, + 1u64, + "0".parse(Uint256), + "c1e342b8870c3932e659251de13b6ae97461023672b8e488c1287c3164048618".toDigest, + "8a0c9017e6ef5d552ec36e1a772b70d6c066432aacc2c619ac16cca7de079321".toDigest), + # <20> + ("00033d8b5b26f057b5d04e3dd4970adb46bb3e50eafc17a19311bb9bba4c8be2".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "51a18c6f6f98c4ec00f9d9ab8655497698311ead2d4f6cf542fa123641896cf5".toDigest), + # <21> + ("00033dbf1c4d9840608884dfeec00e4c6ee0f0ea58fb917d13f10bf8f1357e9a".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <22> + ("000342a954f121b3b0eb05fc4ebe8228fa669b6fcbd86b3124e57b72e6f1730b".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "997dcb56b913f37edfd40bb60c01f713b10d0e0caa4fe1c2f049d4554c97120b".toDigest), + # <23> + ("0003453d7874edccbef0b9bfe08b69bc021dfb17f5503ab27652e9f075a58efb".toDigest, + 1u64, + "0".parse(Uint256), + "1d40ed4738c1b04df8bd8fd102ccde2f4a62f8803134a01d124da5c2389af5b1".toDigest, + "4e3c65796be34e5a3b67459453315d4367389de0dd041a33c53f2ae90870ed76".toDigest), + # <24> + ("00034645cbfbc1feea6f82afb6be6dc804a406be6b6e15f57438143e087dee29".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <25> + ("00034892e04b23ea867ae83094538124e1686e04bafa65df75fa43fb964061df".toDigest, + 0u64, + "1000000000000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest)], + @[# <0> + "F90211A00F41A498B871B7A732DB8B91A4AD4E013596110F383BF3041F2E818714FB57FBA0203149AB733DABFFB4A075E06DFBAC60A68A88CEDA23AAAE3F82A059E73A0DF4A034A036B6A6B8B596BA29F2C1EB8FAC71F21B8BC72481CA2E7C21733E39DAE8E2A0D533FA53AFE1DEFBA7E2CC6E645523F3AA1FB76651634F9EEF68D063F23FEA31A0A3AEFA0444DE17ADCF431EC4C5F057D799A2AC3A568F4C303E43561A7FCB629CA0033F5BC31420628F447A5ABC3DECF498E6852B486C07ADB3BBD5334556463BD4A0015307556953C623310A1D3A70C58D9465C6EEBBA5547DF67BDCB19CA953B3A1A09FFD78C0F9E3A550DD9D45E4C82C78A437F85640E7F86461E101578E898D3FA5A01D624ED0F8C9C98AA2FCD5D8D0AE1954B1DB998E7C79F9A98601A42F255FFCAEA049BF312A5690ACF97114EEC2F0B7BA8541624339DCC4359FA63DD98D942C7745A08045A2EA09815540255B0F0071311084C58F0A16D89CE46B1A7BD3948DFBC7BDA0F86E5DFF074F176403C0E8388587D213890A5B926AFBB521767C384DC64F321AA01AD0BEC29F16127FF644D87CAF6F4EC6FA7F4EF50FBC51AC4984D496DB2CA99BA04981C605424CD0CE7233D469C85DC60FC8D88E585C3C02B4923B184B8FCDE926A012F41CFB88E684A08727C66FEE65D20D93DB71E8508A1476A61056E967494914A059D005E9864E5F502DAF61049C7A8F862E84F2591AE3394E3066C2648239B7FE80".hexToSeqByte, + # <1> + "F90211A0B5E039DE5CD9F82A1647EE05E212486A2DDB81999B80727B76C3156B40641CA9A02569520DCDB1750D48C7060FEE64B1C16388A098648AE4FEF929D75775EBBE53A0CE915C5DE8826650CC3F49E82B307A98E87359B679965A1A7796BB50298947F4A012C8C06E4EF08F8A47BB81A9F7FDD4279A7BE096E793DC10E217F5C17D1C61D7A0A5F8FF8BADFECACB3AD5EAFF8C4EE750D33DD50A1D1CC5130D027D574F4D1A9EA00E35BB939DD5CF81DC2219FB27BAE31DFE2AEE079FDBAD367619DBDBAD276D81A0C012A282076332BB92BEEDD74CF7EF9CC36875B54D1DAAC81AC0EC9CB60CE5AEA00FB445610AA14436F057BE1BC4DBD86BDBE01490AEEC46059C710797591B7E14A0EB99BB2A00E7CC498ABD0B020276A1790628369314133F35E8089B28D55839AAA0C290D06ADA3FFA0760EDD11D57F0B32F08BF668943CE01CF651B8B29DC63A197A088BD981BF81F4F0C0B37A04542269BE7C24A5D09D455807256344C6A864925E0A08924048D8F415C57A2835DB0B33F92AD157C4173D2FF819CB32D5EF89E7060A1A083DA61DBDC26D3202C343A0168D69B0DDB0572F43C20EDB924809DBA6149BE88A01B640A922A59C6DA8860058354FD78A39D00A46E0CDF92FB6A338ED556BABFD3A01D8ACC7CA60FCD378DE1DD765B680D037620F08485ED84BAD1A5BB82702F80D0A0B00F0533277C292308F9FC4BF5A85027D68F693D70CA92F1D983A32EA7C5478480".hexToSeqByte, + # <2> + "F90211A0D126CFCCD9CC595EE94F46C23D2C203D06BBF3E628C050CC34A1B5C76965A978A0B22839972A330E8CE273187A801F7257E8655A9E728283D41DF57E408A423127A06FB243B4185E550D9CA703A47E786E55B01E47968D53BBA5600865F3E7FF739AA01297B6C416BE295952F78AABCDA3D15F6087F1976194CF881A0A55D2CF8E9B6CA0523DF620C80E8C4B48A0DBB2328226921BE949F61671707A604AA95294EE4E75A0917B77A88A260098D6BBF3600D84BA39F22B59426823A2BE8A8D0771DAFA4DA0A007C73D02889414B827DA606183D4A960AAB396777217792EC55C68D6FF29B52CA079B593B9D554CFCDB265AD20A7AD018EC97B580F62904DC1052B7BD725ACB2E1A06EB993C8B75F317A7F73825D84BFA191E041667B20D0E21D2E9EF7BF311A53ACA048484A38E04A2B0001D0D3D34D731F8149459D08987EF7293331CBCCCFB3D09BA044A3A485D94F02B53EB6BF99842F88101A9E3C42D24577720742690F584B0BCCA058E3BF550CE62F775EAE8B9A49196A78F7F8DA68874420D959925F95C2CCF553A0988D0E99F69E71C24C2F17C69B7AC3F448FD7133D624FB68F148A439048C5B2EA01A48E7BAF56999165D3E2DC392D768EA88B4820D0A4D5BD8781B60A247138B36A0DBF8593863396974608C4D47BFAB51662750421D7D431842AF55740089AA3D4CA029AD034ECF2E8054767808DDF956EE2C89720F633C3B3594881A71BC582B9A0480".hexToSeqByte, + # <3> + "F90211A0C61F01AADE2395B4E17DE4C79353729EFFAD97B46C0E425B518D4D77B75CB713A03C2D55238135DA002B914D80556FFD60890E37705E7A5487B0B52FC26DA29299A07CE74DABF9451C37485200571A867213063A8FED408C6BC72F6D84564E9F8F3EA0514930C58E952176FF4C626CED5913394B5115CA7C81A9B5B543F7858F43DC01A02D93C42DC532FE12D6CCFD735EC1F22FBD7D009D386F79E76B1F0088CB412A30A0860EB915C38B045754025B34FD796CED538071EA6B98307754124A5ABB459326A00720A388A06A78305A61D749A3895319611313C5CDBE5A396E3C30793E18C9A4A0847A9DBC85722DF97F2B46D9D95DDA6C69E6D3FA68A98FCFDCF218B26A6EF727A0C7A6A3ECE732C86186CE38D8329D2242E65D8BCC240E186FF797621B9CD75B2DA05879B4B262967609E2D8ED94D0C358B9C8A6FD1759A4AA0AD836EEA77F9C76D8A0132F0874B2E39B4E357F8654B6B6A88F306E6631FAED6C0DF223F5D18A7CD448A0504A50995DB2FACDAC157BA88A6D73CCB2DEC75FCA6424EBE45381D9DBC82151A0DABD28DEDCF493701D612D87BEE5C008C40A46D8B1A5AA7860C7B388E3026A23A021680406AF0738D3FA04C827AA8735FF8ED2C057103AA909BF84C618F63E08A8A0AD769155BAC8DC43ACC0B2FA3A6D909E8C6FD08F54D3D70452197D64D4105B8DA01E1CA8EF41EBA3606E19ECBF58499208653BAD2891AD190227DB3B8705EEE06F80".hexToSeqByte, + # <4> + "F90211A006595B4D3D6123ED45BA88365835D6FBACCB558F8DD33E77E77C9C8BF3DB5CFBA016BB7E4E3DD1FA72F83E564B820C1BCF6C4A4941ED982D47EBCB3BC047CD96AEA0696C7E771243751B5DEA642303DFCC7E1D7983E421D1E6D549F788C343296677A00B7A9FFC945648DEE1D5C10841A754DD393B0114C4E66B7224A8F65CF7D49561A0CA584D0FF992715E1397828FAA48B97BF72D3F60C1DA547791C2AE3A53C2CEC6A04B4638241287B07F85653E4D00EAB8297801960F755DB3D862F20340ED79C4E5A0BC79FEB1C69017D5980E1088972DC5B5AA925099558710F7278A9126C9C91F64A0BA218DE6CC5FF3E23C47CBC266E800C3673766F8EF7E674DA6A9E68B4C0D5AB8A02347B99A7624BCD7DA963389C45F3298D5F4170E0987A530BB1EA34B439AA3C5A088E0648DD88CBD9942917E3BFB4F37711149138642B1C94FF316CFCCCCACF216A05D60A2E9EF66F2AA23614DFF64A1BAC7C5EE1BC45F37BFBF0EBC102666D295F4A04B8CE06E5CFD9CC4002C90CFA8F3B74F4B75B33845768924544035B88D2DD11AA0FC94D998FD40EACD71AE5AA674F363BA779E757E5F9EC6BE4213E6E39E3C1F21A021A54C5361CFF0F6348BA965E1C45C819FC2D6ADB09021A4DFE936555D4E67E7A0CAB0D8B570FC0798B92CABE9614D5706E0EB100AB8C0CD25E3100BC408F7859AA08C70BEBE028D7400128AC1D3A38F4F1EA91B7978840C333B5381926532B3C97E80".hexToSeqByte, + # <5> + "F9013180A0E4E0C70AD95A0463091EC50F9484789831962CFB82F3FDFAC19022CAD57EE3D6A0D9B0458F2A967F2A9A7E944093364B17436DC1FF1A85F6154A89163FFB39D7D5A02F936EAE7EEA94F87BEA071897120B5C349598AF39A44B1B6247F36C72D1993D8080A0DDD388E37F7F097913577952EF64BC7C4C5E44037353D2844BB957423E27F64280A00D61A5FFD19AD36C4549304D5090D4E3336250C309BEEA98C60EDA76072534BAA041B5D1B27BB81232EC801192BCEC4D623ED32137D214FABC0381750E2ADC68C38080A0CE83C77E972F4D50E00D3726EF4CBAACB28176FCF4575165663314388AB67B1CA0D22BD945DAEDC22C86F7D1237CED6A4884AD4B0E15E7447624210B624CA8B05B80A0F446369450B573D124E6A2E89C02A9F71785509D5D7774753CEF73B3F8EEFB3B80".hexToSeqByte, + # <6> + "F8D18080A0CA88E945B830F4CF31706F3ABC383F1F72502ED1630F0F1ABA88CCA102FE42168080A04F8A57D446DDEC8112FE49D11F76C2C10F292B6AFE1FACF2240473A6A26FE801A0EEB94ECB6F6ADC80977BBE91B266EEBA0E687F282C916DB5DC8AE364266C2D7680A04122111E3E8E4249A8C22EF23B1A52AADB29726FFEAB3700C6952D7C8ED0BD02A0663799086629489DEF063844A76A9F43C25AD315585E6E47CA5A1BE9A36B953280808080A05F052C6D52714F7B9FFAD31F78AEB1E5F44CBC489C6661BBF4958B07CB5E3E158080".hexToSeqByte, + # <7> + "F86C9E2092E04B23EA867AE83094538124E1686E04BAFA65DF75FA43FB964061DFB84BF8498085E8D4A51000A056E81F171BCC55A6FF8345E692C0F86E5B48E01B996CADC001622FB5E363B421A0C5D2460186F7233C927E7DB2DCC703C0E500B653CA82273B7BFAD8045D85A470".hexToSeqByte]) + + snapProofData15* = ( + "000346dc5d63886594af4f0d844d013a92a305532617c1bda5119ce075f6fd1b".toDigest, + @[# <0> + ("00034892e04b23ea867ae83094538124e1686e04bafa65df75fa43fb964061df".toDigest, + 0u64, + "1000000000000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <1> + ("000349fc33ee9b6f7f5e1c8b432301cac811c1a6b896d3728058ae2b5c98e984".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <2> + ("00034e6f3c94c1886cbadb52cc401b7c8f0289fda77577bde3c9830325040b96".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <3> + ("000353e96fd4a9275c3716d4e488681ea80f05ca554f36b58d533dcc272603b4".toDigest, + 1u64, + "0".parse(Uint256), + "4f85b30e477e9a5d6882f7f26699f2914c28824c745bb4d3daeaf2cdc38bf131".toDigest, + "8a0c9017e6ef5d552ec36e1a772b70d6c066432aacc2c619ac16cca7de079321".toDigest), + # <4> + ("000357e27336d1eb4cd863dfdfffd92488791592c317facf3b7826581ec81dae".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <5> + ("00035a780a842f8ea5dd9ddfcf2df2911936d90ee7521c0f564838092aedef41".toDigest, + 121u64, + "2194454000000000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <6> + ("00035acf67a7462158f947073cf80d10ba4ef9f23c98c462c13661a88f4e7394".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "7b7e01b1062c9272df150db2cf701f7bd42d0f244ce8d52c8d8cba260b6c7ef4".toDigest), + # <7> + ("00035e213c72c1c72ee6524d1c27e659bc0546afe7c1dba08363ca147582c132".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <8> + ("00035ecd8ecd3f654c4aa148c44f9916932f8874349a546cb91fdf5b0e8d9b0b".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "45b7d51802999d4bb03aa1f0e0724f944c729c1cfab0589b77ebef6b83e03e64".toDigest), + # <9> + ("0003645b30fc18633c27305435041c27dfa6fb6cec1265ba4fe3499909917dd1".toDigest, + 2u64, + "2968499999853000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <10> + ("00036cd216f567155d1ee92b72e8a08131db3606627a97a802919ed98c5156b5".toDigest, + 0u64, + "1".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <11> + ("00036da5deaae64868e3eb96d604804a8971a469fcf342eaea844e6ea7308bbb".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "875191faaccab052ffb33984e8ba823d46be31b93a9127447e8d332f8dedaca9".toDigest), + # <12> + ("00036e0adf8ef7bc8efad3e98a9390b1bb1715460b01a8628cadf9da89903c33".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "875191faaccab052ffb33984e8ba823d46be31b93a9127447e8d332f8dedaca9".toDigest), + # <13> + ("00036f4e41ce6dc41d9a8039861872d402f2bc773a5b3b85e86b2455fb19bb9f".toDigest, + 0u64, + "1000000000000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <14> + ("0003710fac886b47d54905a5cb7f77753b7a355ad9d65ba9f45bd910a78d3347".toDigest, + 1u64, + "0".parse(Uint256), + "cb01ae0386fec71e37337b33dac5b9f358a6fe48a12142c9b4ee19bb1ad47c9e".toDigest, + "c80ea2acb5134ed19266d28c228ad60c342ab1def4c0a7c865dc58d838d4d5a1".toDigest), + # <15> + ("000371345cb2f0a57524cae862f15a3bf520bfe8367b32c4dfc7613bd30aeaee".toDigest, + 1u64, + "155349997879000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <16> + ("0003753da71317333380fc77185d9b0a4cfdc8d15cf79e035fa323a27cd1029f".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <17> + ("000375f8b244b9b80d94f58a5baf0b4e4fac4bd65eb6a888c11a451049cd4e64".toDigest, + 1u64, + "0".parse(Uint256), + "1d40ed4738c1b04df8bd8fd102ccde2f4a62f8803134a01d124da5c2389af5b1".toDigest, + "4e3c65796be34e5a3b67459453315d4367389de0dd041a33c53f2ae90870ed76".toDigest), + # <18> + ("0003774a0214d11bf307e28f0fd66f7c8d68461f146df187b6a44b2f77dbe729".toDigest, + 2u64, + "2968499999853000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <19> + ("00037dbc4033b0b39e39d57ae69dce2c4024eb09ce27e63402b386324f3464b1".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "06817e399132572fee461aa89c5bd94abc354bcc3d1417539d2472fa8318c588".toDigest), + # <20> + ("00038072db13b6e43c7f6a98bbdc667830635d0ec28c3a80dd933b5e2c7d50c0".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest)], + @[# <0> + "F90211A00F41A498B871B7A732DB8B91A4AD4E013596110F383BF3041F2E818714FB57FBA0203149AB733DABFFB4A075E06DFBAC60A68A88CEDA23AAAE3F82A059E73A0DF4A034A036B6A6B8B596BA29F2C1EB8FAC71F21B8BC72481CA2E7C21733E39DAE8E2A0D533FA53AFE1DEFBA7E2CC6E645523F3AA1FB76651634F9EEF68D063F23FEA31A0A3AEFA0444DE17ADCF431EC4C5F057D799A2AC3A568F4C303E43561A7FCB629CA0033F5BC31420628F447A5ABC3DECF498E6852B486C07ADB3BBD5334556463BD4A0015307556953C623310A1D3A70C58D9465C6EEBBA5547DF67BDCB19CA953B3A1A09FFD78C0F9E3A550DD9D45E4C82C78A437F85640E7F86461E101578E898D3FA5A01D624ED0F8C9C98AA2FCD5D8D0AE1954B1DB998E7C79F9A98601A42F255FFCAEA049BF312A5690ACF97114EEC2F0B7BA8541624339DCC4359FA63DD98D942C7745A08045A2EA09815540255B0F0071311084C58F0A16D89CE46B1A7BD3948DFBC7BDA0F86E5DFF074F176403C0E8388587D213890A5B926AFBB521767C384DC64F321AA01AD0BEC29F16127FF644D87CAF6F4EC6FA7F4EF50FBC51AC4984D496DB2CA99BA04981C605424CD0CE7233D469C85DC60FC8D88E585C3C02B4923B184B8FCDE926A012F41CFB88E684A08727C66FEE65D20D93DB71E8508A1476A61056E967494914A059D005E9864E5F502DAF61049C7A8F862E84F2591AE3394E3066C2648239B7FE80".hexToSeqByte, + # <1> + "F90211A0B5E039DE5CD9F82A1647EE05E212486A2DDB81999B80727B76C3156B40641CA9A02569520DCDB1750D48C7060FEE64B1C16388A098648AE4FEF929D75775EBBE53A0CE915C5DE8826650CC3F49E82B307A98E87359B679965A1A7796BB50298947F4A012C8C06E4EF08F8A47BB81A9F7FDD4279A7BE096E793DC10E217F5C17D1C61D7A0A5F8FF8BADFECACB3AD5EAFF8C4EE750D33DD50A1D1CC5130D027D574F4D1A9EA00E35BB939DD5CF81DC2219FB27BAE31DFE2AEE079FDBAD367619DBDBAD276D81A0C012A282076332BB92BEEDD74CF7EF9CC36875B54D1DAAC81AC0EC9CB60CE5AEA00FB445610AA14436F057BE1BC4DBD86BDBE01490AEEC46059C710797591B7E14A0EB99BB2A00E7CC498ABD0B020276A1790628369314133F35E8089B28D55839AAA0C290D06ADA3FFA0760EDD11D57F0B32F08BF668943CE01CF651B8B29DC63A197A088BD981BF81F4F0C0B37A04542269BE7C24A5D09D455807256344C6A864925E0A08924048D8F415C57A2835DB0B33F92AD157C4173D2FF819CB32D5EF89E7060A1A083DA61DBDC26D3202C343A0168D69B0DDB0572F43C20EDB924809DBA6149BE88A01B640A922A59C6DA8860058354FD78A39D00A46E0CDF92FB6A338ED556BABFD3A01D8ACC7CA60FCD378DE1DD765B680D037620F08485ED84BAD1A5BB82702F80D0A0B00F0533277C292308F9FC4BF5A85027D68F693D70CA92F1D983A32EA7C5478480".hexToSeqByte, + # <2> + "F90211A0D126CFCCD9CC595EE94F46C23D2C203D06BBF3E628C050CC34A1B5C76965A978A0B22839972A330E8CE273187A801F7257E8655A9E728283D41DF57E408A423127A06FB243B4185E550D9CA703A47E786E55B01E47968D53BBA5600865F3E7FF739AA01297B6C416BE295952F78AABCDA3D15F6087F1976194CF881A0A55D2CF8E9B6CA0523DF620C80E8C4B48A0DBB2328226921BE949F61671707A604AA95294EE4E75A0917B77A88A260098D6BBF3600D84BA39F22B59426823A2BE8A8D0771DAFA4DA0A007C73D02889414B827DA606183D4A960AAB396777217792EC55C68D6FF29B52CA079B593B9D554CFCDB265AD20A7AD018EC97B580F62904DC1052B7BD725ACB2E1A06EB993C8B75F317A7F73825D84BFA191E041667B20D0E21D2E9EF7BF311A53ACA048484A38E04A2B0001D0D3D34D731F8149459D08987EF7293331CBCCCFB3D09BA044A3A485D94F02B53EB6BF99842F88101A9E3C42D24577720742690F584B0BCCA058E3BF550CE62F775EAE8B9A49196A78F7F8DA68874420D959925F95C2CCF553A0988D0E99F69E71C24C2F17C69B7AC3F448FD7133D624FB68F148A439048C5B2EA01A48E7BAF56999165D3E2DC392D768EA88B4820D0A4D5BD8781B60A247138B36A0DBF8593863396974608C4D47BFAB51662750421D7D431842AF55740089AA3D4CA029AD034ECF2E8054767808DDF956EE2C89720F633C3B3594881A71BC582B9A0480".hexToSeqByte, + # <3> + "F90211A0C61F01AADE2395B4E17DE4C79353729EFFAD97B46C0E425B518D4D77B75CB713A03C2D55238135DA002B914D80556FFD60890E37705E7A5487B0B52FC26DA29299A07CE74DABF9451C37485200571A867213063A8FED408C6BC72F6D84564E9F8F3EA0514930C58E952176FF4C626CED5913394B5115CA7C81A9B5B543F7858F43DC01A02D93C42DC532FE12D6CCFD735EC1F22FBD7D009D386F79E76B1F0088CB412A30A0860EB915C38B045754025B34FD796CED538071EA6B98307754124A5ABB459326A00720A388A06A78305A61D749A3895319611313C5CDBE5A396E3C30793E18C9A4A0847A9DBC85722DF97F2B46D9D95DDA6C69E6D3FA68A98FCFDCF218B26A6EF727A0C7A6A3ECE732C86186CE38D8329D2242E65D8BCC240E186FF797621B9CD75B2DA05879B4B262967609E2D8ED94D0C358B9C8A6FD1759A4AA0AD836EEA77F9C76D8A0132F0874B2E39B4E357F8654B6B6A88F306E6631FAED6C0DF223F5D18A7CD448A0504A50995DB2FACDAC157BA88A6D73CCB2DEC75FCA6424EBE45381D9DBC82151A0DABD28DEDCF493701D612D87BEE5C008C40A46D8B1A5AA7860C7B388E3026A23A021680406AF0738D3FA04C827AA8735FF8ED2C057103AA909BF84C618F63E08A8A0AD769155BAC8DC43ACC0B2FA3A6D909E8C6FD08F54D3D70452197D64D4105B8DA01E1CA8EF41EBA3606E19ECBF58499208653BAD2891AD190227DB3B8705EEE06F80".hexToSeqByte, + # <4> + "F90211A006595B4D3D6123ED45BA88365835D6FBACCB558F8DD33E77E77C9C8BF3DB5CFBA016BB7E4E3DD1FA72F83E564B820C1BCF6C4A4941ED982D47EBCB3BC047CD96AEA0696C7E771243751B5DEA642303DFCC7E1D7983E421D1E6D549F788C343296677A00B7A9FFC945648DEE1D5C10841A754DD393B0114C4E66B7224A8F65CF7D49561A0CA584D0FF992715E1397828FAA48B97BF72D3F60C1DA547791C2AE3A53C2CEC6A04B4638241287B07F85653E4D00EAB8297801960F755DB3D862F20340ED79C4E5A0BC79FEB1C69017D5980E1088972DC5B5AA925099558710F7278A9126C9C91F64A0BA218DE6CC5FF3E23C47CBC266E800C3673766F8EF7E674DA6A9E68B4C0D5AB8A02347B99A7624BCD7DA963389C45F3298D5F4170E0987A530BB1EA34B439AA3C5A088E0648DD88CBD9942917E3BFB4F37711149138642B1C94FF316CFCCCCACF216A05D60A2E9EF66F2AA23614DFF64A1BAC7C5EE1BC45F37BFBF0EBC102666D295F4A04B8CE06E5CFD9CC4002C90CFA8F3B74F4B75B33845768924544035B88D2DD11AA0FC94D998FD40EACD71AE5AA674F363BA779E757E5F9EC6BE4213E6E39E3C1F21A021A54C5361CFF0F6348BA965E1C45C819FC2D6ADB09021A4DFE936555D4E67E7A0CAB0D8B570FC0798B92CABE9614D5706E0EB100AB8C0CD25E3100BC408F7859AA08C70BEBE028D7400128AC1D3A38F4F1EA91B7978840C333B5381926532B3C97E80".hexToSeqByte, + # <5> + "F8D18080A0CA88E945B830F4CF31706F3ABC383F1F72502ED1630F0F1ABA88CCA102FE42168080A04F8A57D446DDEC8112FE49D11F76C2C10F292B6AFE1FACF2240473A6A26FE801A0EEB94ECB6F6ADC80977BBE91B266EEBA0E687F282C916DB5DC8AE364266C2D7680A04122111E3E8E4249A8C22EF23B1A52AADB29726FFEAB3700C6952D7C8ED0BD02A0663799086629489DEF063844A76A9F43C25AD315585E6E47CA5A1BE9A36B953280808080A05F052C6D52714F7B9FFAD31F78AEB1E5F44CBC489C6661BBF4958B07CB5E3E158080".hexToSeqByte, + # <6> + "F8679E2045CBFBC1FEEA6F82AFB6BE6DC804A406BE6B6E15F57438143E087DEE29B846F8440180A056E81F171BCC55A6FF8345E692C0F86E5B48E01B996CADC001622FB5E363B421A0D8990879C8C8E2B683D4F0FC5A76889AEEBA15181AE48F09FBF37ED182286300".hexToSeqByte, + # <7> + "F8F1A0E091704B32131AD5B893015C65ACAE70945BFE574845212031F4F203D9F6BFA38080A0BA97D6654D6AD5ECD7E99E2FB0BAE5FAFC48FE0A438791732B2B7665F770FB5780A028A85A5BC2D648A08C7049D9AF1DD4420405105FB9262FFAF1DE185F066167F680A02C3C5E0031F4986D00A79778392BBB408C0EBE556E0F502500FB112D8B870D5B80A0334DB36F4609CA6D2DA2A26076A1718B2AF23B6A91FB0600DF22D2D3935166EE80A0F449C6D36586F11CA8870BFB1BA997C5D60C721687C4B8F28B68FD29BF25771C808080A0030E2A7CF8A7A2931E69FBF2F3681E56092B1B402B2DC66232F8724EBDDF964C80".hexToSeqByte, + # <8> + "F85180808080808080A02469E4A73E02083437104B87D255D9954A97D951CE1DE820F7AE93F20719E527808080A0AAC71CFCE42E5AE783100234C4CE22EE7250EFBBC1A8175EC9339949D557D38A8080808080".hexToSeqByte, + # <9> + "F8669D32DB13B6E43C7F6A98BBDC667830635D0EC28C3A80DD933B5E2C7D50C0B846F8440180A056E81F171BCC55A6FF8345E692C0F86E5B48E01B996CADC001622FB5E363B421A0D8990879C8C8E2B683D4F0FC5A76889AEEBA15181AE48F09FBF37ED182286300".hexToSeqByte]) + + snapProofData16* = ( + "00037ec8ec25e6d2c0bafefd59ebbd0b471449f24ac401db5abd74229ff66350".toDigest, + @[# <0> + ("00038072db13b6e43c7f6a98bbdc667830635d0ec28c3a80dd933b5e2c7d50c0".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <1> + ("000380befa767b92248684938e556119f798e0758295c27155de757d6e10bd01".toDigest, + 2u64, + "16277086331815".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <2> + ("00038308993601d95d35cea719c8f7aaa6e846c1bb91ee3f8e028e57c8e3610b".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <3> + ("0003853500cc6010965e3f4ca0b7db6582c211976e46c39d7705d2b027ca04c1".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <4> + ("0003871b8515d966bafdc762351c7663cd6213f146b7477df549c8a415dbf4fb".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <5> + ("000389961141e75c1a3dc4c43cdd054719274c5b3f8539362613627ea5491b69".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "a97dc78ea7656df2be80379a8da65eed72cfdfd5d5374bc6b726380c6e177acd".toDigest), + # <6> + ("00038b6188308c0ba9dd341b2c903c013115b29678213580ece4e125965aedf0".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <7> + ("00038f55e9663b3583764eb026a7296560e0d2e985bc3f83c228c5e748c6d787".toDigest, + 1u64, + "0".parse(Uint256), + "4f85b30e477e9a5d6882f7f26699f2914c28824c745bb4d3daeaf2cdc38bf131".toDigest, + "8a0c9017e6ef5d552ec36e1a772b70d6c066432aacc2c619ac16cca7de079321".toDigest), + # <8> + ("0003911781a2150ec9751609872559be125331f8351fef18cf986e2fd17ae278".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "b2db3db35df91e624526d6a1bb922c791d8c99838f794f29ec8446e1b49a8af7".toDigest), + # <9> + ("000392d8b3ac59617c3d17d22b2bfa18224c4d1837f6db54ad0d013f80d32bfe".toDigest, + 0u64, + "1000000000000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <10> + ("000394ee14c5ed0066b6ef779e55a74b013ebf0d639facadd4c4e118952bc836".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "8d5cb509003a6158857e1e21565770fc8f2e785b1cf6a030ab3e1445ecc822ce".toDigest), + # <11> + ("000395a5e27de1ab90798ac7b1ee2c303bf9b296c32aeaf1faf7d5755fce1d70".toDigest, + 1u64, + "0".parse(Uint256), + "1d40ed4738c1b04df8bd8fd102ccde2f4a62f8803134a01d124da5c2389af5b1".toDigest, + "4e3c65796be34e5a3b67459453315d4367389de0dd041a33c53f2ae90870ed76".toDigest), + # <12> + ("00039a47d4d7d4f8ac83db3d94980c9d081601049668a9e51eee419560d31546".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <13> + ("00039c26262593a49596e924f16a7e201ccff9c1e4f68b29c907854bd1a16233".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <14> + ("00039cfda8341d748d15ec5f4d73fa045839d10b9f742bc305ef9a58bbf4521d".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "51a18c6f6f98c4ec00f9d9ab8655497698311ead2d4f6cf542fa123641896cf5".toDigest), + # <15> + ("00039fcb8c2392f4b1e53275a261d6714e1b7f56cebf87e20b91c85a42f1facf".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "f8e96daa847cfdf47407c654fd56cb98753163e44f4f89c063a6588f3d903190".toDigest), + # <16> + ("0003a0ae4d24a2c1de1aa24c9d4e09a92eba5440436afb5d3b51822f8cb4cd20".toDigest, + 1u64, + "0".parse(Uint256), + "1d40ed4738c1b04df8bd8fd102ccde2f4a62f8803134a01d124da5c2389af5b1".toDigest, + "4e3c65796be34e5a3b67459453315d4367389de0dd041a33c53f2ae90870ed76".toDigest), + # <17> + ("0003a7f5a15f5199d50b75147895d2c9bcb57d6b2c01bb407ea88b64c2c09a8b".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <18> + ("0003ace211ff43936ba1e70206841cc5c5f223492a65c4073dffddb3e249e665".toDigest, + 0u64, + "1000000000000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <19> + ("0003b1d37dd6e33f36985702dc71c82d87c80ddbc9b7374474997710e388e99e".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <20> + ("0003b227e12a606f0a1c629ccf69678f81904e9cfd799756e9dcfe6691198cc7".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "0fe18707887bfdddf44e8abc35be361965dd078a9135a02d2705322546b3006e".toDigest), + # <21> + ("0003b2538fbbd34b138afba7bb89356124c1377a9baa9b62b1897be442c103ae".toDigest, + 0u64, + "1000000000000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <22> + ("0003b2b24c5dc077745f0805ac8c0faabcf31a737d539979a1d2d26845ace932".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <23> + ("0003bdc07a403043e21d62f22d3a6791d65eaff7e9f24a4adc62f3ed0ed54cad".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "0d79f20add4e231b24e1fdb0849a6450519a0eb0e8da7b3462f9d79d7ec5898d".toDigest)], + @[# <0> + "F90211A00F41A498B871B7A732DB8B91A4AD4E013596110F383BF3041F2E818714FB57FBA0203149AB733DABFFB4A075E06DFBAC60A68A88CEDA23AAAE3F82A059E73A0DF4A034A036B6A6B8B596BA29F2C1EB8FAC71F21B8BC72481CA2E7C21733E39DAE8E2A0D533FA53AFE1DEFBA7E2CC6E645523F3AA1FB76651634F9EEF68D063F23FEA31A0A3AEFA0444DE17ADCF431EC4C5F057D799A2AC3A568F4C303E43561A7FCB629CA0033F5BC31420628F447A5ABC3DECF498E6852B486C07ADB3BBD5334556463BD4A0015307556953C623310A1D3A70C58D9465C6EEBBA5547DF67BDCB19CA953B3A1A09FFD78C0F9E3A550DD9D45E4C82C78A437F85640E7F86461E101578E898D3FA5A01D624ED0F8C9C98AA2FCD5D8D0AE1954B1DB998E7C79F9A98601A42F255FFCAEA049BF312A5690ACF97114EEC2F0B7BA8541624339DCC4359FA63DD98D942C7745A08045A2EA09815540255B0F0071311084C58F0A16D89CE46B1A7BD3948DFBC7BDA0F86E5DFF074F176403C0E8388587D213890A5B926AFBB521767C384DC64F321AA01AD0BEC29F16127FF644D87CAF6F4EC6FA7F4EF50FBC51AC4984D496DB2CA99BA04981C605424CD0CE7233D469C85DC60FC8D88E585C3C02B4923B184B8FCDE926A012F41CFB88E684A08727C66FEE65D20D93DB71E8508A1476A61056E967494914A059D005E9864E5F502DAF61049C7A8F862E84F2591AE3394E3066C2648239B7FE80".hexToSeqByte, + # <1> + "F90211A0B5E039DE5CD9F82A1647EE05E212486A2DDB81999B80727B76C3156B40641CA9A02569520DCDB1750D48C7060FEE64B1C16388A098648AE4FEF929D75775EBBE53A0CE915C5DE8826650CC3F49E82B307A98E87359B679965A1A7796BB50298947F4A012C8C06E4EF08F8A47BB81A9F7FDD4279A7BE096E793DC10E217F5C17D1C61D7A0A5F8FF8BADFECACB3AD5EAFF8C4EE750D33DD50A1D1CC5130D027D574F4D1A9EA00E35BB939DD5CF81DC2219FB27BAE31DFE2AEE079FDBAD367619DBDBAD276D81A0C012A282076332BB92BEEDD74CF7EF9CC36875B54D1DAAC81AC0EC9CB60CE5AEA00FB445610AA14436F057BE1BC4DBD86BDBE01490AEEC46059C710797591B7E14A0EB99BB2A00E7CC498ABD0B020276A1790628369314133F35E8089B28D55839AAA0C290D06ADA3FFA0760EDD11D57F0B32F08BF668943CE01CF651B8B29DC63A197A088BD981BF81F4F0C0B37A04542269BE7C24A5D09D455807256344C6A864925E0A08924048D8F415C57A2835DB0B33F92AD157C4173D2FF819CB32D5EF89E7060A1A083DA61DBDC26D3202C343A0168D69B0DDB0572F43C20EDB924809DBA6149BE88A01B640A922A59C6DA8860058354FD78A39D00A46E0CDF92FB6A338ED556BABFD3A01D8ACC7CA60FCD378DE1DD765B680D037620F08485ED84BAD1A5BB82702F80D0A0B00F0533277C292308F9FC4BF5A85027D68F693D70CA92F1D983A32EA7C5478480".hexToSeqByte, + # <2> + "F90211A0D126CFCCD9CC595EE94F46C23D2C203D06BBF3E628C050CC34A1B5C76965A978A0B22839972A330E8CE273187A801F7257E8655A9E728283D41DF57E408A423127A06FB243B4185E550D9CA703A47E786E55B01E47968D53BBA5600865F3E7FF739AA01297B6C416BE295952F78AABCDA3D15F6087F1976194CF881A0A55D2CF8E9B6CA0523DF620C80E8C4B48A0DBB2328226921BE949F61671707A604AA95294EE4E75A0917B77A88A260098D6BBF3600D84BA39F22B59426823A2BE8A8D0771DAFA4DA0A007C73D02889414B827DA606183D4A960AAB396777217792EC55C68D6FF29B52CA079B593B9D554CFCDB265AD20A7AD018EC97B580F62904DC1052B7BD725ACB2E1A06EB993C8B75F317A7F73825D84BFA191E041667B20D0E21D2E9EF7BF311A53ACA048484A38E04A2B0001D0D3D34D731F8149459D08987EF7293331CBCCCFB3D09BA044A3A485D94F02B53EB6BF99842F88101A9E3C42D24577720742690F584B0BCCA058E3BF550CE62F775EAE8B9A49196A78F7F8DA68874420D959925F95C2CCF553A0988D0E99F69E71C24C2F17C69B7AC3F448FD7133D624FB68F148A439048C5B2EA01A48E7BAF56999165D3E2DC392D768EA88B4820D0A4D5BD8781B60A247138B36A0DBF8593863396974608C4D47BFAB51662750421D7D431842AF55740089AA3D4CA029AD034ECF2E8054767808DDF956EE2C89720F633C3B3594881A71BC582B9A0480".hexToSeqByte, + # <3> + "F90211A0C61F01AADE2395B4E17DE4C79353729EFFAD97B46C0E425B518D4D77B75CB713A03C2D55238135DA002B914D80556FFD60890E37705E7A5487B0B52FC26DA29299A07CE74DABF9451C37485200571A867213063A8FED408C6BC72F6D84564E9F8F3EA0514930C58E952176FF4C626CED5913394B5115CA7C81A9B5B543F7858F43DC01A02D93C42DC532FE12D6CCFD735EC1F22FBD7D009D386F79E76B1F0088CB412A30A0860EB915C38B045754025B34FD796CED538071EA6B98307754124A5ABB459326A00720A388A06A78305A61D749A3895319611313C5CDBE5A396E3C30793E18C9A4A0847A9DBC85722DF97F2B46D9D95DDA6C69E6D3FA68A98FCFDCF218B26A6EF727A0C7A6A3ECE732C86186CE38D8329D2242E65D8BCC240E186FF797621B9CD75B2DA05879B4B262967609E2D8ED94D0C358B9C8A6FD1759A4AA0AD836EEA77F9C76D8A0132F0874B2E39B4E357F8654B6B6A88F306E6631FAED6C0DF223F5D18A7CD448A0504A50995DB2FACDAC157BA88A6D73CCB2DEC75FCA6424EBE45381D9DBC82151A0DABD28DEDCF493701D612D87BEE5C008C40A46D8B1A5AA7860C7B388E3026A23A021680406AF0738D3FA04C827AA8735FF8ED2C057103AA909BF84C618F63E08A8A0AD769155BAC8DC43ACC0B2FA3A6D909E8C6FD08F54D3D70452197D64D4105B8DA01E1CA8EF41EBA3606E19ECBF58499208653BAD2891AD190227DB3B8705EEE06F80".hexToSeqByte, + # <4> + "F90211A006595B4D3D6123ED45BA88365835D6FBACCB558F8DD33E77E77C9C8BF3DB5CFBA016BB7E4E3DD1FA72F83E564B820C1BCF6C4A4941ED982D47EBCB3BC047CD96AEA0696C7E771243751B5DEA642303DFCC7E1D7983E421D1E6D549F788C343296677A00B7A9FFC945648DEE1D5C10841A754DD393B0114C4E66B7224A8F65CF7D49561A0CA584D0FF992715E1397828FAA48B97BF72D3F60C1DA547791C2AE3A53C2CEC6A04B4638241287B07F85653E4D00EAB8297801960F755DB3D862F20340ED79C4E5A0BC79FEB1C69017D5980E1088972DC5B5AA925099558710F7278A9126C9C91F64A0BA218DE6CC5FF3E23C47CBC266E800C3673766F8EF7E674DA6A9E68B4C0D5AB8A02347B99A7624BCD7DA963389C45F3298D5F4170E0987A530BB1EA34B439AA3C5A088E0648DD88CBD9942917E3BFB4F37711149138642B1C94FF316CFCCCCACF216A05D60A2E9EF66F2AA23614DFF64A1BAC7C5EE1BC45F37BFBF0EBC102666D295F4A04B8CE06E5CFD9CC4002C90CFA8F3B74F4B75B33845768924544035B88D2DD11AA0FC94D998FD40EACD71AE5AA674F363BA779E757E5F9EC6BE4213E6E39E3C1F21A021A54C5361CFF0F6348BA965E1C45C819FC2D6ADB09021A4DFE936555D4E67E7A0CAB0D8B570FC0798B92CABE9614D5706E0EB100AB8C0CD25E3100BC408F7859AA08C70BEBE028D7400128AC1D3A38F4F1EA91B7978840C333B5381926532B3C97E80".hexToSeqByte, + # <5> + "F89180A08A3E3728CC74CEAA55B2240F66DEC69C04B79A5163F096363A2BF8F100FBAB60808080A0A8BEF2F9EBBEE16062147C84D81FB1DBFDE9E1D62DE0C1C8BF3491FA21CAC06580A0E255FD9B39CD92FC5F0E4B76F2B8FE72F2A92E2B08AB9282B8531BF6629ECFAD8080808080A05DBEB031570C3AA08D8346A9AD51652E02A5472EC04DD749108EB609B33E4FCA808080".hexToSeqByte, + # <6> + "F87180A046EF74EC2FA4A2B382E33592BF1BF934326C2C8C124400404611AE9F32535F5EA0C920E4C0EFF49EE79B788A367582F551CE75D7CD3E4186906AD4E4128EAFD76C80808080808080808080A0440DED7DD10444F72F1BCDC4226447A92ED771453A9E2ECD8AB9B6E656BB3BEA808080".hexToSeqByte, + # <7> + "F8679E20C07A403043E21D62F22D3A6791D65EAFF7E9F24A4ADC62F3ED0ED54CADB846F8440180A056E81F171BCC55A6FF8345E692C0F86E5B48E01B996CADC001622FB5E363B421A00D79F20ADD4E231B24E1FDB0849A6450519A0EB0E8DA7B3462F9D79D7EC5898D".hexToSeqByte]) + + snapProofData17* = ( + "0003b6b57ae8453fecc6aeed2f8a78dbfb858e916f7041f910694b64c9f5c985".toDigest, + @[# <0> + ("0003bdc07a403043e21d62f22d3a6791d65eaff7e9f24a4adc62f3ed0ed54cad".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "0d79f20add4e231b24e1fdb0849a6450519a0eb0e8da7b3462f9d79d7ec5898d".toDigest), + # <1> + ("0003c0c8bae0c7b347c7038b93af65f2b4e403d5d58b5713d3a2f615dd9bbed9".toDigest, + 0u64, + "1000000000000000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <2> + ("0003c54dc85ef3940154b643ebe36da57bd25a33206df78f952080d82efa6c12".toDigest, + 0u64, + "1000000000000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <3> + ("0003c565a55c589237bbb17c7f836a1fb9daccd649ed2e83893b5c7b85e829bd".toDigest, + 1u64, + "0".parse(Uint256), + "f5440ed810088865bdf23a88972062614f07ca37c9224fb0d0bd57f6bf69a62d".toDigest, + "8a0c9017e6ef5d552ec36e1a772b70d6c066432aacc2c619ac16cca7de079321".toDigest), + # <4> + ("0003c682a3cf4548a13fddfe33ca76d3d2b5b63d862164fcf3497e66921b1150".toDigest, + 0u64, + "1000000000000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <5> + ("0003c8ba13174722428cac09fc12eb42304adf4d9f38a70b3d9cce656e7f47f4".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <6> + ("0003d3f08c1fc919b004a9fdd8c8ba531548d15b4b9147ee9dcbf24742c7f61f".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "7b7e01b1062c9272df150db2cf701f7bd42d0f244ce8d52c8d8cba260b6c7ef4".toDigest), + # <7> + ("0003d71e8803951b1cf7c27c605bbd4967938a440c7beb946b7ba8398d7f4582".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "51a18c6f6f98c4ec00f9d9ab8655497698311ead2d4f6cf542fa123641896cf5".toDigest), + # <8> + ("0003d80e60d39170761de240e301390aa773a55042170333cf874acf634c9f81".toDigest, + 1u64, + "0".parse(Uint256), + "fceed0e2138e7681736c0623cb078945d87f2bc72b034658fc72ba28ab607138".toDigest, + "8a0c9017e6ef5d552ec36e1a772b70d6c066432aacc2c619ac16cca7de079321".toDigest), + # <9> + ("0003d89988e768e89498637ed743cc93fa0d4c354426d4962c73f5bcf78be76d".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <10> + ("0003ddf110a4190e59bede1c502f12c10c6b62be5036f400852e586351862fce".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "7b7e01b1062c9272df150db2cf701f7bd42d0f244ce8d52c8d8cba260b6c7ef4".toDigest), + # <11> + ("0003e3e5571103697357949046941e704fbe13aff11a5df5ae246f34f7bad969".toDigest, + 0u64, + "1000000000000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <12> + ("0003ee5a0ab5b1721ac5ae76e3cbc767116a4a40c7105f123317a484438441df".toDigest, + 1u64, + "0".parse(Uint256), + "1d40ed4738c1b04df8bd8fd102ccde2f4a62f8803134a01d124da5c2389af5b1".toDigest, + "4e3c65796be34e5a3b67459453315d4367389de0dd041a33c53f2ae90870ed76".toDigest), + # <13> + ("0003f06a1c25e549e130e534ab8425f30faa2c1ca997bbbc155609471f892df1".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "51a18c6f6f98c4ec00f9d9ab8655497698311ead2d4f6cf542fa123641896cf5".toDigest)], + @[# <0> + "F90211A00F41A498B871B7A732DB8B91A4AD4E013596110F383BF3041F2E818714FB57FBA0203149AB733DABFFB4A075E06DFBAC60A68A88CEDA23AAAE3F82A059E73A0DF4A034A036B6A6B8B596BA29F2C1EB8FAC71F21B8BC72481CA2E7C21733E39DAE8E2A0D533FA53AFE1DEFBA7E2CC6E645523F3AA1FB76651634F9EEF68D063F23FEA31A0A3AEFA0444DE17ADCF431EC4C5F057D799A2AC3A568F4C303E43561A7FCB629CA0033F5BC31420628F447A5ABC3DECF498E6852B486C07ADB3BBD5334556463BD4A0015307556953C623310A1D3A70C58D9465C6EEBBA5547DF67BDCB19CA953B3A1A09FFD78C0F9E3A550DD9D45E4C82C78A437F85640E7F86461E101578E898D3FA5A01D624ED0F8C9C98AA2FCD5D8D0AE1954B1DB998E7C79F9A98601A42F255FFCAEA049BF312A5690ACF97114EEC2F0B7BA8541624339DCC4359FA63DD98D942C7745A08045A2EA09815540255B0F0071311084C58F0A16D89CE46B1A7BD3948DFBC7BDA0F86E5DFF074F176403C0E8388587D213890A5B926AFBB521767C384DC64F321AA01AD0BEC29F16127FF644D87CAF6F4EC6FA7F4EF50FBC51AC4984D496DB2CA99BA04981C605424CD0CE7233D469C85DC60FC8D88E585C3C02B4923B184B8FCDE926A012F41CFB88E684A08727C66FEE65D20D93DB71E8508A1476A61056E967494914A059D005E9864E5F502DAF61049C7A8F862E84F2591AE3394E3066C2648239B7FE80".hexToSeqByte, + # <1> + "F90211A0B5E039DE5CD9F82A1647EE05E212486A2DDB81999B80727B76C3156B40641CA9A02569520DCDB1750D48C7060FEE64B1C16388A098648AE4FEF929D75775EBBE53A0CE915C5DE8826650CC3F49E82B307A98E87359B679965A1A7796BB50298947F4A012C8C06E4EF08F8A47BB81A9F7FDD4279A7BE096E793DC10E217F5C17D1C61D7A0A5F8FF8BADFECACB3AD5EAFF8C4EE750D33DD50A1D1CC5130D027D574F4D1A9EA00E35BB939DD5CF81DC2219FB27BAE31DFE2AEE079FDBAD367619DBDBAD276D81A0C012A282076332BB92BEEDD74CF7EF9CC36875B54D1DAAC81AC0EC9CB60CE5AEA00FB445610AA14436F057BE1BC4DBD86BDBE01490AEEC46059C710797591B7E14A0EB99BB2A00E7CC498ABD0B020276A1790628369314133F35E8089B28D55839AAA0C290D06ADA3FFA0760EDD11D57F0B32F08BF668943CE01CF651B8B29DC63A197A088BD981BF81F4F0C0B37A04542269BE7C24A5D09D455807256344C6A864925E0A08924048D8F415C57A2835DB0B33F92AD157C4173D2FF819CB32D5EF89E7060A1A083DA61DBDC26D3202C343A0168D69B0DDB0572F43C20EDB924809DBA6149BE88A01B640A922A59C6DA8860058354FD78A39D00A46E0CDF92FB6A338ED556BABFD3A01D8ACC7CA60FCD378DE1DD765B680D037620F08485ED84BAD1A5BB82702F80D0A0B00F0533277C292308F9FC4BF5A85027D68F693D70CA92F1D983A32EA7C5478480".hexToSeqByte, + # <2> + "F90211A0D126CFCCD9CC595EE94F46C23D2C203D06BBF3E628C050CC34A1B5C76965A978A0B22839972A330E8CE273187A801F7257E8655A9E728283D41DF57E408A423127A06FB243B4185E550D9CA703A47E786E55B01E47968D53BBA5600865F3E7FF739AA01297B6C416BE295952F78AABCDA3D15F6087F1976194CF881A0A55D2CF8E9B6CA0523DF620C80E8C4B48A0DBB2328226921BE949F61671707A604AA95294EE4E75A0917B77A88A260098D6BBF3600D84BA39F22B59426823A2BE8A8D0771DAFA4DA0A007C73D02889414B827DA606183D4A960AAB396777217792EC55C68D6FF29B52CA079B593B9D554CFCDB265AD20A7AD018EC97B580F62904DC1052B7BD725ACB2E1A06EB993C8B75F317A7F73825D84BFA191E041667B20D0E21D2E9EF7BF311A53ACA048484A38E04A2B0001D0D3D34D731F8149459D08987EF7293331CBCCCFB3D09BA044A3A485D94F02B53EB6BF99842F88101A9E3C42D24577720742690F584B0BCCA058E3BF550CE62F775EAE8B9A49196A78F7F8DA68874420D959925F95C2CCF553A0988D0E99F69E71C24C2F17C69B7AC3F448FD7133D624FB68F148A439048C5B2EA01A48E7BAF56999165D3E2DC392D768EA88B4820D0A4D5BD8781B60A247138B36A0DBF8593863396974608C4D47BFAB51662750421D7D431842AF55740089AA3D4CA029AD034ECF2E8054767808DDF956EE2C89720F633C3B3594881A71BC582B9A0480".hexToSeqByte, + # <3> + "F90211A0C61F01AADE2395B4E17DE4C79353729EFFAD97B46C0E425B518D4D77B75CB713A03C2D55238135DA002B914D80556FFD60890E37705E7A5487B0B52FC26DA29299A07CE74DABF9451C37485200571A867213063A8FED408C6BC72F6D84564E9F8F3EA0514930C58E952176FF4C626CED5913394B5115CA7C81A9B5B543F7858F43DC01A02D93C42DC532FE12D6CCFD735EC1F22FBD7D009D386F79E76B1F0088CB412A30A0860EB915C38B045754025B34FD796CED538071EA6B98307754124A5ABB459326A00720A388A06A78305A61D749A3895319611313C5CDBE5A396E3C30793E18C9A4A0847A9DBC85722DF97F2B46D9D95DDA6C69E6D3FA68A98FCFDCF218B26A6EF727A0C7A6A3ECE732C86186CE38D8329D2242E65D8BCC240E186FF797621B9CD75B2DA05879B4B262967609E2D8ED94D0C358B9C8A6FD1759A4AA0AD836EEA77F9C76D8A0132F0874B2E39B4E357F8654B6B6A88F306E6631FAED6C0DF223F5D18A7CD448A0504A50995DB2FACDAC157BA88A6D73CCB2DEC75FCA6424EBE45381D9DBC82151A0DABD28DEDCF493701D612D87BEE5C008C40A46D8B1A5AA7860C7B388E3026A23A021680406AF0738D3FA04C827AA8735FF8ED2C057103AA909BF84C618F63E08A8A0AD769155BAC8DC43ACC0B2FA3A6D909E8C6FD08F54D3D70452197D64D4105B8DA01E1CA8EF41EBA3606E19ECBF58499208653BAD2891AD190227DB3B8705EEE06F80".hexToSeqByte, + # <4> + "F90211A006595B4D3D6123ED45BA88365835D6FBACCB558F8DD33E77E77C9C8BF3DB5CFBA016BB7E4E3DD1FA72F83E564B820C1BCF6C4A4941ED982D47EBCB3BC047CD96AEA0696C7E771243751B5DEA642303DFCC7E1D7983E421D1E6D549F788C343296677A00B7A9FFC945648DEE1D5C10841A754DD393B0114C4E66B7224A8F65CF7D49561A0CA584D0FF992715E1397828FAA48B97BF72D3F60C1DA547791C2AE3A53C2CEC6A04B4638241287B07F85653E4D00EAB8297801960F755DB3D862F20340ED79C4E5A0BC79FEB1C69017D5980E1088972DC5B5AA925099558710F7278A9126C9C91F64A0BA218DE6CC5FF3E23C47CBC266E800C3673766F8EF7E674DA6A9E68B4C0D5AB8A02347B99A7624BCD7DA963389C45F3298D5F4170E0987A530BB1EA34B439AA3C5A088E0648DD88CBD9942917E3BFB4F37711149138642B1C94FF316CFCCCCACF216A05D60A2E9EF66F2AA23614DFF64A1BAC7C5EE1BC45F37BFBF0EBC102666D295F4A04B8CE06E5CFD9CC4002C90CFA8F3B74F4B75B33845768924544035B88D2DD11AA0FC94D998FD40EACD71AE5AA674F363BA779E757E5F9EC6BE4213E6E39E3C1F21A021A54C5361CFF0F6348BA965E1C45C819FC2D6ADB09021A4DFE936555D4E67E7A0CAB0D8B570FC0798B92CABE9614D5706E0EB100AB8C0CD25E3100BC408F7859AA08C70BEBE028D7400128AC1D3A38F4F1EA91B7978840C333B5381926532B3C97E80".hexToSeqByte, + # <5> + "F87180A046EF74EC2FA4A2B382E33592BF1BF934326C2C8C124400404611AE9F32535F5EA0C920E4C0EFF49EE79B788A367582F551CE75D7CD3E4186906AD4E4128EAFD76C80808080808080808080A0440DED7DD10444F72F1BCDC4226447A92ED771453A9E2ECD8AB9B6E656BB3BEA808080".hexToSeqByte, + # <6> + "F8D1A0AA82B69E1052407ED103AEA1B3A87961DCDC2092FE34C3308D59C5167178DD4380A0861128792A84282D914CDEC67D247675F604C3C0016F044776B87166675B819F808080A01142DC9B9812B1E9DFB19590066BF7483DEA6BD4D53B41EE45A85A41A6D62C8FA030FACF4F40421FE1055B820818D9C1E8C808C78A59E14A750B3BE16EC7AC7996808080A0E06A11CAAE58FF63251B148A45507BD2621B238F92C0B88CCEDCE15307AD14CA80A0BB2E60DC697C86C6CFB0F8DB8C7F1E6DE144389D6255FFAEFD9D5A76BBB14851808080".hexToSeqByte, + # <7> + "F8679E206A1C25E549E130E534AB8425F30FAA2C1CA997BBBC155609471F892DF1B846F8440180A056E81F171BCC55A6FF8345E692C0F86E5B48E01B996CADC001622FB5E363B421A051A18C6F6F98C4EC00F9D9AB8655497698311EAD2D4F6CF542FA123641896CF5".hexToSeqByte]) + + snapProofData18* = ( + "0003eea209aaa3ad18d25edd052934acaff6d330941c8216c61522a6f3f52fba".toDigest, + @[# <0> + ("0003f06a1c25e549e130e534ab8425f30faa2c1ca997bbbc155609471f892df1".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "51a18c6f6f98c4ec00f9d9ab8655497698311ead2d4f6cf542fa123641896cf5".toDigest), + # <1> + ("0003f205ba9755092cc186f8d3f4a36600c69b9ae0c94636bc39f467e637ecfd".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "b6086e0169e6426b77b641d7e790795d05142f44076235dfc6ab21f369ed646a".toDigest), + # <2> + ("0003f6f4c7cba84658e5f822f327f16857f27e7890deb42d09f25217d346ce01".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <3> + ("0003f7372e596ea03bd0e147cc528c3d3c508703d4be78fbfb30b54e137bec37".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <4> + ("0003fb6ca90954bbb7959405e57f75611d043d0bd236162c14ff9b06552dbaf4".toDigest, + 0u64, + "1000000000000000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <5> + ("0003fd22dbe8af90496c9aa2785881628c9d51ad089a375b23fbc4cf63d6b01c".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <6> + ("0003fd848f871d6cfe368e901be5cbc06959d064cfffa71ab9df943f9e6c9333".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "7b7e01b1062c9272df150db2cf701f7bd42d0f244ce8d52c8d8cba260b6c7ef4".toDigest), + # <7> + ("000400b4a9618010067e740eb49a68fd5d39f3909c1bd1329d30cc6c5a5ab0b2".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <8> + ("000403535145467accaeb560f7011fceed6c0a586d7d3139ef1ff6dbff0eccd3".toDigest, + 1u64, + "47899999991705100".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <9> + ("000404b1e20e9c6dc97e091e0684fde1c9451e766a390c3d3692463b32109668".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <10> + ("0004074013702688faabd8c8f8a07d74634f4f6342c5e5dd77ea6b6abdf0c345".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <11> + ("0004086912191717346377359ab0c4379fc1a5518dad1be8cd9144cdde59e81d".toDigest, + 0u64, + "1000000000000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <12> + ("00040b03a5522d14e5f547e8c8e8138733645a90470a41cdbad25b54d1d6a19a".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <13> + ("00040b42838c1b34d1a7ef1f4434477bb7a3b8c0978d8c93653e960074c039a4".toDigest, + 2u64, + "16277498728135".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <14> + ("00040b8d5d50d44e6aed47b4274bcec4181554757a9df2f36f3c005d37634272".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <15> + ("00040d5cbebad84a5d0995494f0e680b775b14283affefec23f4ae9b599c7e3a".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <16> + ("00040de1d1a06f1950a9812258484ad9274aa119e6620f9af6dfa5e01c8b8995".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <17> + ("000410b3e7e40eaaeae5be643162730727207c5d913a6fd56cee0e35fa622a43".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <18> + ("00041371c2b3c5e47bc22704e7ab454b45b35327cc8108a5ba54498dc73b7fd6".toDigest, + 5u64, + "1000160140623403936".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <19> + ("000416eca129c10b321821db33d4d53b62119b1cb365c8bd892f102aa573143e".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <20> + ("0004184ce55b6dfb670a66ce45d81200495f6d79815ac11f60656026015d9af3".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "7b7e01b1062c9272df150db2cf701f7bd42d0f244ce8d52c8d8cba260b6c7ef4".toDigest), + # <21> + ("00041a4c877ad0b1db1e0282321b84a256e3155d8c41486eebd77c883b61760a".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <22> + ("000424a6c3c124b430cd5263e776612685317d86aeca757d9329cec99fa7aad2".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <23> + ("00042f313c46aa96b28205b93ce9877bfc88b93c2ca18582ef1bb888bcf449ff".toDigest, + 1u64, + "0".parse(Uint256), + "1d40ed4738c1b04df8bd8fd102ccde2f4a62f8803134a01d124da5c2389af5b1".toDigest, + "4e3c65796be34e5a3b67459453315d4367389de0dd041a33c53f2ae90870ed76".toDigest)], + @[# <0> + "F90211A00F41A498B871B7A732DB8B91A4AD4E013596110F383BF3041F2E818714FB57FBA0203149AB733DABFFB4A075E06DFBAC60A68A88CEDA23AAAE3F82A059E73A0DF4A034A036B6A6B8B596BA29F2C1EB8FAC71F21B8BC72481CA2E7C21733E39DAE8E2A0D533FA53AFE1DEFBA7E2CC6E645523F3AA1FB76651634F9EEF68D063F23FEA31A0A3AEFA0444DE17ADCF431EC4C5F057D799A2AC3A568F4C303E43561A7FCB629CA0033F5BC31420628F447A5ABC3DECF498E6852B486C07ADB3BBD5334556463BD4A0015307556953C623310A1D3A70C58D9465C6EEBBA5547DF67BDCB19CA953B3A1A09FFD78C0F9E3A550DD9D45E4C82C78A437F85640E7F86461E101578E898D3FA5A01D624ED0F8C9C98AA2FCD5D8D0AE1954B1DB998E7C79F9A98601A42F255FFCAEA049BF312A5690ACF97114EEC2F0B7BA8541624339DCC4359FA63DD98D942C7745A08045A2EA09815540255B0F0071311084C58F0A16D89CE46B1A7BD3948DFBC7BDA0F86E5DFF074F176403C0E8388587D213890A5B926AFBB521767C384DC64F321AA01AD0BEC29F16127FF644D87CAF6F4EC6FA7F4EF50FBC51AC4984D496DB2CA99BA04981C605424CD0CE7233D469C85DC60FC8D88E585C3C02B4923B184B8FCDE926A012F41CFB88E684A08727C66FEE65D20D93DB71E8508A1476A61056E967494914A059D005E9864E5F502DAF61049C7A8F862E84F2591AE3394E3066C2648239B7FE80".hexToSeqByte, + # <1> + "F90211A0B5E039DE5CD9F82A1647EE05E212486A2DDB81999B80727B76C3156B40641CA9A02569520DCDB1750D48C7060FEE64B1C16388A098648AE4FEF929D75775EBBE53A0CE915C5DE8826650CC3F49E82B307A98E87359B679965A1A7796BB50298947F4A012C8C06E4EF08F8A47BB81A9F7FDD4279A7BE096E793DC10E217F5C17D1C61D7A0A5F8FF8BADFECACB3AD5EAFF8C4EE750D33DD50A1D1CC5130D027D574F4D1A9EA00E35BB939DD5CF81DC2219FB27BAE31DFE2AEE079FDBAD367619DBDBAD276D81A0C012A282076332BB92BEEDD74CF7EF9CC36875B54D1DAAC81AC0EC9CB60CE5AEA00FB445610AA14436F057BE1BC4DBD86BDBE01490AEEC46059C710797591B7E14A0EB99BB2A00E7CC498ABD0B020276A1790628369314133F35E8089B28D55839AAA0C290D06ADA3FFA0760EDD11D57F0B32F08BF668943CE01CF651B8B29DC63A197A088BD981BF81F4F0C0B37A04542269BE7C24A5D09D455807256344C6A864925E0A08924048D8F415C57A2835DB0B33F92AD157C4173D2FF819CB32D5EF89E7060A1A083DA61DBDC26D3202C343A0168D69B0DDB0572F43C20EDB924809DBA6149BE88A01B640A922A59C6DA8860058354FD78A39D00A46E0CDF92FB6A338ED556BABFD3A01D8ACC7CA60FCD378DE1DD765B680D037620F08485ED84BAD1A5BB82702F80D0A0B00F0533277C292308F9FC4BF5A85027D68F693D70CA92F1D983A32EA7C5478480".hexToSeqByte, + # <2> + "F90211A0D126CFCCD9CC595EE94F46C23D2C203D06BBF3E628C050CC34A1B5C76965A978A0B22839972A330E8CE273187A801F7257E8655A9E728283D41DF57E408A423127A06FB243B4185E550D9CA703A47E786E55B01E47968D53BBA5600865F3E7FF739AA01297B6C416BE295952F78AABCDA3D15F6087F1976194CF881A0A55D2CF8E9B6CA0523DF620C80E8C4B48A0DBB2328226921BE949F61671707A604AA95294EE4E75A0917B77A88A260098D6BBF3600D84BA39F22B59426823A2BE8A8D0771DAFA4DA0A007C73D02889414B827DA606183D4A960AAB396777217792EC55C68D6FF29B52CA079B593B9D554CFCDB265AD20A7AD018EC97B580F62904DC1052B7BD725ACB2E1A06EB993C8B75F317A7F73825D84BFA191E041667B20D0E21D2E9EF7BF311A53ACA048484A38E04A2B0001D0D3D34D731F8149459D08987EF7293331CBCCCFB3D09BA044A3A485D94F02B53EB6BF99842F88101A9E3C42D24577720742690F584B0BCCA058E3BF550CE62F775EAE8B9A49196A78F7F8DA68874420D959925F95C2CCF553A0988D0E99F69E71C24C2F17C69B7AC3F448FD7133D624FB68F148A439048C5B2EA01A48E7BAF56999165D3E2DC392D768EA88B4820D0A4D5BD8781B60A247138B36A0DBF8593863396974608C4D47BFAB51662750421D7D431842AF55740089AA3D4CA029AD034ECF2E8054767808DDF956EE2C89720F633C3B3594881A71BC582B9A0480".hexToSeqByte, + # <3> + "F90211A0C61F01AADE2395B4E17DE4C79353729EFFAD97B46C0E425B518D4D77B75CB713A03C2D55238135DA002B914D80556FFD60890E37705E7A5487B0B52FC26DA29299A07CE74DABF9451C37485200571A867213063A8FED408C6BC72F6D84564E9F8F3EA0514930C58E952176FF4C626CED5913394B5115CA7C81A9B5B543F7858F43DC01A02D93C42DC532FE12D6CCFD735EC1F22FBD7D009D386F79E76B1F0088CB412A30A0860EB915C38B045754025B34FD796CED538071EA6B98307754124A5ABB459326A00720A388A06A78305A61D749A3895319611313C5CDBE5A396E3C30793E18C9A4A0847A9DBC85722DF97F2B46D9D95DDA6C69E6D3FA68A98FCFDCF218B26A6EF727A0C7A6A3ECE732C86186CE38D8329D2242E65D8BCC240E186FF797621B9CD75B2DA05879B4B262967609E2D8ED94D0C358B9C8A6FD1759A4AA0AD836EEA77F9C76D8A0132F0874B2E39B4E357F8654B6B6A88F306E6631FAED6C0DF223F5D18A7CD448A0504A50995DB2FACDAC157BA88A6D73CCB2DEC75FCA6424EBE45381D9DBC82151A0DABD28DEDCF493701D612D87BEE5C008C40A46D8B1A5AA7860C7B388E3026A23A021680406AF0738D3FA04C827AA8735FF8ED2C057103AA909BF84C618F63E08A8A0AD769155BAC8DC43ACC0B2FA3A6D909E8C6FD08F54D3D70452197D64D4105B8DA01E1CA8EF41EBA3606E19ECBF58499208653BAD2891AD190227DB3B8705EEE06F80".hexToSeqByte, + # <4> + "F90211A006595B4D3D6123ED45BA88365835D6FBACCB558F8DD33E77E77C9C8BF3DB5CFBA016BB7E4E3DD1FA72F83E564B820C1BCF6C4A4941ED982D47EBCB3BC047CD96AEA0696C7E771243751B5DEA642303DFCC7E1D7983E421D1E6D549F788C343296677A00B7A9FFC945648DEE1D5C10841A754DD393B0114C4E66B7224A8F65CF7D49561A0CA584D0FF992715E1397828FAA48B97BF72D3F60C1DA547791C2AE3A53C2CEC6A04B4638241287B07F85653E4D00EAB8297801960F755DB3D862F20340ED79C4E5A0BC79FEB1C69017D5980E1088972DC5B5AA925099558710F7278A9126C9C91F64A0BA218DE6CC5FF3E23C47CBC266E800C3673766F8EF7E674DA6A9E68B4C0D5AB8A02347B99A7624BCD7DA963389C45F3298D5F4170E0987A530BB1EA34B439AA3C5A088E0648DD88CBD9942917E3BFB4F37711149138642B1C94FF316CFCCCCACF216A05D60A2E9EF66F2AA23614DFF64A1BAC7C5EE1BC45F37BFBF0EBC102666D295F4A04B8CE06E5CFD9CC4002C90CFA8F3B74F4B75B33845768924544035B88D2DD11AA0FC94D998FD40EACD71AE5AA674F363BA779E757E5F9EC6BE4213E6E39E3C1F21A021A54C5361CFF0F6348BA965E1C45C819FC2D6ADB09021A4DFE936555D4E67E7A0CAB0D8B570FC0798B92CABE9614D5706E0EB100AB8C0CD25E3100BC408F7859AA08C70BEBE028D7400128AC1D3A38F4F1EA91B7978840C333B5381926532B3C97E80".hexToSeqByte, + # <5> + "F851808080A018DE99A3FA001D6A175ACEAABBC66634B5A9B8B50A19BF66F39805EC23A43A2880808080808080808080A06DE0C2BD8FA9438CD6547F3A9157DFE37485EAF80023B9AA520B035C1DE5EE298080".hexToSeqByte, + # <6> + "F8679E205A0AB5B1721AC5AE76E3CBC767116A4A40C7105F123317A484438441DFB846F8440180A01D40ED4738C1B04DF8BD8FD102CCDE2F4A62F8803134A01D124DA5C2389AF5B1A04E3C65796BE34E5A3B67459453315D4367389DE0DD041A33C53F2AE90870ED76".hexToSeqByte, + # <7> + "F901F1A08AE12DED98285E013B8C260A74CB0E5C4B1A1DD5B8507B7E2B55360EF4DA16EDA0D94E509D9E61CFB3CBE5CA80F95EF5D97695A80E8516C58351345A168167D5B3A02BE3F0855464CB8BB008FF2B58D0DF439E2E03AF63AB2B0E1D9F02C69CD1DA0AA0AB7E8018463747E2D78BE89384989C8CE4156A7AA7CA6DBD5E1B71F2C388ADAD80A065D3E69A5841C359147F100A760B43B84AD89FE042AAF9A088A56B8EB7305EF2A0F604D868A28C1E9B00BB30823C690BF2DCBE28F114F49665EE7BC5C9069E1E7CA02D8C29431EED433DA2FCD1C9B58321F8975E78AFB20171F8F736E9EDADDE3C56A04334A821F8375400E7112F63488EC2FE9066A85562552E389F39BAE884915751A0FCEE867686260BFCADDFEE4CB94172ED64F3F77A6A3EFD85DAC09F3719E11BCBA06CB2053A11B17E76121396A4BAEC3057C1B9F708CACEB7ACFB9D2C81BE22C70EA0EDA844AAD743315E1A1A135077659A8A00DE832EA55497573A45D8B48E24C030A00792F3647948B85216C39A322FBDD6ED93FAD8539C8C5FACB7175E200F76BCE3A08E49802660C23EA07C8CFC56F95BF0F36E95FB7132DA12294F615FD12E0B8FFCA0935A03CDA00C557974DC52344C813182ABD4AFF93D9E3E99BA57BBDDD3AF7EA8A0DB1D2EB4AD59C1A701EE2E6699955C0273E313381C8C92FEA8EF1DF1AA68E31680".hexToSeqByte, + # <8> + "F85180808080A0F9D0484B54C5F31106E825810D4659E8C10280CC5C1E0F5E9C4A7316DB23352080808080808080808080A084584194205D038938CFC485953800F065B2B7770D07EC3C43CCAC475D71A2E080".hexToSeqByte, + # <9> + "F8679E20313C46AA96B28205B93CE9877BFC88B93C2CA18582EF1BB888BCF449FFB846F8440180A01D40ED4738C1B04DF8BD8FD102CCDE2F4A62F8803134A01D124DA5C2389AF5B1A04E3C65796BE34E5A3B67459453315D4367389DE0DD041A33C53F2AE90870ED76".hexToSeqByte]) + + snapProofData19* = ( + "0004268e986d021a44de0eccdac7f07d646817cfb8c8c2347bc0f9e91df495ef".toDigest, + @[# <0> + ("00042f313c46aa96b28205b93ce9877bfc88b93c2ca18582ef1bb888bcf449ff".toDigest, + 1u64, + "0".parse(Uint256), + "1d40ed4738c1b04df8bd8fd102ccde2f4a62f8803134a01d124da5c2389af5b1".toDigest, + "4e3c65796be34e5a3b67459453315d4367389de0dd041a33c53f2ae90870ed76".toDigest), + # <1> + ("00043494ef2066af5321d5a33cc9efaedebbb5cd17db5742a541e034b393f020".toDigest, + 0u64, + "1000000000000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <2> + ("000435ee7fcd6690f522562bae11459ba2db9d35000bd6e20c0d1beb01778365".toDigest, + 0u64, + "1000000000000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <3> + ("00043738e916a066896ae70267aa77b6f119aae9b90e9d8689c019613e42423a".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "d8990879c8c8e2b683d4f0fc5a76889aeeba15181ae48f09fbf37ed182286300".toDigest), + # <4> + ("00043c2f1677bae4c0d9eca64b438208f4d5adb78b0c8220ccb8d61e333b551e".toDigest, + 1u64, + "0".parse(Uint256), + "1d40ed4738c1b04df8bd8fd102ccde2f4a62f8803134a01d124da5c2389af5b1".toDigest, + "4e3c65796be34e5a3b67459453315d4367389de0dd041a33c53f2ae90870ed76".toDigest), + # <5> + ("000451b1cd1dbf7c4b113fcbcbd4fcf7ac8c8752018bced1d5678ef34cdcd1ec".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "0016b6956211709befa61d05ee87431e2ff786deafcdb63fdeac00a052055f7d".toDigest), + # <6> + ("000454aabc3c216b5b82e4496d802d7640af75bae71a875b786612f26ebbc6be".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "82f7396dd4346519745f9947ed633258fe0b6b25103a144d72889829781baf29".toDigest), + # <7> + ("0004572f77d99777191318b58a408cb89209cf198b86cef471a940f412883013".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "875191faaccab052ffb33984e8ba823d46be31b93a9127447e8d332f8dedaca9".toDigest), + # <8> + ("0004573af6e96357e77303390abf9b425e7322ff08404f1e23efbc23ce8c083f".toDigest, + 0u64, + "1000000000000".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".toDigest), + # <9> + ("00045a0af1f2075861a918aae44364e833cd8b6c7ee861a8cf2e108894de698b".toDigest, + 1u64, + "0".parse(Uint256), + "1d40ed4738c1b04df8bd8fd102ccde2f4a62f8803134a01d124da5c2389af5b1".toDigest, + "4e3c65796be34e5a3b67459453315d4367389de0dd041a33c53f2ae90870ed76".toDigest), + # <10> + ("00045e7d88310158da1c833a9419b2d6848bef3d05165199cfd9da1dfa502006".toDigest, + 1u64, + "0".parse(Uint256), + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest, + "24dfaa354588dc0fa3c71ec6565e3f4eb6b9b06156ef5459e73013378afa2542".toDigest)], + @[# <0> + "F90211A00F41A498B871B7A732DB8B91A4AD4E013596110F383BF3041F2E818714FB57FBA0203149AB733DABFFB4A075E06DFBAC60A68A88CEDA23AAAE3F82A059E73A0DF4A034A036B6A6B8B596BA29F2C1EB8FAC71F21B8BC72481CA2E7C21733E39DAE8E2A0D533FA53AFE1DEFBA7E2CC6E645523F3AA1FB76651634F9EEF68D063F23FEA31A0A3AEFA0444DE17ADCF431EC4C5F057D799A2AC3A568F4C303E43561A7FCB629CA0033F5BC31420628F447A5ABC3DECF498E6852B486C07ADB3BBD5334556463BD4A0015307556953C623310A1D3A70C58D9465C6EEBBA5547DF67BDCB19CA953B3A1A09FFD78C0F9E3A550DD9D45E4C82C78A437F85640E7F86461E101578E898D3FA5A01D624ED0F8C9C98AA2FCD5D8D0AE1954B1DB998E7C79F9A98601A42F255FFCAEA049BF312A5690ACF97114EEC2F0B7BA8541624339DCC4359FA63DD98D942C7745A08045A2EA09815540255B0F0071311084C58F0A16D89CE46B1A7BD3948DFBC7BDA0F86E5DFF074F176403C0E8388587D213890A5B926AFBB521767C384DC64F321AA01AD0BEC29F16127FF644D87CAF6F4EC6FA7F4EF50FBC51AC4984D496DB2CA99BA04981C605424CD0CE7233D469C85DC60FC8D88E585C3C02B4923B184B8FCDE926A012F41CFB88E684A08727C66FEE65D20D93DB71E8508A1476A61056E967494914A059D005E9864E5F502DAF61049C7A8F862E84F2591AE3394E3066C2648239B7FE80".hexToSeqByte, + # <1> + "F90211A0B5E039DE5CD9F82A1647EE05E212486A2DDB81999B80727B76C3156B40641CA9A02569520DCDB1750D48C7060FEE64B1C16388A098648AE4FEF929D75775EBBE53A0CE915C5DE8826650CC3F49E82B307A98E87359B679965A1A7796BB50298947F4A012C8C06E4EF08F8A47BB81A9F7FDD4279A7BE096E793DC10E217F5C17D1C61D7A0A5F8FF8BADFECACB3AD5EAFF8C4EE750D33DD50A1D1CC5130D027D574F4D1A9EA00E35BB939DD5CF81DC2219FB27BAE31DFE2AEE079FDBAD367619DBDBAD276D81A0C012A282076332BB92BEEDD74CF7EF9CC36875B54D1DAAC81AC0EC9CB60CE5AEA00FB445610AA14436F057BE1BC4DBD86BDBE01490AEEC46059C710797591B7E14A0EB99BB2A00E7CC498ABD0B020276A1790628369314133F35E8089B28D55839AAA0C290D06ADA3FFA0760EDD11D57F0B32F08BF668943CE01CF651B8B29DC63A197A088BD981BF81F4F0C0B37A04542269BE7C24A5D09D455807256344C6A864925E0A08924048D8F415C57A2835DB0B33F92AD157C4173D2FF819CB32D5EF89E7060A1A083DA61DBDC26D3202C343A0168D69B0DDB0572F43C20EDB924809DBA6149BE88A01B640A922A59C6DA8860058354FD78A39D00A46E0CDF92FB6A338ED556BABFD3A01D8ACC7CA60FCD378DE1DD765B680D037620F08485ED84BAD1A5BB82702F80D0A0B00F0533277C292308F9FC4BF5A85027D68F693D70CA92F1D983A32EA7C5478480".hexToSeqByte, + # <2> + "F90211A0D126CFCCD9CC595EE94F46C23D2C203D06BBF3E628C050CC34A1B5C76965A978A0B22839972A330E8CE273187A801F7257E8655A9E728283D41DF57E408A423127A06FB243B4185E550D9CA703A47E786E55B01E47968D53BBA5600865F3E7FF739AA01297B6C416BE295952F78AABCDA3D15F6087F1976194CF881A0A55D2CF8E9B6CA0523DF620C80E8C4B48A0DBB2328226921BE949F61671707A604AA95294EE4E75A0917B77A88A260098D6BBF3600D84BA39F22B59426823A2BE8A8D0771DAFA4DA0A007C73D02889414B827DA606183D4A960AAB396777217792EC55C68D6FF29B52CA079B593B9D554CFCDB265AD20A7AD018EC97B580F62904DC1052B7BD725ACB2E1A06EB993C8B75F317A7F73825D84BFA191E041667B20D0E21D2E9EF7BF311A53ACA048484A38E04A2B0001D0D3D34D731F8149459D08987EF7293331CBCCCFB3D09BA044A3A485D94F02B53EB6BF99842F88101A9E3C42D24577720742690F584B0BCCA058E3BF550CE62F775EAE8B9A49196A78F7F8DA68874420D959925F95C2CCF553A0988D0E99F69E71C24C2F17C69B7AC3F448FD7133D624FB68F148A439048C5B2EA01A48E7BAF56999165D3E2DC392D768EA88B4820D0A4D5BD8781B60A247138B36A0DBF8593863396974608C4D47BFAB51662750421D7D431842AF55740089AA3D4CA029AD034ECF2E8054767808DDF956EE2C89720F633C3B3594881A71BC582B9A0480".hexToSeqByte, + # <3> + "F90211A0C61F01AADE2395B4E17DE4C79353729EFFAD97B46C0E425B518D4D77B75CB713A03C2D55238135DA002B914D80556FFD60890E37705E7A5487B0B52FC26DA29299A07CE74DABF9451C37485200571A867213063A8FED408C6BC72F6D84564E9F8F3EA0514930C58E952176FF4C626CED5913394B5115CA7C81A9B5B543F7858F43DC01A02D93C42DC532FE12D6CCFD735EC1F22FBD7D009D386F79E76B1F0088CB412A30A0860EB915C38B045754025B34FD796CED538071EA6B98307754124A5ABB459326A00720A388A06A78305A61D749A3895319611313C5CDBE5A396E3C30793E18C9A4A0847A9DBC85722DF97F2B46D9D95DDA6C69E6D3FA68A98FCFDCF218B26A6EF727A0C7A6A3ECE732C86186CE38D8329D2242E65D8BCC240E186FF797621B9CD75B2DA05879B4B262967609E2D8ED94D0C358B9C8A6FD1759A4AA0AD836EEA77F9C76D8A0132F0874B2E39B4E357F8654B6B6A88F306E6631FAED6C0DF223F5D18A7CD448A0504A50995DB2FACDAC157BA88A6D73CCB2DEC75FCA6424EBE45381D9DBC82151A0DABD28DEDCF493701D612D87BEE5C008C40A46D8B1A5AA7860C7B388E3026A23A021680406AF0738D3FA04C827AA8735FF8ED2C057103AA909BF84C618F63E08A8A0AD769155BAC8DC43ACC0B2FA3A6D909E8C6FD08F54D3D70452197D64D4105B8DA01E1CA8EF41EBA3606E19ECBF58499208653BAD2891AD190227DB3B8705EEE06F80".hexToSeqByte, + # <4> + "F901F1A08AE12DED98285E013B8C260A74CB0E5C4B1A1DD5B8507B7E2B55360EF4DA16EDA0D94E509D9E61CFB3CBE5CA80F95EF5D97695A80E8516C58351345A168167D5B3A02BE3F0855464CB8BB008FF2B58D0DF439E2E03AF63AB2B0E1D9F02C69CD1DA0AA0AB7E8018463747E2D78BE89384989C8CE4156A7AA7CA6DBD5E1B71F2C388ADAD80A065D3E69A5841C359147F100A760B43B84AD89FE042AAF9A088A56B8EB7305EF2A0F604D868A28C1E9B00BB30823C690BF2DCBE28F114F49665EE7BC5C9069E1E7CA02D8C29431EED433DA2FCD1C9B58321F8975E78AFB20171F8F736E9EDADDE3C56A04334A821F8375400E7112F63488EC2FE9066A85562552E389F39BAE884915751A0FCEE867686260BFCADDFEE4CB94172ED64F3F77A6A3EFD85DAC09F3719E11BCBA06CB2053A11B17E76121396A4BAEC3057C1B9F708CACEB7ACFB9D2C81BE22C70EA0EDA844AAD743315E1A1A135077659A8A00DE832EA55497573A45D8B48E24C030A00792F3647948B85216C39A322FBDD6ED93FAD8539C8C5FACB7175E200F76BCE3A08E49802660C23EA07C8CFC56F95BF0F36E95FB7132DA12294F615FD12E0B8FFCA0935A03CDA00C557974DC52344C813182ABD4AFF93D9E3E99BA57BBDDD3AF7EA8A0DB1D2EB4AD59C1A701EE2E6699955C0273E313381C8C92FEA8EF1DF1AA68E31680".hexToSeqByte, + # <5> + "F85180808080A0F9D0484B54C5F31106E825810D4659E8C10280CC5C1E0F5E9C4A7316DB23352080808080808080808080A084584194205D038938CFC485953800F065B2B7770D07EC3C43CCAC475D71A2E080".hexToSeqByte, + # <6> + "F8D180A0880219FBDFA3F2E80E333299410209968DA9DF6279F2C407D20375478704EAE38080A0B26686550594D14980DA0B5D9A14B75860F71EC9A45809CA3F6D5BDE79BA53108080A0B1CDE0707DF6EB9EF85732DB84642498340705A05EC4823D8248A9205DF1B06B8080A0632B007191312E23F25EBEF6437D4792C2A217CAB4A8D78358490344CADF9652808080A09743EB416241254C9B8DDDB14799F6DB5A92143193D5A621B886D019B1F993F6A0D79637ADC6FACB802CD5E44CE87CA4BF253114E6DFD0C057C8536CB0C000007280".hexToSeqByte, + # <7> + "F8679E207D88310158DA1C833A9419B2D6848BEF3D05165199CFD9DA1DFA502006B846F8440180A056E81F171BCC55A6FF8345E692C0F86E5B48E01B996CADC001622FB5E363B421A024DFAA354588DC0FA3C71EC6565E3F4EB6B9B06156EF5459E73013378AFA2542".hexToSeqByte]) + + snapProofData* = @[ + snapProofData0, snapProofData1, snapProofData2, snapProofData3, snapProofData4, + snapProofData5, snapProofData6, snapProofData7, snapProofData8, snapProofData9, + snapProofData10, snapProofData11, snapProofData12, snapProofData13, snapProofData14, + snapProofData15, snapProofData16, snapProofData17, snapProofData18, snapProofData19] diff --git a/vendor/nim-stew b/vendor/nim-stew index 598246620..49db5b27b 160000 --- a/vendor/nim-stew +++ b/vendor/nim-stew @@ -1 +1 @@ -Subproject commit 598246620da5c41d0e92a8dd6aab0755381b21cd +Subproject commit 49db5b27b9933165cf53287e7302b9d2a37a8d26