nimbus-eth1/hive_integration/nodocker/engine/withdrawals/wd_history.nim

96 lines
3.2 KiB
Nim

import
std/[tables, sets, strutils, math],
eth/common/eth_types,
json_rpc/[rpcclient],
stew/[byteutils, results],
../engine_client
type
Withdrawals* = ref object
list*: seq[Withdrawal]
# Helper structure used to keep history of the amounts
# withdrawn to each test account.
WDHistory* = object
map: Table[uint64, Withdrawals]
proc put*(wh: var WDHistory, blockNumber: uint64, wds: openArray[Withdrawal]) =
wh.map[blockNumber] = Withdrawals(
list: @wds
)
proc get*(wh: WDHistory, blockNumber: uint64): Result[seq[Withdrawal], string] =
let wds = wh.map.getOrDefault(blockNumber)
if wds.isNil:
return err("withdrawal not found in block " & $blockNumber)
ok(wds.list)
# Helper types to convert gwei into wei more easily
func weiAmount(w: Withdrawal): UInt256 =
w.amount.u256 * (10 ^ 9).u256
# Gets an account expected value for a given block, taking into account all
# withdrawals that credited the account.
func getExpectedAccountBalance*(wh: WDHistory, account: EthAddress, blockNumber: uint64): UInt256 =
for b in 0..blockNumber:
let wds = wh.map.getOrDefault(b)
if wds.isNil: continue
for wd in wds.list:
if wd.address == account:
result += wd.weiAmount
# Get a list of all addresses that were credited by withdrawals on a given block.
func getAddressesWithdrawnOnBlock*(wh: WDHistory, blockNumber: uint64): seq[EthAddress] =
var addressMap: HashSet[EthAddress]
let wds = wh.map.getOrDefault(blockNumber)
if wds.isNil.not:
for wd in wds.list:
addressMap.incl wd.address
for address in addressMap:
result.add address
# Get the withdrawals list for a given block.
func getWithdrawals*(wh: WDHistory, blockNumber: uint64): Withdrawals =
let wds = wh.map.getOrDefault(blockNumber)
if wds.isNil:
Withdrawals()
else:
wds
# Get the withdrawn accounts list until a given block height.
func getWithdrawnAccounts*(wh: WDHistory, blockHeight: uint64): Table[EthAddress, UInt256] =
for blockNumber in 0..blockHeight:
let wds = wh.map.getOrDefault(blockNumber)
if wds.isNil: continue
for wd in wds.list:
result.withValue(wd.address, value) do:
value[] += wd.weiAmount
do:
result[wd.address] = wd.weiAmount
# Verify all withdrawals on a client at a given height
proc verifyWithdrawals*(wh: WDHistory, blockNumber: uint64, rpcBlock: Option[UInt256], client: RpcClient): Result[void, string] =
let accounts = wh.getWithdrawnAccounts(blockNumber)
for account, expectedBalance in accounts:
let res = if rpcBlock.isSome:
client.balanceAt(account, rpcBlock.get)
else:
client.balanceAt(account)
res.expectBalanceEqual(account, expectedBalance)
# All withdrawals account have a bytecode that unconditionally set the
# zero storage key to one on EVM execution.
# Withdrawals must not trigger EVM so we expect zero.
let s = if rpcBlock.isSome:
client.storageAt(account, 0.u256, rpcBlock.get)
else:
client.storageAt(account, 0.u256)
s.expectStorageEqual(account, 0.u256)
ok()
# Create a new copy of the withdrawals history
func copy*(wh: WDHistory): WDHistory =
for k, v in wh.map:
result.map[k] = v