Coverage for comm/bitcoin.py: 75%

68 statements  

« 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. 

22 

23import bitcoin.core 

24 

25 

26def get_unsigned_tx(raw_tx_hex, hex=True): 

27 unsigned_bytes = _unsign_tx(raw_tx_hex).serialize() 

28 

29 if hex: 

30 return unsigned_bytes.hex() 

31 

32 return unsigned_bytes 

33 

34 

35def get_tx_hash(raw_tx_hex): 

36 return _deserialize_tx(raw_tx_hex).GetHash()[::-1].hex() 

37 

38 

39def get_tx_hash_for_unsigned_tx(raw_tx_hex): 

40 return _unsign_tx(raw_tx_hex).GetHash()[::-1].hex() 

41 

42 

43def get_tx_version(raw_tx_hex): 

44 tx = _deserialize_tx(raw_tx_hex) 

45 return tx.nVersion 

46 

47 

48def _unsign_tx(raw_tx_hex): 

49 # Given a p2sh-only inputs transaction (all of them corresponding 

50 # to multisig outputs), this method clears any 

51 # existent signatures in all the inputs and then computes 

52 # the hash of the resulting transaction 

53 

54 tx = _deserialize_tx(raw_tx_hex) 

55 

56 tx.vin = list(map(_clear_all_but_last_op_from_scriptsig, tx.vin)) 

57 

58 return tx 

59 

60 

61def _clear_all_but_last_op_from_scriptsig(txin): 

62 # Given a transaction input, this returns a copy 

63 # with its scriptSig replaced by a script with all 

64 # its operations as ZERO, excepting 

65 # the last operation, which is left untouched. 

66 

67 new_txin = bitcoin.core.CMutableTxIn.from_txin(txin) 

68 ops = list(new_txin.scriptSig) 

69 new_ops = ([0] * (len(ops) - 1)) + [ops[-1]] 

70 new_txin.scriptSig = bitcoin.core.CScript(new_ops) 

71 return new_txin 

72 

73 

74def get_signature_hash_for_p2sh_input(raw_tx_hex, input_index): 

75 # Given a raw BTC transaction and an input index, 

76 # this method computes the sighash corresponding to the given 

77 # input index 

78 # This assumes that the input at the given index has a push with the 

79 # redeem script as the last operation of its scriptSig 

80 

81 tx = _deserialize_tx(raw_tx_hex) 

82 

83 if input_index < 0 or input_index >= len(tx.vin): 

84 raise ValueError( 

85 "Asked for signature hash of input at index %d but only %d input(s) available" 

86 % (input_index, len(tx.vin)) 

87 ) 

88 

89 script_chunks = list(tx.vin[input_index].scriptSig.raw_iter()) 

90 if len(script_chunks) == 0: 

91 raise ValueError("No ScriptSig found for input index %d" % input_index) 

92 

93 last_chunk = script_chunks[-1] 

94 

95 last_chunk_operand = last_chunk[1] 

96 if last_chunk_operand is None: 

97 raise ValueError("Last script operation does not have an operand") 

98 

99 try: 

100 redeem_script = bitcoin.core.CScript(last_chunk_operand) 

101 except Exception: 

102 raise ValueError("Invalid redeem script: %s" % last_chunk_operand.hex()) 

103 

104 sighash = bitcoin.core.script.SignatureHash( 

105 redeem_script, tx, input_index, bitcoin.core.script.SIGHASH_ALL 

106 ) 

107 

108 return sighash.hex() 

109 

110 

111def get_signature_hash_for_p2sh_p2wsh_input(raw_tx_hex, input_index, 

112 witness_script_hex, amount): 

113 # Given a raw BTC transaction, an input index, a raw witness script and 

114 # an amount, this method computes the sighash corresponding to the given 

115 # input index for segwit v0 

116 

117 tx = _deserialize_tx(raw_tx_hex) 

118 

119 if input_index < 0 or input_index >= len(tx.vin): 

120 raise ValueError( 

121 "Asked for signature hash of input at index %d but only %d input(s) available" 

122 % (input_index, len(tx.vin)) 

123 ) 

124 

125 try: 

126 witness_script = bitcoin.core.CScript(bytes.fromhex(witness_script_hex)) 

127 except Exception: 

128 raise ValueError("Invalid witness script: %s" % witness_script_hex) 

129 

130 sighash = bitcoin.core.script.SignatureHash( 

131 witness_script, tx, input_index, bitcoin.core.script.SIGHASH_ALL, 

132 amount, bitcoin.core.script.SIGVERSION_WITNESS_V0 

133 ) 

134 

135 return sighash.hex() 

136 

137 

138def get_block_hash_as_int(raw_block_header_hex): 

139 block_header = _deserialize_block_header(raw_block_header_hex) 

140 return int.from_bytes(block_header.GetHash(), byteorder="little", signed=False) 

141 

142 

143def get_merkle_root(raw_block_header_hex): 

144 block_header = _deserialize_block_header(raw_block_header_hex) 

145 return block_header.hashMerkleRoot.hex() 

146 

147 

148def encode_varint(v): 

149 return bitcoin.core.VarIntSerializer.serialize(v).hex() 

150 

151 

152def _deserialize_block_header(raw_block_header_hex): 

153 try: 

154 return bitcoin.core.CBlockHeader.deserialize(bytes.fromhex(raw_block_header_hex)) 

155 except Exception as e: 

156 raise ValueError("Impossible to deserialize btc block header: %s" % str(e)) 

157 

158 

159def _deserialize_tx(raw_tx_hex): 

160 try: 

161 return bitcoin.core.CMutableTransaction.deserialize(bytes.fromhex(raw_tx_hex)) 

162 except Exception as e: 

163 raise ValueError("Impossible to deserialize btc transaction: %s" % str(e))