AppLayer
  • Welcome to AppLayer Docs
  • Introducing AppLayer
    • A Primer on Smart Contracts
    • The Problem With EVMs
    • What is AppLayer?
  • How AppLayer works
    • Validators
    • Sentinels
    • Application Chains
    • Bridging
      • AppLayer-to-AppLayer Data Bridging
      • AppLayer-to-AppLayer Token Bridging
      • AppLayer-to-External Bridging (Ethereum, Solana, etc.)
  • Understanding rdPoS
    • Blockchains overview
    • How rdPoS works
    • Validator implementations
    • Slashing
  • BDK implementation
    • The utils folder
    • The contract folder
    • The core folder
    • Transactions and Blocks
    • Database
    • Contract call handling
    • RLP (Recursive-Length Prefix)
    • P2P Overview
    • P2P Encoding
  • Understanding contracts
    • Solidity ABI
    • Internal and external contract calls
    • Setting up the development environment
    • Contract Tester
  • Precompiled contracts
    • Types of pre-compiled contracts
    • Dynamic and Protocol Contracts
    • SafeVariables and commit/revert logic
    • How to code a precompiled contract
    • Creating a Dynamic Contract (Simple)
      • Simple Contract Header
      • Simple Contract Source
      • Deploying and testing
    • Creating a Dynamic Contract (Advanced)
    • Creating a Protocol Contract (Advanced)
  • EVM contracts
    • State management and VM instance creation
    • Seamless C++/EVM integration
    • C++ to other contract calls
    • EVM to other contract calls
    • Executing contract calls via EVMC
    • Calling EVM contracts from C++
    • Calling C++ contracts from EVM
  • Getting started with AppLayer Testnet
  • Join our Community
  • Get in Touch
  • Glossary
Powered by GitBook
On this page
  • How transactions are parsed
  • Transaction structure
  • Block structure
  • Raw block analysis
  1. BDK implementation

Transactions and Blocks

How transactions and blocks work in Blockchain Development Kit (BDK).

PreviousThe core folderNextDatabase

Last updated 5 months ago

This subchapter explains the structure of blocks, transactions, and how both are properly parsed within BDK and AppLayer.

How transactions are parsed

No matter the type, there are two ways a transaction can be parsed from a bytes string:

  • Directly from RLP

    • Requires deriving the sender (from) address and a validity check using secp256k1

    • Not included in a block, which means it's a new transaction coming from the network

    • Equivalent to Ethereum's rawTransaction

  • Directly from the database

    • Considered trustworthy since it already went through the process above

    • Is included in a block, therefore it's part of the blockchain

Transaction structure

Depending on the type, a transaction will contain the following data fields once it's properly parsed:

  • (TxBlock) to - receiver address (the one that received the funds)

  • (Both) from - sender address (the one that sent the funds)

  • (Both) data - arbitrary data field (used in contracts)

  • (Both) chainId - unique blockchain ID where the transaction was made

  • (TxValidator) nHeight - height of the block where the transaction was included

  • (TxBlock) nonce - number of the transaction made from the sender address - always starts at 0, so a nonce of 4 means this is the fifth transaction from the address

  • (TxBlock) value - transaction value in its lowest unit

    • e.g. "satoshi" for BTC, "Wei" for ETH, etc. - "100000000" satoshis would be 1 BTC, "5000000000" Wei would be 0.000000005 ETH (or 5 gwei), so on and so forth

    • Since we're using an Ethereum-based format, we commonly refer to its terminology, so "value" is in "Wei"

  • (TxBlock) maxPriorityFeePerGas - value paid as an incentive for miners to include the transaction in the block, as per

    • This is implemented but actively ignored since we don't have "miners", therefore only maxFeePerGas is counted

  • (TxBlock) maxFeePerGas - value paid by every unit of gas, in Gwei (e.g. "15" = 15000000000 Wei)

    • The total transaction fee is calculated as (gasLimit * maxFeePerGas) - e.g. 21000 * 15000000000 = 0.000315 ETH

  • (TxBlock) gasLimit - maximum limit of gas units that the transaction will use, in Wei (e.g. "21000")

    • If the transaction uses more than this limit, it will automatically fail - the original transaction value won't be spent, but what was already spent as gas is lost

  • (Both) ECDSA signature (Elliptic Curve Digital Signature Algorithm) for validating transaction integrity, split in three:

    • r - first half of the ECDSA signature (32 hex bytes)

    • s - second half of the ECDSA signature (32 hex bytes)

    • v - recovery ID (1 hex byte)

  • (Both) hash - The transaction's own hash (including the signature), stored as a cache for performance reasons

