Skip to main content

Transaction Validation Lifecycle

Transactions are the most fundamental entities for interacting with Nervos CKB. When you interact with the CKB, you are submitting state transitions through transactions. This document will explain the lifecycle of CKB transaction validation.

Submit the transaction through RPC

First, a sender constructs a transaction, then submits it through RPC. The transaction will be validated by the outputs_validator (introduced in v0.27.0) which has been submitted.

The default validation logic involves checking various things:

transaction.outputs.all{ |output|
let script = output.script
(script.code_hash == secp256k1_blake160_sighash_all && script.hash_type == "type" && script.args.size == 20) ||
(script.code_hash == secp256k1_blake160_multisig_all && script.hash_type == "type" &&
(script.args.size == 20 || (script.args.size == 28 && script.args[20..28].is_valid_since_format)))
}
transaction.outputs.all{ |output|
let script = output.type
script.is_null || script.code_hash == dao && script.hash_type == "type"
|| (script.has_lock_period() && since.is_absolute())
}

This validation is intended to prevent improperly constructed transactions, such as mentioned in Common-Gotchas#nervos-dao

Although the node can be configured to passthrough to skip this validation, once the transaction has been submitted to your local node, the node also exports the transaction id, which can then be used to track transaction status.

Verification

Before the transaction is broadcasted and enters into the mempool, it should be verified and executed locally.

STEP 1 — Resolve

Essentially, transaction inputs are just pointers, as shown here:

struct OutPoint {
tx_hash: Byte32,
index: Uint32,
}

We gather the referenced data through the pointer prior to transaction execution, this process is called “resolve transaction”. We will also need to check that all inputs of this transaction are valid (no duplicate or double-spending).

STEP 2 — Verify

The process of verification involves checking the following items:

  1. version (currently must be 0)
  2. The serialized size of the transaction's data must be less than or equal to a specified limit.
     pub fn verify(&self) -> Result<(), Error> {
    let size = self.transaction.data().serialized_size_in_block() as u64;
    if size <= self.block_bytes_limit {
    Ok(())
    } else {
    Err(TransactionError::ExceededMaximumBlockBytes {
    actual: size,
    limit: self.block_bytes_limit,
    }
    .into())
    }
    }
  3. inputs and outputs are not empty
    • The transaction should not have empty inputs, and it should also not have empty outputs, except in the case of a cellbase transaction.
  4. inputs are mature
    • For each input and dep, if the referenced output transaction is a cellbase, it must have at least 4 epoch confirmations
  5. capacity
    • Sum of inputs’ capacity must be greater than or equal to sum of outputs’ capacity
  6. duplicate_deps
    • deps should not be duplicated
  7. outputs_data_verifier
    • number of ‘output data' fields must equal number of outputs
  8. since
    • since value must follow the rules described in RFC0017

Then CKB VM will execute the transaction script and output the number of cycles consumed.

Broadcast to the network

If the verification is successful, the current node broadcasts the transaction (with cycles value) to all of its connected peer nodes.

If verification fails, the transaction is not broadcasted anymore. The transaction flows through various “full nodes”, which repeat the verification process described in the previous step, and check that the cycles value matches the actual cycles consumed when the transaction is verified.

Tx-pool (mempool)

CKB uses a two-step process for transaction confirmation. Transactions will be divided into different status (pending and proposed) in the tx-pool. The status of transactions will change when a block is added to the blockchain. When the latest block changes, all transactions in the tx-pool will be re-scanned to ensure they are still valid.

BlockAssembler will fetch proposals and transactions from the pending pool and proposed pool for block template.

More information about this two-step transaction confirmation process can be found here.

Tx-pool Two-Step Transaction Confirmation

Since CKB v0.101, the tx-pool will not run the two-step transaction confirmation mechanism for non-miner nodes, i.e. nodes that are not configured with block assembler, and the status of the transaction will not be displayed when the transaction is fetched via rpc.