mirror of
https://github.com/status-im/nimbus-eth1.git
synced 2025-02-02 15:24:01 +00:00
Add new Portal BlockBody type for Shanghai fork (#1589)
This commit is contained in:
parent
48930970ce
commit
c9f3f82877
@ -8,8 +8,9 @@
|
|||||||
{.push raises: [].}
|
{.push raises: [].}
|
||||||
|
|
||||||
import
|
import
|
||||||
ssz_serialization/types,
|
ssz_serialization,
|
||||||
stew/byteutils, nimcrypto/hash
|
eth/rlp,
|
||||||
|
stew/[byteutils, results], nimcrypto/hash
|
||||||
|
|
||||||
export hash
|
export hash
|
||||||
|
|
||||||
@ -23,3 +24,21 @@ type
|
|||||||
|
|
||||||
func `$`*(x: ByteList): string =
|
func `$`*(x: ByteList): string =
|
||||||
x.asSeq.toHex()
|
x.asSeq.toHex()
|
||||||
|
|
||||||
|
func decodeRlp*(input: openArray[byte], T: type): Result[T, string] =
|
||||||
|
try:
|
||||||
|
ok(rlp.decode(input, T))
|
||||||
|
except RlpError as e:
|
||||||
|
err(e.msg)
|
||||||
|
|
||||||
|
func decodeSsz*(input: openArray[byte], T: type): Result[T, string] =
|
||||||
|
try:
|
||||||
|
ok(SSZ.decode(input, T))
|
||||||
|
except SszError as e:
|
||||||
|
err(e.msg)
|
||||||
|
|
||||||
|
func decodeSszOrRaise*(input: openArray[byte], T: type): T =
|
||||||
|
try:
|
||||||
|
SSZ.decode(input, T)
|
||||||
|
except SszError as e:
|
||||||
|
raiseAssert(e.msg)
|
@ -97,12 +97,6 @@ func toContentId*(contentKey: ByteList): ContentId =
|
|||||||
func toContentId*(contentKey: ContentKey): ContentId =
|
func toContentId*(contentKey: ContentKey): ContentId =
|
||||||
toContentId(encode(contentKey))
|
toContentId(encode(contentKey))
|
||||||
|
|
||||||
func decodeSsz*(input: openArray[byte], T: type): Result[T, string] =
|
|
||||||
try:
|
|
||||||
ok(SSZ.decode(input, T))
|
|
||||||
except SszError as e:
|
|
||||||
err(e.msg)
|
|
||||||
|
|
||||||
# Yes, this API is odd as you pass a SomeForkedLightClientObject yet still have
|
# Yes, this API is odd as you pass a SomeForkedLightClientObject yet still have
|
||||||
# to also pass the ForkDigest. This is because we can't just select the right
|
# to also pass the ForkDigest. This is because we can't just select the right
|
||||||
# digest through the LightClientDataFork here as LightClientDataFork and
|
# digest through the LightClientDataFork here as LightClientDataFork and
|
||||||
|
@ -15,6 +15,9 @@ import
|
|||||||
ssz_serialization,
|
ssz_serialization,
|
||||||
../../common/common_types
|
../../common/common_types
|
||||||
|
|
||||||
|
from beacon_chain/spec/datatypes/capella import Withdrawal
|
||||||
|
from beacon_chain/spec/presets/mainnet import MAX_WITHDRAWALS_PER_PAYLOAD
|
||||||
|
|
||||||
export ssz_serialization, common_types, hash, results
|
export ssz_serialization, common_types, hash, results
|
||||||
|
|
||||||
## Types and calls for history network content keys
|
## Types and calls for history network content keys
|
||||||
@ -117,6 +120,8 @@ const
|
|||||||
MAX_RECEIPT_LENGTH* = 2^27 # ~= 134 million
|
MAX_RECEIPT_LENGTH* = 2^27 # ~= 134 million
|
||||||
MAX_HEADER_LENGTH = 2^13 # = 8192
|
MAX_HEADER_LENGTH = 2^13 # = 8192
|
||||||
MAX_ENCODED_UNCLES_LENGTH* = MAX_HEADER_LENGTH * 2^4 # = 2**17 ~= 131k
|
MAX_ENCODED_UNCLES_LENGTH* = MAX_HEADER_LENGTH * 2^4 # = 2**17 ~= 131k
|
||||||
|
MAX_WITHDRAWAL_LENGTH = 64
|
||||||
|
MAX_WITHDRAWALS_COUNT = MAX_WITHDRAWALS_PER_PAYLOAD
|
||||||
|
|
||||||
type
|
type
|
||||||
## Types for content
|
## Types for content
|
||||||
@ -126,12 +131,23 @@ type
|
|||||||
Transactions* = List[TransactionByteList, MAX_TRANSACTION_COUNT]
|
Transactions* = List[TransactionByteList, MAX_TRANSACTION_COUNT]
|
||||||
Uncles* = List[byte, MAX_ENCODED_UNCLES_LENGTH] # RLP data
|
Uncles* = List[byte, MAX_ENCODED_UNCLES_LENGTH] # RLP data
|
||||||
|
|
||||||
BlockBodySSZ* = object
|
WithdrawalByteList* = List[byte, MAX_WITHDRAWAL_LENGTH] # RLP data
|
||||||
|
Withdrawals* = List[WithdrawalByteList, MAX_WITHDRAWALS_COUNT]
|
||||||
|
|
||||||
|
# Pre-shanghai block body
|
||||||
|
# Post-merge this block body is required to have an empty list for uncles
|
||||||
|
PortalBlockBodyLegacy* = object
|
||||||
transactions*: Transactions
|
transactions*: Transactions
|
||||||
uncles*: Uncles
|
uncles*: Uncles
|
||||||
|
|
||||||
|
# Post-shanghai block body, added withdrawals
|
||||||
|
PortalBlockBodyShanghai* = object
|
||||||
|
transactions*: Transactions
|
||||||
|
uncles*: Uncles
|
||||||
|
withdrawals*: Withdrawals
|
||||||
|
|
||||||
ReceiptByteList* = List[byte, MAX_RECEIPT_LENGTH] # RLP data
|
ReceiptByteList* = List[byte, MAX_RECEIPT_LENGTH] # RLP data
|
||||||
ReceiptsSSZ* = List[ReceiptByteList, MAX_TRANSACTION_COUNT]
|
PortalReceipts* = List[ReceiptByteList, MAX_TRANSACTION_COUNT]
|
||||||
|
|
||||||
AccumulatorProof* = array[15, Digest]
|
AccumulatorProof* = array[15, Digest]
|
||||||
|
|
||||||
|
@ -11,11 +11,15 @@ import
|
|||||||
stew/results, chronos, chronicles,
|
stew/results, chronos, chronicles,
|
||||||
eth/[common/eth_types_rlp, rlp, trie, trie/db],
|
eth/[common/eth_types_rlp, rlp, trie, trie/db],
|
||||||
eth/p2p/discoveryv5/[protocol, enr],
|
eth/p2p/discoveryv5/[protocol, enr],
|
||||||
|
../../common/common_types,
|
||||||
../../content_db,
|
../../content_db,
|
||||||
|
../../network_metadata,
|
||||||
../../../nimbus/constants,
|
../../../nimbus/constants,
|
||||||
../wire/[portal_protocol, portal_stream, portal_protocol_config],
|
../wire/[portal_protocol, portal_stream, portal_protocol_config],
|
||||||
"."/[history_content, accumulator]
|
"."/[history_content, accumulator]
|
||||||
|
|
||||||
|
from std/times import toUnix
|
||||||
|
|
||||||
logScope:
|
logScope:
|
||||||
topics = "portal_hist"
|
topics = "portal_hist"
|
||||||
|
|
||||||
@ -61,23 +65,12 @@ type
|
|||||||
func toContentIdHandler(contentKey: ByteList): results.Opt[ContentId] =
|
func toContentIdHandler(contentKey: ByteList): results.Opt[ContentId] =
|
||||||
ok(toContentId(contentKey))
|
ok(toContentId(contentKey))
|
||||||
|
|
||||||
func decodeRlp*(input: openArray[byte], T: type): Result[T, string] =
|
## Calls to go from SSZ decoded Portal types to RLP fully decoded EL types
|
||||||
try:
|
|
||||||
ok(rlp.decode(input, T))
|
|
||||||
except RlpError as e:
|
|
||||||
err(e.msg)
|
|
||||||
|
|
||||||
func decodeSsz*(input: openArray[byte], T: type): Result[T, string] =
|
|
||||||
try:
|
|
||||||
ok(SSZ.decode(input, T))
|
|
||||||
except SszError as e:
|
|
||||||
err(e.msg)
|
|
||||||
|
|
||||||
## Calls to go from SSZ decoded types to RLP fully decoded types
|
|
||||||
|
|
||||||
func fromPortalBlockBody*(
|
func fromPortalBlockBody*(
|
||||||
T: type BlockBody, body: BlockBodySSZ): Result[T, string] =
|
T: type BlockBody, body: PortalBlockBodyLegacy):
|
||||||
## Get the full decoded BlockBody from the SSZ-decoded `PortalBlockBody`.
|
Result[T, string] =
|
||||||
|
## Get the EL BlockBody from the SSZ-decoded `PortalBlockBodyLegacy`.
|
||||||
try:
|
try:
|
||||||
var transactions: seq[Transaction]
|
var transactions: seq[Transaction]
|
||||||
for tx in body.transactions:
|
for tx in body.transactions:
|
||||||
@ -89,9 +82,42 @@ func fromPortalBlockBody*(
|
|||||||
except RlpError as e:
|
except RlpError as e:
|
||||||
err("RLP decoding failed: " & e.msg)
|
err("RLP decoding failed: " & e.msg)
|
||||||
|
|
||||||
func fromReceipts*(
|
func fromPortalBlockBody*(
|
||||||
T: type seq[Receipt], receipts: ReceiptsSSZ): Result[T, string] =
|
T: type BlockBody, body: PortalBlockBodyShanghai): Result[T, string] =
|
||||||
## Get the full decoded seq[Receipt] from the SSZ-decoded `Receipts`.
|
## Get the EL BlockBody from the SSZ-decoded `PortalBlockBodyShanghai`.
|
||||||
|
try:
|
||||||
|
var transactions: seq[Transaction]
|
||||||
|
for tx in body.transactions:
|
||||||
|
transactions.add(rlp.decode(tx.asSeq(), Transaction))
|
||||||
|
|
||||||
|
var withdrawals: seq[Withdrawal]
|
||||||
|
for w in body.withdrawals:
|
||||||
|
withdrawals.add(rlp.decode(w.asSeq(), Withdrawal))
|
||||||
|
|
||||||
|
ok(BlockBody(
|
||||||
|
transactions: transactions,
|
||||||
|
uncles: @[], # Uncles must be empty: TODO where validation?
|
||||||
|
withdrawals: some(withdrawals)))
|
||||||
|
except RlpError as e:
|
||||||
|
err("RLP decoding failed: " & e.msg)
|
||||||
|
|
||||||
|
func fromPortalBlockBodyOrRaise*(
|
||||||
|
T: type BlockBody,
|
||||||
|
body: PortalBlockBodyLegacy | PortalBlockBodyShanghai):
|
||||||
|
T =
|
||||||
|
## Get the EL BlockBody from one of the SSZ-decoded Portal BlockBody types.
|
||||||
|
## Will raise Assertion in case of invalid RLP encodings. Only use of data
|
||||||
|
## has been validated before!
|
||||||
|
# TODO: Using ValueOr here gives compile error
|
||||||
|
let res = BlockBody.fromPortalBlockBody(body)
|
||||||
|
if res.isOk():
|
||||||
|
res.get()
|
||||||
|
else:
|
||||||
|
raiseAssert(res.error)
|
||||||
|
|
||||||
|
func fromPortalReceipts*(
|
||||||
|
T: type seq[Receipt], receipts: PortalReceipts): Result[T, string] =
|
||||||
|
## Get the full decoded EL seq[Receipt] from the SSZ-decoded `PortalReceipts`.
|
||||||
try:
|
try:
|
||||||
var res: seq[Receipt]
|
var res: seq[Receipt]
|
||||||
for receipt in receipts:
|
for receipt in receipts:
|
||||||
@ -101,31 +127,55 @@ func fromReceipts*(
|
|||||||
except RlpError as e:
|
except RlpError as e:
|
||||||
err("RLP decoding failed: " & e.msg)
|
err("RLP decoding failed: " & e.msg)
|
||||||
|
|
||||||
## Calls to encode Block types to the SSZ types.
|
## Calls to encode EL block types to the SSZ encoded Portal types.
|
||||||
|
|
||||||
func fromBlockBody(T: type BlockBodySSZ, body: BlockBody): T =
|
# TODO: The fact that we have different Portal BlockBody types for the different
|
||||||
|
# forks but not for the EL BlockBody (usage of Option) does not play so well
|
||||||
|
# together.
|
||||||
|
|
||||||
|
func fromBlockBody(T: type PortalBlockBodyLegacy, body: BlockBody): T =
|
||||||
var transactions: Transactions
|
var transactions: Transactions
|
||||||
for tx in body.transactions:
|
for tx in body.transactions:
|
||||||
discard transactions.add(TransactionByteList(rlp.encode(tx)))
|
discard transactions.add(TransactionByteList(rlp.encode(tx)))
|
||||||
|
|
||||||
let uncles = Uncles(rlp.encode(body.uncles))
|
let uncles = Uncles(rlp.encode(body.uncles))
|
||||||
|
|
||||||
BlockBodySSZ(transactions: transactions, uncles: uncles)
|
PortalBlockBodyLegacy(transactions: transactions, uncles: uncles)
|
||||||
|
|
||||||
func fromReceipts*(T: type ReceiptsSSZ, receipts: seq[Receipt]): T =
|
func fromBlockBody(T: type PortalBlockBodyShanghai, body: BlockBody): T =
|
||||||
var receiptsSSZ: ReceiptsSSZ
|
var transactions: Transactions
|
||||||
|
for tx in body.transactions:
|
||||||
|
discard transactions.add(TransactionByteList(rlp.encode(tx)))
|
||||||
|
|
||||||
|
let uncles = Uncles(rlp.encode(body.uncles))
|
||||||
|
|
||||||
|
doAssert(body.withdrawals.isSome())
|
||||||
|
|
||||||
|
var withdrawals: Withdrawals
|
||||||
|
for w in body.withdrawals.get():
|
||||||
|
discard withdrawals.add(WithdrawalByteList(rlp.encode(w)))
|
||||||
|
PortalBlockBodyShanghai(transactions: transactions, uncles: uncles, withdrawals: withdrawals)
|
||||||
|
|
||||||
|
func fromReceipts*(T: type PortalReceipts, receipts: seq[Receipt]): T =
|
||||||
|
var portalReceipts: PortalReceipts
|
||||||
for receipt in receipts:
|
for receipt in receipts:
|
||||||
discard receiptsSSZ.add(ReceiptByteList(rlp.encode(receipt)))
|
discard portalReceipts.add(ReceiptByteList(rlp.encode(receipt)))
|
||||||
|
|
||||||
receiptsSSZ
|
portalReceipts
|
||||||
|
|
||||||
func encode*(blockBody: BlockBody): seq[byte] =
|
func encode*(blockBody: BlockBody): seq[byte] =
|
||||||
let portalBlockBody = BlockBodySSZ.fromBlockBody(blockBody)
|
if blockBody.withdrawals.isSome():
|
||||||
|
SSZ.encode(PortalBlockBodyShanghai.fromBlockBody(blockBody))
|
||||||
|
else:
|
||||||
|
SSZ.encode(PortalBlockBodyLegacy.fromBlockBody(blockBody))
|
||||||
|
|
||||||
|
func encode*(blockBody: BlockBody, T: type PortalBlockBodyShanghai): seq[byte] =
|
||||||
|
let portalBlockBody = PortalBlockBodyShanghai.fromBlockBody(blockBody)
|
||||||
|
|
||||||
SSZ.encode(portalBlockBody)
|
SSZ.encode(portalBlockBody)
|
||||||
|
|
||||||
func encode*(receipts: seq[Receipt]): seq[byte] =
|
func encode*(receipts: seq[Receipt]): seq[byte] =
|
||||||
let portalReceipts = ReceiptsSSZ.fromReceipts(receipts)
|
let portalReceipts = PortalReceipts.fromReceipts(receipts)
|
||||||
|
|
||||||
SSZ.encode(portalReceipts)
|
SSZ.encode(portalReceipts)
|
||||||
|
|
||||||
@ -133,7 +183,7 @@ func encode*(receipts: seq[Receipt]): seq[byte] =
|
|||||||
# TODO: Failures on validation and perhaps deserialisation should be punished
|
# TODO: Failures on validation and perhaps deserialisation should be punished
|
||||||
# for if/when peer scoring/banning is added.
|
# for if/when peer scoring/banning is added.
|
||||||
|
|
||||||
proc calcRootHash(items: Transactions | ReceiptsSSZ): Hash256 =
|
proc calcRootHash(items: Transactions | PortalReceipts| Withdrawals): Hash256 =
|
||||||
var tr = initHexaryTrie(newMemoryDB())
|
var tr = initHexaryTrie(newMemoryDB())
|
||||||
for i, item in items:
|
for i, item in items:
|
||||||
try:
|
try:
|
||||||
@ -148,7 +198,10 @@ proc calcRootHash(items: Transactions | ReceiptsSSZ): Hash256 =
|
|||||||
template calcTxsRoot*(transactions: Transactions): Hash256 =
|
template calcTxsRoot*(transactions: Transactions): Hash256 =
|
||||||
calcRootHash(transactions)
|
calcRootHash(transactions)
|
||||||
|
|
||||||
template calcReceiptsRoot*(receipts: ReceiptsSSZ): Hash256 =
|
template calcReceiptsRoot*(receipts: PortalReceipts): Hash256 =
|
||||||
|
calcRootHash(receipts)
|
||||||
|
|
||||||
|
template calcWithdrawalsRoot*(receipts: Withdrawals): Hash256 =
|
||||||
calcRootHash(receipts)
|
calcRootHash(receipts)
|
||||||
|
|
||||||
func validateBlockHeaderBytes*(
|
func validateBlockHeaderBytes*(
|
||||||
@ -168,34 +221,91 @@ func validateBlockHeaderBytes*(
|
|||||||
ok(header)
|
ok(header)
|
||||||
|
|
||||||
proc validateBlockBody(
|
proc validateBlockBody(
|
||||||
body: BlockBodySSZ, txsRoot, ommersHash: KeccakHash):
|
body: PortalBlockBodyLegacy, header: BlockHeader):
|
||||||
Result[void, string] =
|
Result[void, string] =
|
||||||
## Validate the block body against the txRoot amd ommersHash from the header.
|
## Validate the block body against the txRoot and ommersHash from the header.
|
||||||
# TODO: should be checked for hash for empty uncles after merge block
|
|
||||||
let calculatedOmmersHash = keccakHash(body.uncles.asSeq())
|
let calculatedOmmersHash = keccakHash(body.uncles.asSeq())
|
||||||
if calculatedOmmersHash != ommersHash:
|
if calculatedOmmersHash != header.ommersHash:
|
||||||
return err("Invalid ommers hash")
|
return err("Invalid ommers hash")
|
||||||
|
|
||||||
let calculatedTxsRoot = calcTxsRoot(body.transactions)
|
let calculatedTxsRoot = calcTxsRoot(body.transactions)
|
||||||
if calculatedTxsRoot != txsRoot:
|
if calculatedTxsRoot != header.txRoot:
|
||||||
return err("Invalid transactions root")
|
return err("Invalid transactions root")
|
||||||
|
|
||||||
# TODO: Add root check for withdrawals after Shanghai
|
|
||||||
|
|
||||||
ok()
|
ok()
|
||||||
|
|
||||||
|
proc validateBlockBody(
|
||||||
|
body: PortalBlockBodyShanghai, header: BlockHeader):
|
||||||
|
Result[void, string] =
|
||||||
|
## Validate the block body against the txRoot, ommersHash and withdrawalsRoot
|
||||||
|
## from the header.
|
||||||
|
# Shortcutting the ommersHash calculation as uncles needs to be empty
|
||||||
|
# TODO: This is since post-merge, however, we would need an additional object
|
||||||
|
# type for that period to do this.
|
||||||
|
if body.uncles.len > 0:
|
||||||
|
return err("Invalid ommers hash")
|
||||||
|
|
||||||
|
let calculatedTxsRoot = calcTxsRoot(body.transactions)
|
||||||
|
if calculatedTxsRoot != header.txRoot:
|
||||||
|
return err("Invalid transactions root")
|
||||||
|
|
||||||
|
# TODO: This check is done higher up but perhaps this can become cleaner with
|
||||||
|
# some refactor.
|
||||||
|
doAssert(header.withdrawalsRoot.isSome())
|
||||||
|
|
||||||
|
let calculatedWithdrawalsRoot = calcWithdrawalsRoot(body.withdrawals)
|
||||||
|
if calculatedWithdrawalsRoot != header.txRoot:
|
||||||
|
return err("Invalid transactions root")
|
||||||
|
|
||||||
|
ok()
|
||||||
|
|
||||||
|
proc decodeBlockBodyBytes*(bytes: openArray[byte]): Result[BlockBody, string] =
|
||||||
|
if (let body = decodeSsz(bytes, PortalBlockBodyShanghai); body.isOk()):
|
||||||
|
BlockBody.fromPortalBlockBody(body.get())
|
||||||
|
elif (let body = decodeSsz(bytes, PortalBlockBodyLegacy); body.isOk()):
|
||||||
|
BlockBody.fromPortalBlockBody(body.get())
|
||||||
|
else:
|
||||||
|
err("All Portal block body decodings failed")
|
||||||
|
|
||||||
proc validateBlockBodyBytes*(
|
proc validateBlockBodyBytes*(
|
||||||
bytes: openArray[byte], txRoot, ommersHash: KeccakHash):
|
bytes: openArray[byte], header: BlockHeader):
|
||||||
Result[BlockBody, string] =
|
Result[BlockBody, string] =
|
||||||
## Fully decode the SSZ Block Body and validate it against the header.
|
## Fully decode the SSZ encoded Portal Block Body and validate it against the
|
||||||
let body = ? decodeSsz(bytes, BlockBodySSZ)
|
## header.
|
||||||
|
## TODO: improve this decoding in combination with the block body validation
|
||||||
? validateBlockBody(body, txRoot, ommersHash)
|
## calls.
|
||||||
|
let timestamp = Moment.init(header.timestamp.toUnix(), Second)
|
||||||
|
# TODO: The additional header checks are not needed as header is implicitly
|
||||||
|
# verified by means of the accumulator? Except that we don't use this yet
|
||||||
|
# post merge, so the checks are still useful, for now.
|
||||||
|
if isShanghai(chainConfig, timestamp):
|
||||||
|
if header.withdrawalsRoot.isNone():
|
||||||
|
return err("Expected withdrawalsRoot for Shanghai block")
|
||||||
|
elif header.ommersHash != EMPTY_UNCLE_HASH:
|
||||||
|
return err("Expected empty uncles for a Shanghai block")
|
||||||
|
else:
|
||||||
|
let body = ? decodeSsz(bytes, PortalBlockBodyShanghai)
|
||||||
|
? validateBlockBody(body, header)
|
||||||
|
BlockBody.fromPortalBlockBody(body)
|
||||||
|
elif isPoSBlock(chainConfig, header.blockNumber.truncate(uint64)):
|
||||||
|
if header.withdrawalsRoot.isSome():
|
||||||
|
return err("Expected no withdrawalsRoot for pre Shanghai block")
|
||||||
|
elif header.ommersHash != EMPTY_UNCLE_HASH:
|
||||||
|
return err("Expected empty uncles for a PoS block")
|
||||||
|
else:
|
||||||
|
let body = ? decodeSsz(bytes, PortalBlockBodyLegacy)
|
||||||
|
? validateBlockBody(body, header)
|
||||||
|
BlockBody.fromPortalBlockBody(body)
|
||||||
|
else:
|
||||||
|
if header.withdrawalsRoot.isSome():
|
||||||
|
return err("Expected no withdrawalsRoot for pre Shanghai block")
|
||||||
|
else:
|
||||||
|
let body = ? decodeSsz(bytes, PortalBlockBodyLegacy)
|
||||||
|
? validateBlockBody(body, header)
|
||||||
BlockBody.fromPortalBlockBody(body)
|
BlockBody.fromPortalBlockBody(body)
|
||||||
|
|
||||||
proc validateReceipts*(
|
proc validateReceipts*(
|
||||||
receipts: ReceiptsSSZ, receiptsRoot: KeccakHash): Result[void, string] =
|
receipts: PortalReceipts, receiptsRoot: KeccakHash): Result[void, string] =
|
||||||
let calculatedReceiptsRoot = calcReceiptsRoot(receipts)
|
let calculatedReceiptsRoot = calcReceiptsRoot(receipts)
|
||||||
|
|
||||||
if calculatedReceiptsRoot != receiptsRoot:
|
if calculatedReceiptsRoot != receiptsRoot:
|
||||||
@ -207,11 +317,11 @@ proc validateReceiptsBytes*(
|
|||||||
bytes: openArray[byte],
|
bytes: openArray[byte],
|
||||||
receiptsRoot: KeccakHash): Result[seq[Receipt], string] =
|
receiptsRoot: KeccakHash): Result[seq[Receipt], string] =
|
||||||
## Fully decode the SSZ Block Body and validate it against the header.
|
## Fully decode the SSZ Block Body and validate it against the header.
|
||||||
let receipts = ? decodeSsz(bytes, ReceiptsSSZ)
|
let receipts = ? decodeSsz(bytes, PortalReceipts)
|
||||||
|
|
||||||
? validateReceipts(receipts, receiptsRoot)
|
? validateReceipts(receipts, receiptsRoot)
|
||||||
|
|
||||||
seq[Receipt].fromReceipts(receipts)
|
seq[Receipt].fromPortalReceipts(receipts)
|
||||||
|
|
||||||
## ContentDB helper calls for specific history network types
|
## ContentDB helper calls for specific history network types
|
||||||
|
|
||||||
@ -232,21 +342,30 @@ proc get(db: ContentDB, T: type BlockHeader, contentId: ContentId): Opt[T] =
|
|||||||
else:
|
else:
|
||||||
Opt.none(T)
|
Opt.none(T)
|
||||||
|
|
||||||
proc get(db: ContentDB, T: type BlockBody, contentId: ContentId): Opt[T] =
|
proc get(db: ContentDB, T: type BlockBody, contentId: ContentId,
|
||||||
let contentFromDB = db.getSszDecoded(contentId, BlockBodySSZ)
|
header: BlockHeader): Opt[T] =
|
||||||
if contentFromDB.isSome():
|
let encoded = db.get(contentId)
|
||||||
let res = T.fromPortalBlockBody(contentFromDB.get())
|
if encoded.isNone():
|
||||||
if res.isErr():
|
return Opt.none(T)
|
||||||
raiseAssert(res.error)
|
|
||||||
|
let timestamp = Moment.init(header.timestamp.toUnix(), Second)
|
||||||
|
let body =
|
||||||
|
if isShanghai(chainConfig, timestamp):
|
||||||
|
BlockBody.fromPortalBlockBodyOrRaise(
|
||||||
|
decodeSszOrRaise(encoded.get(), PortalBlockBodyShanghai))
|
||||||
|
elif isPoSBlock(chainConfig, header.blockNumber.truncate(uint64)):
|
||||||
|
BlockBody.fromPortalBlockBodyOrRaise(
|
||||||
|
decodeSszOrRaise(encoded.get(), PortalBlockBodyLegacy))
|
||||||
else:
|
else:
|
||||||
Opt.some(res.get())
|
BlockBody.fromPortalBlockBodyOrRaise(
|
||||||
else:
|
decodeSszOrRaise(encoded.get(), PortalBlockBodyLegacy))
|
||||||
Opt.none(T)
|
|
||||||
|
Opt.some(body)
|
||||||
|
|
||||||
proc get(db: ContentDB, T: type seq[Receipt], contentId: ContentId): Opt[T] =
|
proc get(db: ContentDB, T: type seq[Receipt], contentId: ContentId): Opt[T] =
|
||||||
let contentFromDB = db.getSszDecoded(contentId, ReceiptsSSZ)
|
let contentFromDB = db.getSszDecoded(contentId, PortalReceipts)
|
||||||
if contentFromDB.isSome():
|
if contentFromDB.isSome():
|
||||||
let res = T.fromReceipts(contentFromDB.get())
|
let res = T.fromPortalReceipts(contentFromDB.get())
|
||||||
if res.isErr():
|
if res.isErr():
|
||||||
raiseAssert(res.error)
|
raiseAssert(res.error)
|
||||||
else:
|
else:
|
||||||
@ -352,7 +471,7 @@ proc getBlockBody*(
|
|||||||
hash
|
hash
|
||||||
contentKey
|
contentKey
|
||||||
|
|
||||||
let bodyFromDb = n.getContentFromDb(BlockBody, contentId)
|
let bodyFromDb = n.contentDB.get(BlockBody, contentId, header)
|
||||||
if bodyFromDb.isSome():
|
if bodyFromDb.isSome():
|
||||||
info "Fetched block body from database"
|
info "Fetched block body from database"
|
||||||
return bodyFromDb
|
return bodyFromDb
|
||||||
@ -365,7 +484,7 @@ proc getBlockBody*(
|
|||||||
return Opt.none(BlockBody)
|
return Opt.none(BlockBody)
|
||||||
|
|
||||||
body = validateBlockBodyBytes(
|
body = validateBlockBodyBytes(
|
||||||
bodyContent.content, header.txRoot, header.ommersHash).valueOr:
|
bodyContent.content, header).valueOr:
|
||||||
warn "Validation of block body failed", error
|
warn "Validation of block body failed", error
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@ -536,7 +655,7 @@ proc validateContent(
|
|||||||
warn "Failed getting canonical header for block"
|
warn "Failed getting canonical header for block"
|
||||||
return false
|
return false
|
||||||
|
|
||||||
let res = validateBlockBodyBytes(content, header.txRoot, header.ommersHash)
|
let res = validateBlockBodyBytes(content, header)
|
||||||
if res.isErr():
|
if res.isErr():
|
||||||
warn "Failed validating block body", error = res.error
|
warn "Failed validating block body", error = res.error
|
||||||
return false
|
return false
|
||||||
|
@ -8,7 +8,10 @@
|
|||||||
{.push raises: [].}
|
{.push raises: [].}
|
||||||
|
|
||||||
import
|
import
|
||||||
std/[sequtils, strutils, os, macros]
|
std/[sequtils, strutils, os, macros],
|
||||||
|
stew/results,
|
||||||
|
chronos/timer#,
|
||||||
|
# eth/common/eth_types
|
||||||
|
|
||||||
proc loadBootstrapNodes(
|
proc loadBootstrapNodes(
|
||||||
path: string): seq[string] {.raises: [IOError].} =
|
path: string): seq[string] {.raises: [IOError].} =
|
||||||
@ -62,3 +65,40 @@ const
|
|||||||
|
|
||||||
finishedAccumulator* = loadEncodedAccumulator(
|
finishedAccumulator* = loadEncodedAccumulator(
|
||||||
portalTestDir / "mainnet" / "history" / "accumulator" / "finished_accumulator.ssz")
|
portalTestDir / "mainnet" / "history" / "accumulator" / "finished_accumulator.ssz")
|
||||||
|
|
||||||
|
type
|
||||||
|
# TODO: I guess we could use the nimbus ChainConfig but:
|
||||||
|
# - Only need some of the values right now
|
||||||
|
# - `EthTime` uses std/times while chronos Moment is sufficient and more
|
||||||
|
# sensible
|
||||||
|
ChainConfig* = object
|
||||||
|
mergeForkBlock* : uint64
|
||||||
|
shanghaiTime* : Opt[Moment]
|
||||||
|
cancunTime* : Opt[Moment]
|
||||||
|
|
||||||
|
const
|
||||||
|
# Allow this to be adjusted at compile time for testing. If more constants
|
||||||
|
# need to be adjusted we can add some more ChainConfig presets either at
|
||||||
|
# compile or runtime.
|
||||||
|
mergeBlockNumber* {.intdefine.}: uint64 = 15537394
|
||||||
|
|
||||||
|
chainConfig* = ChainConfig(
|
||||||
|
mergeForkBlock: mergeBlockNumber,
|
||||||
|
shanghaiTime: Opt.some(Moment.init(1681338455'i64, Second)),
|
||||||
|
cancunTime: Opt.none(Moment)
|
||||||
|
)
|
||||||
|
|
||||||
|
func isTimestampForked(forkTime: Opt[Moment], timestamp: Moment): bool =
|
||||||
|
if forkTime.isNone():
|
||||||
|
false
|
||||||
|
else:
|
||||||
|
forkTime.get() <= timestamp
|
||||||
|
|
||||||
|
func isPoSBlock*(c: ChainConfig, blockNumber: uint64): bool =
|
||||||
|
c.mergeForkBlock <= blockNumber
|
||||||
|
|
||||||
|
func isShanghai*(c: ChainConfig, timestamp: Moment): bool =
|
||||||
|
isTimestampForked(c.shanghaiTime, timestamp)
|
||||||
|
|
||||||
|
func isCancun*(c: ChainConfig, timestamp: Moment): bool =
|
||||||
|
isTimestampForked(c.cancunTime, timestamp)
|
||||||
|
@ -12,7 +12,6 @@ import
|
|||||||
./test_state_distance,
|
./test_state_distance,
|
||||||
./test_state_network,
|
./test_state_network,
|
||||||
./test_accumulator,
|
./test_accumulator,
|
||||||
./test_history_validation,
|
|
||||||
./test_history_network,
|
./test_history_network,
|
||||||
./test_content_db,
|
./test_content_db,
|
||||||
./test_discovery_rpc,
|
./test_discovery_rpc,
|
||||||
|
@ -11,6 +11,7 @@ import
|
|||||||
./test_portal_wire_encoding,
|
./test_portal_wire_encoding,
|
||||||
./test_history_content_keys,
|
./test_history_content_keys,
|
||||||
./test_history_content,
|
./test_history_content,
|
||||||
|
./test_history_content_validation,
|
||||||
./test_header_content,
|
./test_header_content,
|
||||||
./test_state_content,
|
./test_state_content,
|
||||||
./test_accumulator_root
|
./test_accumulator_root
|
||||||
|
@ -134,7 +134,7 @@ suite "History Content Encodings":
|
|||||||
SSZ.encode(blockHeaderWithProof) == contentValueEncoded
|
SSZ.encode(blockHeaderWithProof) == contentValueEncoded
|
||||||
encode(contentKey.get()).asSeq() == contentKeyEncoded
|
encode(contentKey.get()).asSeq() == contentKeyEncoded
|
||||||
|
|
||||||
test "Block Body Encoding/Decoding and Verification":
|
test "PortalBlockBody (Legacy) Encoding/Decoding and Verification":
|
||||||
const
|
const
|
||||||
dataFile =
|
dataFile =
|
||||||
"./vendor/portal-spec-tests/tests/mainnet/history/bodies/14764013.json"
|
"./vendor/portal-spec-tests/tests/mainnet/history/bodies/14764013.json"
|
||||||
@ -171,7 +171,7 @@ suite "History Content Encodings":
|
|||||||
|
|
||||||
# Decode (SSZ + RLP decode step) and validate block body
|
# Decode (SSZ + RLP decode step) and validate block body
|
||||||
let contentValue = validateBlockBodyBytes(
|
let contentValue = validateBlockBodyBytes(
|
||||||
contentValueEncoded, header.txRoot, header.ommersHash)
|
contentValueEncoded, header)
|
||||||
check contentValue.isOk()
|
check contentValue.isOk()
|
||||||
|
|
||||||
# Encode content and content key
|
# Encode content and content key
|
||||||
@ -179,6 +179,35 @@ suite "History Content Encodings":
|
|||||||
encode(contentValue.get()) == contentValueEncoded
|
encode(contentValue.get()) == contentValueEncoded
|
||||||
encode(contentKey.get()).asSeq() == contentKeyEncoded
|
encode(contentKey.get()).asSeq() == contentKeyEncoded
|
||||||
|
|
||||||
|
test "PortalBlockBody (Shanghai) Encoding/Decoding":
|
||||||
|
# TODO: We don't have the header (without proof) ready here so cannot do
|
||||||
|
# full validation for now. Add this header and then we can do like above.
|
||||||
|
const
|
||||||
|
dataFile =
|
||||||
|
"./vendor/portal-spec-tests/tests/mainnet/history/bodies/17139055.json"
|
||||||
|
|
||||||
|
let res = readJsonType(dataFile, JsonPortalContentTable)
|
||||||
|
check res.isOk()
|
||||||
|
let content = res.get()
|
||||||
|
|
||||||
|
for k, v in content:
|
||||||
|
let
|
||||||
|
contentKeyEncoded = v.content_key.hexToSeqByte()
|
||||||
|
contentValueEncoded = v.content_value.hexToSeqByte()
|
||||||
|
|
||||||
|
# Decode content key
|
||||||
|
let contentKey = decodeSsz(contentKeyEncoded, ContentKey)
|
||||||
|
check contentKey.isOk()
|
||||||
|
|
||||||
|
# Decode (SSZ + RLP decode step) and validate block body
|
||||||
|
let contentValue = decodeBlockBodyBytes(
|
||||||
|
contentValueEncoded)
|
||||||
|
check contentValue.isOk()
|
||||||
|
|
||||||
|
# Encode content and content key
|
||||||
|
check:
|
||||||
|
encode(contentValue.get()) == contentValueEncoded
|
||||||
|
encode(contentKey.get()).asSeq() == contentKeyEncoded
|
||||||
|
|
||||||
test "Receipts Encoding/Decoding and Verification":
|
test "Receipts Encoding/Decoding and Verification":
|
||||||
const
|
const
|
||||||
|
@ -13,9 +13,9 @@ import
|
|||||||
unittest2, stint,
|
unittest2, stint,
|
||||||
stew/[byteutils, results],
|
stew/[byteutils, results],
|
||||||
eth/[common/eth_types, rlp],
|
eth/[common/eth_types, rlp],
|
||||||
../common/common_types,
|
../../../common/common_types,
|
||||||
../eth_data/history_data_json_store,
|
../../../eth_data/history_data_json_store,
|
||||||
../network/history/history_network
|
../../../network/history/history_network
|
||||||
|
|
||||||
const
|
const
|
||||||
dataFile = "./fluffy/tests/blocks/mainnet_blocks_selected.json"
|
dataFile = "./fluffy/tests/blocks/mainnet_blocks_selected.json"
|
||||||
@ -43,7 +43,7 @@ suite "History Network Content Validation":
|
|||||||
blockHeader = decodeRlp(blockHeaderBytes, BlockHeader).expect(
|
blockHeader = decodeRlp(blockHeaderBytes, BlockHeader).expect(
|
||||||
"Valid header should decode")
|
"Valid header should decode")
|
||||||
blockBody = validateBlockBodyBytes(
|
blockBody = validateBlockBodyBytes(
|
||||||
blockBodyBytes, blockHeader.txRoot, blockHeader.ommersHash).expect(
|
blockBodyBytes, blockHeader).expect(
|
||||||
"Should be Valid decoded block body")
|
"Should be Valid decoded block body")
|
||||||
receipts = validateReceiptsBytes(
|
receipts = validateReceiptsBytes(
|
||||||
receiptsBytes, blockHeader.receiptRoot).expect(
|
receiptsBytes, blockHeader.receiptRoot).expect(
|
||||||
@ -68,13 +68,13 @@ suite "History Network Content Validation":
|
|||||||
|
|
||||||
test "Valid Block Body":
|
test "Valid Block Body":
|
||||||
check validateBlockBodyBytes(
|
check validateBlockBodyBytes(
|
||||||
blockBodyBytes, blockHeader.txRoot, blockHeader.ommersHash).isOk()
|
blockBodyBytes, blockHeader).isOk()
|
||||||
|
|
||||||
test "Malformed Block Body":
|
test "Malformed Block Body":
|
||||||
let malformedBytes = blockBodyBytes[10..blockBodyBytes.high]
|
let malformedBytes = blockBodyBytes[10..blockBodyBytes.high]
|
||||||
|
|
||||||
check validateBlockBodyBytes(
|
check validateBlockBodyBytes(
|
||||||
malformedBytes, blockHeader.txRoot, blockHeader.ommersHash).isErr()
|
malformedBytes, blockHeader).isErr()
|
||||||
|
|
||||||
test "Invalid Block Body - Modified Transaction List":
|
test "Invalid Block Body - Modified Transaction List":
|
||||||
var modifiedBody = blockBody
|
var modifiedBody = blockBody
|
||||||
@ -88,7 +88,7 @@ suite "History Network Content Validation":
|
|||||||
let modifiedBodyBytes = encode(modifiedBody)
|
let modifiedBodyBytes = encode(modifiedBody)
|
||||||
|
|
||||||
check validateBlockBodyBytes(
|
check validateBlockBodyBytes(
|
||||||
modifiedBodyBytes, blockHeader.txRoot, blockHeader.ommersHash).isErr()
|
modifiedBodyBytes, blockHeader).isErr()
|
||||||
|
|
||||||
test "Invalid Block Body - Modified Uncles List":
|
test "Invalid Block Body - Modified Uncles List":
|
||||||
var modifiedBody = blockBody
|
var modifiedBody = blockBody
|
||||||
@ -98,7 +98,7 @@ suite "History Network Content Validation":
|
|||||||
let modifiedBodyBytes = encode(modifiedBody)
|
let modifiedBodyBytes = encode(modifiedBody)
|
||||||
|
|
||||||
check validateBlockBodyBytes(
|
check validateBlockBodyBytes(
|
||||||
modifiedBodyBytes, blockHeader.txRoot, blockHeader.ommersHash).isErr()
|
modifiedBodyBytes, blockHeader).isErr()
|
||||||
|
|
||||||
test "Valid Receipts":
|
test "Valid Receipts":
|
||||||
check validateReceiptsBytes(receiptsBytes, blockHeader.receiptRoot).isOk()
|
check validateReceiptsBytes(receiptsBytes, blockHeader.receiptRoot).isOk()
|
@ -185,10 +185,7 @@ func asReceipt(
|
|||||||
else:
|
else:
|
||||||
err("No root nor status field in the JSON receipt object")
|
err("No root nor status field in the JSON receipt object")
|
||||||
|
|
||||||
proc asPortalBlockData*(
|
proc calculateTransactionData(
|
||||||
payload: ExecutionPayloadV1 | ExecutionPayloadV2 | ExecutionPayloadV3):
|
|
||||||
(common_types.BlockHash, BlockHeaderWithProof, BlockBodySSZ) =
|
|
||||||
proc calculateTransactionData(
|
|
||||||
items: openArray[TypedTransaction]):
|
items: openArray[TypedTransaction]):
|
||||||
Hash256 {.raises: [].} =
|
Hash256 {.raises: [].} =
|
||||||
|
|
||||||
@ -204,16 +201,16 @@ proc asPortalBlockData*(
|
|||||||
|
|
||||||
return tr.rootHash()
|
return tr.rootHash()
|
||||||
|
|
||||||
# TODO: Since Capella we can also access ExecutionPayloadHeader and thus
|
# TODO: Since Capella we can also access ExecutionPayloadHeader and thus
|
||||||
# could get the Roots through there instead.
|
# could get the Roots through there instead.
|
||||||
proc calculateWithdrawalsRoot(
|
proc calculateWithdrawalsRoot(
|
||||||
items: openArray[WithdrawalV1]):
|
items: openArray[WithdrawalV1]):
|
||||||
Hash256 {.raises: [].} =
|
Hash256 {.raises: [].} =
|
||||||
|
|
||||||
var tr = initHexaryTrie(newMemoryDB())
|
var tr = initHexaryTrie(newMemoryDB())
|
||||||
for i, w in items:
|
for i, w in items:
|
||||||
try:
|
try:
|
||||||
let withdrawal = Withdrawal(
|
let withdrawal = etypes.Withdrawal(
|
||||||
index: distinctBase(w.index),
|
index: distinctBase(w.index),
|
||||||
validatorIndex: distinctBase(w.validatorIndex),
|
validatorIndex: distinctBase(w.validatorIndex),
|
||||||
address: distinctBase(w.address),
|
address: distinctBase(w.address),
|
||||||
@ -225,13 +222,12 @@ proc asPortalBlockData*(
|
|||||||
|
|
||||||
return tr.rootHash()
|
return tr.rootHash()
|
||||||
|
|
||||||
|
proc asPortalBlockData*(
|
||||||
|
payload: ExecutionPayloadV1):
|
||||||
|
(common_types.BlockHash, BlockHeaderWithProof, PortalBlockBodyLegacy) =
|
||||||
let
|
let
|
||||||
txRoot = calculateTransactionData(payload.transactions)
|
txRoot = calculateTransactionData(payload.transactions)
|
||||||
withdrawalsRoot =
|
withdrawalsRoot = options.none(Hash256)
|
||||||
when type(payload) is ExecutionPayloadV1:
|
|
||||||
options.none(Hash256)
|
|
||||||
else:
|
|
||||||
some(calculateWithdrawalsRoot(payload.withdrawals))
|
|
||||||
|
|
||||||
header = etypes.BlockHeader(
|
header = etypes.BlockHeader(
|
||||||
parentHash: payload.parentHash.asEthHash,
|
parentHash: payload.parentHash.asEthHash,
|
||||||
@ -251,7 +247,7 @@ proc asPortalBlockData*(
|
|||||||
nonce: default(BlockNonce),
|
nonce: default(BlockNonce),
|
||||||
fee: some(payload.baseFeePerGas),
|
fee: some(payload.baseFeePerGas),
|
||||||
withdrawalsRoot: withdrawalsRoot,
|
withdrawalsRoot: withdrawalsRoot,
|
||||||
excessDataGas: options.none(UInt256) # TODO: Update later according to fork
|
excessDataGas: options.none(UInt256)
|
||||||
)
|
)
|
||||||
|
|
||||||
headerWithProof = BlockHeaderWithProof(
|
headerWithProof = BlockHeaderWithProof(
|
||||||
@ -262,9 +258,7 @@ proc asPortalBlockData*(
|
|||||||
for tx in payload.transactions:
|
for tx in payload.transactions:
|
||||||
discard transactions.add(TransactionByteList(distinctBase(tx)))
|
discard transactions.add(TransactionByteList(distinctBase(tx)))
|
||||||
|
|
||||||
# TODO: Specifications are not ready for Shanghai/Capella on how to add the
|
let body = PortalBlockBodyLegacy(
|
||||||
# withdrawals here.
|
|
||||||
let body = BlockBodySSZ(
|
|
||||||
transactions: transactions,
|
transactions: transactions,
|
||||||
uncles: Uncles(@[byte 0xc0]))
|
uncles: Uncles(@[byte 0xc0]))
|
||||||
|
|
||||||
@ -272,6 +266,64 @@ proc asPortalBlockData*(
|
|||||||
|
|
||||||
(hash, headerWithProof, body)
|
(hash, headerWithProof, body)
|
||||||
|
|
||||||
|
proc asPortalBlockData*(
|
||||||
|
payload: ExecutionPayloadV2 | ExecutionPayloadV3):
|
||||||
|
(common_types.BlockHash, BlockHeaderWithProof, PortalBlockBodyShanghai) =
|
||||||
|
let
|
||||||
|
txRoot = calculateTransactionData(payload.transactions)
|
||||||
|
withdrawalsRoot = some(calculateWithdrawalsRoot(payload.withdrawals))
|
||||||
|
|
||||||
|
header = etypes.BlockHeader(
|
||||||
|
parentHash: payload.parentHash.asEthHash,
|
||||||
|
ommersHash: EMPTY_UNCLE_HASH,
|
||||||
|
coinbase: EthAddress payload.feeRecipient,
|
||||||
|
stateRoot: payload.stateRoot.asEthHash,
|
||||||
|
txRoot: txRoot,
|
||||||
|
receiptRoot: payload.receiptsRoot.asEthHash,
|
||||||
|
bloom: distinctBase(payload.logsBloom),
|
||||||
|
difficulty: default(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(BlockNonce),
|
||||||
|
fee: some(payload.baseFeePerGas),
|
||||||
|
withdrawalsRoot: withdrawalsRoot,
|
||||||
|
excessDataGas: options.none(UInt256) # TODO: adjust later according to deneb fork
|
||||||
|
)
|
||||||
|
|
||||||
|
headerWithProof = BlockHeaderWithProof(
|
||||||
|
header: ByteList(rlp.encode(header)),
|
||||||
|
proof: BlockHeaderProof.init())
|
||||||
|
|
||||||
|
var transactions: Transactions
|
||||||
|
for tx in payload.transactions:
|
||||||
|
discard transactions.add(TransactionByteList(distinctBase(tx)))
|
||||||
|
|
||||||
|
func toWithdrawal(x: WithdrawalV1): Withdrawal =
|
||||||
|
Withdrawal(
|
||||||
|
index: x.index.uint64,
|
||||||
|
validatorIndex: x.validatorIndex.uint64,
|
||||||
|
address: x.address.EthAddress,
|
||||||
|
amount: x.amount.uint64
|
||||||
|
)
|
||||||
|
|
||||||
|
var withdrawals: Withdrawals
|
||||||
|
for w in payload.withdrawals:
|
||||||
|
discard withdrawals.add(WithdrawalByteList(rlp.encode(toWithdrawal(w))))
|
||||||
|
|
||||||
|
let body = PortalBlockBodyShanghai(
|
||||||
|
transactions: transactions,
|
||||||
|
uncles: Uncles(@[byte 0xc0]),
|
||||||
|
withdrawals: withdrawals
|
||||||
|
)
|
||||||
|
|
||||||
|
let hash = common_types.BlockHash(data: distinctBase(payload.blockHash))
|
||||||
|
|
||||||
|
(hash, headerWithProof, body)
|
||||||
|
|
||||||
func forkDigestAtEpoch(
|
func forkDigestAtEpoch(
|
||||||
forkDigests: ForkDigests, epoch: Epoch, cfg: RuntimeConfig): ForkDigest =
|
forkDigests: ForkDigests, epoch: Epoch, cfg: RuntimeConfig): ForkDigest =
|
||||||
forkDigests.atEpoch(epoch, cfg)
|
forkDigests.atEpoch(epoch, cfg)
|
||||||
@ -453,8 +505,8 @@ proc run(config: BeaconBridgeConf) {.raises: [CatchableError].} =
|
|||||||
error "Error getting block receipts", error
|
error "Error getting block receipts", error
|
||||||
return
|
return
|
||||||
|
|
||||||
let sszReceipts = ReceiptsSSZ.fromReceipts(receipts)
|
let portalReceipts = PortalReceipts.fromReceipts(receipts)
|
||||||
if validateReceipts(sszReceipts, payload.receiptsRoot).isErr():
|
if validateReceipts(portalReceipts, payload.receiptsRoot).isErr():
|
||||||
error "Receipts root is invalid"
|
error "Receipts root is invalid"
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -466,7 +518,7 @@ proc run(config: BeaconBridgeConf) {.raises: [CatchableError].} =
|
|||||||
try:
|
try:
|
||||||
let peers = await portalRpcClient.portal_historyGossip(
|
let peers = await portalRpcClient.portal_historyGossip(
|
||||||
encodedContentKeyHex,
|
encodedContentKeyHex,
|
||||||
SSZ.encode(sszReceipts).toHex())
|
SSZ.encode(portalReceipts).toHex())
|
||||||
info "Block receipts gossiped", peers,
|
info "Block receipts gossiped", peers,
|
||||||
contentKey = encodedContentKeyHex
|
contentKey = encodedContentKeyHex
|
||||||
except CatchableError as e:
|
except CatchableError as e:
|
||||||
|
2
vendor/portal-spec-tests
vendored
2
vendor/portal-spec-tests
vendored
@ -1 +1 @@
|
|||||||
Subproject commit 6adbbe43830a8b9329f650e341972f9ec23980f4
|
Subproject commit df86ab856782f5e24b983b7ec85c2b811d27bc31
|
Loading…
x
Reference in New Issue
Block a user