LCOV - code coverage report
Current view: top level - powhsm/src - bc_advance.c (source / functions) Hit Total Coverage
Test: powHSM firmware Lines: 332 380 87.4 %
Date: 2025-07-10 13:49:13 Functions: 22 22 100.0 %

          Line data    Source code
       1             : /**
       2             :  * The MIT License (MIT)
       3             :  *
       4             :  * Copyright (c) 2021 RSK Labs Ltd
       5             :  *
       6             :  * Permission is hereby granted, free of charge, to any person obtaining a copy
       7             :  * of this software and associated documentation files (the "Software"), to
       8             :  * deal in the Software without restriction, including without limitation the
       9             :  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
      10             :  * sell copies of the Software, and to permit persons to whom the Software is
      11             :  * furnished to do so, subject to the following conditions:
      12             :  *
      13             :  * The above copyright notice and this permission notice shall be included in
      14             :  * all copies or substantial portions of the Software.
      15             :  *
      16             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      17             :  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      18             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      19             :  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      20             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      21             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
      22             :  * IN THE SOFTWARE.
      23             :  */
      24             : 
      25             : #include <string.h>
      26             : 
      27             : #include "bc.h"
      28             : #include "hal/log.h"
      29             : #include "defs.h"
      30             : #include "ints.h"
      31             : #include "mem.h"
      32             : #include "srlp.h"
      33             : #include "bigdigits.h"
      34             : #include "memutil.h"
      35             : 
      36             : #include "bc_block.h"
      37             : #include "bc_blockutils.h"
      38             : #include "bc_advance.h"
      39             : #include "bc_diff.h"
      40             : #include "bc_err.h"
      41             : #include "bc_hash.h"
      42             : #include "bc_mm.h"
      43             : #include "bc_nu.h"
      44             : #include "bc_state.h"
      45             : #include "util.h"
      46             : 
      47             : // We'll be asking for block header chunks of at most this size
      48             : #define MAX_CHUNK_SIZE 80
      49             : 
      50             : // Threshold difficulty to achieve when advancing the blockchain
      51             : // NOTE: for example, to specify 0x 000001d5 bcab6123 1c92f6c6, use
      52             : // const DIGIT_T MIN_REQUIRED_DIFFICULTY[BIGINT_LEN] = { 0x1c92f6c6, 0xbcab6123,
      53             : // 0x000001d5, 0, 0, 0, 0, 0, 0 };
      54             : 
      55             : // Here we take it from an external definition (see Makefile for details)
      56             : #if (defined(HSM_PLATFORM_LEDGER) || defined(HSM_PLATFORM_SGX)) && \
      57             :     defined(PARAM_MIN_REQUIRED_DIFFICULTY)
      58             : static const DIGIT_T MIN_REQUIRED_DIFFICULTY[BIGINT_LEN] =
      59             :     PARAM_MIN_REQUIRED_DIFFICULTY;
      60             : #elif defined(HSM_PLATFORM_X86)
      61             : DIGIT_T MIN_REQUIRED_DIFFICULTY[BIGINT_LEN];
      62             : #else
      63             : #error "Minimum required difficulty not defined!"
      64             : #endif
      65             : 
      66             : // -----------------------------------------------------------------------
      67             : // Blockchain advance validation state
      68             : // -----------------------------------------------------------------------
      69             : 
      70             : // Number of blocks to validate
      71             : static uint32_t expected_blocks;
      72             : 
      73             : // Count of validated blocks
      74             : static uint32_t curr_block;
      75             : 
      76             : // Expected OP for next message
      77             : static uint8_t expected_state;
      78             : 
      79             : // -----------------------------------------------------------------------
      80             : // Storage utilities
      81             : // -----------------------------------------------------------------------
      82             : 
      83             : /*
      84             :  * Process merkle proof chunk.
      85             :  *
      86             :  * @arg[in] chunk pointer to chunk to process
      87             :  * @arg[in] size  chunk size in bytes
      88             :  */
      89         315 : static void process_merkle_proof(const uint8_t* chunk, uint16_t size) {
      90             :     // Size limit established from Iris network upgrade onwards
      91         315 :     if (block.network_upgrade >= NU_IRIS &&
      92         315 :         block.merkle_off + size > MAX_MERKLE_PROOF_SIZE) {
      93           0 :         FAIL(MERKLE_PROOF_OVERFLOW);
      94             :     }
      95             : 
      96             :     // Record the additional bytes on the merkle proof (for size tracking)
      97         315 :     block.merkle_off += size;
      98             : 
      99             :     // Process as many hashes as possible
     100         315 :     uint8_t offset = 0;
     101         394 :     while (block.wa_off + size - offset >= HASH_SIZE) {
     102          79 :         if (block.wa_off < HASH_SIZE) {
     103          79 :             uint8_t old_offset = offset;
     104          79 :             offset += HASH_SIZE - block.wa_off;
     105         158 :             WA_STORE(chunk + old_offset, HASH_SIZE - block.wa_off);
     106             :         }
     107          79 :         fold_left(&block.ctx, block.merkle_proof_left, block.wa_buf);
     108          79 :         block.wa_off = 0;
     109             :     }
     110             : 
     111             :     // Copy any remaining bytes in the chunk to the work area
     112         315 :     if (offset < size) {
     113          40 :         WA_STORE(chunk + offset, size - offset);
     114             :     }
     115         315 : }
     116             : 
     117             : /*
     118             :  * Store coinbase transaction chunk in block.cb_txn.
     119             :  *
     120             :  * @arg[in] chunk pointer to chunk to store
     121             :  * @arg[in] size  chunk size in bytes
     122             :  */
     123         732 : static void store_cb_txn_bytes(const uint8_t* chunk, uint16_t size) {
     124        1464 :     SAFE_MEMMOVE(block.cb_txn,
     125             :                  sizeof(block.cb_txn),
     126             :                  block.cb_off,
     127             :                  chunk,
     128             :                  size,
     129             :                  MEMMOVE_ZERO_OFFSET,
     130             :                  size,
     131             :                  FAIL(CB_TXN_OVERFLOW));
     132             : 
     133         732 :     block.cb_off += size;
     134         732 : }
     135             : 
     136             : // -----------------------------------------------------------------------
     137             : // Blockchain advance actions and validations
     138             : // -----------------------------------------------------------------------
     139             : 
     140             : /*
     141             :  * Patch the current block's hash_for_mm.
     142             :  *
     143             :  * @arg[in] has_umm_root whether the current block has a umm root
     144             :  */
     145         291 : static void patch_hash_for_mm(bool has_umm_root) {
     146         291 :     if (has_umm_root) {
     147           0 :         KECCAK_INIT(&block.mm_ctx);
     148           0 :         KECCAK_UPDATE(&block.mm_ctx, block.hash_for_mm, UMM_ROOT_SIZE);
     149           0 :         KECCAK_UPDATE(&block.mm_ctx, block.umm_root, UMM_ROOT_SIZE);
     150           0 :         KECCAK_FINAL(&block.mm_ctx, block.hash_for_mm);
     151             :     }
     152             : 
     153        1455 :     VAR_BIGENDIAN_TO(
     154             :         &block.hash_for_mm[KECCAK256_HASH_SIZE - sizeof(block.number)],
     155             :         block.number,
     156             :         sizeof(block.number));
     157             : 
     158         291 :     uint8_t size =
     159             :         KECCAK256_HASH_SIZE - MM_HASH_PREFIX_SIZE - MM_HASH_SUFFIX_SIZE;
     160         291 :     memset(&block.hash_for_mm[MM_HASH_PREFIX_SIZE], 0, size);
     161         291 : }
     162             : 
     163             : /*
     164             :  * Validate the current block's merkle proof. This function assumes
     165             :  * that the cb_txn_hash is stored in the block's work area.
     166             :  */
     167         295 : static void validate_merkle_proof() {
     168        5015 :     REV_HASH(block.merkle_proof_left);
     169             : 
     170         295 :     if (HNEQ(block.merkle_root, block.merkle_proof_left)) {
     171           1 :         FAIL(MERKLE_PROOF_MISMATCH);
     172             :     }
     173         294 : }
     174             : 
     175             : /*
     176             :  * Compute the cb_txn_hash for the current block. It stores
     177             :  * the cb_txn_hash in the block's work area, so this method
     178             :  * MUST be called before validate_cb_txn_hash().
     179             :  */
     180         291 : static void compute_cb_txn_hash() {
     181         291 :     memset(block.wa_buf, 0, CB_MIDSTATE_PREFIX);
     182         291 :     SAFE_MEMMOVE(block.wa_buf,
     183             :                  sizeof(block.wa_buf),
     184             :                  CB_MIDSTATE_PREFIX,
     185             :                  block.cb_txn,
     186             :                  sizeof(block.cb_txn),
     187             :                  MEMMOVE_ZERO_OFFSET,
     188             :                  CB_MIDSTATE_DATA,
     189             :                  FAIL(BUFFER_OVERFLOW));
     190         291 :     memset(block.wa_buf + CB_MIDSTATE_PREFIX + CB_MIDSTATE_DATA,
     191             :            0,
     192             :            CB_MIDSTATE_SUFFIX);
     193         291 :     hash_sha256_ms_init(&block.mid_ctx);
     194         291 :     hash_sha256_ms_midstate(&block.mid_ctx, block.wa_buf);
     195         291 :     hash_sha256_ms_update(&block.mid_ctx,
     196             :                           block.cb_txn + CB_MIDSTATE_DATA,
     197         291 :                           block.cb_off - CB_MIDSTATE_DATA);
     198         291 :     hash_sha256_ms_final(&block.mid_ctx, block.wa_buf);
     199         291 :     hash_sha256_ms_init(&block.mid_ctx);
     200         291 :     hash_sha256_ms_update(&block.mid_ctx, block.wa_buf, HASH_SIZE);
     201         291 :     hash_sha256_ms_final(&block.mid_ctx, block.wa_buf);
     202        4947 :     REV_HASH(block.wa_buf);
     203         291 : }
     204             : 
     205             : /*
     206             :  * Validate the computed coinbase transaction hash
     207             :  * against the metadata coinbase transaction hash
     208             :  */
     209         291 : static void validate_cb_txn_hash() {
     210         291 :     if (HNEQ(block.wa_buf, block.cb_txn_hash)) {
     211           0 :         FAIL(CB_TXN_HASH_MISMATCH);
     212             :     }
     213         291 : }
     214             : 
     215             : const char rsk_tag[] = "RSKBLOCK:";
     216             : #define RSK_TAG_LEN 9 // Length of "RSKBLOCK:"
     217             : 
     218             : /*
     219             :  * Extract mm_hash from the coinbase transaction, and compare
     220             :  * it with the current block's hash_for_mm.
     221             :  */
     222         291 : static void validate_mm_hash() {
     223         291 :     uint8_t* tail = block.cb_txn + CB_MIDSTATE_DATA;
     224         291 :     uint8_t* last = block.cb_txn + block.cb_off - 1;
     225             : 
     226             :     // If present, ptr will point to the rightmost rsk_tag occurrence.
     227             :     // Otherwise, ptr will be 1 less than tail.
     228         291 :     uint8_t* ptr = last;
     229       27354 :     for (; ptr >= tail; ptr--) {
     230       27354 :         if (last - ptr + 1U >= RSK_TAG_LEN &&
     231       25026 :             memcmp(ptr, rsk_tag, RSK_TAG_LEN) == 0) {
     232         291 :             break;
     233             :         }
     234             :     }
     235             : 
     236         291 :     if (ptr < tail) {
     237           0 :         FAIL(BTC_CB_TXN_INVALID);
     238             :     }
     239             : 
     240         291 :     if (ptr - tail > CB_MAX_RSK_TAG_POSITION) {
     241           0 :         FAIL(BTC_CB_TXN_INVALID);
     242             :     }
     243             : 
     244         291 :     if (ptr + RSK_TAG_LEN + HASH_SIZE > last) {
     245           0 :         FAIL(BTC_CB_TXN_INVALID);
     246             :     }
     247             : 
     248         291 :     if (last - (ptr + RSK_TAG_LEN + HASH_SIZE) + 1 > CB_MAX_AFTER_MM_HASH) {
     249           0 :         FAIL(BTC_CB_TXN_INVALID);
     250             :     }
     251             : 
     252             :     uint64_t n;
     253        2619 :     BIGENDIAN_FROM(block.cb_txn, n);
     254         291 :     if (n + (last - tail) + 1 <= CB_MIN_TX_SIZE) {
     255           0 :         FAIL(BTC_CB_TXN_INVALID);
     256             :     }
     257             : 
     258         291 :     uint8_t size =
     259             :         KECCAK256_HASH_SIZE - MM_HASH_PREFIX_SIZE - MM_HASH_SUFFIX_SIZE;
     260         291 :     if (block.network_upgrade >= NU_WASABI) {
     261         291 :         memset(&(ptr + RSK_TAG_LEN)[MM_HASH_PREFIX_SIZE], 0, size);
     262             :     }
     263             : 
     264         291 :     if (HNEQ(ptr + RSK_TAG_LEN, block.hash_for_mm)) {
     265           1 :         FAIL(MM_HASH_MISMATCH);
     266             :     }
     267         290 : }
     268             : 
     269             : /*
     270             :  * Blockchain advance prologue: call once we have the first block's hash.
     271             :  */
     272          34 : static void bc_adv_prologue() {
     273          34 :     if (bc_st_updating.in_progress &&
     274          14 :         HNEQ(block.block_hash, bc_st_updating.next_expected_block)) {
     275           0 :         FAIL(CHAIN_MISMATCH);
     276             :     }
     277             : 
     278          34 :     if (!bc_st_updating.in_progress) {
     279          20 :         bc_st_updating.in_progress = true;
     280          20 :         HSTORE(bc_st_updating.newest_valid_block, block.block_hash);
     281             :     }
     282          34 : }
     283             : 
     284             : /*
     285             :  * Accumulate total blockchain difficulty. Only call this once you
     286             :  * know that the current block is valid. If there is enough difficulty
     287             :  * accumulated, record in the state that we found our new best block.
     288             :  * Also used to accumulate each brother's difficulty once its parsed.
     289             :  */
     290         292 : static void bc_adv_accum_diff() {
     291             :     // Nothing to do it we already have a best block
     292         292 :     if (bc_st_updating.found_best_block) {
     293         124 :         return;
     294             :     }
     295             : 
     296         168 :     LOG_BIGD_HEX("Total difficulty before = ",
     297             :                  aux_bc_st.total_difficulty,
     298             :                  BIGINT_LEN,
     299             :                  "\n");
     300             : 
     301             :     // Cap the block difficulty
     302         168 :     int cap_error = cap_block_difficulty(block.difficulty);
     303         168 :     if (cap_error) {
     304           0 :         LOG("Error while capping block difficulty\n");
     305           0 :         FAIL(BUFFER_OVERFLOW);
     306             :     }
     307             : 
     308             :     // Otherwise accumulate total difficulty
     309             :     DIGIT_T carry =
     310         168 :         accum_difficulty(block.difficulty, aux_bc_st.total_difficulty);
     311         168 :     if (carry) {
     312           0 :         FAIL(TOTAL_DIFF_OVERFLOW);
     313             :     }
     314             : 
     315         168 :     LOG_BIGD_HEX("Min required difficulty = ",
     316             :                  MIN_REQUIRED_DIFFICULTY,
     317             :                  BIGINT_LEN,
     318             :                  "\n");
     319         168 :     LOG_BIGD_HEX(
     320             :         "Total difficulty = ", aux_bc_st.total_difficulty, BIGINT_LEN, "\n");
     321         168 :     LOG("Comparison: %d\n",
     322             :         mpCompare_ct(
     323             :             aux_bc_st.total_difficulty, MIN_REQUIRED_DIFFICULTY, BIGINT_LEN));
     324             : 
     325             :     // Not enough difficulty yet: we are done
     326         168 :     if (mpCompare_ct(aux_bc_st.total_difficulty,
     327             :                      MIN_REQUIRED_DIFFICULTY,
     328             :                      BIGINT_LEN) < 0) {
     329         164 :         return;
     330             :     }
     331             : 
     332             :     // Enough difficulty accumulated? We found our best block!
     333           4 :     bc_st_updating.found_best_block = true;
     334           4 :     HSTORE(bc_st_updating.best_block, block.main_block_hash);
     335             : }
     336             : 
     337             : /*
     338             :  * State updates to perform when successfully advanced blockchain.
     339             :  */
     340           3 : static void bc_adv_success() {
     341           3 :     NVM_WRITE(N_bc_state.best_block, bc_st_updating.best_block, HASH_SIZE);
     342           3 :     NVM_WRITE(N_bc_state.newest_valid_block,
     343             :               bc_st_updating.newest_valid_block,
     344             :               HASH_SIZE);
     345           3 :     RESET_BC_STATE();
     346           3 : }
     347             : 
     348             : /*
     349             :  * State updates to perform on partial sucess.
     350             :  */
     351          24 : static void bc_adv_partial_success() {
     352          24 :     HSTORE(bc_st_updating.next_expected_block, aux_bc_st.prev_parent_hash);
     353          24 :     SAFE_MEMMOVE(bc_st_updating.total_difficulty,
     354             :                  sizeof(bc_st_updating.total_difficulty),
     355             :                  MEMMOVE_ZERO_OFFSET,
     356             :                  aux_bc_st.total_difficulty,
     357             :                  sizeof(aux_bc_st.total_difficulty),
     358             :                  MEMMOVE_ZERO_OFFSET,
     359             :                  sizeof(aux_bc_st.total_difficulty),
     360             :                  FAIL(BUFFER_OVERFLOW));
     361          24 : }
     362             : 
     363             : /*
     364             :  * We have received the whole BTC merge mining header. We can:
     365             :  *  - Perform advance blockchain validations
     366             :  *  - Check that merge mining header matches block's difficulty
     367             :  *  - Finish block.hash_for_mm computation
     368             :  */
     369         294 : static void bc_mm_header_received() {
     370         294 :     if (PROCESSING_BLOCK()) {
     371             :         // First block: perform blockchain advance prologue
     372             :         // Otherwise: verify block chains to parent
     373         275 :         if (curr_block == 0) {
     374          34 :             bc_adv_prologue();
     375         241 :         } else if (HNEQ(aux_bc_st.prev_parent_hash, block.block_hash)) {
     376           0 :             LOG_HEX("PAR", aux_bc_st.prev_parent_hash, HASH_LENGTH);
     377           0 :             LOG_HEX("BLK", block.block_hash, HASH_LENGTH);
     378           0 :             FAIL(CHAIN_MISMATCH);
     379             :         }
     380             :         // Store parent hash to validate chaining for next block
     381             :         // (next block hash must match this block's parent hash)
     382         275 :         HSTORE(aux_bc_st.prev_parent_hash, block.parent_hash);
     383             : 
     384             :         // Store block hash to validate against brothers
     385             :         // and use after we finished processing this block
     386         275 :         HSTORE(block.main_block_hash, block.block_hash);
     387             : 
     388             :         // If we already validated the current block, signal that.
     389         275 :         if (HEQ(block.block_hash, N_bc_state.newest_valid_block)) {
     390           1 :             bc_st_updating.already_validated = true;
     391             :         }
     392             :     }
     393             : 
     394             :     // If block is known to be valid, set HEADER_VALID flag.
     395             :     // Otherwise perform validations enabled by having the mm header.
     396             :     // This optimization is not valid for brothers, only for blocks
     397             :     // on the chain given that it depends on chaining.
     398         294 :     if (PROCESSING_BLOCK() && bc_st_updating.already_validated) {
     399           2 :         SET_FLAG(block.flags, HEADER_VALID);
     400             :     } else {
     401             :         // Check difficulty
     402         292 :         diff_result r = check_difficulty(block.difficulty, block.mm_hdr_hash);
     403         292 :         if (r == DIFF_ZERO) {
     404           0 :             FAIL(BLOCK_DIFF_INVALID);
     405             :         }
     406         292 :         if (r == DIFF_MISMATCH) {
     407           1 :             FAIL(BTC_DIFF_MISMATCH);
     408             :         }
     409             : 
     410             :         // Finish hash for merge mining computation
     411         291 :         patch_hash_for_mm(HAS_FLAG(block.flags, HAS_UMM_ROOT));
     412             :     }
     413         293 : }
     414             : 
     415             : // -----------------------------------------------------------------------
     416             : // RLP parser callbacks
     417             : // -----------------------------------------------------------------------
     418             : 
     419             : // Valid blocks have the form [field_1, ..., field_n]. That is, a single
     420             : // top level list with bytearrays inside. Anything not having this shape
     421             : // is an invalid block.
     422             : 
     423             : /*
     424             :  * Block starts: nesting level must not exceed one.
     425             :  *
     426             :  * @arg[in] size: size of list payload in bytes
     427             :  */
     428         299 : static void list_start(const uint16_t size) {
     429         299 :     ++block.depth;
     430         299 :     if (block.depth != 1) {
     431           0 :         FAIL(RLP_INVALID);
     432             :     }
     433             : 
     434         299 :     block.size = size;
     435         299 :     block.recv = 0;
     436         299 : }
     437             : 
     438             : /*
     439             :  * List finishes: we must be closing the top level list, and received
     440             :  * bytes must match block size. In addition, we must have seen the
     441             :  * expected number of fields.
     442             :  */
     443         291 : static void list_end() {
     444         291 :     --block.depth;
     445         291 :     if (block.depth != 0 || block.size != block.recv) {
     446           0 :         FAIL(RLP_INVALID);
     447             :     }
     448             : 
     449         291 :     if (block.field != F_COINBASE_TXN) {
     450           0 :         FAIL(BLOCK_TOO_SHORT);
     451             :     }
     452         291 : }
     453             : 
     454             : /*
     455             :  * Block field starts: field must be inside top level list.
     456             :  *
     457             :  * @arg[in] field size in bytes
     458             :  */
     459        5955 : static void str_start(const uint16_t size) {
     460        5955 :     if (block.depth != 1) {
     461           0 :         FAIL(RLP_INVALID);
     462             :     }
     463             : 
     464        5955 :     block.wa_off = 0;
     465             : 
     466             :     // NOTE: This will return 1 even for a single byte "bad" string
     467             :     // where str[0] <= 0x7f (it should return 0). That's ok because
     468             :     // in that case we actually received a single byte. But we must
     469             :     // not add 1 to block.recv in str_chunk.
     470        5955 :     block.recv += guess_rlp_str_prefix_size(size);
     471             : 
     472             :     // Signal bad string
     473        5955 :     if (size == 1) {
     474         627 :         SET_FLAG(block.flags, BAD_STR);
     475             :     }
     476             : 
     477        5955 :     ++block.field;
     478             : 
     479        5955 :     if (block.field == F_PARENT_HASH && size != HASH_SIZE) {
     480           0 :         FAIL(PARENT_HASH_INVALID);
     481             :     }
     482             : 
     483             :     // UMM root field and papyrus active?
     484             :     // Validate length of UMM root (either zero or expected length) and
     485             :     // mark whether it's present so that we save it when we get the data.
     486        5955 :     if (block.network_upgrade >= NU_PAPYRUS && block.field == F_UMM_ROOT) {
     487         298 :         if (size != 0 && size != UMM_ROOT_SIZE) {
     488           0 :             FAIL(UMM_ROOT_INVALID);
     489             :         }
     490         298 :         if (size != 0) {
     491           0 :             SET_FLAG(block.flags, HAS_UMM_ROOT);
     492             :         }
     493             :     }
     494             : 
     495        5955 :     if (SHOULD_COMPUTE_BLOCK_HASH) {
     496             :         // The starting str contributes to the block (and possibly mm) hash
     497             :         //   - Good string: we can hash RLP prefix right now.
     498             :         //   - Bad string: RLP prefix will depend on str contents.
     499        5365 :         if (!HAS_FLAG(block.flags, BAD_STR)) {
     500        4738 :             mm_hash_good_rlp_str_prefix(&block.block_ctx, size);
     501        4738 :             WHEN_MM(mm_hash_good_rlp_str_prefix(&block.mm_ctx, size));
     502             :         }
     503             :     }
     504             : 
     505        5955 :     if (block.field == F_MM_HEADER && size != BTC_HEADER_SIZE) {
     506           0 :         FAIL(BTC_HEADER_INVALID);
     507             :     }
     508             : 
     509             :     // Prepare for the processing of the merkle proof
     510             :     // if we don't already know this block is valid
     511             :     // or we are processing a brother
     512        5955 :     if (block.field == F_MERKLE_PROOF && !BLOCK_ALREADY_VALID()) {
     513         296 :         if (size % HASH_SIZE != 0) {
     514           0 :             FAIL(MERKLE_PROOF_INVALID);
     515             :         }
     516         296 :         block.merkle_off = 0;
     517             : 
     518             :         // In preparation for the reduction of the merkle proof to the
     519             :         // merkle root, copy the coinbase transaction hash to the
     520             :         // reduction area
     521         296 :         SAFE_MEMMOVE(block.merkle_proof_left,
     522             :                      sizeof(block.merkle_proof_left),
     523             :                      MEMMOVE_ZERO_OFFSET,
     524             :                      block.cb_txn_hash,
     525             :                      sizeof(block.cb_txn_hash),
     526             :                      MEMMOVE_ZERO_OFFSET,
     527             :                      sizeof(block.cb_txn_hash),
     528             :                      FAIL(MERKLE_PROOF_INVALID));
     529             :     }
     530             : 
     531        5955 :     if (block.field == F_COINBASE_TXN) {
     532             :         // size <= CB_MIDSTATE_DATA: txn has no tail
     533         294 :         if (size <= CB_MIDSTATE_DATA || size > MAX_CB_TXN_SIZE) {
     534           0 :             FAIL(BTC_CB_TXN_INVALID);
     535             :         }
     536         294 :         block.cb_off = 0;
     537             :     }
     538        5955 : }
     539             : 
     540             : /*
     541             :  * Block chunk arrived.
     542             :  *
     543             :  * @arg[in] chunk  pointer to arrived chunk
     544             :  * @arg[in] size   size in bytes of arrived chunk
     545             :  */
     546        8497 : static void str_chunk(const uint8_t* chunk, const size_t size) {
     547             :     // Count chunk length as received bytes only if:
     548             :     //  - Chunk doesn't belong to a bad string, or
     549             :     //  - Bad string actually has RLP prefix
     550        8497 :     if (!HAS_FLAG(block.flags, BAD_STR) || HAS_RLP_PREFIX(chunk[0])) {
     551        7870 :         block.recv += size;
     552             :     }
     553             : 
     554        8497 :     if (block.field == F_PARENT_HASH) {
     555         598 :         WA_STORE(chunk, size);
     556             :     }
     557             : 
     558        8497 :     if (block.field == F_BLOCK_DIFF) {
     559         596 :         WA_STORE(chunk, size);
     560             :     }
     561             : 
     562        8497 :     if (block.field == F_BLOCK_NUM) {
     563         596 :         WA_STORE(chunk, size);
     564             :     }
     565             : 
     566        8497 :     if (block.field == F_MM_HEADER) {
     567        1192 :         WA_STORE(chunk, size);
     568             :     }
     569             : 
     570        8497 :     if (block.field == F_UMM_ROOT && HAS_FLAG(block.flags, HAS_UMM_ROOT)) {
     571           0 :         WA_STORE(chunk, size);
     572             :     }
     573             : 
     574        8497 :     if (block.field == F_MERKLE_PROOF && !BLOCK_ALREADY_VALID()) {
     575         315 :         process_merkle_proof(chunk, size);
     576             :     }
     577             : 
     578        8497 :     if (block.field == F_COINBASE_TXN) {
     579         732 :         store_cb_txn_bytes(chunk, size);
     580             :     }
     581             : 
     582        8497 :     if (SHOULD_COMPUTE_BLOCK_HASH) {
     583             :         // Chunk corresponds to a bad str, so we couldn't hash its RLP prefix
     584             :         // until now, when str contents are available. Hash prefix now.
     585        7450 :         if (HAS_FLAG(block.flags, BAD_STR)) {
     586         627 :             mm_hash_bad_rlp_str_prefix(&block.block_ctx, chunk);
     587         627 :             WHEN_MM(mm_hash_bad_rlp_str_prefix(&block.mm_ctx, chunk));
     588             :         }
     589             : 
     590             :         // And then hash the str chunk itself
     591        7450 :         KECCAK_UPDATE(&block.block_ctx, chunk, size);
     592        7450 :         WHEN_MM(KECCAK_UPDATE(&block.mm_ctx, chunk, size));
     593             :     }
     594        8497 : }
     595             : 
     596             : /* Current block chunk finished */
     597        5951 : static void str_end() {
     598        5951 :     if (block.field == F_PARENT_HASH) {
     599         299 :         HSTORE(block.parent_hash, block.wa_buf);
     600             : 
     601             :         // If we're processing a brother, then its parent hash
     602             :         // must match the main block's parent hash
     603             :         // (i.e., validate they are actually brothers)
     604         299 :         if (!PROCESSING_BLOCK() &&
     605          23 :             HNEQ(aux_bc_st.prev_parent_hash, block.parent_hash)) {
     606           1 :             FAIL(BROTHER_PARENT_MISMATCH);
     607             :         }
     608             :     }
     609             : 
     610        5950 :     if (block.field == F_BLOCK_DIFF) {
     611         298 :         if (block.wa_off > MAX_DIFFICULTY_SIZE) {
     612           0 :             FAIL(BLOCK_DIFF_INVALID);
     613             :         }
     614         298 :         store_difficulty(block.wa_buf, block.wa_off, block.difficulty);
     615             :     }
     616             : 
     617             :     // Block number in wa_buf:
     618             :     // Store for computing mm_hash and determine network upgrade
     619        5950 :     if (block.field == F_BLOCK_NUM) {
     620         298 :         if (block.wa_off > sizeof(block.number)) {
     621           0 :             FAIL(BLOCK_NUM_INVALID);
     622             :         }
     623         895 :         VAR_BIGENDIAN_FROM(block.wa_buf, block.number, block.wa_off);
     624         298 :         SET_NETWORK_UPGRADE(block.number, &block.network_upgrade);
     625         298 :         if (block.network_upgrade == NU_ANCIENT) {
     626           0 :             FAIL(BLOCK_TOO_OLD);
     627             :         }
     628             :     }
     629             : 
     630        5950 :     if (block.field == MM_HASH_LAST_FIELD) {
     631         298 :         if (block.recv != block.mm_rlp_len) {
     632           0 :             FAIL(MM_RLP_LEN_MISMATCH);
     633             :         }
     634             : 
     635             :         // If there's a umm_root, we now have it in block.wa_buf. Store it.
     636         298 :         if (HAS_FLAG(block.flags, HAS_UMM_ROOT)) {
     637           0 :             if (block.wa_off != UMM_ROOT_SIZE) {
     638           0 :                 FAIL(UMM_ROOT_INVALID);
     639             :             }
     640             : 
     641           0 :             SAFE_MEMMOVE(block.umm_root,
     642             :                          sizeof(block.umm_root),
     643             :                          MEMMOVE_ZERO_OFFSET,
     644             :                          block.wa_buf,
     645             :                          sizeof(block.wa_buf),
     646             :                          MEMMOVE_ZERO_OFFSET,
     647             :                          block.wa_off,
     648             :                          FAIL(UMM_ROOT_INVALID));
     649             :         }
     650             :     }
     651             : 
     652             :     // We have the complete merge mining header in wa_buf. We must:
     653             :     //   - Finalize block and hash_for_mm computation
     654             :     //   - Store the Merkle root in block.merkle_root
     655             :     //   - Compute btc mm hdr hash
     656             :     //   - Signal the merge mining header was received
     657        5950 :     if (block.field == F_MM_HEADER) {
     658         298 :         KECCAK_FINAL(&block.block_ctx, block.block_hash);
     659             : 
     660             :         // Check that the brother is not the same as the main block
     661         298 :         if (!PROCESSING_BLOCK() &&
     662          22 :             HEQ(block.main_block_hash, block.block_hash)) {
     663           1 :             FAIL(BROTHER_SAME_AS_BLOCK);
     664             :         }
     665             : 
     666             :         // Check that the brothers are sent in ascending order
     667             :         // wrt their block hash
     668         297 :         if (!PROCESSING_BLOCK() &&
     669          21 :             !HLT(block.prev_brother_hash, block.block_hash)) {
     670           1 :             FAIL(BROTHER_ORDER_INVALID);
     671             :         }
     672             : 
     673         296 :         KECCAK_FINAL(&block.mm_ctx, block.hash_for_mm);
     674         296 :         HSTORE(block.merkle_root, block.wa_buf + MERKLE_ROOT_OFFSET);
     675             : 
     676             :         // If we haven't reached a valid block while descending the chain,
     677             :         // we need to compute the btc mm header hash to then do the difficulty
     678             :         // validation. We do this here because if within this part of the RLP
     679             :         // processing there's also a portion of the mm merkle proof, then the
     680             :         // wa_buf will be overwritten.
     681             :         // We also always do this in case we are processing a brother
     682         296 :         if (!PROCESSING_BLOCK() || !bc_st_updating.already_validated) {
     683             :             // Compute merge mining header hash
     684         295 :             double_sha256_rev(
     685             :                 &block.ctx, block.wa_buf, BTC_HEADER_SIZE, block.mm_hdr_hash);
     686             :         }
     687             : 
     688         296 :         SET_FLAG(block.flags, MM_HEADER_RECV);
     689             :     }
     690             : 
     691             :     // We have finished processing the merkle proof.
     692             :     // Validate the reduction against the stored coinbase
     693             :     // transaction hash.
     694             :     // All this given that the block hasn't been marked as valid and is not a
     695             :     // brother.
     696        5948 :     if (block.field == F_MERKLE_PROOF && !BLOCK_ALREADY_VALID()) {
     697         295 :         validate_merkle_proof();
     698             :     }
     699             : 
     700        5947 :     if (block.field == F_COINBASE_TXN) {
     701         291 :         SET_FLAG(block.flags, CB_TXN_RECV);
     702             :     }
     703             : 
     704             :     // Done with this chunk, clear BAD_STR flag if set
     705        5947 :     if (HAS_FLAG(block.flags, BAD_STR)) {
     706         627 :         CLR_FLAG(block.flags, BAD_STR);
     707             :     }
     708        5947 : }
     709             : 
     710             : static const rlp_callbacks_t callbacks = {
     711             :     str_start,
     712             :     str_chunk,
     713             :     str_end,
     714             :     list_start,
     715             :     list_end,
     716             : };
     717             : 
     718         268 : static unsigned int handle_end_of_block() {
     719             :     // Successfully advanced the blockchain: leave with success
     720         268 :     if (bc_st_updating.found_best_block &&
     721         128 :         HEQ(N_bc_state.best_block, aux_bc_st.prev_parent_hash)) {
     722           3 :         bc_adv_success();
     723           3 :         expected_state = OP_ADVANCE_INIT;
     724           3 :         SET_APDU_OP(OP_ADVANCE_SUCCESS);
     725           3 :         return TX_NO_DATA();
     726             :     }
     727             : 
     728             :     // Current block valid, yet no success.
     729             :     // If no more blocks available, leave with partial success
     730         265 :     ++curr_block;
     731         265 :     if (curr_block == expected_blocks) {
     732          24 :         bc_adv_partial_success();
     733          24 :         expected_state = OP_ADVANCE_INIT;
     734          24 :         SET_APDU_OP(OP_ADVANCE_PARTIAL);
     735          24 :         return TX_NO_DATA();
     736             :     }
     737             : 
     738             :     // Current block valid, yet no success.
     739             :     // More blocks available, ask for them and continue.
     740         241 :     expected_state = OP_ADVANCE_HEADER_META;
     741         241 :     SET_APDU_OP(OP_ADVANCE_HEADER_META);
     742         241 :     return TX_NO_DATA();
     743             : }
     744             : 
     745             : // -----------------------------------------------------------------------
     746             : // Blockchain advance protocol implementation
     747             : // -----------------------------------------------------------------------
     748             : 
     749             : /*
     750             :  * Initialize Blockchain advance protocol state.
     751             :  */
     752         270 : void bc_init_advance() {
     753         270 :     expected_blocks = 0;
     754         270 :     curr_block = 0;
     755         270 :     expected_state = OP_ADVANCE_INIT;
     756         270 : }
     757             : 
     758             : /*
     759             :  * Advance blockchain state.
     760             :  *
     761             :  * @arg[in] rx number of received bytes from the Host
     762             :  * @ret     number of transmited bytes to the host
     763             :  */
     764        3324 : unsigned int bc_advance(volatile unsigned int rx) {
     765        3324 :     uint8_t op = APDU_OP();
     766             : 
     767             :     // Check we are getting expected OP
     768        3324 :     if (op != OP_ADVANCE_INIT && op != expected_state) {
     769           0 :         FAIL(PROT_INVALID);
     770             :     }
     771             : 
     772             :     // Check we are getting the expected amount of data
     773        3324 :     if (op == OP_ADVANCE_INIT && APDU_DATA_SIZE(rx) != sizeof(uint32_t)) {
     774           0 :         FAIL(PROT_INVALID);
     775             :     }
     776        3324 :     if ((op == OP_ADVANCE_HEADER_META || op == OP_ADVANCE_BROTHER_META) &&
     777         299 :         APDU_DATA_SIZE(rx) !=
     778             :             (sizeof(block.mm_rlp_len) + sizeof(block.cb_txn_hash))) {
     779           0 :         FAIL(PROT_INVALID);
     780             :     }
     781        3324 :     if (op == OP_ADVANCE_BROTHER_LIST_META &&
     782         148 :         APDU_DATA_SIZE(rx) != sizeof(block.brother_count)) {
     783           0 :         FAIL(PROT_INVALID);
     784             :     }
     785        3324 :     if (op == OP_ADVANCE_HEADER_CHUNK || op == OP_ADVANCE_BROTHER_CHUNK) {
     786         299 :         uint16_t expected_txlen =
     787        2842 :             block.size > 0 ? MIN(block.size - block.recv, MAX_CHUNK_SIZE)
     788             :                            : MAX_CHUNK_SIZE;
     789        2842 :         if (APDU_DATA_SIZE(rx) != expected_txlen) {
     790           0 :             FAIL(PROT_INVALID);
     791             :         }
     792             :     }
     793             : 
     794             :     // -------------------------------------------------------------------
     795             :     // OP_INIT
     796             :     // -------------------------------------------------------------------
     797        3324 :     if (op == OP_ADVANCE_INIT) {
     798          35 :         expected_state = OP_ADVANCE_HEADER_META;
     799             : 
     800          35 :         memset(aux_bc_st.prev_parent_hash, 0, HASH_SIZE);
     801          35 :         SAFE_MEMMOVE(aux_bc_st.total_difficulty,
     802             :                      sizeof(aux_bc_st.total_difficulty),
     803             :                      MEMMOVE_ZERO_OFFSET,
     804             :                      bc_st_updating.total_difficulty,
     805             :                      sizeof(bc_st_updating.total_difficulty),
     806             :                      MEMMOVE_ZERO_OFFSET,
     807             :                      sizeof(aux_bc_st.total_difficulty),
     808             :                      FAIL(PROT_INVALID));
     809             : 
     810          35 :         curr_block = 0;
     811         175 :         BIGENDIAN_FROM(APDU_DATA_PTR, expected_blocks);
     812          35 :         if (expected_blocks == 0) {
     813           0 :             FAIL(PROT_INVALID);
     814             :         }
     815             : 
     816          35 :         SET_APDU_OP(OP_ADVANCE_HEADER_META);
     817          35 :         return TX_NO_DATA();
     818             :     }
     819             : 
     820             :     // -------------------------------------------------------------------
     821             :     // OP_BROTHER_LIST_META
     822             :     // -------------------------------------------------------------------
     823        3289 :     if (op == OP_ADVANCE_BROTHER_LIST_META) {
     824             :         // Read the brother count
     825         296 :         BIGENDIAN_FROM(APDU_DATA_PTR, block.brother_count);
     826             : 
     827             :         // No brothers? => we've finished processing the current block
     828         148 :         if (!block.brother_count) {
     829         137 :             return handle_end_of_block();
     830             :         }
     831             : 
     832             :         // Validate brother count
     833          11 :         if (block.brother_count > MAX_BROTHERS) {
     834           1 :             LOG("Too many brothers");
     835           1 :             FAIL(BROTHERS_TOO_MANY);
     836             :         }
     837             : 
     838             :         // Set previous brother hash to initial value
     839          10 :         memset(block.prev_brother_hash, 0, sizeof(block.prev_brother_hash));
     840             : 
     841             :         // Now expecting the first brother's meta
     842          10 :         expected_state = OP_ADVANCE_BROTHER_META;
     843          10 :         SET_APDU_OP(OP_ADVANCE_BROTHER_META);
     844          10 :         return TX_NO_DATA();
     845             :     }
     846             : 
     847             :     // -------------------------------------------------------------------
     848             :     // OP_HEADER_META or OP_BROTHER_META
     849             :     // -------------------------------------------------------------------
     850        3141 :     if (op == OP_ADVANCE_HEADER_META || op == OP_ADVANCE_BROTHER_META) {
     851         299 :         if (PROCESSING_BLOCK()) {
     852         276 :             LOG("---- Block %u of %u\n", curr_block + 1, expected_blocks);
     853             : 
     854             :             // Clear block data
     855         276 :             memset(&block, 0, sizeof(block));
     856             :         } else { // Processing brother
     857          23 :             LOG("---- Brother (%u remaining)\n", block.brother_count);
     858             : 
     859             :             // Init brother header data
     860             :             // (keep some of the current block data)
     861          23 :             block.size = 0;
     862          23 :             block.recv = 0;
     863          23 :             block.field = 0;
     864          23 :             block.depth = 0;
     865          23 :             block.flags = 0;
     866             :         }
     867             :         // Init RLP parser
     868         299 :         rlp_start(&callbacks);
     869             : 
     870             :         // Network upgrade unknown until we get to the block number
     871         299 :         block.network_upgrade = NU_UNKNOWN;
     872             : 
     873             :         // Read the RLP payload length
     874         897 :         BIGENDIAN_FROM(APDU_DATA_PTR, block.mm_rlp_len);
     875             : 
     876             :         // Read the coinbase transaction hash
     877         598 :         SAFE_MEMMOVE(block.cb_txn_hash,
     878             :                      sizeof(block.cb_txn_hash),
     879             :                      MEMMOVE_ZERO_OFFSET,
     880             :                      APDU_DATA_PTR,
     881             :                      APDU_TOTAL_DATA_SIZE,
     882             :                      sizeof(block.mm_rlp_len),
     883             :                      sizeof(block.cb_txn_hash),
     884             :                      FAIL(PROT_INVALID));
     885             : 
     886             :         // Block hash computation: encode and hash payload len
     887             :         // (mm payload len + BTC header len)
     888             :         // Sanity check: make sure given mm_rlp_len plus BTC_HEADER_RLP_LEN does
     889             :         // not overflow
     890         299 :         if ((uint16_t)(block.mm_rlp_len + BTC_HEADER_RLP_LEN) <
     891             :             block.mm_rlp_len) {
     892           0 :             LOG("Given MM RLP list length too large, would overflow: %u\n",
     893           0 :                 block.mm_rlp_len);
     894           0 :             FAIL(PROT_INVALID);
     895             :         }
     896             : 
     897         299 :         KECCAK_INIT(&block.block_ctx);
     898         299 :         mm_hash_rlp_list_prefix(&block.block_ctx,
     899         299 :                                 block.mm_rlp_len + BTC_HEADER_RLP_LEN);
     900             : 
     901             :         // Merge mining hash computation: encode and hash mm_rlp_payload_len
     902         299 :         KECCAK_INIT(&block.mm_ctx);
     903         299 :         mm_hash_rlp_list_prefix(&block.mm_ctx, block.mm_rlp_len);
     904             : 
     905             :         // Now waiting for either block or brother data
     906         299 :         expected_state = PROCESSING_BLOCK() ? OP_ADVANCE_HEADER_CHUNK
     907             :                                             : OP_ADVANCE_BROTHER_CHUNK;
     908         299 :         SET_APDU_OP(expected_state);
     909         299 :         SET_APDU_TXLEN(MAX_CHUNK_SIZE);
     910             : 
     911         299 :         return TX_FOR_DATA_SIZE(1);
     912             :     }
     913             : 
     914             :     // -------------------------------------------------------------------
     915             :     // OP_HEADER_CHUNK || OP_BROTHER_CHUNK
     916             :     // -------------------------------------------------------------------
     917        2842 :     if (op == OP_ADVANCE_HEADER_CHUNK || op == OP_ADVANCE_BROTHER_CHUNK) {
     918        2842 :         if (rlp_consume(APDU_DATA_PTR, APDU_DATA_SIZE(rx)) < 0) {
     919           1 :             FAIL(RLP_INVALID);
     920             :         }
     921             : 
     922             :         // Check flags. Don't forget to reset them, or they
     923             :         // will activated on successive chunks!
     924             : 
     925             :         // Do validations after full BTC MM header is received
     926        2837 :         if (HAS_FLAG(block.flags, MM_HEADER_RECV)) {
     927         294 :             CLR_FLAG(block.flags, MM_HEADER_RECV);
     928         294 :             bc_mm_header_received();
     929             :         }
     930             : 
     931             :         // We have received the whole coinbase transaction.
     932             :         // Provided the block is not valid, we can:
     933             :         //  - Extract the cb_txn_hash
     934             :         //  - Validate it against the metadata given cb txn hash
     935             :         //  - Extract mm_hash and compare it with hash_for_mm
     936             :         //
     937             :         // This completes the current block's validation
     938        2836 :         if (HAS_FLAG(block.flags, CB_TXN_RECV) &&
     939         291 :             !HAS_FLAG(block.flags, HEADER_VALID)) {
     940         291 :             CLR_FLAG(block.flags, CB_TXN_RECV);
     941         291 :             compute_cb_txn_hash();
     942         291 :             validate_cb_txn_hash();
     943         291 :             validate_mm_hash();
     944         290 :             SET_FLAG(block.flags, HEADER_VALID);
     945             :         }
     946             : 
     947             :         // We know this block is valid
     948        2835 :         if (HAS_FLAG(block.flags, HEADER_VALID)) {
     949             :             // Since we have a valid block, we can accumulate difficulty.
     950             :             // This will set updating.found_best_block if we accumulated
     951             :             // enough difficulty.
     952         292 :             bc_adv_accum_diff();
     953             : 
     954         292 :             if (PROCESSING_BLOCK()) {
     955             :                 // If we haven't yet found our new best block,
     956             :                 // then request any brothers of this block
     957             :                 // in order to accumulate their difficulty too
     958         275 :                 if (!bc_st_updating.found_best_block) {
     959             :                     // Request brother list meta information
     960         148 :                     expected_state = OP_ADVANCE_BROTHER_LIST_META;
     961         148 :                     SET_APDU_OP(OP_ADVANCE_BROTHER_LIST_META);
     962         148 :                     SET_APDU_TXLEN(0);
     963             : 
     964         148 :                     return TX_NO_DATA();
     965             :                 }
     966             : 
     967         127 :                 return handle_end_of_block();
     968             :             } else { // Processing brother
     969             :                 // Have we finished parsing this brother?
     970          17 :                 if (block.size == block.recv) {
     971          17 :                     --block.brother_count;
     972             : 
     973             :                     // Have we finished parsing all the brothers?
     974          17 :                     if (!block.brother_count) {
     975           4 :                         return handle_end_of_block();
     976             :                     }
     977             : 
     978             :                     // Remember this brother's hash to
     979             :                     // compare against the next one
     980          13 :                     HSTORE(block.prev_brother_hash, block.block_hash);
     981             : 
     982             :                     // Request next brother meta
     983          13 :                     expected_state = OP_ADVANCE_BROTHER_META;
     984          13 :                     SET_APDU_OP(OP_ADVANCE_BROTHER_META);
     985          13 :                     SET_APDU_TXLEN(0);
     986          13 :                     return TX_NO_DATA();
     987             :                 }
     988             :             }
     989             :         }
     990             : 
     991             :         // Current block header not exhausted, ask for next chunk
     992        2543 :         expected_state = (op == OP_ADVANCE_HEADER_CHUNK)
     993             :                              ? OP_ADVANCE_HEADER_CHUNK
     994             :                              : OP_ADVANCE_BROTHER_CHUNK;
     995        2543 :         SET_APDU_OP(expected_state);
     996        2543 :         SET_APDU_TXLEN(MIN(block.size - block.recv, MAX_CHUNK_SIZE));
     997        2543 :         return TX_FOR_DATA_SIZE(1);
     998             :     }
     999             : 
    1000             :     // You shouldn't be here
    1001           0 :     FAIL(PROT_INVALID);
    1002             :     return 0;
    1003             : }
    1004             : 
    1005             : // -----------------------------------------------------------------------
    1006             : // Blockchain advance protocol precompiled parameters dumping
    1007             : // -----------------------------------------------------------------------
    1008             : 
    1009             : /*
    1010             :  * Dump minimum required difficulty to the specified APDU buffer data offset.
    1011             :  * This function will copy to the the buffer the bytes comprising the
    1012             :  * precompiled minimum cumulative difficulty in big endian order,
    1013             :  * including leading zeroes.
    1014             :  *
    1015             :  * @arg[in] offset APDU buffer data dump offset
    1016             :  * @ret number of bytes dumped to APDU buffer
    1017             :  */
    1018           1 : static uint8_t dump_min_req_difficulty(int offset) {
    1019             :     // Make sure the minimum required difficulty fits into the output buffer
    1020           1 :     if (APDU_TOTAL_DATA_SIZE_OUT < sizeof(MIN_REQUIRED_DIFFICULTY) + offset)
    1021           0 :         FAIL(BUFFER_OVERFLOW);
    1022           1 :     dump_bigint_be(APDU_DATA_PTR + offset, MIN_REQUIRED_DIFFICULTY, BIGINT_LEN);
    1023           1 :     return sizeof(MIN_REQUIRED_DIFFICULTY);
    1024             : }
    1025             : 
    1026             : /*
    1027             :  * Get advance blockchain protocol precompiled parameters.
    1028             :  * Dump format:
    1029             :  * Bytes 0-31: initial block hash
    1030             :  * Bytes 32-67: minimum required difficulty (big endian)
    1031             :  * Byte 68: network identifier (see bc_nu.h for the definition)
    1032             :  *
    1033             :  * @ret number of transmited bytes to the host
    1034             :  */
    1035           1 : unsigned int bc_advance_get_params() {
    1036           1 :     int dump_size = 0;
    1037           1 :     dump_size += bc_dump_initial_block_hash(0);
    1038           1 :     dump_size += dump_min_req_difficulty(dump_size);
    1039           1 :     APDU_DATA_PTR[dump_size++] = GET_NETWORK_IDENTIFIER();
    1040             : 
    1041           1 :     return TX_FOR_DATA_SIZE(dump_size);
    1042             : }

Generated by: LCOV version 1.16