LCOV - code coverage report
Current view: top level - powhsm/src - bc_ancestor.c (source / functions) Hit Total Coverage
Test: powHSM firmware Lines: 125 148 84.5 %
Date: 2025-07-10 13:49:13 Functions: 9 9 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 "memutil.h"
      34             : 
      35             : #include "bc_block.h"
      36             : #include "bc_blockutils.h"
      37             : #include "bc_ancestor.h"
      38             : #include "bc_err.h"
      39             : #include "bc_hash.h"
      40             : #include "bc_mm.h"
      41             : #include "bc_state.h"
      42             : #include "util.h"
      43             : 
      44             : // We'll be asking for block header chunks of at most this size
      45             : #define MAX_CHUNK_SIZE 80
      46             : 
      47             : // Number of blocks to validate
      48             : static uint32_t expected_blocks;
      49             : 
      50             : // Count of validated blocks
      51             : static uint32_t curr_block;
      52             : 
      53             : // Expected OP for next message
      54             : static uint8_t expected_state;
      55             : 
      56             : // -----------------------------------------------------------------------
      57             : // Update ancestor validations
      58             : // -----------------------------------------------------------------------
      59             : 
      60             : /*
      61             :  * Update ancestor prologue: call once we have the first block's hash.
      62             :  */
      63           3 : static void bc_upd_ancestor_prologue() {
      64           3 :     if (HNEQ(block.block_hash, N_bc_state.ancestor_block) &&
      65           2 :         HNEQ(block.block_hash, N_bc_state.best_block)) {
      66           0 :         FAIL(ANCESTOR_TIP_MISMATCH);
      67             :     }
      68           3 : }
      69             : 
      70             : /*
      71             :  * State updates to perform when successfully updated ancestor.
      72             :  */
      73           3 : static void bc_upd_ancestor_success() {
      74           3 :     NVM_WRITE(N_bc_state.ancestor_block, block.block_hash, HASH_SIZE);
      75           3 :     NVM_WRITE(N_bc_state.ancestor_receipt_root, block.receipt_root, HASH_SIZE);
      76           3 : }
      77             : 
      78             : // -----------------------------------------------------------------------
      79             : // RLP parser callbacks
      80             : // -----------------------------------------------------------------------
      81             : 
      82             : /*
      83             :  * Block starts: nesting level must not exceed one.
      84             :  *
      85             :  * @arg[in] size: size of list payload in bytes
      86             :  */
      87          93 : static void list_start(const uint16_t size) {
      88          93 :     ++block.depth;
      89          93 :     if (block.depth != 1) {
      90           0 :         FAIL(RLP_INVALID);
      91             :     }
      92             : 
      93          93 :     block.size = size;
      94          93 :     block.recv = 0;
      95          93 : }
      96             : 
      97             : /*
      98             :  * List finishes: we must be closing the top level list, and received
      99             :  * bytes must match block size. In addition, we must have seen the
     100             :  * expected number of fields.
     101             :  */
     102          93 : static void list_end() {
     103          93 :     --block.depth;
     104          93 :     if (block.depth != 0 || block.size != block.recv) {
     105           0 :         FAIL(RLP_INVALID);
     106             :     }
     107             : 
     108             :     // Block can end at the BTC mm header or anywhere past it
     109          93 :     if (block.field < F_MM_HEADER) {
     110           0 :         FAIL(BLOCK_TOO_SHORT);
     111             :     }
     112          93 : }
     113             : 
     114             : /*
     115             :  * Block field starts: field must be inside top level list.
     116             :  *
     117             :  * @arg[in] field size in bytes
     118             :  */
     119        1674 : static void str_start(const uint16_t size) {
     120        1674 :     if (block.depth != 1) {
     121           0 :         FAIL(RLP_INVALID);
     122             :     }
     123             : 
     124        1674 :     block.wa_off = 0;
     125             : 
     126             :     // NOTE: This will return 1 even for a single byte "bad" string
     127             :     // where str[0] <= 0x7f (it should return 0). That's ok because
     128             :     // in that case we actually received a single byte. But we must
     129             :     // not add 1 to block.recv in str_chunk.
     130        1674 :     block.recv += guess_rlp_str_prefix_size(size);
     131             : 
     132             :     // Signal bad string
     133        1674 :     if (size == 1) {
     134         190 :         SET_FLAG(block.flags, BAD_STR);
     135             :     }
     136             : 
     137        1674 :     ++block.field;
     138             : 
     139        1674 :     if (block.field == F_PARENT_HASH && size != HASH_SIZE) {
     140           0 :         FAIL(PARENT_HASH_INVALID);
     141             :     }
     142             : 
     143        1674 :     if (block.field == F_RECEIPT_ROOT && size != HASH_SIZE) {
     144           0 :         FAIL(RECEIPT_ROOT_INVALID);
     145             :     }
     146             : 
     147        1674 :     if (SHOULD_COMPUTE_BLOCK_HASH) {
     148             :         // The starting str contributes to the block (and possibly mm) hash
     149             :         //   - Good string: we can hash RLP prefix right now.
     150             :         //   - Bad string: RLP prefix will depend on str contents.
     151        1674 :         if (!HAS_FLAG(block.flags, BAD_STR)) {
     152        1484 :             mm_hash_good_rlp_str_prefix(&block.block_ctx, size);
     153             :         }
     154             :     }
     155             : 
     156        1674 :     if (block.field == F_MM_HEADER && size != BTC_HEADER_SIZE) {
     157           0 :         FAIL(BTC_HEADER_INVALID);
     158             :     }
     159        1674 : }
     160             : 
     161             : /*
     162             :  * Block chunk arrived.
     163             :  *
     164             :  * @arg[in] chunk  pointer to arrived chunk
     165             :  * @arg[in] size   size in bytes of arrived chunk
     166             :  */
     167        2325 : static void str_chunk(const uint8_t* chunk, const size_t size) {
     168             :     // Count chunk length as received bytes only if:
     169             :     //  - Chunk doesn't belong to a bad string, or
     170             :     //  - Bad string actually has RLP prefix
     171        2325 :     if (!HAS_FLAG(block.flags, BAD_STR) || HAS_RLP_PREFIX(chunk[0])) {
     172        2135 :         block.recv += size;
     173             :     }
     174             : 
     175        2325 :     if (block.field == F_PARENT_HASH) {
     176         186 :         WA_STORE(chunk, size);
     177             :     }
     178             : 
     179             :     // Store receipt only for last block
     180        2325 :     if (block.field == F_RECEIPT_ROOT && curr_block + 1 == expected_blocks) {
     181          12 :         WA_STORE(chunk, size);
     182             :     }
     183             : 
     184        2325 :     if (block.field == F_BLOCK_NUM) {
     185         186 :         WA_STORE(chunk, size);
     186             :     }
     187             : 
     188        2325 :     if (block.field == F_MM_HEADER) {
     189         372 :         WA_STORE(chunk, size);
     190             :     }
     191             : 
     192        2325 :     if (SHOULD_COMPUTE_BLOCK_HASH) {
     193             :         // Chunk corresponds to a bad str, so we couldn't hash its RLP prefix
     194             :         // until now, when str contents are available. Hash prefix now.
     195        2325 :         if (HAS_FLAG(block.flags, BAD_STR)) {
     196         190 :             mm_hash_bad_rlp_str_prefix(&block.block_ctx, chunk);
     197             :         }
     198             : 
     199             :         // And then hash the str chunk itself
     200        2325 :         KECCAK_UPDATE(&block.block_ctx, chunk, size);
     201             :     }
     202        2325 : }
     203             : 
     204             : /* Current block chunk finished */
     205        1674 : static void str_end() {
     206        1674 :     if (block.field == F_PARENT_HASH) {
     207          93 :         HSTORE(block.parent_hash, block.wa_buf);
     208             :     }
     209             : 
     210             :     // Store receipt root only for last block
     211        1674 :     if (block.field == F_RECEIPT_ROOT && curr_block + 1 == expected_blocks) {
     212           3 :         HSTORE(block.receipt_root, block.wa_buf);
     213             :     }
     214             : 
     215             :     // Block number in wa_buf:
     216             :     // Store for computing mm_hash and determine network upgrade
     217        1674 :     if (block.field == F_BLOCK_NUM) {
     218          93 :         if (block.wa_off > sizeof(block.number)) {
     219           0 :             FAIL(BLOCK_NUM_INVALID);
     220             :         }
     221         279 :         VAR_BIGENDIAN_FROM(block.wa_buf, block.number, block.wa_off);
     222          93 :         SET_NETWORK_UPGRADE(block.number, &block.network_upgrade);
     223          93 :         if (block.network_upgrade == NU_ANCIENT) {
     224           0 :             FAIL(BLOCK_TOO_OLD);
     225             :         }
     226             :     }
     227             : 
     228             :     // Verify that we got valid mm_rlp_len in OP_UPD_ANCESTOR_HEADER_META
     229        1674 :     if (block.field == MM_HASH_LAST_FIELD) {
     230          93 :         if (block.recv != block.mm_rlp_len) {
     231           0 :             FAIL(MM_RLP_LEN_MISMATCH);
     232             :         }
     233             :     }
     234             : 
     235             :     // We have the complete merge mining header in wa_buf. We must:
     236             :     //  - Finalize the block hash computation
     237             :     //  - Signal the merge mining header was received
     238        1674 :     if (block.field == F_MM_HEADER) {
     239          93 :         KECCAK_FINAL(&block.block_ctx, block.block_hash);
     240          93 :         SET_FLAG(block.flags, MM_HEADER_RECV);
     241             :     }
     242             : 
     243             :     // Done with this chunk, clear BAD_STR flag if set
     244        1674 :     if (HAS_FLAG(block.flags, BAD_STR)) {
     245         190 :         CLR_FLAG(block.flags, BAD_STR);
     246             :     }
     247        1674 : }
     248             : 
     249             : static const rlp_callbacks_t callbacks = {
     250             :     str_start,
     251             :     str_chunk,
     252             :     str_end,
     253             :     list_start,
     254             :     list_end,
     255             : };
     256             : 
     257             : // -----------------------------------------------------------------------
     258             : // Blockchain update ancestor implementation
     259             : // -----------------------------------------------------------------------
     260             : 
     261             : /*
     262             :  * Initialize Blockchain update ancestor protocol state.
     263             :  */
     264         270 : void bc_init_upd_ancestor() {
     265         270 :     expected_blocks = 0;
     266         270 :     curr_block = 0;
     267         270 :     expected_state = OP_UPD_ANCESTOR_INIT;
     268         270 : }
     269             : 
     270             : /*
     271             :  * Update blockchain ancestor.
     272             :  *
     273             :  * @arg[in] rx number of received bytes from the Host
     274             :  * @ret     number of transmited bytes to the host
     275             :  */
     276         840 : unsigned int bc_upd_ancestor(volatile unsigned int rx) {
     277         840 :     uint8_t op = APDU_OP();
     278             : 
     279             :     // Check we are getting expected OP
     280         840 :     if (op != OP_UPD_ANCESTOR_INIT && op != expected_state) {
     281           0 :         FAIL(PROT_INVALID);
     282             :     }
     283             : 
     284             :     // Check we are getting the expected amount of data
     285         840 :     if (op == OP_UPD_ANCESTOR_INIT && APDU_DATA_SIZE(rx) != sizeof(uint32_t)) {
     286           0 :         FAIL(PROT_INVALID);
     287             :     }
     288         840 :     if (op == OP_UPD_ANCESTOR_HEADER_META &&
     289          93 :         APDU_DATA_SIZE(rx) != sizeof(block.mm_rlp_len)) {
     290           0 :         FAIL(PROT_INVALID);
     291             :     }
     292         840 :     if (op == OP_UPD_ANCESTOR_HEADER_CHUNK) {
     293          93 :         uint16_t expected_txlen =
     294         744 :             block.size > 0 ? MIN(block.size - block.recv, MAX_CHUNK_SIZE)
     295             :                            : MAX_CHUNK_SIZE;
     296         744 :         if (APDU_DATA_SIZE(rx) != expected_txlen) {
     297           0 :             FAIL(PROT_INVALID);
     298             :         }
     299             :     }
     300             : 
     301             :     // -------------------------------------------------------------------
     302             :     // OP_INIT
     303             :     // -------------------------------------------------------------------
     304         840 :     if (op == OP_UPD_ANCESTOR_INIT) {
     305           3 :         expected_state = OP_UPD_ANCESTOR_HEADER_META;
     306             : 
     307           3 :         memset(aux_bc_st.prev_parent_hash, 0, HASH_SIZE);
     308             : 
     309           3 :         curr_block = 0;
     310          15 :         BIGENDIAN_FROM(APDU_DATA_PTR, expected_blocks);
     311           3 :         if (expected_blocks == 0) {
     312           0 :             FAIL(PROT_INVALID);
     313             :         }
     314             : 
     315           3 :         SET_APDU_OP(OP_UPD_ANCESTOR_HEADER_META);
     316           3 :         return TX_NO_DATA();
     317             :     }
     318             : 
     319             :     // -------------------------------------------------------------------
     320             :     // OP_HEADER_META
     321             :     // -------------------------------------------------------------------
     322         837 :     if (op == OP_UPD_ANCESTOR_HEADER_META) {
     323          93 :         LOG("---- Block %u of %u\n", curr_block + 1, expected_blocks);
     324             : 
     325             :         // Clear block data
     326          93 :         memset(&block, 0, sizeof(block));
     327          93 :         rlp_start(&callbacks);
     328             : 
     329             :         // Read the RLP payload length
     330         279 :         BIGENDIAN_FROM(APDU_DATA_PTR, block.mm_rlp_len);
     331             : 
     332             :         // Block hash computation: encode and hash payload len
     333             : 
     334             :         // Sanity check: make sure given mm_rlp_len plus BTC_HEADER_RLP_LEN does
     335             :         // not overflow
     336          93 :         if ((uint16_t)(block.mm_rlp_len + BTC_HEADER_RLP_LEN) <
     337             :             block.mm_rlp_len) {
     338           0 :             LOG("Given MM RLP list length too large, would overflow: %u\n",
     339           0 :                 block.mm_rlp_len);
     340           0 :             FAIL(PROT_INVALID);
     341             :         }
     342             : 
     343          93 :         KECCAK_INIT(&block.block_ctx);
     344          93 :         mm_hash_rlp_list_prefix(&block.block_ctx,
     345          93 :                                 block.mm_rlp_len + BTC_HEADER_RLP_LEN);
     346             : 
     347             :         // Now waiting for block data
     348          93 :         expected_state = OP_UPD_ANCESTOR_HEADER_CHUNK;
     349          93 :         SET_APDU_OP(OP_UPD_ANCESTOR_HEADER_CHUNK);
     350          93 :         SET_APDU_TXLEN(MAX_CHUNK_SIZE);
     351             : 
     352          93 :         return TX_FOR_DATA_SIZE(1);
     353             :     }
     354             : 
     355             :     // -------------------------------------------------------------------
     356             :     // OP_HEADER_CHUNK
     357             :     // -------------------------------------------------------------------
     358         744 :     if (op == OP_UPD_ANCESTOR_HEADER_CHUNK) {
     359         744 :         if (rlp_consume(APDU_DATA_PTR, APDU_DATA_SIZE(rx)) < 0) {
     360           0 :             FAIL(RLP_INVALID);
     361             :         }
     362             : 
     363             :         // We have received the whole BTC merge mining header.
     364             :         // So we have the block hash. Since we aren't validating
     365             :         // blocks here, after validating parent chaining we can
     366             :         // ask for next block or leave.
     367         744 :         if (HAS_FLAG(block.flags, MM_HEADER_RECV)) {
     368             : 
     369          93 :             LOG_HEX("Block hash", block.block_hash, HASH_SIZE);
     370          93 :             LOG_HEX("Parent hash", block.parent_hash, HASH_SIZE);
     371             : 
     372             :             // First block: perform update ancestor prologue
     373             :             // Otherwise: verify block chains to parent
     374          93 :             if (curr_block == 0) {
     375           3 :                 bc_upd_ancestor_prologue();
     376          90 :             } else if (HNEQ(aux_bc_st.prev_parent_hash, block.block_hash)) {
     377           0 :                 FAIL(CHAIN_MISMATCH);
     378             :             }
     379             :             // Store parent hash to validate chaining for next block
     380             :             // (next block hash must match this block's parent hash)
     381          93 :             HSTORE(aux_bc_st.prev_parent_hash, block.parent_hash);
     382             : 
     383          93 :             ++curr_block;
     384             : 
     385             :             // More blocks? Ask for next block metadata
     386          93 :             if (curr_block < expected_blocks) {
     387          90 :                 expected_state = OP_UPD_ANCESTOR_HEADER_META;
     388          90 :                 SET_APDU_OP(OP_UPD_ANCESTOR_HEADER_META);
     389          90 :                 return TX_NO_DATA();
     390             :             }
     391             : 
     392             :             // Blocks exhausted? Leave
     393           3 :             bc_upd_ancestor_success();
     394           3 :             expected_state = OP_UPD_ANCESTOR_INIT;
     395           3 :             SET_APDU_OP(OP_UPD_ANCESTOR_SUCCESS);
     396           3 :             return TX_NO_DATA();
     397             :         }
     398             : 
     399             :         // Current block not fully consumed? Ask for next chunk
     400         651 :         if (block.recv < block.size) {
     401         651 :             SET_APDU_OP(OP_UPD_ANCESTOR_HEADER_CHUNK);
     402         651 :             SET_APDU_TXLEN(MIN(block.size - block.recv, MAX_CHUNK_SIZE));
     403         651 :             return TX_FOR_DATA_SIZE(1);
     404             :         }
     405             : 
     406             :         // Reached end of block and haven't seen BTC mm header? That's bad!
     407           0 :         FAIL(RLP_INVALID);
     408             :     }
     409             : 
     410             :     // You shouldn't be here
     411           0 :     FAIL(PROT_INVALID);
     412             :     return 0;
     413             : }

Generated by: LCOV version 1.16