From be9ecfa1c935adc0c4eb64ada8dd12c6933a3c92 Mon Sep 17 00:00:00 2001 From: Etan Kissling Date: Mon, 4 Sep 2023 20:44:03 +0200 Subject: [PATCH] add helpers for processing withdrawals to `libnimbus_lc.a` (#5374) Similar to the existing helpers for processing transactions / receipts, extend `libnimbus_lc.a` with support for processing withdrawals as well. --- beacon_chain/libnimbus_lc/libnimbus_lc.h | 128 ++++++++++++ beacon_chain/libnimbus_lc/libnimbus_lc.nim | 187 +++++++++++++++++- beacon_chain/libnimbus_lc/test_libnimbus_lc.c | 30 ++- beacon_chain/spec/helpers.nim | 2 +- 4 files changed, 341 insertions(+), 6 deletions(-) diff --git a/beacon_chain/libnimbus_lc/libnimbus_lc.h b/beacon_chain/libnimbus_lc/libnimbus_lc.h index b4eee0250..922b5c118 100644 --- a/beacon_chain/libnimbus_lc/libnimbus_lc.h +++ b/beacon_chain/libnimbus_lc/libnimbus_lc.h @@ -1080,6 +1080,26 @@ ETH_RESULT_USE_CHECK const ETHRoot *ETHExecutionBlockHeaderGetWithdrawalsRoot( const ETHExecutionBlockHeader *executionBlockHeader); +/** + * Withdrawal sequence. + */ +typedef struct ETHWithdrawals ETHWithdrawals; + +/** + * Obtains the withdrawal sequence of a given execution block header. + * + * - The returned value is allocated in the given execution block header. + * It must neither be released nor written to, and the execution block + * header must not be released while the returned value is in use. + * + * @param executionBlockHeader Execution block header. + * + * @return Withdrawal sequence. + */ +ETH_RESULT_USE_CHECK +const ETHWithdrawals *ETHExecutionBlockHeaderGetWithdrawals( + const ETHExecutionBlockHeader *executionBlockHeader); + /** * Transaction sequence. */ @@ -1765,6 +1785,114 @@ const void *ETHReceiptGetBytes( const ETHReceipt *receipt, int *numBytes); +/** + * Indicates the total number of withdrawals in a withdrawal sequence. + * + * - Individual withdrawals may be investigated using `ETHWithdrawalsGet`. + * + * @param withdrawals Withdrawal sequence. + * + * @return Number of available withdrawals. + */ +ETH_RESULT_USE_CHECK +int ETHWithdrawalsGetCount(const ETHWithdrawals *withdrawals); + +/** + * Withdrawal. + */ +typedef struct ETHWithdrawal ETHWithdrawal; + +/** + * Obtains an individual withdrawal by sequential index + * in a withdrawal sequence. + * + * - The returned value is allocated in the given withdrawal sequence. + * It must neither be released nor written to, and the withdrawal + * sequence must not be released while the returned value is in use. + * + * @param withdrawals Withdrawal sequence. + * @param withdrawalIndex Sequential withdrawal index. + * + * @return Withdrawal. + */ +ETH_RESULT_USE_CHECK +const ETHWithdrawal *ETHWithdrawalsGet( + const ETHWithdrawals *withdrawals, + int withdrawalIndex); + +/** + * Obtains the index of a withdrawal. + * + * - The returned value is allocated in the given withdrawal. + * It must neither be released nor written to, and the withdrawal + * must not be released while the returned value is in use. + * + * @param withdrawal Withdrawal. + * + * @return Index. + */ +ETH_RESULT_USE_CHECK +const uint64_t *ETHWithdrawalGetIndex(const ETHWithdrawal *withdrawal); + +/** + * Obtains the validator index of a withdrawal. + * + * - The returned value is allocated in the given withdrawal. + * It must neither be released nor written to, and the withdrawal + * must not be released while the returned value is in use. + * + * @param withdrawal Withdrawal. + * + * @return Validator index. + */ +ETH_RESULT_USE_CHECK +const uint64_t *ETHWithdrawalGetValidatorIndex(const ETHWithdrawal *withdrawal); + +/** + * Obtains the address of a withdrawal. + * + * - The returned value is allocated in the given withdrawal. + * It must neither be released nor written to, and the withdrawal + * must not be released while the returned value is in use. + * + * @param withdrawal Withdrawal. + * + * @return Address. + */ +ETH_RESULT_USE_CHECK +const ETHExecutionAddress *ETHWithdrawalGetAddress(const ETHWithdrawal *withdrawal); + +/** + * Obtains the amount of a withdrawal. + * + * - The returned value is allocated in the given withdrawal. + * It must neither be released nor written to, and the withdrawal + * must not be released while the returned value is in use. + * + * @param withdrawal Withdrawal. + * + * @return Amount. + */ +ETH_RESULT_USE_CHECK +const uint64_t *ETHWithdrawalGetAmount(const ETHWithdrawal *withdrawal); + +/** + * Obtains the raw byte representation of a withdrawal. + * + * - The returned value is allocated in the given withdrawal. + * It must neither be released nor written to, and the withdrawal + * must not be released while the returned value is in use. + * + * @param withdrawal Withdrawal. + * @param[out] numBytes Length of buffer. + * + * @return Buffer with raw withdrawal data. + */ +ETH_RESULT_USE_CHECK +const void *ETHWithdrawalGetBytes( + const ETHWithdrawal *withdrawal, + int *numBytes); + #if __has_feature(nullability) #pragma clang assume_nonnull end #endif diff --git a/beacon_chain/libnimbus_lc/libnimbus_lc.nim b/beacon_chain/libnimbus_lc/libnimbus_lc.nim index 82c16514b..591996507 100644 --- a/beacon_chain/libnimbus_lc/libnimbus_lc.nim +++ b/beacon_chain/libnimbus_lc/libnimbus_lc.nim @@ -1169,9 +1169,18 @@ func ETHExecutionPayloadHeaderGetExcessBlobGas( ## * Excess blob gas. execution[].excess_blob_gas.cint -type ETHExecutionBlockHeader = object - transactionsRoot: Eth2Digest - withdrawalsRoot: Eth2Digest +type + ETHWithdrawal = object + index: uint64 + validatorIndex: uint64 + address: ExecutionAddress + amount: uint64 + bytes: seq[byte] + + ETHExecutionBlockHeader = object + transactionsRoot: Eth2Digest + withdrawalsRoot: Eth2Digest + withdrawals: seq[ETHWithdrawal] proc ETHExecutionBlockHeaderCreateFromJson( executionHash: ptr Eth2Digest, @@ -1277,10 +1286,51 @@ proc ETHExecutionBlockHeaderCreateFromJson( if rlpHash(blockHeader) != executionHash[]: return nil + # Construct withdrawals + var wds: seq[ETHWithdrawal] + if data.withdrawals.isSome: + doAssert data.withdrawalsRoot.isSome # Checked above + + wds = newSeqOfCap[ETHWithdrawal](data.withdrawals.get.len) + for data in data.withdrawals.get: + # Check fork consistency + static: doAssert totalSerializedFields(WithdrawalObject) == 4, + "Only update this number once code is adjusted to check new fields!" + + # Construct withdrawal + let + wd = ExecutionWithdrawal( + index: distinctBase(data.index), + validatorIndex: distinctBase(data.validatorIndex), + address: distinctBase(data.address), + amount: distinctBase(data.amount)) + rlpBytes = + try: + rlp.encode(wd) + except RlpError: + raiseAssert "Unreachable" + + wds.add ETHWithdrawal( + index: wd.index, + validatorIndex: wd.validatorIndex, + address: ExecutionAddress(data: wd.address), + amount: wd.amount, + bytes: rlpBytes) + + var tr = initHexaryTrie(newMemoryDB()) + for i, wd in wds: + try: + tr.put(rlp.encode(i), wd.bytes) + except RlpError: + raiseAssert "Unreachable" + if tr.rootHash() != data.withdrawalsRoot.get.asEth2Digest: + return nil + let executionBlockHeader = ETHExecutionBlockHeader.new() executionBlockHeader[] = ETHExecutionBlockHeader( transactionsRoot: blockHeader.txRoot, - withdrawalsRoot: blockHeader.withdrawalsRoot.get(ZERO_HASH)) + withdrawalsRoot: blockHeader.withdrawalsRoot.get(ZERO_HASH), + withdrawals: wds) executionBlockHeader.toUnmanagedPtr() proc ETHExecutionBlockHeaderDestroy( @@ -1325,6 +1375,22 @@ func ETHExecutionBlockHeaderGetWithdrawalsRoot( ## * Execution withdrawals root. addr executionBlockHeader[].withdrawalsRoot +func ETHExecutionBlockHeaderGetWithdrawals( + executionBlockHeader: ptr ETHExecutionBlockHeader +): ptr seq[ETHWithdrawal] {.exported.} = + ## Obtains the withdrawal sequence of a given execution block header. + ## + ## * The returned value is allocated in the given execution block header. + ## It must neither be released nor written to, and the execution block + ## header must not be released while the returned value is in use. + ## + ## Parameters: + ## * `executionBlockHeader` - Execution block header. + ## + ## Returns: + ## * Withdrawal sequence. + addr executionBlockHeader[].withdrawals + type ETHAccessTuple = object address: ExecutionAddress @@ -2391,3 +2457,116 @@ func ETHReceiptGetBytes( const defaultBytes: cstring = "" return cast[ptr UncheckedArray[byte]](defaultBytes) cast[ptr UncheckedArray[byte]](addr distinctBase(receipt[].bytes)[0]) + +func ETHWithdrawalsGetCount( + withdrawals: ptr seq[ETHWithdrawal]): cint {.exported.} = + ## Indicates the total number of withdrawals in a withdrawal sequence. + ## + ## * Individual withdrawals may be investigated using `ETHWithdrawalsGet`. + ## + ## Parameters: + ## * `withdrawals` - Withdrawal sequence. + ## + ## Returns: + ## * Number of available withdrawals. + withdrawals[].len.cint + +func ETHWithdrawalsGet( + withdrawals: ptr seq[ETHWithdrawal], + withdrawalIndex: cint): ptr ETHWithdrawal {.exported.} = + ## Obtains an individual withdrawal by sequential index + ## in a withdrawal sequence. + ## + ## * The returned value is allocated in the given withdrawal sequence. + ## It must neither be released nor written to, and the withdrawal + ## sequence must not be released while the returned value is in use. + ## + ## Parameters: + ## * `withdrawals` - Withdrawal sequence. + ## * `withdrawalIndex` - Sequential withdrawal index. + ## + ## Returns: + ## * Withdrawal. + addr withdrawals[][withdrawalIndex.int] + +func ETHWithdrawalGetIndex( + withdrawal: ptr ETHWithdrawal): ptr uint64 {.exported.} = + ## Obtains the index of a withdrawal. + ## + ## * The returned value is allocated in the given withdrawal. + ## It must neither be released nor written to, and the withdrawal + ## must not be released while the returned value is in use. + ## + ## Parameters: + ## * `withdrawal` - Withdrawal. + ## + ## Returns: + ## * Index. + addr withdrawal[].index + +func ETHWithdrawalGetValidatorIndex( + withdrawal: ptr ETHWithdrawal): ptr uint64 {.exported.} = + ## Obtains the validator index of a withdrawal. + ## + ## * The returned value is allocated in the given withdrawal. + ## It must neither be released nor written to, and the withdrawal + ## must not be released while the returned value is in use. + ## + ## Parameters: + ## * `withdrawal` - Withdrawal. + ## + ## Returns: + ## * Validator index. + addr withdrawal[].validatorIndex + +func ETHWithdrawalGetAddress( + withdrawal: ptr ETHWithdrawal): ptr ExecutionAddress {.exported.} = + ## Obtains the address of a withdrawal. + ## + ## * The returned value is allocated in the given withdrawal. + ## It must neither be released nor written to, and the withdrawal + ## must not be released while the returned value is in use. + ## + ## Parameters: + ## * `withdrawal` - Withdrawal. + ## + ## Returns: + ## * Address. + addr withdrawal[].address + +func ETHWithdrawalGetAmount( + withdrawal: ptr ETHWithdrawal): ptr uint64 {.exported.} = + ## Obtains the amount of a withdrawal. + ## + ## * The returned value is allocated in the given withdrawal. + ## It must neither be released nor written to, and the withdrawal + ## must not be released while the returned value is in use. + ## + ## Parameters: + ## * `withdrawal` - Withdrawal. + ## + ## Returns: + ## * Amount. + addr withdrawal[].amount + +func ETHWithdrawalGetBytes( + withdrawal: ptr ETHWithdrawal, + numBytes #[out]#: ptr cint): ptr UncheckedArray[byte] {.exported.} = + ## Obtains the raw byte representation of a withdrawal. + ## + ## * The returned value is allocated in the given withdrawal. + ## It must neither be released nor written to, and the withdrawal + ## must not be released while the returned value is in use. + ## + ## Parameters: + ## * `withdrawal` - Withdrawal. + ## * `numBytes` [out] - Length of buffer. + ## + ## Returns: + ## * Buffer with raw withdrawal data. + numBytes[] = distinctBase(withdrawal[].bytes).len.cint + if distinctBase(withdrawal[].bytes).len == 0: + # https://github.com/nim-lang/Nim/issues/22389 + const defaultBytes: cstring = "" + return cast[ptr UncheckedArray[byte]](defaultBytes) + cast[ptr UncheckedArray[byte]](addr distinctBase(withdrawal[].bytes)[0]) diff --git a/beacon_chain/libnimbus_lc/test_libnimbus_lc.c b/beacon_chain/libnimbus_lc/test_libnimbus_lc.c index 2553fdd03..1c9afce2e 100644 --- a/beacon_chain/libnimbus_lc/test_libnimbus_lc.c +++ b/beacon_chain/libnimbus_lc/test_libnimbus_lc.c @@ -375,7 +375,7 @@ int main(void) ETHRootDestroy(copiedExecutionHash); ETHLightClientHeaderDestroy(copiedHeader); - printf("- finalized_header (execution block header):\n"); + printf("\nFinalized_header (execution block header):\n"); const ETHRoot *executionTransactionsRoot = ETHExecutionBlockHeaderGetTransactionsRoot(executionBlockHeader); @@ -389,6 +389,34 @@ int main(void) printHexString(executionWithdrawalsRoot, sizeof *executionWithdrawalsRoot); printf("\n"); + const ETHWithdrawals *withdrawals = + ETHExecutionBlockHeaderGetWithdrawals(executionBlockHeader); + int numWithdrawals = ETHWithdrawalsGetCount(withdrawals); + printf(" - withdrawals:\n"); + for (int withdrawalIndex = 0; withdrawalIndex < numWithdrawals; withdrawalIndex++) { + const ETHWithdrawal *withdrawal = ETHWithdrawalsGet(withdrawals, withdrawalIndex); + + const uint64_t *index = ETHWithdrawalGetIndex(withdrawal); + printf(" - index: %" PRIu64 "\n", *index); + + const uint64_t *validatorIndex = ETHWithdrawalGetValidatorIndex(withdrawal); + printf(" - validator_index: %" PRIu64 "\n", *validatorIndex); + + const ETHExecutionAddress *address = ETHWithdrawalGetAddress(withdrawal); + printf(" - address: "); + printHexString(address, sizeof *address); + printf("\n"); + + const uint64_t *amount = ETHWithdrawalGetAmount(withdrawal); + printf(" - amount: %" PRIu64 "\n", *amount); + + int numBytes; + const void *bytes = ETHWithdrawalGetBytes(withdrawal, &numBytes); + printf(" - bytes: "); + printHexString(bytes, numBytes); + printf("\n"); + } + ETHExecutionBlockHeaderDestroy(executionBlockHeader); ETHRoot sampleTransactionsRoot = {{ diff --git a/beacon_chain/spec/helpers.nim b/beacon_chain/spec/helpers.nim index d0267c34f..e4f4543be 100644 --- a/beacon_chain/spec/helpers.nim +++ b/beacon_chain/spec/helpers.nim @@ -34,7 +34,7 @@ type ExecutionHash256* = eth_types.Hash256 ExecutionTransaction* = eth_types.Transaction ExecutionReceipt* = eth_types.Receipt - ExecutionWithdrawal = eth_types.Withdrawal + ExecutionWithdrawal* = eth_types.Withdrawal ExecutionBlockHeader* = eth_types.BlockHeader FinalityCheckpoints* = object