add helpers for processing transactions to `libnimbus_lc.a` (#5269)
It is useful to verify transactions data against `transactionsRoot`. Add corresponding functionality to the light client library.
This commit is contained in:
parent
c211a3849e
commit
991c31f42b
|
@ -1002,22 +1002,22 @@ const ETHUInt256 *ETHExecutionPayloadHeaderGetBaseFeePerGas(
|
|||
const ETHExecutionPayloadHeader *execution);
|
||||
|
||||
/**
|
||||
* Obtains the data gas used of a given execution payload header.
|
||||
* Obtains the blob gas used of a given execution payload header.
|
||||
*
|
||||
* @param execution Execution payload header.
|
||||
*
|
||||
* @return Data gas used.
|
||||
* @return Blob gas used.
|
||||
*/
|
||||
ETH_RESULT_USE_CHECK
|
||||
int ETHExecutionPayloadHeaderGetBlobGasUsed(
|
||||
const ETHExecutionPayloadHeader *execution);
|
||||
|
||||
/**
|
||||
* Obtains the excess data gas of a given execution payload header.
|
||||
* Obtains the excess blob gas of a given execution payload header.
|
||||
*
|
||||
* @param execution Execution payload header.
|
||||
*
|
||||
* @return Excess data gas.
|
||||
* @return Excess blob gas.
|
||||
*/
|
||||
ETH_RESULT_USE_CHECK
|
||||
int ETHExecutionPayloadHeaderGetExcessBlobGas(
|
||||
|
@ -1034,7 +1034,7 @@ typedef struct ETHExecutionBlockHeader ETHExecutionBlockHeader;
|
|||
*
|
||||
* - The JSON-RPC `eth_getBlockByHash` with params `[executionHash, false]`
|
||||
* may be used to obtain execution block header data for a given execution
|
||||
* block hash. Pass the response's `result` property to `blockHeaderJson`.
|
||||
* block hash. Pass the response's `result` property as `blockHeaderJson`.
|
||||
*
|
||||
* - The execution block header must be destroyed with
|
||||
* `ETHExecutionBlockHeaderDestroy` once no longer needed,
|
||||
|
@ -1092,6 +1092,414 @@ ETH_RESULT_USE_CHECK
|
|||
const ETHRoot *ETHExecutionBlockHeaderGetWithdrawalsRoot(
|
||||
const ETHExecutionBlockHeader *executionBlockHeader);
|
||||
|
||||
/**
|
||||
* Transaction sequence.
|
||||
*/
|
||||
typedef struct ETHTransactions ETHTransactions;
|
||||
|
||||
/**
|
||||
* Verifies that JSON transactions data is valid and that it matches
|
||||
* the given `transactionsRoot`.
|
||||
*
|
||||
* - The JSON-RPC `eth_getBlockByHash` with params `[executionHash, true]`
|
||||
* may be used to obtain transactions data for a given execution
|
||||
* block hash. Pass `result.transactions` as `transactionsJson`.
|
||||
*
|
||||
* - The transaction sequence must be destroyed with
|
||||
* `ETHTransactionsDestroy` once no longer needed,
|
||||
* to release memory.
|
||||
*
|
||||
* @param transactionsRoot Execution transactions root.
|
||||
* @param transactionsJson Buffer with JSON transactions list. NULL-terminated.
|
||||
*
|
||||
* @return Pointer to an initialized transaction sequence - If successful.
|
||||
* @return `NULL` - If the given `transactionsJson` is malformed or incompatible.
|
||||
*
|
||||
* @see https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_getblockbyhash
|
||||
*/
|
||||
ETH_RESULT_USE_CHECK
|
||||
ETHTransactions *ETHTransactionsCreateFromJson(
|
||||
const ETHRoot *transactionsRoot,
|
||||
const char *transactionsJson);
|
||||
|
||||
/**
|
||||
* Destroys a transaction sequence.
|
||||
*
|
||||
* - The transaction sequence must no longer be used after destruction.
|
||||
*
|
||||
* @param transactions Transaction sequence.
|
||||
*/
|
||||
void ETHTransactionsDestroy(ETHTransactions *transactions);
|
||||
|
||||
/**
|
||||
* Indicates the total number of transactions in a transaction sequence.
|
||||
*
|
||||
* - Individual transactions may be investigated using `ETHTransactionsGet`.
|
||||
*
|
||||
* @param transactions Transaction sequence.
|
||||
*
|
||||
* @return Number of available transactions.
|
||||
*/
|
||||
ETH_RESULT_USE_CHECK
|
||||
int ETHTransactionsGetCount(const ETHTransactions *transactions);
|
||||
|
||||
/**
|
||||
* Transaction.
|
||||
*/
|
||||
typedef struct ETHTransaction ETHTransaction;
|
||||
|
||||
/**
|
||||
* Obtains an individual transaction by sequential index
|
||||
* in a transaction sequence.
|
||||
*
|
||||
* - The returned value is allocated in the given transaction sequence.
|
||||
* It must neither be released nor written to, and the transaction
|
||||
* sequence must not be released while the returned value is in use.
|
||||
*
|
||||
* @param transactions Transaction sequence.
|
||||
* @param transactionIndex Sequential transaction index.
|
||||
*
|
||||
* @return Transaction.
|
||||
*/
|
||||
ETH_RESULT_USE_CHECK
|
||||
const ETHTransaction *ETHTransactionsGet(
|
||||
const ETHTransactions *transactions,
|
||||
int transactionIndex);
|
||||
|
||||
/**
|
||||
* Obtains the transaction hash of a transaction.
|
||||
*
|
||||
* - The returned value is allocated in the given transaction.
|
||||
* It must neither be released nor written to, and the transaction
|
||||
* must not be released while the returned value is in use.
|
||||
*
|
||||
* @param transaction Transaction.
|
||||
*
|
||||
* @return Transaction hash.
|
||||
*/
|
||||
ETH_RESULT_USE_CHECK
|
||||
const ETHRoot *ETHTransactionGetHash(const ETHTransaction *transaction);
|
||||
|
||||
/**
|
||||
* Obtains the chain ID of a transaction.
|
||||
*
|
||||
* - The returned value is allocated in the given transaction.
|
||||
* It must neither be released nor written to, and the transaction
|
||||
* must not be released while the returned value is in use.
|
||||
*
|
||||
* @param transaction Transaction.
|
||||
*
|
||||
* @return Chain ID.
|
||||
*/
|
||||
ETH_RESULT_USE_CHECK
|
||||
const ETHUInt256 *ETHTransactionGetChainId(const ETHTransaction *transaction);
|
||||
|
||||
/**
|
||||
* Obtains the from address of a transaction.
|
||||
*
|
||||
* - The returned value is allocated in the given transaction.
|
||||
* It must neither be released nor written to, and the transaction
|
||||
* must not be released while the returned value is in use.
|
||||
*
|
||||
* @param transaction Transaction.
|
||||
*
|
||||
* @return From execution address.
|
||||
*/
|
||||
ETH_RESULT_USE_CHECK
|
||||
const ETHExecutionAddress *ETHTransactionGetFrom(const ETHTransaction *transaction);
|
||||
|
||||
/**
|
||||
* Obtains the nonce of a transaction.
|
||||
*
|
||||
* - The returned value is allocated in the given transaction.
|
||||
* It must neither be released nor written to, and the transaction
|
||||
* must not be released while the returned value is in use.
|
||||
*
|
||||
* @param transaction Transaction.
|
||||
*
|
||||
* @return Nonce.
|
||||
*/
|
||||
ETH_RESULT_USE_CHECK
|
||||
const uint64_t *ETHTransactionGetNonce(const ETHTransaction *transaction);
|
||||
|
||||
/**
|
||||
* Obtains the max priority fee per gas of a transaction.
|
||||
*
|
||||
* - The returned value is allocated in the given transaction.
|
||||
* It must neither be released nor written to, and the transaction
|
||||
* must not be released while the returned value is in use.
|
||||
*
|
||||
* @param transaction Transaction.
|
||||
*
|
||||
* @return Max priority fee per gas.
|
||||
*/
|
||||
ETH_RESULT_USE_CHECK
|
||||
const uint64_t *ETHTransactionGetMaxPriorityFeePerGas(const ETHTransaction *transaction);
|
||||
|
||||
/**
|
||||
* Obtains the max fee per gas of a transaction.
|
||||
*
|
||||
* - The returned value is allocated in the given transaction.
|
||||
* It must neither be released nor written to, and the transaction
|
||||
* must not be released while the returned value is in use.
|
||||
*
|
||||
* @param transaction Transaction.
|
||||
*
|
||||
* @return Max fee per gas.
|
||||
*/
|
||||
ETH_RESULT_USE_CHECK
|
||||
const uint64_t *ETHTransactionGetMaxFeePerGas(const ETHTransaction *transaction);
|
||||
|
||||
/**
|
||||
* Obtains the gas of a transaction.
|
||||
*
|
||||
* - The returned value is allocated in the given transaction.
|
||||
* It must neither be released nor written to, and the transaction
|
||||
* must not be released while the returned value is in use.
|
||||
*
|
||||
* @param transaction Transaction.
|
||||
*
|
||||
* @return Gas.
|
||||
*/
|
||||
ETH_RESULT_USE_CHECK
|
||||
const uint64_t *ETHTransactionGetGas(const ETHTransaction *transaction);
|
||||
|
||||
/**
|
||||
* Indicates whether or not a transaction is creating a contract.
|
||||
*
|
||||
* @param transaction Transaction.
|
||||
*
|
||||
* @return Whether or not the transaction is creating a contract.
|
||||
*/
|
||||
ETH_RESULT_USE_CHECK
|
||||
bool ETHTransactionIsCreatingContract(const ETHTransaction *transaction);
|
||||
|
||||
/**
|
||||
* Obtains the to address of a transaction.
|
||||
*
|
||||
* - If the transaction is creating a contract, this function returns
|
||||
* the address of the new contract.
|
||||
*
|
||||
* - The returned value is allocated in the given transaction.
|
||||
* It must neither be released nor written to, and the transaction
|
||||
* must not be released while the returned value is in use.
|
||||
*
|
||||
* @param transaction Transaction.
|
||||
*
|
||||
* @return To execution address.
|
||||
*/
|
||||
ETH_RESULT_USE_CHECK
|
||||
const ETHExecutionAddress *ETHTransactionGetTo(const ETHTransaction *transaction);
|
||||
|
||||
/**
|
||||
* Obtains the value of a transaction.
|
||||
*
|
||||
* - The returned value is allocated in the given transaction.
|
||||
* It must neither be released nor written to, and the transaction
|
||||
* must not be released while the returned value is in use.
|
||||
*
|
||||
* @param transaction Transaction.
|
||||
*
|
||||
* @return Value.
|
||||
*/
|
||||
ETH_RESULT_USE_CHECK
|
||||
const ETHUInt256 *ETHTransactionGetValue(const ETHTransaction *transaction);
|
||||
|
||||
/**
|
||||
* Obtains the input of a transaction.
|
||||
*
|
||||
* - The returned value is allocated in the given transaction.
|
||||
* It must neither be released nor written to, and the transaction
|
||||
* must not be released while the returned value is in use.
|
||||
*
|
||||
* @param transaction Transaction.
|
||||
* @param[out] numBytes Length of buffer.
|
||||
*
|
||||
* @return Buffer with input.
|
||||
*/
|
||||
ETH_RESULT_USE_CHECK
|
||||
const void *ETHTransactionGetInputBytes(
|
||||
const ETHTransaction *transaction,
|
||||
int *numBytes);
|
||||
|
||||
/**
|
||||
* Transaction access list.
|
||||
*/
|
||||
typedef struct ETHAccessList ETHAccessList;
|
||||
|
||||
/**
|
||||
* Obtains the access list of a transaction.
|
||||
*
|
||||
* - The returned value is allocated in the given transaction.
|
||||
* It must neither be released nor written to, and the transaction
|
||||
* must not be released while the returned value is in use.
|
||||
*
|
||||
* @param transaction Transaction.
|
||||
*
|
||||
* @return Transaction access list.
|
||||
*/
|
||||
ETH_RESULT_USE_CHECK
|
||||
const ETHAccessList *ETHTransactionGetAccessList(const ETHTransaction *transaction);
|
||||
|
||||
/**
|
||||
* Indicates the total number of access tuples in a transaction access list.
|
||||
*
|
||||
* - Individual access tuples may be investigated using `ETHAccessListGet`.
|
||||
*
|
||||
* @param accessList Transaction access list.
|
||||
*
|
||||
* @return Number of available access tuples.
|
||||
*/
|
||||
ETH_RESULT_USE_CHECK
|
||||
int ETHAccessListGetCount(const ETHAccessList *accessList);
|
||||
|
||||
/**
|
||||
* Access tuple.
|
||||
*/
|
||||
typedef struct ETHAccessTuple ETHAccessTuple;
|
||||
|
||||
/**
|
||||
* Obtains an individual access tuple by sequential index
|
||||
* in a transaction access list.
|
||||
*
|
||||
* - The returned value is allocated in the given transaction access list.
|
||||
* It must neither be released nor written to, and the transaction
|
||||
* access list must not be released while the returned value is in use.
|
||||
*
|
||||
* @param accessList Transaction access list.
|
||||
* @param accessTupleIndex Sequential access tuple index.
|
||||
*
|
||||
* @return Access tuple.
|
||||
*/
|
||||
ETH_RESULT_USE_CHECK
|
||||
const ETHAccessTuple *ETHAccessListGet(
|
||||
const ETHAccessList *accessList,
|
||||
int accessTupleIndex);
|
||||
|
||||
/**
|
||||
* Obtains the address of an access tuple.
|
||||
*
|
||||
* - The returned value is allocated in the given access tuple.
|
||||
* It must neither be released nor written to, and the access tuple
|
||||
* must not be released while the returned value is in use.
|
||||
*
|
||||
* @param accessTuple Access tuple.
|
||||
*
|
||||
* @return Address.
|
||||
*/
|
||||
ETH_RESULT_USE_CHECK
|
||||
const ETHExecutionAddress *ETHAccessTupleGetAddress(const ETHAccessTuple *accessTuple);
|
||||
|
||||
/**
|
||||
* Indicates the total number of storage keys in an access tuple.
|
||||
*
|
||||
* - Individual storage keys may be investigated using
|
||||
* `ETHAccessTupleGetStorageKey`.
|
||||
*
|
||||
* @param accessTuple Access tuple.
|
||||
*
|
||||
* @return Number of available storage keys.
|
||||
*/
|
||||
ETH_RESULT_USE_CHECK
|
||||
int ETHAccessTupleGetNumStorageKeys(const ETHAccessTuple *accessTuple);
|
||||
|
||||
/**
|
||||
* Obtains an individual storage key by sequential index
|
||||
* in an access tuple.
|
||||
*
|
||||
* - The returned value is allocated in the given transaction access tuple.
|
||||
* It must neither be released nor written to, and the transaction
|
||||
* access tuple must not be released while the returned value is in use.
|
||||
*
|
||||
* @param accessTuple Access tuple.
|
||||
* @param storageKeyIndex Sequential storage key index.
|
||||
*
|
||||
* @return Storage key.
|
||||
*/
|
||||
ETH_RESULT_USE_CHECK
|
||||
const ETHRoot *ETHAccessTupleGetStorageKey(
|
||||
const ETHAccessTuple *accessTuple,
|
||||
int storageKeyIndex);
|
||||
|
||||
/**
|
||||
* Obtains the max fee per blob gas of a transaction.
|
||||
*
|
||||
* - The returned value is allocated in the given transaction.
|
||||
* It must neither be released nor written to, and the transaction
|
||||
* must not be released while the returned value is in use.
|
||||
*
|
||||
* @param transaction Transaction.
|
||||
*
|
||||
* @return Max fee per blob gas.
|
||||
*/
|
||||
ETH_RESULT_USE_CHECK
|
||||
const uint64_t *ETHTransactionGetMaxFeePerBlobGas(const ETHTransaction *transaction);
|
||||
|
||||
/**
|
||||
* Indicates the total number of blob versioned hashes of a transaction.
|
||||
*
|
||||
* - Individual blob versioned hashes may be investigated using
|
||||
* `ETHTransactionGetBlobVersionedHash`.
|
||||
*
|
||||
* @param transaction Transaction.
|
||||
*
|
||||
* @return Number of available blob versioned hashes.
|
||||
*/
|
||||
ETH_RESULT_USE_CHECK
|
||||
int ETHTransactionGetNumBlobVersionedHashes(const ETHTransaction *transaction);
|
||||
|
||||
/**
|
||||
* Obtains an individual blob versioned hash by sequential index
|
||||
* in a transaction.
|
||||
*
|
||||
* - The returned value is allocated in the given transaction.
|
||||
* It must neither be released nor written to, and the transaction
|
||||
* must not be released while the returned value is in use.
|
||||
*
|
||||
* @param transaction Transaction.
|
||||
* @param versionedHashIndex Sequential blob versioned hash index.
|
||||
*
|
||||
* @return Blob versioned hash.
|
||||
*/
|
||||
ETH_RESULT_USE_CHECK
|
||||
const ETHRoot *ETHTransactionGetBlobVersionedHash(
|
||||
const ETHTransaction *transaction,
|
||||
int versionedHashIndex);
|
||||
|
||||
/**
|
||||
* Obtains the signature of a transaction.
|
||||
*
|
||||
* - The returned value is allocated in the given transaction.
|
||||
* It must neither be released nor written to, and the transaction
|
||||
* must not be released while the returned value is in use.
|
||||
*
|
||||
* @param transaction Transaction.
|
||||
* @param[out] numBytes Length of buffer.
|
||||
*
|
||||
* @return Buffer with signature.
|
||||
*/
|
||||
ETH_RESULT_USE_CHECK
|
||||
const void *ETHTransactionGetSignatureBytes(
|
||||
const ETHTransaction *transaction,
|
||||
int *numBytes);
|
||||
|
||||
/**
|
||||
* Obtains the raw byte representation of a transaction.
|
||||
*
|
||||
* - The returned value is allocated in the given transaction.
|
||||
* It must neither be released nor written to, and the transaction
|
||||
* must not be released while the returned value is in use.
|
||||
*
|
||||
* @param transaction Transaction.
|
||||
* @param[out] numBytes Length of buffer.
|
||||
*
|
||||
* @return Buffer with raw transaction data.
|
||||
*/
|
||||
ETH_RESULT_USE_CHECK
|
||||
const void *ETHTransactionGetBytes(
|
||||
const ETHTransaction *transaction,
|
||||
int *numBytes);
|
||||
|
||||
#if __has_feature(nullability)
|
||||
#pragma clang assume_nonnull end
|
||||
#endif
|
||||
|
|
|
@ -8,11 +8,15 @@
|
|||
{.push raises: [].}
|
||||
|
||||
import
|
||||
std/[json, times],
|
||||
std/[json, sequtils, times],
|
||||
stew/saturation_arith,
|
||||
eth/common/eth_types_rlp,
|
||||
eth/common/[eth_types_rlp, transaction],
|
||||
eth/keys,
|
||||
eth/p2p/discoveryv5/random2,
|
||||
eth/rlp,
|
||||
eth/trie/[db, hexary],
|
||||
json_rpc/jsonmarshal,
|
||||
secp256k1,
|
||||
web3/ethtypes,
|
||||
../el/el_manager,
|
||||
../spec/eth2_apis/[eth2_rest_serialization, rest_light_client_calls],
|
||||
|
@ -1174,7 +1178,7 @@ func ETHExecutionPayloadHeaderGetExcessBlobGas(
|
|||
execution[].excess_blob_gas.cint
|
||||
|
||||
type ETHExecutionBlockHeader = object
|
||||
txRoot: Eth2Digest
|
||||
transactionsRoot: Eth2Digest
|
||||
withdrawalsRoot: Eth2Digest
|
||||
|
||||
proc ETHExecutionBlockHeaderCreateFromJson(
|
||||
|
@ -1185,7 +1189,7 @@ proc ETHExecutionBlockHeaderCreateFromJson(
|
|||
##
|
||||
## * The JSON-RPC `eth_getBlockByHash` with params `[executionHash, false]`
|
||||
## may be used to obtain execution block header data for a given execution
|
||||
## block hash. Pass the response's `result` property to `blockHeaderJson`.
|
||||
## block hash. Pass the response's `result` property as `blockHeaderJson`.
|
||||
##
|
||||
## * The execution block header must be destroyed with
|
||||
## `ETHExecutionBlockHeaderDestroy` once no longer needed,
|
||||
|
@ -1206,78 +1210,76 @@ proc ETHExecutionBlockHeaderCreateFromJson(
|
|||
parseJson($blockHeaderJson)
|
||||
except Exception:
|
||||
return nil
|
||||
var bdata: BlockObject
|
||||
var data: BlockObject
|
||||
try:
|
||||
fromJson(node, argName = "", bdata)
|
||||
fromJson(node, argName = "", data)
|
||||
except KeyError, ValueError:
|
||||
return nil
|
||||
if bdata == nil:
|
||||
if data == nil:
|
||||
return nil
|
||||
|
||||
# Sanity check
|
||||
if bdata.hash.asEth2Digest != executionHash[]:
|
||||
if data.hash.asEth2Digest != executionHash[]:
|
||||
return nil
|
||||
|
||||
# Check fork consistency
|
||||
static: doAssert totalSerializedFields(BlockObject) == 26,
|
||||
"Only update this number once code is adjusted to check new fields!"
|
||||
if bdata.baseFeePerGas.isNone and (
|
||||
bdata.withdrawals.isSome or bdata.withdrawalsRoot.isSome or
|
||||
bdata.blobGasUsed.isSome or bdata.excessBlobGas.isSome):
|
||||
if data.baseFeePerGas.isNone and (
|
||||
data.withdrawals.isSome or data.withdrawalsRoot.isSome or
|
||||
data.blobGasUsed.isSome or data.excessBlobGas.isSome):
|
||||
return nil
|
||||
if bdata.withdrawalsRoot.isNone and (
|
||||
bdata.blobGasUsed.isSome or bdata.excessBlobGas.isSome):
|
||||
if data.withdrawalsRoot.isNone and (
|
||||
data.blobGasUsed.isSome or data.excessBlobGas.isSome):
|
||||
return nil
|
||||
if bdata.withdrawals.isSome != bdata.withdrawalsRoot.isSome:
|
||||
if data.withdrawals.isSome != data.withdrawalsRoot.isSome:
|
||||
return nil
|
||||
if bdata.blobGasUsed.isSome != bdata.excessBlobGas.isSome:
|
||||
return nil
|
||||
if bdata.parentBeaconBlockRoot.isSome != bdata.parentBeaconBlockRoot.isSome:
|
||||
if data.blobGasUsed.isSome != data.excessBlobGas.isSome:
|
||||
return nil
|
||||
|
||||
# Construct block header
|
||||
static: # `GasInt` is signed. We only use it for hashing.
|
||||
doAssert sizeof(int64) == sizeof(bdata.gasLimit)
|
||||
doAssert sizeof(int64) == sizeof(bdata.gasUsed)
|
||||
if distinctBase(bdata.timestamp) > int64.high.uint64:
|
||||
doAssert sizeof(int64) == sizeof(data.gasLimit)
|
||||
doAssert sizeof(int64) == sizeof(data.gasUsed)
|
||||
if distinctBase(data.timestamp) > int64.high.uint64:
|
||||
return nil
|
||||
if bdata.nonce.isNone:
|
||||
if data.nonce.isNone:
|
||||
return nil
|
||||
let blockHeader = ExecutionBlockHeader(
|
||||
parentHash: bdata.parentHash.asEth2Digest,
|
||||
ommersHash: bdata.sha3Uncles.asEth2Digest,
|
||||
coinbase: distinctBase(bdata.miner),
|
||||
stateRoot: bdata.stateRoot.asEth2Digest,
|
||||
txRoot: bdata.transactionsRoot.asEth2Digest,
|
||||
receiptRoot: bdata.receiptsRoot.asEth2Digest,
|
||||
bloom: distinctBase(bdata.logsBloom),
|
||||
difficulty: bdata.difficulty,
|
||||
blockNumber: distinctBase(bdata.number).u256,
|
||||
gasLimit: cast[int64](bdata.gasLimit),
|
||||
gasUsed: cast[int64](bdata.gasUsed),
|
||||
timestamp: fromUnix(int64.saturate distinctBase(bdata.timestamp)),
|
||||
extraData: distinctBase(bdata.extraData),
|
||||
mixDigest: bdata.mixHash.asEth2Digest,
|
||||
nonce: distinctBase(bdata.nonce.get),
|
||||
fee: bdata.baseFeePerGas,
|
||||
parentHash: data.parentHash.asEth2Digest,
|
||||
ommersHash: data.sha3Uncles.asEth2Digest,
|
||||
coinbase: distinctBase(data.miner),
|
||||
stateRoot: data.stateRoot.asEth2Digest,
|
||||
txRoot: data.transactionsRoot.asEth2Digest,
|
||||
receiptRoot: data.receiptsRoot.asEth2Digest,
|
||||
bloom: distinctBase(data.logsBloom),
|
||||
difficulty: data.difficulty,
|
||||
blockNumber: distinctBase(data.number).u256,
|
||||
gasLimit: cast[int64](data.gasLimit),
|
||||
gasUsed: cast[int64](data.gasUsed),
|
||||
timestamp: fromUnix(int64.saturate distinctBase(data.timestamp)),
|
||||
extraData: distinctBase(data.extraData),
|
||||
mixDigest: data.mixHash.asEth2Digest,
|
||||
nonce: distinctBase(data.nonce.get),
|
||||
fee: data.baseFeePerGas,
|
||||
withdrawalsRoot:
|
||||
if bdata.withdrawalsRoot.isSome:
|
||||
some(bdata.withdrawalsRoot.get.asEth2Digest)
|
||||
if data.withdrawalsRoot.isSome:
|
||||
some(data.withdrawalsRoot.get.asEth2Digest)
|
||||
else:
|
||||
none(ExecutionHash256),
|
||||
blobGasUsed:
|
||||
if bdata.blobGasUsed.isSome:
|
||||
some distinctBase(bdata.blobGasUsed.get)
|
||||
if data.blobGasUsed.isSome:
|
||||
some distinctBase(data.blobGasUsed.get)
|
||||
else:
|
||||
none(uint64),
|
||||
excessBlobGas:
|
||||
if bdata.excessBlobGas.isSome:
|
||||
some distinctBase(bdata.excessBlobGas.get)
|
||||
if data.excessBlobGas.isSome:
|
||||
some distinctBase(data.excessBlobGas.get)
|
||||
else:
|
||||
none(uint64),
|
||||
parentBeaconBlockRoot:
|
||||
if bdata.parentBeaconBlockRoot.isSome:
|
||||
some distinctBase(bdata.parentBeaconBlockRoot.get.asEth2Digest)
|
||||
if data.parentBeaconBlockRoot.isSome:
|
||||
some distinctBase(data.parentBeaconBlockRoot.get.asEth2Digest)
|
||||
else:
|
||||
none(ExecutionHash256))
|
||||
if rlpHash(blockHeader) != executionHash[]:
|
||||
|
@ -1285,7 +1287,7 @@ proc ETHExecutionBlockHeaderCreateFromJson(
|
|||
|
||||
let executionBlockHeader = ETHExecutionBlockHeader.new()
|
||||
executionBlockHeader[] = ETHExecutionBlockHeader(
|
||||
txRoot: blockHeader.txRoot,
|
||||
transactionsRoot: blockHeader.txRoot,
|
||||
withdrawalsRoot: blockHeader.withdrawalsRoot.get(ZERO_HASH))
|
||||
executionBlockHeader.toUnmanagedPtr()
|
||||
|
||||
|
@ -1313,7 +1315,7 @@ func ETHExecutionBlockHeaderGetTransactionsRoot(
|
|||
##
|
||||
## Returns:
|
||||
## * Execution transactions root.
|
||||
addr executionBlockHeader[].txRoot
|
||||
addr executionBlockHeader[].transactionsRoot
|
||||
|
||||
func ETHExecutionBlockHeaderGetWithdrawalsRoot(
|
||||
executionBlockHeader: ptr ETHExecutionBlockHeader
|
||||
|
@ -1330,3 +1332,645 @@ func ETHExecutionBlockHeaderGetWithdrawalsRoot(
|
|||
## Returns:
|
||||
## * Execution withdrawals root.
|
||||
addr executionBlockHeader[].withdrawalsRoot
|
||||
|
||||
type
|
||||
ETHAccessTuple = object
|
||||
address: ExecutionAddress
|
||||
storageKeys: seq[Eth2Digest]
|
||||
|
||||
DestinationType {.pure.} = enum
|
||||
Regular,
|
||||
Create
|
||||
|
||||
ETHTransaction = object
|
||||
hash: Eth2Digest
|
||||
chainId: UInt256
|
||||
`from`: ExecutionAddress
|
||||
nonce: uint64
|
||||
maxPriorityFeePerGas: uint64
|
||||
maxFeePerGas: uint64
|
||||
gas: uint64
|
||||
destinationType: DestinationType
|
||||
to: ExecutionAddress
|
||||
value: UInt256
|
||||
input: seq[byte]
|
||||
accessList: seq[ETHAccessTuple]
|
||||
maxFeePerBlobGas: uint64
|
||||
blobVersionedHashes: seq[Eth2Digest]
|
||||
signature: seq[byte]
|
||||
bytes: TypedTransaction
|
||||
|
||||
proc ETHTransactionsCreateFromJson(
|
||||
transactionsRoot: ptr Eth2Digest,
|
||||
transactionsJson: cstring): ptr seq[ETHTransaction] {.exported.} =
|
||||
## Verifies that JSON transactions data is valid and that it matches
|
||||
## the given `transactionsRoot`.
|
||||
##
|
||||
## * The JSON-RPC `eth_getBlockByHash` with params `[executionHash, true]`
|
||||
## may be used to obtain transactions data for a given execution
|
||||
## block hash. Pass `result.transactions` as `transactionsJson`.
|
||||
##
|
||||
## * The transaction sequence must be destroyed with
|
||||
## `ETHTransactionsDestroy` once no longer needed,
|
||||
## to release memory.
|
||||
##
|
||||
## Parameters:
|
||||
## * `transactionsRoot` - Execution transactions root.
|
||||
## * `transactionsJson` - Buffer with JSON transactions list. NULL-terminated.
|
||||
##
|
||||
## Returns:
|
||||
## * Pointer to an initialized transaction sequence - If successful.
|
||||
## * `NULL` - If the given `transactionsJson` is malformed or incompatible.
|
||||
##
|
||||
## See:
|
||||
## * https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_getblockbyhash
|
||||
let node =
|
||||
try:
|
||||
parseJson($transactionsJson)
|
||||
except Exception:
|
||||
return nil
|
||||
var datas: seq[TransactionObject]
|
||||
try:
|
||||
fromJson(node, argName = "", datas)
|
||||
except KeyError, ValueError:
|
||||
return nil
|
||||
|
||||
var txs = newSeqOfCap[ETHTransaction](datas.len)
|
||||
for i, data in datas:
|
||||
# Sanity check
|
||||
if data.transactionIndex.isNone:
|
||||
return nil
|
||||
if distinctBase(data.transactionIndex.get) != i.uint64:
|
||||
return nil
|
||||
|
||||
# Check fork consistency
|
||||
static: doAssert totalSerializedFields(TransactionObject) == 21,
|
||||
"Only update this number once code is adjusted to check new fields!"
|
||||
let txType =
|
||||
case data.`type`.get(0.Quantity):
|
||||
of 0.Quantity:
|
||||
if data.accessList.isSome or
|
||||
data.maxFeePerGas.isSome or data.maxPriorityFeePerGas.isSome or
|
||||
data.maxFeePerBlobGas.isSome or data.blobVersionedHashes.isSome:
|
||||
return nil
|
||||
TxLegacy
|
||||
of 1.Quantity:
|
||||
if data.chainId.isNone or data.accessList.isNone:
|
||||
return nil
|
||||
if data.maxFeePerGas.isSome or data.maxPriorityFeePerGas.isSome or
|
||||
data.maxFeePerBlobGas.isSome or data.blobVersionedHashes.isSome:
|
||||
return nil
|
||||
TxEip2930
|
||||
of 2.Quantity:
|
||||
if data.chainId.isNone or data.accessList.isNone or
|
||||
data.maxFeePerGas.isNone or data.maxPriorityFeePerGas.isNone:
|
||||
return nil
|
||||
if data.maxFeePerBlobGas.isSome or data.blobVersionedHashes.isSome:
|
||||
return nil
|
||||
TxEip1559
|
||||
of 3.Quantity:
|
||||
if data.to.isNone or data.chainId.isNone or data.accessList.isNone or
|
||||
data.maxFeePerGas.isNone or data.maxPriorityFeePerGas.isNone or
|
||||
data.maxFeePerBlobGas.isNone or data.blobVersionedHashes.isNone:
|
||||
return nil
|
||||
TxEip4844
|
||||
else:
|
||||
return nil
|
||||
|
||||
# Construct transaction
|
||||
static:
|
||||
doAssert sizeof(uint64) == sizeof(ChainId)
|
||||
doAssert sizeof(int64) == sizeof(data.gasPrice)
|
||||
doAssert sizeof(int64) == sizeof(data.maxPriorityFeePerGas.get)
|
||||
doAssert sizeof(int64) == sizeof(data.maxFeePerBlobGas.get)
|
||||
if data.chainId.get(default(UInt256)) > distinctBase(ChainId.high).u256:
|
||||
return nil
|
||||
if distinctBase(data.gasPrice) > int64.high.uint64:
|
||||
return nil
|
||||
if distinctBase(data.maxFeePerGas.get(0.Quantity)) > int64.high.uint64:
|
||||
return nil
|
||||
if distinctBase(data.maxPriorityFeePerGas.get(0.Quantity)) >
|
||||
int64.high.uint64:
|
||||
return nil
|
||||
if distinctBase(data.maxFeePerBlobGas.get(0.Quantity)) >
|
||||
int64.high.uint64:
|
||||
return nil
|
||||
if distinctBase(data.gas) > int64.high.uint64:
|
||||
return nil
|
||||
if data.v > int64.high.u256:
|
||||
return nil
|
||||
let
|
||||
tx = ExecutionTransaction(
|
||||
txType: txType,
|
||||
chainId: data.chainId.get(default(UInt256)).truncate(uint64).ChainId,
|
||||
nonce: distinctBase(data.nonce),
|
||||
gasPrice: data.gasPrice.GasInt,
|
||||
maxPriorityFee:
|
||||
distinctBase(data.maxPriorityFeePerGas.get(data.gasPrice)).GasInt,
|
||||
maxFee: distinctBase(data.maxFeePerGas.get(data.gasPrice)).GasInt,
|
||||
gasLimit: distinctBase(data.gas).GasInt,
|
||||
to:
|
||||
if data.to.isSome:
|
||||
some(distinctBase(data.to.get))
|
||||
else:
|
||||
none(EthAddress),
|
||||
value: data.value,
|
||||
payload: data.input,
|
||||
accessList:
|
||||
if data.accessList.isSome:
|
||||
data.accessList.get.mapIt(AccessPair(
|
||||
address: distinctBase(it.address),
|
||||
storageKeys: it.storageKeys.mapIt(distinctBase(it))))
|
||||
else:
|
||||
@[],
|
||||
maxFeePerBlobGas:
|
||||
distinctBase(data.maxFeePerBlobGas.get(0.Quantity)).GasInt,
|
||||
versionedHashes:
|
||||
if data.blobVersionedHashes.isSome:
|
||||
data.blobVersionedHashes.get.mapIt(
|
||||
ExecutionHash256(data: distinctBase(it)))
|
||||
else:
|
||||
@[],
|
||||
V: data.v.truncate(uint64).int64,
|
||||
R: data.r,
|
||||
S: data.s)
|
||||
rlpBytes =
|
||||
try:
|
||||
rlp.encode(tx)
|
||||
except RlpError:
|
||||
raiseAssert "Unreachable"
|
||||
hash = keccakHash(rlpBytes)
|
||||
if data.hash.asEth2Digest != hash:
|
||||
return nil
|
||||
|
||||
# Compute from execution address
|
||||
var rawSig {.noinit.}: array[65, byte]
|
||||
rawSig[0 ..< 32] = tx.R.toBytesBE()
|
||||
rawSig[32 ..< 64] = tx.S.toBytesBE()
|
||||
rawSig[64] =
|
||||
if txType != TxLegacy:
|
||||
tx.V.uint8
|
||||
elif tx.V.isEven:
|
||||
1
|
||||
else:
|
||||
0
|
||||
let
|
||||
sig = SkRecoverableSignature.fromRaw(rawSig).valueOr:
|
||||
return nil
|
||||
sigHash = SkMessage.fromBytes(tx.txHashNoSignature().data).valueOr:
|
||||
return nil
|
||||
fromPubkey = sig.recover(sigHash).valueOr:
|
||||
return nil
|
||||
fromAddress = keys.PublicKey(fromPubkey).toCanonicalAddress()
|
||||
if distinctBase(data.`from`) != fromAddress:
|
||||
return nil
|
||||
|
||||
# Compute to execution address
|
||||
let
|
||||
destinationType =
|
||||
if tx.to.isSome:
|
||||
DestinationType.Regular
|
||||
else:
|
||||
DestinationType.Create
|
||||
toAddress =
|
||||
case destinationType
|
||||
of DestinationType.Regular:
|
||||
tx.to.get
|
||||
of DestinationType.Create:
|
||||
var res {.noinit.}: array[20, byte]
|
||||
res[0 ..< 20] = keccakHash(rlp.encodeList(fromAddress, tx.nonce))
|
||||
.data.toOpenArray(12, 31)
|
||||
res
|
||||
|
||||
txs.add ETHTransaction(
|
||||
hash: keccakHash(rlpBytes),
|
||||
chainId: distinctBase(tx.chainId).u256,
|
||||
`from`: ExecutionAddress(data: fromAddress),
|
||||
nonce: tx.nonce,
|
||||
maxPriorityFeePerGas: tx.maxPriorityFee.uint64,
|
||||
maxFeePerGas: tx.maxFee.uint64,
|
||||
gas: tx.gasLimit.uint64,
|
||||
destinationType: destinationType,
|
||||
to: ExecutionAddress(data: toAddress),
|
||||
value: tx.value,
|
||||
input: tx.payload,
|
||||
accessList: tx.accessList.mapIt(ETHAccessTuple(
|
||||
address: ExecutionAddress(data: it.address),
|
||||
storageKeys: it.storageKeys.mapIt(Eth2Digest(data: it)))),
|
||||
maxFeePerBlobGas: tx.maxFeePerBlobGas.uint64,
|
||||
blobVersionedHashes: tx.versionedHashes,
|
||||
signature: @rawSig,
|
||||
bytes: rlpBytes.TypedTransaction)
|
||||
|
||||
var tr = initHexaryTrie(newMemoryDB())
|
||||
for i, transaction in txs:
|
||||
try:
|
||||
tr.put(rlp.encode(i), distinctBase(transaction.bytes))
|
||||
except RlpError:
|
||||
raiseAssert "Unreachable"
|
||||
if tr.rootHash() != transactionsRoot[]:
|
||||
return nil
|
||||
|
||||
let transactions = seq[ETHTransaction].new()
|
||||
transactions[] = txs
|
||||
transactions.toUnmanagedPtr()
|
||||
|
||||
proc ETHTransactionsDestroy(
|
||||
transactions: ptr seq[ETHTransaction]) {.exported.} =
|
||||
## Destroys a transaction sequence.
|
||||
##
|
||||
## * The transaction sequence must no longer be used after destruction.
|
||||
##
|
||||
## Parameters:
|
||||
## * `transactions` - Transaction sequence.
|
||||
transactions.destroy()
|
||||
|
||||
func ETHTransactionsGetCount(
|
||||
transactions: ptr seq[ETHTransaction]): cint {.exported.} =
|
||||
## Indicates the total number of transactions in a transaction sequence.
|
||||
##
|
||||
## * Individual transactions may be investigated using `ETHTransactionsGet`.
|
||||
##
|
||||
## Parameters:
|
||||
## * `transactions` - Transaction sequence.
|
||||
##
|
||||
## Returns:
|
||||
## * Number of available transactions.
|
||||
transactions[].len.cint
|
||||
|
||||
func ETHTransactionsGet(
|
||||
transactions: ptr seq[ETHTransaction],
|
||||
transactionIndex: cint): ptr ETHTransaction {.exported.} =
|
||||
## Obtains an individual transaction by sequential index
|
||||
## in a transaction sequence.
|
||||
##
|
||||
## * The returned value is allocated in the given transaction sequence.
|
||||
## It must neither be released nor written to, and the transaction
|
||||
## sequence must not be released while the returned value is in use.
|
||||
##
|
||||
## Parameters:
|
||||
## * `transactions` - Transaction sequence.
|
||||
## * `transactionIndex` - Sequential transaction index.
|
||||
##
|
||||
## Returns:
|
||||
## * Transaction.
|
||||
addr transactions[][transactionIndex.int]
|
||||
|
||||
func ETHTransactionGetHash(
|
||||
transaction: ptr ETHTransaction): ptr Eth2Digest {.exported.} =
|
||||
## Obtains the transaction hash of a transaction.
|
||||
##
|
||||
## * The returned value is allocated in the given transaction.
|
||||
## It must neither be released nor written to, and the transaction
|
||||
## must not be released while the returned value is in use.
|
||||
##
|
||||
## Parameters:
|
||||
## * `transaction` - Transaction.
|
||||
##
|
||||
## Returns:
|
||||
## * Transaction hash.
|
||||
addr transaction[].hash
|
||||
|
||||
func ETHTransactionGetChainId(
|
||||
transaction: ptr ETHTransaction): ptr UInt256 {.exported.} =
|
||||
## Obtains the chain ID of a transaction.
|
||||
##
|
||||
## * The returned value is allocated in the given transaction.
|
||||
## It must neither be released nor written to, and the transaction
|
||||
## must not be released while the returned value is in use.
|
||||
##
|
||||
## Parameters:
|
||||
## * `transaction` - Transaction.
|
||||
##
|
||||
## Returns:
|
||||
## * Chain ID.
|
||||
addr transaction[].chainId
|
||||
|
||||
func ETHTransactionGetFrom(
|
||||
transaction: ptr ETHTransaction): ptr ExecutionAddress {.exported.} =
|
||||
## Obtains the from address of a transaction.
|
||||
##
|
||||
## * The returned value is allocated in the given transaction.
|
||||
## It must neither be released nor written to, and the transaction
|
||||
## must not be released while the returned value is in use.
|
||||
##
|
||||
## Parameters:
|
||||
## * `transaction` - Transaction.
|
||||
##
|
||||
## Returns:
|
||||
## * From execution address.
|
||||
addr transaction[].`from`
|
||||
|
||||
func ETHTransactionGetNonce(
|
||||
transaction: ptr ETHTransaction): ptr uint64 {.exported.} =
|
||||
## Obtains the nonce of a transaction.
|
||||
##
|
||||
## * The returned value is allocated in the given transaction.
|
||||
## It must neither be released nor written to, and the transaction
|
||||
## must not be released while the returned value is in use.
|
||||
##
|
||||
## Parameters:
|
||||
## * `transaction` - Transaction.
|
||||
##
|
||||
## Returns:
|
||||
## * Nonce.
|
||||
addr transaction[].nonce
|
||||
|
||||
func ETHTransactionGetMaxPriorityFeePerGas(
|
||||
transaction: ptr ETHTransaction): ptr uint64 {.exported.} =
|
||||
## Obtains the max priority fee per gas of a transaction.
|
||||
##
|
||||
## * The returned value is allocated in the given transaction.
|
||||
## It must neither be released nor written to, and the transaction
|
||||
## must not be released while the returned value is in use.
|
||||
##
|
||||
## Parameters:
|
||||
## * `transaction` - Transaction.
|
||||
##
|
||||
## Returns:
|
||||
## * Max priority fee per gas.
|
||||
addr transaction[].maxPriorityFeePerGas
|
||||
|
||||
func ETHTransactionGetMaxFeePerGas(
|
||||
transaction: ptr ETHTransaction): ptr uint64 {.exported.} =
|
||||
## Obtains the max fee per gas of a transaction.
|
||||
##
|
||||
## * The returned value is allocated in the given transaction.
|
||||
## It must neither be released nor written to, and the transaction
|
||||
## must not be released while the returned value is in use.
|
||||
##
|
||||
## Parameters:
|
||||
## * `transaction` - Transaction.
|
||||
##
|
||||
## Returns:
|
||||
## * Max fee per gas.
|
||||
addr transaction[].maxFeePerGas
|
||||
|
||||
func ETHTransactionGetGas(
|
||||
transaction: ptr ETHTransaction): ptr uint64 {.exported.} =
|
||||
## Obtains the gas of a transaction.
|
||||
##
|
||||
## * The returned value is allocated in the given transaction.
|
||||
## It must neither be released nor written to, and the transaction
|
||||
## must not be released while the returned value is in use.
|
||||
##
|
||||
## Parameters:
|
||||
## * `transaction` - Transaction.
|
||||
##
|
||||
## Returns:
|
||||
## * Gas.
|
||||
addr transaction[].gas
|
||||
|
||||
func ETHTransactionIsCreatingContract(
|
||||
transaction: ptr ETHTransaction): bool {.exported.} =
|
||||
## Indicates whether or not a transaction is creating a contract.
|
||||
##
|
||||
## Parameters:
|
||||
## * `transaction` - Transaction.
|
||||
##
|
||||
## Returns:
|
||||
## * Whether or not the transaction is creating a contract.
|
||||
case transaction[].destinationType
|
||||
of DestinationType.Regular:
|
||||
false
|
||||
of DestinationType.Create:
|
||||
true
|
||||
|
||||
func ETHTransactionGetTo(
|
||||
transaction: ptr ETHTransaction): ptr ExecutionAddress {.exported.} =
|
||||
## Obtains the to address of a transaction.
|
||||
##
|
||||
## * If the transaction is creating a contract, this function returns
|
||||
## the address of the new contract.
|
||||
##
|
||||
## * The returned value is allocated in the given transaction.
|
||||
## It must neither be released nor written to, and the transaction
|
||||
## must not be released while the returned value is in use.
|
||||
##
|
||||
## Parameters:
|
||||
## * `transaction` - Transaction.
|
||||
##
|
||||
## Returns:
|
||||
## * To execution address.
|
||||
addr transaction[].to
|
||||
|
||||
func ETHTransactionGetValue(
|
||||
transaction: ptr ETHTransaction): ptr UInt256 {.exported.} =
|
||||
## Obtains the value of a transaction.
|
||||
##
|
||||
## * The returned value is allocated in the given transaction.
|
||||
## It must neither be released nor written to, and the transaction
|
||||
## must not be released while the returned value is in use.
|
||||
##
|
||||
## Parameters:
|
||||
## * `transaction` - Transaction.
|
||||
##
|
||||
## Returns:
|
||||
## * Value.
|
||||
addr transaction[].value
|
||||
|
||||
func ETHTransactionGetInputBytes(
|
||||
transaction: ptr ETHTransaction,
|
||||
numBytes #[out]#: ptr cint): ptr UncheckedArray[byte] {.exported.} =
|
||||
## Obtains the input of a transaction.
|
||||
##
|
||||
## * The returned value is allocated in the given transaction.
|
||||
## It must neither be released nor written to, and the transaction
|
||||
## must not be released while the returned value is in use.
|
||||
##
|
||||
## Parameters:
|
||||
## * `transaction` - Transaction.
|
||||
## * `numBytes` [out] - Length of buffer.
|
||||
##
|
||||
## Returns:
|
||||
## * Buffer with input.
|
||||
numBytes[] = transaction[].input.len.cint
|
||||
if transaction[].input.len == 0:
|
||||
# https://github.com/nim-lang/Nim/issues/22389
|
||||
const defaultInput: cstring = ""
|
||||
return cast[ptr UncheckedArray[byte]](defaultInput)
|
||||
cast[ptr UncheckedArray[byte]](addr transaction[].input[0])
|
||||
|
||||
func ETHTransactionGetAccessList(
|
||||
transaction: ptr ETHTransaction): ptr seq[ETHAccessTuple] {.exported.} =
|
||||
## Obtains the access list of a transaction.
|
||||
##
|
||||
## * The returned value is allocated in the given transaction.
|
||||
## It must neither be released nor written to, and the transaction
|
||||
## must not be released while the returned value is in use.
|
||||
##
|
||||
## Parameters:
|
||||
## * `transaction` - Transaction.
|
||||
##
|
||||
## Returns:
|
||||
## * Transaction access list.
|
||||
addr transaction[].accessList
|
||||
|
||||
func ETHAccessListGetCount(
|
||||
accessList: ptr seq[ETHAccessTuple]): cint {.exported.} =
|
||||
## Indicates the total number of access tuples in a transaction access list.
|
||||
##
|
||||
## * Individual access tuples may be investigated using `ETHAccessListGet`.
|
||||
##
|
||||
## Parameters:
|
||||
## * `accessList` - Transaction access list.
|
||||
##
|
||||
## Returns:
|
||||
## * Number of available access tuples.
|
||||
accessList[].len.cint
|
||||
|
||||
func ETHAccessListGet(
|
||||
accessList: ptr seq[ETHAccessTuple],
|
||||
accessTupleIndex: cint): ptr ETHAccessTuple {.exported.} =
|
||||
## Obtains an individual access tuple by sequential index
|
||||
## in a transaction access list.
|
||||
##
|
||||
## * The returned value is allocated in the given transaction access list.
|
||||
## It must neither be released nor written to, and the transaction
|
||||
## access list must not be released while the returned value is in use.
|
||||
##
|
||||
## Parameters:
|
||||
## * `accessList` - Transaction access list.
|
||||
## * `accessTupleIndex` - Sequential access tuple index.
|
||||
##
|
||||
## Returns:
|
||||
## * Access tuple.
|
||||
addr accessList[][accessTupleIndex.int]
|
||||
|
||||
func ETHAccessTupleGetAddress(
|
||||
accessTuple: ptr ETHAccessTuple): ptr ExecutionAddress {.exported.} =
|
||||
## Obtains the address of an access tuple.
|
||||
##
|
||||
## * The returned value is allocated in the given access tuple.
|
||||
## It must neither be released nor written to, and the access tuple
|
||||
## must not be released while the returned value is in use.
|
||||
##
|
||||
## Parameters:
|
||||
## * `accessTuple` - Access tuple.
|
||||
##
|
||||
## Returns:
|
||||
## * Address.
|
||||
addr accessTuple[].address
|
||||
|
||||
func ETHAccessTupleGetNumStorageKeys(
|
||||
accessTuple: ptr ETHAccessTuple): cint {.exported.} =
|
||||
## Indicates the total number of storage keys in an access tuple.
|
||||
##
|
||||
## * Individual storage keys may be investigated using
|
||||
## `ETHAccessTupleGetStorageKey`.
|
||||
##
|
||||
## Parameters:
|
||||
## * `accessTuple` - Access tuple.
|
||||
##
|
||||
## Returns:
|
||||
## * Number of available storage keys.
|
||||
accessTuple[].storageKeys.len.cint
|
||||
|
||||
func ETHAccessTupleGetStorageKey(
|
||||
accessTuple: ptr ETHAccessTuple,
|
||||
storageKeyIndex: cint): ptr Eth2Digest {.exported.} =
|
||||
## Obtains an individual storage key by sequential index
|
||||
## in an access tuple.
|
||||
##
|
||||
## * The returned value is allocated in the given transaction access tuple.
|
||||
## It must neither be released nor written to, and the transaction
|
||||
## access tuple must not be released while the returned value is in use.
|
||||
##
|
||||
## Parameters:
|
||||
## * `accessTuple` - Access tuple.
|
||||
## * `storageKeyIndex` - Sequential storage key index.
|
||||
##
|
||||
## Returns:
|
||||
## * Storage key.
|
||||
addr accessTuple[].storageKeys[storageKeyIndex.int]
|
||||
|
||||
func ETHTransactionGetMaxFeePerBlobGas(
|
||||
transaction: ptr ETHTransaction): ptr uint64 {.exported.} =
|
||||
## Obtains the max fee per blob gas of a transaction.
|
||||
##
|
||||
## * The returned value is allocated in the given transaction.
|
||||
## It must neither be released nor written to, and the transaction
|
||||
## must not be released while the returned value is in use.
|
||||
##
|
||||
## Parameters:
|
||||
## * `transaction` - Transaction.
|
||||
##
|
||||
## Returns:
|
||||
## * Max fee per blob gas.
|
||||
addr transaction[].maxFeePerBlobGas
|
||||
|
||||
func ETHTransactionGetNumBlobVersionedHashes(
|
||||
transaction: ptr ETHTransaction): cint {.exported.} =
|
||||
## Indicates the total number of blob versioned hashes of a transaction.
|
||||
##
|
||||
## * Individual blob versioned hashes may be investigated using
|
||||
## `ETHTransactionGetBlobVersionedHash`.
|
||||
##
|
||||
## Parameters:
|
||||
## * `transaction` - Transaction.
|
||||
##
|
||||
## Returns:
|
||||
## * Number of available blob versioned hashes.
|
||||
transaction[].blobVersionedHashes.len.cint
|
||||
|
||||
func ETHTransactionGetBlobVersionedHash(
|
||||
transaction: ptr ETHTransaction,
|
||||
versionedHashIndex: cint): ptr Eth2Digest {.exported.} =
|
||||
## Obtains an individual blob versioned hash by sequential index
|
||||
## in a transaction.
|
||||
##
|
||||
## * The returned value is allocated in the given transaction.
|
||||
## It must neither be released nor written to, and the transaction
|
||||
## must not be released while the returned value is in use.
|
||||
##
|
||||
## Parameters:
|
||||
## * `transaction` - Transaction.
|
||||
## * `versionedHashIndex` - Sequential blob versioned hash index.
|
||||
##
|
||||
## Returns:
|
||||
## * Blob versioned hash.
|
||||
addr transaction[].blobVersionedHashes[versionedHashIndex.int]
|
||||
|
||||
func ETHTransactionGetSignatureBytes(
|
||||
transaction: ptr ETHTransaction,
|
||||
numBytes #[out]#: ptr cint): ptr UncheckedArray[byte] {.exported.} =
|
||||
## Obtains the signature of a transaction.
|
||||
##
|
||||
## * The returned value is allocated in the given transaction.
|
||||
## It must neither be released nor written to, and the transaction
|
||||
## must not be released while the returned value is in use.
|
||||
##
|
||||
## Parameters:
|
||||
## * `transaction` - Transaction.
|
||||
## * `numBytes` [out] - Length of buffer.
|
||||
##
|
||||
## Returns:
|
||||
## * Buffer with signature.
|
||||
numBytes[] = distinctBase(transaction[].signature).len.cint
|
||||
if distinctBase(transaction[].signature).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(transaction[].signature)[0])
|
||||
|
||||
func ETHTransactionGetBytes(
|
||||
transaction: ptr ETHTransaction,
|
||||
numBytes #[out]#: ptr cint): ptr UncheckedArray[byte] {.exported.} =
|
||||
## Obtains the raw byte representation of a transaction.
|
||||
##
|
||||
## * The returned value is allocated in the given transaction.
|
||||
## It must neither be released nor written to, and the transaction
|
||||
## must not be released while the returned value is in use.
|
||||
##
|
||||
## Parameters:
|
||||
## * `transaction` - Transaction.
|
||||
## * `numBytes` [out] - Length of buffer.
|
||||
##
|
||||
## Returns:
|
||||
## * Buffer with raw transaction data.
|
||||
numBytes[] = distinctBase(transaction[].bytes).len.cint
|
||||
if distinctBase(transaction[].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(transaction[].bytes)[0])
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -7,6 +7,7 @@
|
|||
* at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
@ -96,6 +97,15 @@ static void printHexString(const void *bytes, int numBytes)
|
|||
}
|
||||
}
|
||||
|
||||
static void printHexStringReversed(const void *bytes, int numBytes)
|
||||
{
|
||||
const uint8_t *bytes_ = bytes;
|
||||
printf("0x");
|
||||
for (int i = numBytes - 1; i >= 0; i--) {
|
||||
printf("%02x", bytes_[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void printGweiString(const ETHUInt256 *wei)
|
||||
{
|
||||
ETHUInt256 value;
|
||||
|
@ -380,5 +390,125 @@ int main(void)
|
|||
|
||||
ETHExecutionBlockHeaderDestroy(executionBlockHeader);
|
||||
|
||||
ETHRoot sampleTransactionsRoot = {{
|
||||
0x4e, 0x90, 0xdc, 0x06, 0xca, 0xd6, 0xa7, 0xc0,
|
||||
0x57, 0xd2, 0xd7, 0x7f, 0x8f, 0x77, 0xd1, 0x45,
|
||||
0xb4, 0x6f, 0xf3, 0xad, 0x9c, 0xa7, 0xe1, 0xef,
|
||||
0x57, 0x11, 0x5f, 0xa8, 0xbf, 0xad, 0xfe, 0xe1,
|
||||
}};
|
||||
void *sampleTransactionsJson = readEntireFile(
|
||||
__DIR__ "/test_files/transactions.json", /* numBytes: */ NULL);
|
||||
ETHTransactions *transactions =
|
||||
ETHTransactionsCreateFromJson(&sampleTransactionsRoot, sampleTransactionsJson);
|
||||
check(transactions);
|
||||
free(sampleTransactionsJson);
|
||||
|
||||
int numTransactions = ETHTransactionsGetCount(transactions);
|
||||
printf("\nSample transactions:\n");
|
||||
for (int transactionIndex = 0; transactionIndex < numTransactions; transactionIndex++) {
|
||||
const ETHTransaction *transaction = ETHTransactionsGet(transactions, transactionIndex);
|
||||
|
||||
const ETHRoot *transactionHash = ETHTransactionGetHash(transaction);
|
||||
printf("- ");
|
||||
printHexString(transactionHash, sizeof *transactionHash);
|
||||
printf("\n");
|
||||
|
||||
const ETHUInt256 *transactionChainId = ETHTransactionGetChainId(transaction);
|
||||
printf(" - chain_id: ");
|
||||
printHexStringReversed(transactionChainId, sizeof *transactionChainId);
|
||||
printf("\n");
|
||||
|
||||
const ETHExecutionAddress *transactionFrom = ETHTransactionGetFrom(transaction);
|
||||
printf(" - from: ");
|
||||
printHexString(transactionFrom, sizeof *transactionFrom);
|
||||
printf("\n");
|
||||
|
||||
const uint64_t *transactionNonce = ETHTransactionGetNonce(transaction);
|
||||
printf(" - nonce: %" PRIu64 "\n", *transactionNonce);
|
||||
|
||||
const uint64_t *transactionMaxPriorityFeePerGas =
|
||||
ETHTransactionGetMaxPriorityFeePerGas(transaction);
|
||||
printf(" - max_priority_fee_per_gas: %" PRIu64 "\n", *transactionMaxPriorityFeePerGas);
|
||||
|
||||
const uint64_t *transactionMaxFeePerGas = ETHTransactionGetMaxFeePerGas(transaction);
|
||||
printf(" - max_fee_per_gas: %" PRIu64 "\n", *transactionMaxFeePerGas);
|
||||
|
||||
const uint64_t *transactionGas = ETHTransactionGetGas(transaction);
|
||||
printf(" - gas: %" PRIu64 "\n", *transactionGas);
|
||||
|
||||
bool transactionIsCreatingContract = ETHTransactionIsCreatingContract(transaction);
|
||||
const ETHExecutionAddress *transactionTo = ETHTransactionGetTo(transaction);
|
||||
if (transactionIsCreatingContract) {
|
||||
printf(" - contract_address: ");
|
||||
} else {
|
||||
printf(" - to: ");
|
||||
}
|
||||
printHexString(transactionTo, sizeof *transactionTo);
|
||||
printf("\n");
|
||||
|
||||
const ETHUInt256 *transactionValue = ETHTransactionGetValue(transaction);
|
||||
printf(" - value: ");
|
||||
printGweiString(transactionValue);
|
||||
printf(" Gwei\n");
|
||||
|
||||
int numTransactionInputBytes;
|
||||
const void *transactionInputBytes =
|
||||
ETHTransactionGetInputBytes(transaction, &numTransactionInputBytes);
|
||||
printf(" - input: ");
|
||||
printHexString(transactionInputBytes, numTransactionInputBytes);
|
||||
printf("\n");
|
||||
|
||||
const ETHAccessList *transactionAccessList = ETHTransactionGetAccessList(transaction);
|
||||
printf(" - access_list:\n");
|
||||
int numAccessTuples = ETHAccessListGetCount(transactionAccessList);
|
||||
for (int accessTupleIndex = 0; accessTupleIndex < numAccessTuples; accessTupleIndex++) {
|
||||
const ETHAccessTuple *accessTuple =
|
||||
ETHAccessListGet(transactionAccessList, accessTupleIndex);
|
||||
|
||||
const ETHExecutionAddress *accessTupleAddress = ETHAccessTupleGetAddress(accessTuple);
|
||||
printf(" - ");
|
||||
printHexString(accessTupleAddress, sizeof *accessTupleAddress);
|
||||
printf("\n");
|
||||
|
||||
int numStorageKeys = ETHAccessTupleGetNumStorageKeys(accessTuple);
|
||||
for (int storageKeyIndex = 0; storageKeyIndex < numStorageKeys; storageKeyIndex++) {
|
||||
const ETHRoot *storageKey =
|
||||
ETHAccessTupleGetStorageKey(accessTuple, storageKeyIndex);
|
||||
printf(" - ");
|
||||
printHexString(storageKey, sizeof *storageKey);
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
const uint64_t *transactionMaxFeePerBlobGas =
|
||||
ETHTransactionGetMaxFeePerBlobGas(transaction);
|
||||
printf(" - max_fee_per_blob_gas: %" PRIu64 "\n", *transactionMaxFeePerBlobGas);
|
||||
|
||||
printf(" - blob_versioned_hashes:\n");
|
||||
int numBlobVersionedHashes = ETHTransactionGetNumBlobVersionedHashes(transaction);
|
||||
for (int hashIndex = 0; hashIndex < numBlobVersionedHashes; hashIndex++) {
|
||||
const ETHRoot *blobVersionedHash =
|
||||
ETHTransactionGetBlobVersionedHash(transaction, hashIndex);
|
||||
printf(" - ");
|
||||
printHexString(blobVersionedHash, sizeof *blobVersionedHash);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
int numTransactionSignatureBytes;
|
||||
const void *transactionSignatureBytes =
|
||||
ETHTransactionGetSignatureBytes(transaction, &numTransactionSignatureBytes);
|
||||
printf(" - signature: ");
|
||||
printHexString(transactionSignatureBytes, numTransactionSignatureBytes);
|
||||
printf("\n");
|
||||
|
||||
int numTransactionBytes;
|
||||
const void *transactionBytes = ETHTransactionGetBytes(transaction, &numTransactionBytes);
|
||||
printf(" - bytes: ");
|
||||
printHexString(transactionBytes, numTransactionBytes);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
ETHTransactionsDestroy(transactions);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ func toEther*(gwei: Gwei): Ether =
|
|||
|
||||
type
|
||||
ExecutionHash256* = eth_types.Hash256
|
||||
ExecutionTransaction* = eth_types.Transaction
|
||||
ExecutionWithdrawal = eth_types.Withdrawal
|
||||
ExecutionBlockHeader* = eth_types.BlockHeader
|
||||
|
||||
|
|
Loading…
Reference in New Issue