Coverage for ledger/block_utils.py: 78%
41 statements
« prev ^ index » next coverage.py v7.5.3, created at 2025-07-10 13:43 +0000
« prev ^ index » next coverage.py v7.5.3, created at 2025-07-10 13:43 +0000
1# The MIT License (MIT)
2#
3# Copyright (c) 2021 RSK Labs Ltd
4#
5# Permission is hereby granted, free of charge, to any person obtaining a copy of
6# this software and associated documentation files (the "Software"), to deal in
7# the Software without restriction, including without limitation the rights to
8# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
9# of the Software, and to permit persons to whom the Software is furnished to do
10# so, subject to the following conditions:
11#
12# The above copyright notice and this permission notice shall be included in all
13# copies or substantial portions of the Software.
14#
15# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21# SOFTWARE.
23import rlp
24from comm.utils import keccak_256
27# Compute the given block's top-level RLP encoding list payload length in bytes,
28# but excluding all merge mining fields.
29#
30# This is used by the ledger to compute the merge-mining
31# hash of a block to be validated without having to transmit
32# the block information twice (or having to save the block information
33# elsewhere -- which wouldn't be possible given the lack of memory).
34def rlp_mm_payload_size(raw_block_hex):
35 return rlp_first_element_list_payload_length(
36 remove_mm_fields_if_present(raw_block_hex, leave_btcblock=False, hex=False)
37 )
40# Exclude a given block's merge mining fields (either leaving or not the
41# BTC merge mining header, depending on parameter).
42# That is, parse the header, exclude the last one (none) or three (two) fields
43# (depending on which mm fields it originally includes) and re-encode it
44def remove_mm_fields_if_present(raw_block_hex, leave_btcblock=True, hex=True):
45 # Decode
46 try:
47 block = rlp.decode(bytes.fromhex(raw_block_hex))
48 except Exception as e:
49 raise ValueError(e)
50 # Sanity validation: list length (w/wo/umm_root and/or mm fields)
51 num_fields = len(block)
52 if num_fields not in [17, 18, 19, 20]:
53 raise ValueError(
54 "Block header must have 17, 18, 19 or 20 elements, got %d", num_fields
55 )
57 # Exclude merge mining fields and re-encode
58 if num_fields in [19, 20]:
59 block_without_mm_fields = block[:-2] if leave_btcblock else block[:-3]
60 else:
61 block_without_mm_fields = block if leave_btcblock else block[:-1]
63 block_without_mm_fields_rlp = rlp.encode(block_without_mm_fields)
65 if not hex:
66 return block_without_mm_fields_rlp
68 return block_without_mm_fields_rlp.hex()
71# Given a raw block hex, compute its block hash
72# and return it as a hex string
73def get_block_hash(raw_block_hex):
74 return keccak_256(remove_mm_fields_if_present(
75 raw_block_hex,
76 leave_btcblock=True,
77 hex=False)).hex()
80# Given a raw block hex,
81# extract the coinbase transaction (last field)
82def get_coinbase_txn(raw_block_hex):
83 # Decode
84 try:
85 block = rlp.decode(bytes.fromhex(raw_block_hex))
86 except Exception as e:
87 raise ValueError(e)
88 # Sanity validation: list length (w/wo/umm_root)
89 num_fields = len(block)
90 if num_fields not in [19, 20]:
91 raise ValueError("Block header must have 19 or 20 elements, got %d", num_fields)
92 return block[-1].hex()
95# Given a bytes object that represents an RLP-encoded list,
96# compute the top level list's payload length.
97def rlp_first_element_list_payload_length(bs):
98 # Infer length L of payload of the first element of the given RLP-encoded bytes
99 # First element *MUST* be a list. Validate that.
101 # Encoding corresponds to a list, so first byte b will be one of:
102 # 1. anything in the range 0xc0..0xf7: L = b - 0xc0
103 # 2. anything in the range 0xf8..0xff:
104 # N = b - 0xf7
105 # length follows, encoded as a bigendian integer of length N
106 b = bs[0]
107 if b >= 0xC0 and b <= 0xF7:
108 L = b - 0xC0
109 elif b >= 0xF8 and b <= 0xFF:
110 N = b - 0xF7
111 L = 0
112 for i in range(N):
113 L = (L << 8) | bs[1 + i]
114 else:
115 raise ValueError("Invalid RLP encoded list - got %s as first byte" % hex(b))
116 return L