Handle blocks from cache (#1312)

* Handle blocks from cache
This commit is contained in:
KonradStaniec 2022-11-23 12:46:09 +01:00 committed by GitHub
parent e56bd994ff
commit 52a2ba4bd7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 127 additions and 21 deletions

View File

@ -60,3 +60,8 @@ proc getByNumber*(
break
return payloadResult
proc getPayloadByHash*(
self: BlockCache,
hash: BlockHash): Opt[ExecutionPayloadV1] =
return self.blocks.eq(hash)

View File

@ -19,6 +19,7 @@ import
beacon_chain/eth1/eth1_monitor,
beacon_chain/networking/network_metadata,
beacon_chain/spec/forks,
./rpc_utils,
../validate_proof,
../block_cache
@ -88,7 +89,7 @@ template rpcClient(lcProxy: LightClientRpcProxy): RpcClient = lcProxy.proxy.getC
proc getPayloadByTag(
proxy: LightClientRpcProxy,
quantityTag: string): ExecutionPayloadV1 {.raises: [ValueError, Defect].} =
quantityTag: string): results.Opt[ExecutionPayloadV1] {.raises: [ValueError, Defect].} =
checkPreconditions(proxy)
let tagResult = parseQuantityTag(quantityTag)
@ -98,25 +99,25 @@ proc getPayloadByTag(
let tag = tagResult.get()
var payload: ExecutionPayloadV1
case tag.kind
of LatestBlock:
# this will always be ok as we always validate that cache is not empty
payload = proxy.blockCache.latest.get
# this will always return some block, as we always checkPreconditions
return proxy.blockCache.latest
of BlockNumber:
let payLoadResult = proxy.blockCache.getByNumber(tag.blockNumber)
if payLoadResult.isErr():
raise newException(
ValueError, "Block not stored in cache " & $tag.blockNumber
)
payload = payLoadResult.get
return proxy.blockCache.getByNumber(tag.blockNumber)
return payload
proc getPayloadByTagOrThrow(
proxy: LightClientRpcProxy,
quantityTag: string): ExecutionPayloadV1 {.raises: [ValueError, Defect].} =
let tagResult = getPayloadByTag(proxy, quantityTag)
if tagResult.isErr:
raise newException(ValueError, "No block stored for given tag " & quantityTag)
return tagResult.get()
proc installEthApiHandlers*(lcProxy: LightClientRpcProxy) =
template payload(): Opt[ExecutionPayloadV1] = lcProxy.executionPayload
lcProxy.proxy.rpc("eth_chainId") do() -> HexQuantityStr:
return encodeQuantity(lcProxy.chainId)
@ -132,7 +133,7 @@ proc installEthApiHandlers*(lcProxy: LightClientRpcProxy) =
# can mean different blocks and ultimatly piece received piece of state
# must by validated against correct state root
let
executionPayload = lcProxy.getPayloadByTag(quantityTag)
executionPayload = lcProxy.getPayloadByTagOrThrow(quantityTag)
blockNumber = executionPayload.blockNumber.uint64
info "Forwarding get_Balance", executionBn = blockNumber
@ -156,7 +157,7 @@ proc installEthApiHandlers*(lcProxy: LightClientRpcProxy) =
lcProxy.proxy.rpc("eth_getStorageAt") do(address: Address, slot: HexDataStr, quantityTag: string) -> HexDataStr:
let
executionPayload = lcProxy.getPayloadByTag(quantityTag)
executionPayload = lcProxy.getPayloadByTagOrThrow(quantityTag)
uslot = UInt256.fromHex(slot.string)
blockNumber = executionPayload.blockNumber.uint64
@ -174,7 +175,7 @@ proc installEthApiHandlers*(lcProxy: LightClientRpcProxy) =
lcProxy.proxy.rpc("eth_getTransactionCount") do(address: Address, quantityTag: string) -> HexQuantityStr:
let
executionPayload = lcProxy.getPayloadByTag(quantityTag)
executionPayload = lcProxy.getPayloadByTagOrThrow(quantityTag)
blockNumber = executionPayload.blockNumber.uint64
info "Forwarding eth_getTransactionCount", executionBn = blockNumber
@ -198,7 +199,7 @@ proc installEthApiHandlers*(lcProxy: LightClientRpcProxy) =
lcProxy.proxy.rpc("eth_getCode") do(address: Address, quantityTag: string) -> HexDataStr:
let
executionPayload = lcProxy.getPayloadByTag(quantityTag)
executionPayload = lcProxy.getPayloadByTagOrThrow(quantityTag)
blockNumber = executionPayload.blockNumber.uint64
let
@ -239,9 +240,23 @@ proc installEthApiHandlers*(lcProxy: LightClientRpcProxy) =
lcProxy.proxy.registerProxyMethod("eth_sendRawTransaction")
lcProxy.proxy.registerProxyMethod("eth_getTransactionReceipt")
# TODO Respond to this calls using BlockCache
lcProxy.proxy.registerProxyMethod("eth_getBlockByNumber")
lcProxy.proxy.registerProxyMethod("eth_getBlockByHash")
# TODO currently we do not handle fullTransactions flag. It require updates on
# nim-web3 side
lcProxy.proxy.rpc("eth_getBlockByNumber") do(quantityTag: string, fullTransactions: bool) -> Option[BlockObject]:
let executionPayload = lcProxy.getPayloadByTag(quantityTag)
if executionPayload.isErr:
return none(BlockObject)
return some(asBlockObject(executionPayload.get()))
lcProxy.proxy.rpc("eth_getBlockByHash") do(blockHash: BlockHash, fullTransactions: bool) -> Option[BlockObject]:
let executionPayload = lcProxy.blockCache.getPayloadByHash(blockHash)
if executionPayload.isErr:
return none(BlockObject)
return some(asBlockObject(executionPayload.get()))
proc new*(
T: type LightClientRpcProxy,

View File

@ -0,0 +1,86 @@
import
std/typetraits,
eth/common/eth_types as etypes,
eth/[trie, rlp, trie/db],
stint,
web3
template unsafeQuantityToInt64(q: Quantity): int64 =
int64 q
func toFixedBytes(d: MDigest[256]): FixedBytes[32] =
FixedBytes[32](d.data)
template asEthHash(hash: BlockHash): Hash256 =
Hash256(data: distinctBase(hash))
proc calculateTransactionData(
items: openArray[TypedTransaction]): (etypes.Hash256, seq[TxHash], uint64) =
## returns tuple composed of
## - root of transactions trie
## - list of transactions hashes
## - total size of transactions in block
var tr = initHexaryTrie(newMemoryDB())
var txHashes: seq[TxHash]
var txSize: uint64
for i, t in items:
let tx = distinctBase(t)
txSize = txSize + uint64(len(tx))
tr.put(rlp.encode(i), tx)
txHashes.add(toFixedBytes(keccakHash(tx)))
return (tr.rootHash(), txHashes, txSize)
func blockHeaderSize(payload: ExecutionPayloadV1, txRoot: etypes.Hash256): uint64 =
let bh = etypes.BlockHeader(
parentHash : payload.parentHash.asEthHash,
ommersHash : etypes.EMPTY_UNCLE_HASH,
coinbase : etypes.EthAddress payload.feeRecipient,
stateRoot : payload.stateRoot.asEthHash,
txRoot : txRoot,
receiptRoot : payload.receiptsRoot.asEthHash,
bloom : distinctBase(payload.logsBloom),
difficulty : default(etypes.DifficultyInt),
blockNumber : payload.blockNumber.distinctBase.u256,
gasLimit : payload.gasLimit.unsafeQuantityToInt64,
gasUsed : payload.gasUsed.unsafeQuantityToInt64,
timestamp : fromUnix payload.timestamp.unsafeQuantityToInt64,
extraData : bytes payload.extraData,
mixDigest : payload.prevRandao.asEthHash,
nonce : default(etypes.BlockNonce),
fee : some payload.baseFeePerGas
)
return uint64(len(rlp.encode(bh)))
proc asBlockObject*(p: ExecutionPayloadV1): BlockObject =
# TODO currently we always calculate txHashes as BlockObject does not have
# option of returning full transactions. It needs fixing at nim-web3 library
# level
let (txRoot, txHashes, txSize) = calculateTransactionData(p.transactions)
let headerSize = blockHeaderSize(p, txRoot)
let blockSize = txSize + headerSize
BlockObject(
number: p.blockNumber,
hash: p.blockHash,
parentHash: p.parentHash,
sha3Uncles: FixedBytes(etypes.EMPTY_UNCLE_HASH.data),
logsBloom: p.logsBloom,
transactionsRoot: toFixedBytes(txRoot),
stateRoot: p.stateRoot,
receiptsRoot: p.receiptsRoot,
miner: p.feeRecipient,
difficulty: UInt256.zero,
extraData: p.extraData.toHex,
gasLimit: p.gasLimit,
gasUsed: p.gasUsed,
timestamp: p.timestamp,
nonce: some(default(FixedBytes[8])),
size: Quantity(blockSize),
# TODO It does not matter what we put here in after merge blocks.
# Other projects like `helios` return `0`, data providers like alchemy return
# transition difficulty. For now retruning `0` as this is a bit easier to do.
totalDifficulty: UInt256.zero,
transactions: txHashes,
uncles: @[],
baseFeePerGas: some(p.baseFeePerGas)
)