Skip to content
Igor Grkavac edited this page Apr 7, 2021 · 2 revisions

Verification of incoming transactions

Upon receiving of NOTIFY_NEW_TRANSACTIONS request message from other node using p2p message overlay, p2p payload handler t_cryptonote_protocol_handler::handle_notify_new_transactions callback is executed. Following verifications are performed:

  • Handler checks if p2p connection with the remote peer is in cryptonote_connection_context::state_normal
  • local t_cryptonote_protocol_handler status is synchronized
  • continue with processing binary transaction blob list from NOTIFY_NEW_TRANSACTIONS request. One by one, transactions are forwarded to cryptonote::core::handle_incoming_tx with verification context as return value. If verification of new transaction fails, connection with the remote peer is dropped, as peer node seems to be unreliable. Handle_notify_new_transactions also keeps list of transactions that should be relayed. In case that some of the transactions from the incoming NOTIFY_NEW_TRANSACTIONS list should be relayed further, at the end of the handler t_cryptonote_protocol_handler<t_core>::relay_transactions is called. Cryptonote::core transaction pool (class cryptonote::tx_memory_pool) keeps info if transaction from the txpool is relayed.

Cryptonote::core::handle_incoming_tx, core::handle_incoming_txs implements handling process of incoming transactions. To increase speed of transaction verification, core::handle_incoming_txs uses tools::threadpool to process incoming transactions in parallel, calling in separate thread core::handle_incoming_tx_pre to check each transaction:

  • transaction blob is checked for size
  • blob is parsed with verification of integrity
  • transaction is checked for transaction version
  • is transaction on blacklist of bad transactions in core::bad_semanticds_txes
  • check if transaction hash is already in the database of the transaction pool, current database of transactions in blocks After all transactions are prechecked, it is, and again tread-pool is activated with core::handle_incoming_tx_post parallel calls:
  • check for syntax (currently trivially)
  • Semantic check in core::check_tx_semantic Transaction semantic check:
  • check of number of inputs (must not be 0)
  • check if input types are supported
  • check if output types are valid
  • checks for inputs and outputs overflow
  • checks that sum of inputs is less or equal the sum of outputs (for both cash and tokens)
  • check if transaction size is bigger that current block limit (in that case in is too big to enter the block)
  • check if there are not duplicated key image in the utxo inputs of the transaction
  • check input keyimages domain If all this checks are passed, transaction if forwarded to core::add_new_tx call:
  • additional check that transaction hash is not already in the transaction pool and transaction database From there, tx_memory_pool::add_tx is called:
  • additional checks for type of tx inputs
  • is transaction on the list of timed-out transactions
  • is transaction fee larger than zero
  • is fee properly calculated (Blockchain::check_fee)
  • is transaction larger than transaction size limit
  • are key images already in the txpool as spent
  • check tx outputs (Blockchain::check_tx_outputs)
    • Is token whole amount
    • Is valid transaction output type
    • Check validity of out public key
    • Don’t allow bulletproofs
  • Check transaction inputs with Blockchain::check_tx_inputs
    • Check if minimum mixin is required (currently not used)
    • Check if key images in input are sorted
    • Check if all transaction input types are valid
    • Check if token amount is whole amount
    • Check that there is no more that maximum number of migrated tokens
    • Make sure that txin has valid key offsets
    • Check if input has key image as spent
    • Check every tx input with particular checking function for that type in _Blockchain::check_tx_input
    • Start thread pool to check ring signature or migration signature for relevant inputs with Blockchain::check_ring_signature and Blockchain::check_migration_signature
    • Check if max_used_block_height is smaller then database height

If transaction has passed all this check, then it is included in the transaction pool database with Blockchain::add_txpool_tx.

Verification of incoming blocks

