Transactions and Blocks
How transactions and blocks work in Blockchain Development Kit (BDK).
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 secp256k1Not 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 EIP-1559
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):
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 =
...
Last updated