web3-developer f9956eba59
Fluffy State Bridge - Building state using state diffs. (#2486)
* Started state bridge.

* Implement call to fetch stateDiffs using trace_replayBlockTransactions.

* Convert JSON responses to stateDiff types.

* State updates working for first few blocks.

* Correctly building state for first 200K blocks.

* Add storage of code and cleanup.

* Start state bridge refactor.

* More cleanup and fixes.

* Use RocksDb as backend for state.

* Implement transactions.

* Build RocksDb dependency when building fluffy tools.

* Move code to world state helper.

* Implement producer and consumer queue.

* Cleanup exceptions.

* Improve logging.

* Add update caches to DatabaseRef backends.
2024-07-18 17:01:40 +08:00

113 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/[eth_types, eth_types_rlp],
../../../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
StateDiffRef* = ref object
balances*: Table[EthAddress, StateValueDiff[UInt256]]
nonces*: Table[EthAddress, StateValueDiff[AccountNonce]]
storage*: Table[EthAddress, Table[UInt256, StateValueDiff[UInt256]]]
code*: Table[EthAddress, StateValueDiff[Code]]
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 toStateDiff(stateDiffJson: JsonNode): StateDiffRef {.raises: [ValueError].} =
let stateDiff = StateDiffRef()
for addrJson, accJson in stateDiffJson.pairs:
let address = EthAddress.fromHex(addrJson)
stateDiff.balances[address] = toStateValueDiff(accJson["balance"], UInt256)
stateDiff.nonces[address] = toStateValueDiff(accJson["nonce"], AccountNonce)
stateDiff.code[address] = toStateValueDiff(accJson["code"], Code)
let storageDiff = accJson["storage"]
var accountStorage: Table[UInt256, StateValueDiff[UInt256]]
for slotKeyJson, slotValueJson in storageDiff.pairs:
let slotKey = UInt256.fromHex(slotKeyJson)
accountStorage[slotKey] = toStateValueDiff(slotValueJson, UInt256)
stateDiff.storage[address] = ensureMove(accountStorage)
stateDiff
proc toStateDiffs(
blockTraceJson: JsonNode
): seq[StateDiffRef] {.raises: [ValueError].} =
var stateDiffs = newSeqOfCap[StateDiffRef](blockTraceJson.len())
for blockTrace in blockTraceJson:
stateDiffs.add(blockTrace["stateDiff"].toStateDiff())
stateDiffs
proc getStateDiffsByBlockNumber*(
client: RpcClient, blockId: BlockIdentifier
): Future[Result[seq[StateDiffRef], 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.toStateDiffs())
except CatchableError as e:
return err("EL JSON-RPC trace_replayBlockTransactions failed: " & e.msg)