Ordered trie for trie root computations (#6610)
Gives a 100x speed boost for this operation which happens during optimistic sync when computing the block hash. Co-authored-by: Etan Kissling <etan@nimbus.team>
This commit is contained in:
parent
e2d65a39a3
commit
7990cc2d1c
|
@ -9,11 +9,11 @@
|
||||||
|
|
||||||
import
|
import
|
||||||
std/[json, sequtils, times],
|
std/[json, sequtils, times],
|
||||||
eth/common/[eth_types_rlp, transaction],
|
eth/common/eth_types_rlp,
|
||||||
eth/keys,
|
eth/keys,
|
||||||
eth/p2p/discoveryv5/random2,
|
eth/p2p/discoveryv5/random2,
|
||||||
eth/rlp,
|
eth/rlp,
|
||||||
eth/trie/[db, hexary],
|
eth/trie/ordered_trie,
|
||||||
json_rpc/jsonmarshal,
|
json_rpc/jsonmarshal,
|
||||||
secp256k1,
|
secp256k1,
|
||||||
web3/[engine_api_types, eth_api_types, conversions],
|
web3/[engine_api_types, eth_api_types, conversions],
|
||||||
|
@ -1210,6 +1210,12 @@ type
|
||||||
withdrawalRequests: seq[ETHWithdrawalRequest]
|
withdrawalRequests: seq[ETHWithdrawalRequest]
|
||||||
consolidationRequests: seq[ETHConsolidationRequest]
|
consolidationRequests: seq[ETHConsolidationRequest]
|
||||||
|
|
||||||
|
template append*(
|
||||||
|
w: var RlpWriter, v:
|
||||||
|
ETHWithdrawal | ETHDepositRequest | ETHWithdrawalRequest |
|
||||||
|
ETHConsolidationRequest) =
|
||||||
|
w.appendRawBytes(v.bytes)
|
||||||
|
|
||||||
proc ETHExecutionBlockHeaderCreateFromJson(
|
proc ETHExecutionBlockHeaderCreateFromJson(
|
||||||
executionHash: ptr Eth2Digest,
|
executionHash: ptr Eth2Digest,
|
||||||
blockHeaderJson: cstring): ptr ETHExecutionBlockHeader {.exported.} =
|
blockHeaderJson: cstring): ptr ETHExecutionBlockHeader {.exported.} =
|
||||||
|
@ -1357,13 +1363,8 @@ proc ETHExecutionBlockHeaderCreateFromJson(
|
||||||
amount: wd.amount,
|
amount: wd.amount,
|
||||||
bytes: rlpBytes)
|
bytes: rlpBytes)
|
||||||
|
|
||||||
var tr = initHexaryTrie(newMemoryDB())
|
let tr = orderedTrieRoot(wds)
|
||||||
for i, wd in wds:
|
if tr != data.withdrawalsRoot.get.asEth2Digest:
|
||||||
try:
|
|
||||||
tr.put(rlp.encode(i.uint), wd.bytes)
|
|
||||||
except RlpError:
|
|
||||||
raiseAssert "Unreachable"
|
|
||||||
if tr.rootHash() != data.withdrawalsRoot.get.asEth2Digest:
|
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
# Construct deposit requests
|
# Construct deposit requests
|
||||||
|
@ -1460,28 +1461,14 @@ proc ETHExecutionBlockHeaderCreateFromJson(
|
||||||
data.consolidationRequests.isSome:
|
data.consolidationRequests.isSome:
|
||||||
doAssert data.requestsRoot.isSome # Checked above
|
doAssert data.requestsRoot.isSome # Checked above
|
||||||
|
|
||||||
var
|
var b = OrderedTrieRootBuilder.init(
|
||||||
tr = initHexaryTrie(newMemoryDB())
|
depositRequests.len + withdrawalRequests.len + consolidationRequests.len)
|
||||||
i = 0'u64
|
|
||||||
for req in depositRequests:
|
b.add(depositRequests)
|
||||||
try:
|
b.add(withdrawalRequests)
|
||||||
tr.put(rlp.encode(i.uint), req.bytes)
|
b.add(consolidationRequests)
|
||||||
except RlpError:
|
|
||||||
raiseAssert "Unreachable"
|
if b.rootHash() != data.requestsRoot.get.asEth2Digest:
|
||||||
inc i
|
|
||||||
for req in withdrawalRequests:
|
|
||||||
try:
|
|
||||||
tr.put(rlp.encode(i.uint), req.bytes)
|
|
||||||
except RlpError:
|
|
||||||
raiseAssert "Unreachable"
|
|
||||||
inc i
|
|
||||||
for req in consolidationRequests:
|
|
||||||
try:
|
|
||||||
tr.put(rlp.encode(i.uint), req.bytes)
|
|
||||||
except RlpError:
|
|
||||||
raiseAssert "Unreachable"
|
|
||||||
inc i
|
|
||||||
if tr.rootHash() != data.requestsRoot.get.asEth2Digest:
|
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
let executionBlockHeader = ETHExecutionBlockHeader.new()
|
let executionBlockHeader = ETHExecutionBlockHeader.new()
|
||||||
|
@ -1653,6 +1640,9 @@ type
|
||||||
signature: seq[byte]
|
signature: seq[byte]
|
||||||
bytes: TypedTransaction
|
bytes: TypedTransaction
|
||||||
|
|
||||||
|
template append*(w: var RlpWriter, v: ETHTransaction) =
|
||||||
|
w.appendRawBytes(distinctBase v.bytes)
|
||||||
|
|
||||||
proc ETHTransactionsCreateFromJson(
|
proc ETHTransactionsCreateFromJson(
|
||||||
transactionsRoot: ptr Eth2Digest,
|
transactionsRoot: ptr Eth2Digest,
|
||||||
transactionsJson: cstring): ptr seq[ETHTransaction] {.exported.} =
|
transactionsJson: cstring): ptr seq[ETHTransaction] {.exported.} =
|
||||||
|
@ -1900,13 +1890,7 @@ proc ETHTransactionsCreateFromJson(
|
||||||
signature: @rawSig,
|
signature: @rawSig,
|
||||||
bytes: rlpBytes.TypedTransaction)
|
bytes: rlpBytes.TypedTransaction)
|
||||||
|
|
||||||
var tr = initHexaryTrie(newMemoryDB())
|
if orderedTrieRoot(txs) != transactionsRoot[]:
|
||||||
for i, transaction in txs:
|
|
||||||
try:
|
|
||||||
tr.put(rlp.encode(i.uint), distinctBase(transaction.bytes))
|
|
||||||
except RlpError:
|
|
||||||
raiseAssert "Unreachable"
|
|
||||||
if tr.rootHash() != transactionsRoot[]:
|
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
let transactions = seq[ETHTransaction].new()
|
let transactions = seq[ETHTransaction].new()
|
||||||
|
@ -2466,6 +2450,9 @@ type
|
||||||
logs: seq[ETHLog]
|
logs: seq[ETHLog]
|
||||||
bytes: seq[byte]
|
bytes: seq[byte]
|
||||||
|
|
||||||
|
template append*(w: var RlpWriter, v: ETHReceipt) =
|
||||||
|
w.appendRawBytes(v.bytes)
|
||||||
|
|
||||||
proc ETHReceiptsCreateFromJson(
|
proc ETHReceiptsCreateFromJson(
|
||||||
receiptsRoot: ptr Eth2Digest,
|
receiptsRoot: ptr Eth2Digest,
|
||||||
receiptsJson: cstring,
|
receiptsJson: cstring,
|
||||||
|
@ -2610,13 +2597,7 @@ proc ETHReceiptsCreateFromJson(
|
||||||
data: it.data)),
|
data: it.data)),
|
||||||
bytes: rlpBytes)
|
bytes: rlpBytes)
|
||||||
|
|
||||||
var tr = initHexaryTrie(newMemoryDB())
|
if orderedTrieRoot(recs) != receiptsRoot[]:
|
||||||
for i, rec in recs:
|
|
||||||
try:
|
|
||||||
tr.put(rlp.encode(i.uint), rec.bytes)
|
|
||||||
except RlpError:
|
|
||||||
raiseAssert "Unreachable"
|
|
||||||
if tr.rootHash() != receiptsRoot[]:
|
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
let receipts = seq[ETHReceipt].new()
|
let receipts = seq[ETHReceipt].new()
|
||||||
|
|
|
@ -14,7 +14,7 @@ import
|
||||||
stew/[byteutils, endians2, objects],
|
stew/[byteutils, endians2, objects],
|
||||||
chronicles,
|
chronicles,
|
||||||
eth/common/[eth_types, eth_types_rlp],
|
eth/common/[eth_types, eth_types_rlp],
|
||||||
eth/rlp, eth/trie/[db, hexary],
|
eth/rlp, eth/trie/ordered_trie,
|
||||||
# Internal
|
# Internal
|
||||||
"."/[eth2_merkleization, forks, ssz_codec]
|
"."/[eth2_merkleization, forks, ssz_codec]
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ import
|
||||||
# generics sandwich where rlp/writer.append() is not seen, by a caller outside
|
# generics sandwich where rlp/writer.append() is not seen, by a caller outside
|
||||||
# this module via compute_execution_block_hash() called from block_processor.
|
# this module via compute_execution_block_hash() called from block_processor.
|
||||||
export
|
export
|
||||||
eth2_merkleization, forks, rlp, ssz_codec
|
eth2_merkleization, forks, ssz_codec, rlp, eth_types_rlp.append
|
||||||
|
|
||||||
# https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.7/specs/phase0/weak-subjectivity.md#constants
|
# https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.7/specs/phase0/weak-subjectivity.md#constants
|
||||||
const ETH_TO_GWEI = 1_000_000_000.Gwei
|
const ETH_TO_GWEI = 1_000_000_000.Gwei
|
||||||
|
@ -440,67 +440,42 @@ func compute_timestamp_at_slot*(state: ForkyBeaconState, slot: Slot): uint64 =
|
||||||
let slots_since_genesis = slot - GENESIS_SLOT
|
let slots_since_genesis = slot - GENESIS_SLOT
|
||||||
state.genesis_time + slots_since_genesis * SECONDS_PER_SLOT
|
state.genesis_time + slots_since_genesis * SECONDS_PER_SLOT
|
||||||
|
|
||||||
proc computeTransactionsTrieRoot*(
|
template append*(w: var RlpWriter, v: bellatrix.Transaction) =
|
||||||
payload: ForkyExecutionPayload): ExecutionHash256 =
|
w.appendRawBytes(distinctBase v)
|
||||||
if payload.transactions.len == 0:
|
|
||||||
return EMPTY_ROOT_HASH
|
|
||||||
|
|
||||||
var tr = initHexaryTrie(newMemoryDB())
|
template append*(w: var RlpWriter, withdrawal: capella.Withdrawal) =
|
||||||
for i, transaction in payload.transactions:
|
w.appendRecordType(ExecutionWithdrawal(
|
||||||
try:
|
|
||||||
# Transactions are already RLP encoded
|
|
||||||
tr.put(rlp.encode(i.uint), distinctBase(transaction))
|
|
||||||
except RlpError as exc:
|
|
||||||
raiseAssert "HexaryTrie.put failed: " & $exc.msg
|
|
||||||
tr.rootHash()
|
|
||||||
|
|
||||||
func toExecutionWithdrawal(
|
|
||||||
withdrawal: capella.Withdrawal): ExecutionWithdrawal =
|
|
||||||
ExecutionWithdrawal(
|
|
||||||
index: withdrawal.index,
|
index: withdrawal.index,
|
||||||
validatorIndex: withdrawal.validator_index,
|
validatorIndex: withdrawal.validator_index,
|
||||||
address: EthAddress withdrawal.address.data,
|
address: EthAddress withdrawal.address.data,
|
||||||
amount: distinctBase(withdrawal.amount))
|
amount: distinctBase(withdrawal.amount)))
|
||||||
|
|
||||||
proc rlpEncode(withdrawal: capella.Withdrawal): seq[byte] =
|
proc computeTransactionsTrieRoot(
|
||||||
# TODO if this encode call is in a generic function, nim doesn't find the
|
payload: ForkyExecutionPayload): ExecutionHash256 =
|
||||||
# right `append` to use with `Address` (!)
|
orderedTrieRoot(payload.transactions.asSeq)
|
||||||
rlp.encode(toExecutionWithdrawal(withdrawal))
|
|
||||||
|
|
||||||
# https://eips.ethereum.org/EIPS/eip-4895
|
# https://eips.ethereum.org/EIPS/eip-4895
|
||||||
proc computeWithdrawalsTrieRoot*(
|
proc computeWithdrawalsTrieRoot(
|
||||||
payload: capella.ExecutionPayload | deneb.ExecutionPayload |
|
payload: capella.ExecutionPayload | deneb.ExecutionPayload |
|
||||||
electra.ExecutionPayload): ExecutionHash256 =
|
electra.ExecutionPayload): ExecutionHash256 =
|
||||||
if payload.withdrawals.len == 0:
|
orderedTrieRoot(payload.withdrawals.asSeq)
|
||||||
return EMPTY_ROOT_HASH
|
|
||||||
|
|
||||||
var tr = initHexaryTrie(newMemoryDB())
|
func append*(w: var RlpWriter, request: electra.DepositRequest) =
|
||||||
for i, withdrawal in payload.withdrawals:
|
w.append ExecutionDepositRequest(
|
||||||
try:
|
|
||||||
tr.put(rlp.encode(i.uint), rlpEncode(withdrawal))
|
|
||||||
except RlpError as exc:
|
|
||||||
raiseAssert "HexaryTrie.put failed: " & $exc.msg
|
|
||||||
tr.rootHash()
|
|
||||||
|
|
||||||
func toExecutionDepositRequest*(
|
|
||||||
request: electra.DepositRequest): ExecutionDepositRequest =
|
|
||||||
ExecutionDepositRequest(
|
|
||||||
pubkey: Bytes48 request.pubkey.blob,
|
pubkey: Bytes48 request.pubkey.blob,
|
||||||
withdrawalCredentials: Bytes32 request.withdrawal_credentials.data,
|
withdrawalCredentials: Bytes32 request.withdrawal_credentials.data,
|
||||||
amount: distinctBase(request.amount),
|
amount: distinctBase(request.amount),
|
||||||
signature: Bytes96 request.signature.blob,
|
signature: Bytes96 request.signature.blob,
|
||||||
index: request.index)
|
index: request.index)
|
||||||
|
|
||||||
func toExecutionWithdrawalRequest*(
|
func append*(w: var RlpWriter, request: electra.WithdrawalRequest) =
|
||||||
request: electra.WithdrawalRequest): ExecutionWithdrawalRequest =
|
w.append ExecutionWithdrawalRequest(
|
||||||
ExecutionWithdrawalRequest(
|
|
||||||
sourceAddress: Address request.source_address.data,
|
sourceAddress: Address request.source_address.data,
|
||||||
validatorPubkey: Bytes48 request.validator_pubkey.blob,
|
validatorPubkey: Bytes48 request.validator_pubkey.blob,
|
||||||
amount: distinctBase(request.amount))
|
amount: distinctBase(request.amount))
|
||||||
|
|
||||||
func toExecutionConsolidationRequest*(
|
func append*(w: var RlpWriter, request: electra.ConsolidationRequest) =
|
||||||
request: electra.ConsolidationRequest): ExecutionConsolidationRequest =
|
w.append ExecutionConsolidationRequest(
|
||||||
ExecutionConsolidationRequest(
|
|
||||||
sourceAddress: Address request.source_address.data,
|
sourceAddress: Address request.source_address.data,
|
||||||
sourcePubkey: Bytes48 request.source_pubkey.blob,
|
sourcePubkey: Bytes48 request.source_pubkey.blob,
|
||||||
targetPubkey: Bytes48 request.target_pubkey.blob)
|
targetPubkey: Bytes48 request.target_pubkey.blob)
|
||||||
|
@ -508,47 +483,22 @@ func toExecutionConsolidationRequest*(
|
||||||
# https://eips.ethereum.org/EIPS/eip-7685
|
# https://eips.ethereum.org/EIPS/eip-7685
|
||||||
proc computeRequestsTrieRoot(
|
proc computeRequestsTrieRoot(
|
||||||
requests: electra.ExecutionRequests): ExecutionHash256 =
|
requests: electra.ExecutionRequests): ExecutionHash256 =
|
||||||
if requests.deposits.len == 0 and
|
let n =
|
||||||
requests.withdrawals.len == 0 and
|
requests.deposits.len +
|
||||||
requests.consolidations.len == 0:
|
requests.withdrawals.len +
|
||||||
return EMPTY_ROOT_HASH
|
requests.consolidations.len
|
||||||
|
|
||||||
var
|
var b = OrderedTrieRootBuilder.init(n)
|
||||||
tr = initHexaryTrie(newMemoryDB())
|
|
||||||
i = 0'u64
|
|
||||||
|
|
||||||
static:
|
static:
|
||||||
doAssert DEPOSIT_REQUEST_TYPE < WITHDRAWAL_REQUEST_TYPE
|
doAssert DEPOSIT_REQUEST_TYPE < WITHDRAWAL_REQUEST_TYPE
|
||||||
doAssert WITHDRAWAL_REQUEST_TYPE < CONSOLIDATION_REQUEST_TYPE
|
doAssert WITHDRAWAL_REQUEST_TYPE < CONSOLIDATION_REQUEST_TYPE
|
||||||
|
|
||||||
# EIP-6110
|
b.add(requests.deposits.asSeq) # EIP-6110
|
||||||
for request in requests.deposits:
|
b.add(requests.withdrawals.asSeq) # EIP-7002
|
||||||
try:
|
b.add(requests.consolidations.asSeq) # EIP-7251
|
||||||
tr.put(rlp.encode(i.uint), rlp.encode(
|
|
||||||
toExecutionDepositRequest(request)))
|
|
||||||
except RlpError as exc:
|
|
||||||
raiseAssert "HexaryTree.put failed: " & $exc.msg
|
|
||||||
inc i
|
|
||||||
|
|
||||||
# EIP-7002
|
b.rootHash()
|
||||||
for request in requests.withdrawals:
|
|
||||||
try:
|
|
||||||
tr.put(rlp.encode(i.uint), rlp.encode(
|
|
||||||
toExecutionWithdrawalRequest(request)))
|
|
||||||
except RlpError as exc:
|
|
||||||
raiseAssert "HexaryTree.put failed: " & $exc.msg
|
|
||||||
inc i
|
|
||||||
|
|
||||||
# EIP-7251
|
|
||||||
for request in requests.consolidations:
|
|
||||||
try:
|
|
||||||
tr.put(rlp.encode(i.uint), rlp.encode(
|
|
||||||
toExecutionConsolidationRequest(request)))
|
|
||||||
except RlpError as exc:
|
|
||||||
raiseAssert "HexaryTree.put failed: " & $exc.msg
|
|
||||||
inc i
|
|
||||||
|
|
||||||
tr.rootHash()
|
|
||||||
|
|
||||||
proc blockToBlockHeader*(blck: ForkyBeaconBlock): ExecutionBlockHeader =
|
proc blockToBlockHeader*(blck: ForkyBeaconBlock): ExecutionBlockHeader =
|
||||||
template payload: auto = blck.body.execution_payload
|
template payload: auto = blck.body.execution_payload
|
||||||
|
@ -561,7 +511,7 @@ proc blockToBlockHeader*(blck: ForkyBeaconBlock): ExecutionBlockHeader =
|
||||||
txRoot = payload.computeTransactionsTrieRoot()
|
txRoot = payload.computeTransactionsTrieRoot()
|
||||||
withdrawalsRoot =
|
withdrawalsRoot =
|
||||||
when typeof(payload).kind >= ConsensusFork.Capella:
|
when typeof(payload).kind >= ConsensusFork.Capella:
|
||||||
Opt.some payload.computeWithdrawalsTrieRoot()
|
Opt.some orderedTrieRoot(payload.withdrawals.asSeq)
|
||||||
else:
|
else:
|
||||||
Opt.none(ExecutionHash256)
|
Opt.none(ExecutionHash256)
|
||||||
blobGasUsed =
|
blobGasUsed =
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit cea821df604cc89304afee261278b76d050222b6
|
Subproject commit 00c91a1dcaf488046bbc9b9fcbd430934312930f
|
|
@ -1 +1 @@
|
||||||
Subproject commit 763147cf821ea105936502e029ac9f5cfad86568
|
Subproject commit 45b0e9d1579b06aedfe4ffe39e832ec5e88f9377
|
Loading…
Reference in New Issue