Block structure

A block will contain the following conceptual structure:

  • Validator signature (the one responsible for creating and signing the block)

  • Validator public key (for verifying the signature)

  • The block's header, which is made of:

    • Previous block hash (a hash of the previous block's header, signed by the Validator)

    • "Randomness" (a random seed generated by RandomGen during the previous block's creation)

    • Validator Tx Merkle Tree (to verify the integrity of Validator transactions)

    • Block Tx Merkle Tree (to verify the integrity of Block transactions)

    • UNIX timestamp of the block, in microseconds

    • Block height (commonly known as nHeight)

  • List of Validator transactions

  • List of block transactions

  • A cached hash of the block's header

  • The total size of the block, in bytes

In practice, a block is simply a serialized bytes string, transmitted through the network and stored in the blockchain, so it's up to the code logic to properly parse it. Another way to see the block in a more "raw" format would be ("bytes" are in hex format, e.g. 0xFF = 1 byte):

Outside header:
  65 bytes - Validator signature
  65 bytes - Validator public key
Header (144 bytes):
  32 bytes - Previous block hash
  32 bytes - "Randomness"
  32 bytes - Validator Tx Merkle Root
  32 bytes - Block Tx Merkle Root
  8 bytes - Timestamp (in microseconds)
  8 bytes - Block height
Content:
  8 bytes - Offset of Validator transactions array
  [
    4 bytes - Block transaction size
    N bytes - Block transaction
    ...
  ]
  [
    4 bytes - Validator transaction size
    N bytes - Validator transaction
    ...
  ]

Blocks transmitted through the network must have the Validator signature, which is part of the block but is outside its structure. This is intentional, so the block header can be hashed and signed on its own without hashing the signature along with it.

Raw block analysis

A "raw" finalized block would look something like this:

5c37d504e9415c3b75afaa3ad24484382274bba31f10dcd268e554785d5b807500000181810eb6507a8b54dfbfe9f21d00000001000000aff8ad81be850c92a69c0082e18c94d586e7f844cea2f87f50152665bcbc2c279d8d7080b844a9059cbb00000000000000000000000026548521f99d0709f615aa0f766a7df60f99250b00000000000000000000000000000000000000000000002086ac351052600000830150f7a07e16328b7f3823abeb13d0cab11cdffaf967c9b2eaf3757c42606d6f2ecd8ce6a040684c94b289cdda2282...

Where:

  • Validator signature = 5c37d504e9415c3b75afaa3ad24484382274bba31f10dcd268e554785d5b807500000181810eb6507a8b54dfbfe9f21d00000001000000aff8ad81be850c92a69c

  • Previous block hash = 0082e18c94d586e7f844cea2f87f50152665bcbc2c279d8d7080b844a9059cbb

  • "Randomness" = 00000000000000000000000026548521f99d0709f615aa0f766a7df60f99250b

  • Validator Merkle Tree = 00000000000000000000000000000000000000000000002086ac351052600000

  • Block Merkle Tree = 830150f7a07e16328b7f3823abeb13d0cab11cdffaf967c9b2eaf3757c42606d

  • Timestamp = 6f2ecd8ce6a04068

  • Block height = 4c94b289cdda2282

  • Rest of the content = ...

EIP-1559