nimbus-eth1/fluffy/tools/portal_bridge/state_bridge/state_diff.nim

122 lines
3.8 KiB
Nim

# Fluffy
# Copyright (c) 2024 Status Research & Development GmbH
# Licensed and distributed under either of
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
# at your option. This file may not be copied, modified, or distributed except according to those terms.
{.push raises: [].}
import
chronicles,
stew/byteutils,
stint,
eth/common/base,
../../../rpc/rpc_calls/rpc_trace_calls,
../portal_bridge_common
type
DiffType* = enum
unchanged
create
update
delete
Code = seq[byte]
StateValue* = UInt256 | AccountNonce | Code
StateValueDiff*[StateValue] = object
kind*: DiffType
before*: StateValue
after*: StateValue
SlotDiff* = tuple[slotKey: UInt256, slotValueDiff: StateValueDiff[UInt256]]
AccountDiff* = object
address*: Address
balanceDiff*: StateValueDiff[UInt256]
nonceDiff*: StateValueDiff[AccountNonce]
storageDiff*: seq[SlotDiff]
codeDiff*: StateValueDiff[Code]
TransactionDiff* = seq[AccountDiff]
proc toStateValue(T: type UInt256, hex: string): T {.raises: [ValueError].} =
UInt256.fromHex(hex)
proc toStateValue(T: type AccountNonce, hex: string): T {.raises: [ValueError].} =
UInt256.fromHex(hex).truncate(uint64)
proc toStateValue(T: type Code, hex: string): T {.raises: [ValueError].} =
hexToSeqByte(hex)
proc toStateValueDiff(
diffJson: JsonNode, T: type StateValue
): StateValueDiff[T] {.raises: [ValueError].} =
if diffJson.kind == JString and diffJson.getStr() == "=":
return StateValueDiff[T](kind: unchanged)
elif diffJson.kind == JObject:
if diffJson{"+"} != nil:
return
StateValueDiff[T](kind: create, after: T.toStateValue(diffJson{"+"}.getStr()))
elif diffJson{"-"} != nil:
return
StateValueDiff[T](kind: delete, before: T.toStateValue(diffJson{"-"}.getStr()))
elif diffJson{"*"} != nil:
return StateValueDiff[T](
kind: update,
before: T.toStateValue(diffJson{"*"}{"from"}.getStr()),
after: T.toStateValue(diffJson{"*"}{"to"}.getStr()),
)
else:
doAssert false # unreachable
else:
doAssert false # unreachable
proc toTransactionDiff(
stateDiffJson: JsonNode
): TransactionDiff {.raises: [ValueError].} =
var txDiff = newSeqOfCap[AccountDiff](stateDiffJson.len())
for addrJson, accJson in stateDiffJson:
let storageDiffJson = accJson["storage"]
var storageDiff = newSeqOfCap[SlotDiff](storageDiffJson.len())
for slotKeyJson, slotValueJson in storageDiffJson:
storageDiff.add(
(UInt256.fromHex(slotKeyJson), toStateValueDiff(slotValueJson, UInt256))
)
let accountDiff = AccountDiff(
address: Address.fromHex(addrJson),
balanceDiff: toStateValueDiff(accJson["balance"], UInt256),
nonceDiff: toStateValueDiff(accJson["nonce"], AccountNonce),
storageDiff: storageDiff,
codeDiff: toStateValueDiff(accJson["code"], Code),
)
txDiff.add(accountDiff)
txDiff
proc toTransactionDiffs(
blockTraceJson: JsonNode
): seq[TransactionDiff] {.raises: [ValueError].} =
var txDiffs = newSeqOfCap[TransactionDiff](blockTraceJson.len())
for blockTrace in blockTraceJson:
txDiffs.add(blockTrace["stateDiff"].toTransactionDiff())
txDiffs
proc getStateDiffsByBlockNumber*(
client: RpcClient, blockId: BlockIdentifier
): Future[Result[seq[TransactionDiff], string]] {.async: (raises: []).} =
const traceOpts = @["stateDiff"]
try:
let blockTraceJson = await client.trace_replayBlockTransactions(blockId, traceOpts)
if blockTraceJson.isNil:
return err("EL failed to provide requested state diff")
ok(blockTraceJson.toTransactionDiffs())
except CatchableError as e:
return err("EL JSON-RPC trace_replayBlockTransactions failed: " & e.msg)