Upon receiving of NOTIFY_NEW_BLOCK request message from other node using p2p message overlay, p2p payload handler t_cryptonote_protocol_handler::handle_notify_new_block callback is executed. NOTIFY_NEW_BLOCK request payload contains block blob and list of transaction blobs in the block. Following verifications are performed:

  • Handler checks if p2p connection with the remote peer is in cryptonote_connection_context::state_normal

  • local t_cryptonote_protocol_handler status is synchronized

  • Mining is paused

  • Core is prepared to receive incoming blocks by taking lock for incoming transactions and calling Blockchain::prepare_handle_incoming_blocks.

    • Pool and blockchain locks are acquired.
    • Blocks are processed using threadpool and multiple batches
    • Check if block hash is already in db, alt chains or pool
    • Block is parsed and validated from blob
    • Thread with Blockchain::block_longhash_worker is called
      • Block id is calculated with cryptonote::get_block_hash and proof of work is calculated with cryptonote::get_block_longhash
  • Blockchain::m_blocks_longhash_table map keeps the list of block ids and their corresponding proof of works:

    • All transactions in the block are parsed for outputs, inputs, amounts
      • Check if tx prefix hash is already in Blokchchain::m_scan_table
      • Check for duplicated key image in one transaction
      • Parse amounts and makes list of input amounts
      • Keeps, sorts and filters list of absolut offsets from tx inputs
  • Call core::handle_incoming_tx for all incoming transactions in the block

  • Call core::handle_incoming_block for incoming block

    • Parse and validate block from blob
    • Call core::add_new_block for block and then Blockchain::add_new_block where we check we already know about block in database, alternative chains or invalid block list and then we handle block to be added to the main chain Blockchain::handle_block_to_main_chain or alternative chain Blockchain::handle_alternative_block. Criteria for deciding if new block belongs to the main chain is previous block id, it should be same as last known main block id.
    • If new block is added, miner block template is also updated with core::update_miner_block_template

Blockchain::handle_block_to_main_chain is important function that validates block and adds it to the end of the chain:

  • Block major_version is checked for current expected version and warning is issued if client is not updated but block of higher version has come
  • Block is checked to be of current hard fork version
  • Block timestamp is checked with Blockchain::check_block_timestamp. Block timestamp must not be larger than local time + block future time limit which is 500 seconds. Then, timestamps of last 12 blocks are collected and their median is found, and new block timestamp must not smaller than that median.
  • Block proof of work is checked. New block proof of work calculated with cryptonote::get_block_longhash is compared to expected difficulty calculated with Blockchain::get_difficulty_for_next_block
  • Checkpoint for the block is checked, if in the checkpoint zone
  • Prevalidate miner transaction for one input of type txin_gen, height set to block height, correct unlock time, non-overflowing tx amount
  • Check all other transactions:
    • Check if transaction is already in the database (BlockchainDB->tx_exists())
    • Take transaction from transaction pool if it is available there. If it is not, fail verification and Blockchain::handle_block_to_main_chain
    • Call Blockchain:check_tx_inputs for every transaction (function described in verification of incoming transactions chapter)
  • Fully validate miner transaction with Blockchain::validate_miner_transaction
    • Check if miner tx outputs are decomposed correctly
    • Check if block size is bigger than 2*median with cryptonote::get_block_reward
    • Check if coinbase outputs are in total block reward + fee
    • Allow miner to take less reward if he wants too (to ignore dust outputs)
  • If block verification has not failed, call BlockchainDB::add_block with updated already_generated_coins and already_migrated_tokens
    • This function checks if key images are already in the database and throw exception KEY_IMAGE_EXISTS
    • Add all block and tx related info to database

Blockchain class has list of invalid blocks that fail notification, and keeps dynamic list of them Blockchain::m_invalid_blocks.

Alternative chains and chain switch

Often, blocks in the network make soft forks, due to competing mining. Goal of the nodes is to always be on the fork that has maximum difficulty (maximum of the mining work invested in them).

For a new incoming block, this is the logic applied: if a block arrives and is to be added and its parent block is not the current main chain top block, then daemon needs to see if it knows about its parent block. If its parent block is part of a known forked chain, then daemon needs to see if that chain is long enough to become the main chain and reorganize accordingly if so. If not, daemon needs to hang on to the block in case it becomes part of a long forked chain eventually.

If it is not applicable to the main chain, block is passed to the Blockchain::handle_alternative_block function. There, following checks are performed:

  • Block is checked for hard fork height, if it has expected block version and vote
  • Search for block parent in alternative chain and in main chain
  • Form alternative chain in form of the list, check its connection to main chain and complete timestamps vector list so that alternative chain timestamp check window is valid
  • Check block timestamp
  • Check if block is correct with respect to checkpoints
  • Check block difficulty and work invested, with respect to Blockchain::get_next_difficulty_for_alternative_chain()
  • Prevalidate miner transaction
  • Add block to Blockchain::m_alternative_chains list
  • Check if alternative chain difficulty is bigger that the main chain. In case it is, switch to alternative chain with Blockchain::switch_to_alternative_blockchain()

List of alternative chain blocks is kept dynamically, in RAM, and is not saved to database permanent storage.

Dynamic fee

Nominal fee is 0.01 Cash. Dynamic fee is calculated with formula:

fee = (R/R0) * (M0/M) * F0

R: current base block reward

R0: reference base reward

M: current median block size limit

M0: minimum block size limit

F0: 0.01 Cash

RPC documentation

Advanced functionalities

Blockchain logic and database

Manuals

Clone this wiki locally