/** * beacon_chain * Copyright (c) 2023-2024 Status Research & Development GmbH * Licensed and distributed under either of * * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT). * * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0). * at your option. This file may not be copied, modified, or distributed except according to those terms. */ #include #include #include #include #include #include "libnimbus_lc.h" #define check(condition) \ do { \ if (!(condition)) { \ printf("assertion failed: %s - %s @ %s:%d", \ #condition, __func__, __FILE__, __LINE__); \ exit(1); \ } \ } while (0) #ifndef __DIR__ #define __DIR__ "." #endif ETH_RESULT_USE_CHECK static void *readEntireFile(const char *path, int *numBytes) { int err; FILE *file = fopen(path, "rb"); check(file); err = fseek(file, 0, SEEK_END); check(!err); long size = ftell(file); check(size >= 0); err = fseek(file, 0, SEEK_SET); check(!err); char *buffer = malloc((size_t) size + 1); check(buffer); size_t actualSize = fread(buffer, 1, (size_t) size, file); check(actualSize == (size_t) size); buffer[size] = '\0'; fclose(file); if (numBytes) { check(size <= INT_MAX); *numBytes = (int) actualSize; } return buffer; } ETH_RESULT_USE_CHECK static ETHConsensusConfig *loadCfg(const char *path) { void *fileContent = readEntireFile(path, /* numBytes: */ NULL); ETHConsensusConfig *cfg = ETHConsensusConfigCreateFromYaml(fileContent); check(cfg); free(fileContent); return cfg; } ETH_RESULT_USE_CHECK static ETHBeaconState *loadGenesis(const ETHConsensusConfig *cfg, const char *path) { const char *consensusFork = ETHConsensusConfigGetConsensusVersionAtEpoch(cfg, /* epoch: */ 0); check(consensusFork); int numSszBytes; void *sszBytes = readEntireFile(path, &numSszBytes); ETHBeaconState *state = ETHBeaconStateCreateFromSsz( cfg, consensusFork, sszBytes, numSszBytes); check(state); free(sszBytes); return state; } static void printHexString(const void *bytes, int numBytes) { const uint8_t *bytes_ = bytes; printf("0x"); for (int i = 0; i < numBytes; i++) { printf("%02x", bytes_[i]); } } 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; memcpy(&value, wei, sizeof value); char weiString[80]; int o = 0; for (;;) { bool isZero = true; for (size_t i = 0; i < sizeof value; i++) { if (value.bytes[i]) { isZero = false; break; } } if (isZero) { break; } uint8_t remainder = 0; for (int i = sizeof value - 1; i >= 0; i--) { uint16_t temp = (uint16_t) ((uint16_t) remainder << 8) | value.bytes[i]; value.bytes[i] = (uint8_t) (temp / 10); remainder = temp % 10; } weiString[o++] = '0' + (char) remainder; } if (!o) { weiString[o++] = '0'; } if (o < 9) { printf("0"); } else { while (o > 9) { printf("%c", weiString[--o]); } } int z = 0; while (z < o && weiString[z] == '0') { z++; } if (o > z) { printf("."); while (o > z) { printf("%c", weiString[--o]); } } } static void visualizeHeader(const ETHLightClientHeader *header, const ETHConsensusConfig *cfg) { ETHRoot *beaconRoot = ETHLightClientHeaderCopyBeaconRoot(header, cfg); printf(" - beacon: "); printHexString(beaconRoot, sizeof *beaconRoot); printf("\n"); ETHRootDestroy(beaconRoot); const ETHBeaconBlockHeader *beacon = ETHLightClientHeaderGetBeacon(header); int beaconSlot = ETHBeaconBlockHeaderGetSlot(beacon); printf(" - slot: %d\n", beaconSlot); int beaconProposerIndex = ETHBeaconBlockHeaderGetProposerIndex(beacon); printf(" - proposer_index: %d\n", beaconProposerIndex); const ETHRoot *beaconParentRoot = ETHBeaconBlockHeaderGetParentRoot(beacon); printf(" - parent_root: "); printHexString(beaconParentRoot, sizeof *beaconParentRoot); printf("\n"); const ETHRoot *beaconStateRoot = ETHBeaconBlockHeaderGetStateRoot(beacon); printf(" - state_root: "); printHexString(beaconStateRoot, sizeof *beaconStateRoot); printf("\n"); const ETHRoot *beaconBodyRoot = ETHBeaconBlockHeaderGetBodyRoot(beacon); printf(" - body_root: "); printHexString(beaconBodyRoot, sizeof *beaconBodyRoot); printf("\n"); ETHRoot *executionHash = ETHLightClientHeaderCopyExecutionHash(header, cfg); printf(" - execution: "); printHexString(executionHash, sizeof *executionHash); printf("\n"); ETHRootDestroy(executionHash); const ETHExecutionPayloadHeader *execution = ETHLightClientHeaderGetExecution(header); const ETHRoot *executionParentHash = ETHExecutionPayloadHeaderGetParentHash(execution); printf(" - parent_hash: "); printHexString(executionParentHash, sizeof *executionParentHash); printf("\n"); const ETHExecutionAddress *executionFeeRecipient = ETHExecutionPayloadHeaderGetFeeRecipient(execution); printf(" - fee_recipient: "); printHexString(executionFeeRecipient, sizeof *executionFeeRecipient); printf("\n"); const ETHRoot *executionStateRoot = ETHExecutionPayloadHeaderGetStateRoot(execution); printf(" - state_root: "); printHexString(executionStateRoot, sizeof *executionStateRoot); printf("\n"); const ETHRoot *executionReceiptsRoot = ETHExecutionPayloadHeaderGetReceiptsRoot(execution); printf(" - receipts_root: "); printHexString(executionReceiptsRoot, sizeof *executionReceiptsRoot); printf("\n"); const ETHLogsBloom *executionLogsBloom = ETHExecutionPayloadHeaderGetLogsBloom(execution); printf(" - logs_bloom: "); printHexString(executionLogsBloom, sizeof *executionLogsBloom); printf("\n"); const ETHRoot *executionPrevRandao = ETHExecutionPayloadHeaderGetPrevRandao(execution); printf(" - prev_randao: "); printHexString(executionPrevRandao, sizeof *executionPrevRandao); printf("\n"); int executionBlockNumber = ETHExecutionPayloadHeaderGetBlockNumber(execution); printf(" - block_number: %d\n", executionBlockNumber); int executionGasLimit = ETHExecutionPayloadHeaderGetGasLimit(execution); printf(" - gas_limit: %d\n", executionGasLimit); int executionGasUsed = ETHExecutionPayloadHeaderGetGasUsed(execution); printf(" - gas_used: %d\n", executionGasUsed); int executionTimestamp = ETHExecutionPayloadHeaderGetTimestamp(execution); printf(" - timestamp: %d\n", executionTimestamp); int numExecutionExtraDataBytes; const void *executionExtraDataBytes = ETHExecutionPayloadHeaderGetExtraDataBytes(execution, &numExecutionExtraDataBytes); printf(" - extra_data: "); printHexString(executionExtraDataBytes, numExecutionExtraDataBytes); printf("\n"); const ETHUInt256 *executionBaseFeePerGas = ETHExecutionPayloadHeaderGetBaseFeePerGas(execution); printf(" - base_fee_per_gas: "); printGweiString(executionBaseFeePerGas); printf(" Gwei\n"); int executionBlobGasUsed = ETHExecutionPayloadHeaderGetBlobGasUsed(execution); printf(" - blob_gas_used: %d\n", executionBlobGasUsed); int executionExcessBlobGas = ETHExecutionPayloadHeaderGetExcessBlobGas(execution); printf(" - excess_blob_gas: %d\n", executionExcessBlobGas); } ETH_RESULT_USE_CHECK int main(void) { NimMain(); ETHRandomNumber *rng = ETHRandomNumberCreate(); check(rng); ETHConsensusConfig *cfg = loadCfg(__DIR__ "/test_files/config.yaml"); ETHBeaconState *genesisState = loadGenesis(cfg, __DIR__ "/test_files/genesis.ssz"); ETHRoot *genesisValRoot = ETHBeaconStateCopyGenesisValidatorsRoot(genesisState); ETHForkDigests *forkDigests = ETHForkDigestsCreateFromState(cfg, genesisState); ETHBeaconClock *beaconClock = ETHBeaconClockCreateFromState(cfg, genesisState); ETHBeaconStateDestroy(genesisState); printf("Current slot: %d\n", ETHBeaconClockGetSlot(beaconClock)); printf("\n"); const ETHRoot trustedBlockRoot = {{ 0x15, 0xcf, 0x56, 0xeb, 0xf8, 0x87, 0xed, 0xe9, 0xcf, 0x3f, 0xc1, 0x0a, 0x26, 0xec, 0x83, 0x82, 0x86, 0x28, 0x93, 0x2c, 0x10, 0x0e, 0x42, 0xc9, 0x8c, 0x84, 0xf8, 0x3d, 0xa7, 0x10, 0xc8, 0x63 }}; int numBootstrapBytes; void *bootstrapBytes = readEntireFile(__DIR__ "/test_files/bootstrap.ssz", &numBootstrapBytes); ETHLightClientStore *store = ETHLightClientStoreCreateFromBootstrap( cfg, &trustedBlockRoot, "application/octet-stream", "capella", bootstrapBytes, numBootstrapBytes); check(store); free(bootstrapBytes); int startPeriod; int count; int syncKind = ETHLightClientStoreGetNextSyncTask(store, beaconClock, &startPeriod, &count); check(syncKind == kETHLcSyncKind_UpdatesByRange); check(startPeriod == 800); check(count > 0 && count <= 128); printf("Sync task: UpdatesByRange(%d, %d)\n", startPeriod, count); int latestProcessResult; int numUpdatesBytes; void *updatesBytes = readEntireFile(__DIR__ "/test_files/updates.ssz", &numUpdatesBytes); latestProcessResult = ETHLightClientStoreProcessUpdatesByRange( store, cfg, forkDigests, genesisValRoot, beaconClock, startPeriod, count, "application/octet-stream", updatesBytes, numUpdatesBytes); check(!latestProcessResult); free(updatesBytes); int millisecondsToNextSyncTask = ETHLightClientStoreGetMillisecondsToNextSyncTask( store, rng, beaconClock, latestProcessResult); printf("Next sync task: %d.%03ds\n", millisecondsToNextSyncTask / 1000, millisecondsToNextSyncTask % 1000); int numFinUpdateBytes; void *finUpdateBytes = readEntireFile(__DIR__ "/test_files/finUpdate.ssz", &numFinUpdateBytes); latestProcessResult = ETHLightClientStoreProcessFinalityUpdate( store, cfg, forkDigests, genesisValRoot, beaconClock, "application/octet-stream", "capella", finUpdateBytes, numFinUpdateBytes); check(!latestProcessResult); free(finUpdateBytes); int numOptUpdateBytes; void *optUpdateBytes = readEntireFile(__DIR__ "/test_files/optUpdate.ssz", &numOptUpdateBytes); latestProcessResult = ETHLightClientStoreProcessOptimisticUpdate( store, cfg, forkDigests, genesisValRoot, beaconClock, "application/octet-stream", "capella", optUpdateBytes, numOptUpdateBytes); check(!latestProcessResult); free(optUpdateBytes); finUpdateBytes = readEntireFile(__DIR__ "/test_files/finUpdate.json", &numFinUpdateBytes); latestProcessResult = ETHLightClientStoreProcessFinalityUpdate( store, cfg, forkDigests, genesisValRoot, beaconClock, "application/json", /* consensusVersion: */ NULL, finUpdateBytes, numFinUpdateBytes); check(!latestProcessResult); free(finUpdateBytes); optUpdateBytes = readEntireFile(__DIR__ "/test_files/optUpdate.json", &numOptUpdateBytes); latestProcessResult = ETHLightClientStoreProcessOptimisticUpdate( store, cfg, forkDigests, genesisValRoot, beaconClock, "application/json", /* consensusVersion: */ NULL, optUpdateBytes, numOptUpdateBytes); check(!latestProcessResult); free(optUpdateBytes); printf("\n"); printf("- finalized_header\n"); visualizeHeader(ETHLightClientStoreGetFinalizedHeader(store), cfg); bool isNextSyncCommitteeKnown = ETHLightClientStoreIsNextSyncCommitteeKnown(store); printf("- next_sync_committee: %s\n", isNextSyncCommitteeKnown ? "known" : "unknown"); printf("- optimistic_header\n"); visualizeHeader(ETHLightClientStoreGetOptimisticHeader(store), cfg); int safetyThreshold = ETHLightClientStoreGetSafetyThreshold(store); printf("- safety_threshold: %d\n", safetyThreshold); ETHLightClientHeader *copiedHeader = ETHLightClientHeaderCreateCopy(ETHLightClientStoreGetFinalizedHeader(store)); ETHLightClientStoreDestroy(store); ETHBeaconClockDestroy(beaconClock); ETHForkDigestsDestroy(forkDigests); ETHRootDestroy(genesisValRoot); ETHConsensusConfigDestroy(cfg); ETHRandomNumberDestroy(rng); ETHRoot *copiedExecutionHash = ETHLightClientHeaderCopyExecutionHash(copiedHeader, cfg); void *blockHeaderJson = readEntireFile( __DIR__ "/test_files/executionBlockHeader.json", /* numBytes: */ NULL); ETHExecutionBlockHeader *executionBlockHeader = ETHExecutionBlockHeaderCreateFromJson(copiedExecutionHash, blockHeaderJson); check(executionBlockHeader); free(blockHeaderJson); ETHRootDestroy(copiedExecutionHash); ETHLightClientHeaderDestroy(copiedHeader); printf("\nFinalized_header (execution block header):\n"); const ETHRoot *executionTransactionsRoot = ETHExecutionBlockHeaderGetTransactionsRoot(executionBlockHeader); printf(" - transactions_root: "); printHexString(executionTransactionsRoot, sizeof *executionTransactionsRoot); printf("\n"); const ETHRoot *executionWithdrawalsRoot = ETHExecutionBlockHeaderGetWithdrawalsRoot(executionBlockHeader); printf(" - withdrawals_root: "); 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"); } const ETHRoot *executionRequestsHash = ETHExecutionBlockHeaderGetRequestsHash(executionBlockHeader); printf(" - requests_hash: "); printHexString(executionRequestsHash, sizeof *executionRequestsHash); printf("\n"); ETHExecutionBlockHeaderDestroy(executionBlockHeader); ETHRoot sampleTransactionsRoot = {{ 0x7d, 0x42, 0x30, 0x71, 0x99, 0x8f, 0xab, 0x13, 0xf5, 0x3f, 0xc2, 0x13, 0xa3, 0xea, 0xed, 0x4f, 0x46, 0x68, 0x43, 0xed, 0x07, 0x4d, 0x86, 0x8e, 0xca, 0x02, 0x78, 0x85, 0x0c, 0xb9, 0x20, 0xe4, }}; void *sampleTransactionsJson = readEntireFile( __DIR__ "/test_files/transactions.json", /* numBytes: */ NULL); ETHTransactions *transactions = ETHTransactionsCreateFromJson(&sampleTransactionsRoot, sampleTransactionsJson); check(transactions); free(sampleTransactionsJson); ETHRoot sampleReceiptsRoot = {{ 0xef, 0x6b, 0x38, 0x00, 0x44, 0x1d, 0xad, 0xba, 0x3c, 0xe8, 0xba, 0xed, 0xcd, 0xf8, 0x49, 0x5c, 0x91, 0x8d, 0x03, 0xd6, 0xf9, 0xb0, 0xb3, 0x39, 0xda, 0x0f, 0x6d, 0xf5, 0xfc, 0xbb, 0x1a, 0x68, }}; void *sampleReceiptsJson = readEntireFile( __DIR__ "/test_files/receipts.json", /* numBytes: */ NULL); ETHReceipts *receipts = ETHReceiptsCreateFromJson(&sampleReceiptsRoot, sampleReceiptsJson, transactions); check(receipts); free(sampleReceiptsJson); int numTransactions = ETHTransactionsGetCount(transactions); int numReceipts = ETHReceiptsGetCount(receipts); check(numTransactions == numReceipts); printf("\nSample transactions:\n"); for (int transactionIndex = 0; transactionIndex < numTransactions; transactionIndex++) { const ETHTransaction *transaction = ETHTransactionsGet(transactions, transactionIndex); const ETHReceipt *receipt = ETHReceiptsGet(receipts, transactionIndex); const ETHRoot *transactionHash = ETHTransactionGetHash(transaction); printf("- "); printHexString(transactionHash, sizeof *transactionHash); printf("\n"); const uint64_t *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"); } const ETHAuthorizationList *transactionAuthorizationList = ETHTransactionGetAuthorizationList(transaction); printf(" - authorization_list:\n"); int numAuthorizations = ETHAuthorizationListGetCount(transactionAuthorizationList); for (int tupleIndex = 0; tupleIndex < numAuthorizations; tupleIndex++) { const ETHAuthorization *authorization = ETHAuthorizationListGet(transactionAuthorizationList, tupleIndex); const ETHExecutionAddress *authority = ETHAuthorizationGetAuthority(authorization); printf(" - "); printHexString(authority, sizeof *authority); printf("\n"); const uint64_t *chainId = ETHAuthorizationGetChainId(authorization); printf(" - chain_id: "); printHexStringReversed(chainId, sizeof *chainId); printf("\n"); const ETHExecutionAddress *address = ETHAuthorizationGetAddress(authorization); printf(" - address: "); printHexString(address, sizeof *address); printf("\n"); const uint64_t *nonce = ETHAuthorizationGetNonce(authorization); printf(" - nonce: %" PRIu64 "\n", *nonce); int numSignatureBytes; const void *signatureBytes = ETHAuthorizationGetSignatureBytes(authorization, &numSignatureBytes); printf(" - signature: "); printHexString(signatureBytes, numSignatureBytes); 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"); printf(" - receipt:\n"); bool receiptHasStatus = ETHReceiptHasStatus(receipt); if (!receiptHasStatus) { const ETHRoot *receiptRoot = ETHReceiptGetRoot(receipt); printf(" - root: "); printHexString(receiptRoot, sizeof *receiptRoot); printf("\n"); } else { bool receiptStatus = ETHReceiptGetStatus(receipt); printf(" - status: %d\n", receiptStatus); } const uint64_t *receiptGasUsed = ETHReceiptGetGasUsed(receipt); printf(" - gas_used: %" PRIu64 "\n", *receiptGasUsed); const ETHLogsBloom *receiptLogsBloom = ETHReceiptGetLogsBloom(receipt); printf(" - logs_bloom: "); printHexString(receiptLogsBloom, sizeof *receiptLogsBloom); printf("\n"); const ETHLogs *receiptLogs = ETHReceiptGetLogs(receipt); printf(" - logs:\n"); int numLogs = ETHLogsGetCount(receiptLogs); for (int logIndex = 0; logIndex < numLogs; logIndex++) { const ETHLog *log = ETHLogsGet(receiptLogs, logIndex); const ETHExecutionAddress *logAddress = ETHLogGetAddress(log); printf(" - address: "); printHexString(logAddress, sizeof *logAddress); printf("\n"); printf(" - topics:\n"); int numTopics = ETHLogGetNumTopics(log); for (int topicIndex = 0; topicIndex < numTopics; topicIndex++) { const ETHRoot *topic = ETHLogGetTopic(log, topicIndex); printf(" - "); printHexString(topic, sizeof *topic); printf("\n"); } int numLogDataBytes; const void *logDataBytes = ETHLogGetDataBytes(log, &numLogDataBytes); printf(" - data: "); printHexString(logDataBytes, numLogDataBytes); printf("\n"); } int numReceiptBytes; const void *receiptBytes = ETHReceiptGetBytes(receipt, &numReceiptBytes); printf(" - bytes: "); printHexString(receiptBytes, numReceiptBytes); printf("\n"); } ETHReceiptsDestroy(receipts); ETHTransactionsDestroy(transactions); return 0; }