Skip to content

Commit 36ef4bc

Browse files
authored
Merge pull request #127 from saikiran57/fix/identical_to_bitcoin_cores_signature
fix/identical_to_bitcoin_cores_signature
2 parents 6ef33bf + 22ebb98 commit 36ef4bc

3 files changed

Lines changed: 122 additions & 59 deletions

File tree

bitcoinutils/keys.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -470,7 +470,12 @@ def _sign_taproot_input(
470470
# it is the hash of the tx_digest and private key
471471
# TODO not identical to Bitcoin Core's signature, rand_aux
472472
# needs to change if we want identical signatures!
473-
rand_aux = hashlib.sha256(tx_digest + byte_key).digest()
473+
# if rand_aux is None:
474+
# rand_aux = hashlib.sha256(tx_digest + byte_key).digest()
475+
476+
# Currently bitcoin core is passing 32 bytes data as randam aux
477+
# https://github.com/bitcoin/bitcoin/blob/master/src/script/sign.cpp#L88
478+
rand_aux = bytes(32)
474479

475480
# use BIP-340 python's reference implementation for signing
476481
sig = schnorr_sign(tx_digest, byte_key, rand_aux)
@@ -534,7 +539,7 @@ def __init__(self, hex_str: str = None, message: str = None, signature: bytes =
534539
----------
535540
hex_str : str, optional
536541
the public key in hex string
537-
542+
538543
In case of generating public key from message and signature:-
539544
message : str, optional
540545
The original message that was signed
@@ -611,7 +616,7 @@ def __init__(self, hex_str: str = None, message: str = None, signature: bytes =
611616
elif message or signature:
612617
if not message:
613618
raise ValueError("Empty message provided for public key recovery.")
614-
619+
615620
if(len(signature) != 65):
616621
raise ValueError("Invalid signature length, must be exactly 65 bytes")
617622

@@ -620,9 +625,9 @@ def __init__(self, hex_str: str = None, message: str = None, signature: bytes =
620625
recovery_id = signature[0] - 31
621626
if not (0 <= recovery_id <= 3): # A valid recovery ID is between 0 and 3
622627
raise ValueError(f"Invalid recovery ID: expected 31-34, got {signature[0]}")
623-
628+
624629
signature = signature[1:] #Remove recovery id from signature
625-
630+
626631
# All bitcoin signatures include the magic prefix. It is just a string
627632
# added to the message to distinguish Bitcoin-specific messages.
628633
message_magic = add_magic_prefix(message)
@@ -687,7 +692,7 @@ def to_taproot_hex(
687692
tweak_int = calculate_tweak(self, scripts)
688693

689694
# keep x-only coordinate
690-
tweak_and_odd = tweak_taproot_pubkey(self.key.to_string(), tweak_int)
695+
tweak_and_odd = tweak_taproot_pubkey(self.key.to_string(), tweak_int)
691696
pubkey = tweak_and_odd[0][:32]
692697
is_odd = tweak_and_odd[1]
693698

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
# Copyright (C) 2018-2024 The python-bitcoin-utils developers
2+
#
3+
# This file is part of python-bitcoin-utils
4+
#
5+
# It is subject to the license terms in the LICENSE file found in the top-level
6+
# directory of this distribution.
7+
#
8+
# No part of python-bitcoin-utils, including this file, may be copied,
9+
# modified, propagated, or distributed except according to the terms contained
10+
# in the LICENSE file.
11+
12+
from binascii import unhexlify
13+
from bitcoinutils.setup import setup
14+
from bitcoinutils.utils import to_satoshis
15+
from bitcoinutils.transactions import Transaction, TxInput, TxOutput, TxWitnessInput
16+
from bitcoinutils.keys import P2pkhAddress, PrivateKey, P2trAddress
17+
from bitcoinutils.script import Script
18+
19+
20+
def main():
21+
try:
22+
# always remember to setup the network
23+
setup("testnet")
24+
25+
# the key that corresponds to the P2WPKH address
26+
#priv = PrivateKey("cPiTDzdpEfCwotToidTcNjh4WsdTZRLkYYScs1vHXT4Dzncwasdo")
27+
priv = PrivateKey(b=unhexlify("3fa9909fad4c01625adc47639626877073d94ead84e9630a66b731776cb66fcf"))
28+
print(f"internal priv key: {priv.to_bytes().hex()}")
29+
30+
pub = priv.get_public_key()
31+
32+
fromAddress = pub.get_taproot_address()
33+
print(fromAddress.to_string())
34+
35+
# UTXO of fromAddress
36+
txid = "bf0dc482d03fd2b57fb97cefd84de42f0bf370930856785e8ec9c019f1fb3aca"
37+
vout = 0
38+
39+
# all amounts are needed to sign a taproot input
40+
# (depending on sighash)
41+
first_amount = to_satoshis(0.00385004)
42+
amounts = [first_amount]
43+
44+
# all scriptPubKeys are needed to sign a taproot input
45+
# (depending on sighash) but always of the spend input
46+
first_script_pubkey = fromAddress.to_script_pub_key()
47+
48+
script_pubkey02 = Script(["OP_1", pub.to_taproot_hex()[0]])
49+
print(f"first_script_pubkey: {first_script_pubkey.to_hex()}")
50+
print(f"script_pubkey02: {script_pubkey02.to_hex()}")
51+
52+
# alternatively:
53+
# first_script_pubkey = Script(['OP_1', pub.to_taproot_hex()])
54+
55+
utxos_script_pubkeys = [first_script_pubkey]
56+
57+
# create transaction input from tx id of UTXO
58+
seq = "fdffffff"
59+
txin = TxInput(txid, vout, sequence=seq)
60+
61+
toAddress1 = P2trAddress("tb1pz38c8e54f6fkfpetmw8j7ft0ft54yeqy7cvzyckwxgjany8vv7vssddyfa")
62+
toAddress2 = P2trAddress("tb1pdtpkgjvkc77nuzssqh420ulgu3xm88m5vy59wrmgg5ctklkandgqmyh9ah")
63+
64+
# create transaction output
65+
txOut1 = TxOutput(to_satoshis(0.00364261), toAddress1.to_script_pub_key())
66+
txOut2 = TxOutput(to_satoshis(0.00005000 ), toAddress2.to_script_pub_key())
67+
# create transaction without change output - if at least a single input is
68+
# segwit we need to set has_segwit=True
69+
70+
locktime = "a8643400"
71+
tx = Transaction([txin], [txOut1, txOut2], has_segwit=True, locktime=locktime)
72+
73+
print("\nRaw transaction:\n" + tx.serialize())
74+
75+
print("\ntxid: " + tx.get_txid())
76+
print("\ntxwid: " + tx.get_wtxid())
77+
78+
# sign taproot input
79+
# to create the digest message to sign in taproot we need to
80+
# pass all the utxos' scriptPubKeys and their amounts
81+
sig = priv.sign_taproot_input(tx, 0, utxos_script_pubkeys, amounts)
82+
# print(sig)
83+
84+
tx.witnesses.append(TxWitnessInput([sig]))
85+
86+
# print raw signed transaction ready to be broadcasted
87+
print("\nRaw signed transaction:\n" + tx.serialize())
88+
89+
print("\nTxId:", tx.get_txid())
90+
print("\nTxwId:", tx.get_wtxid())
91+
92+
print("\nSize:", tx.get_size())
93+
print("\nvSize:", tx.get_vsize())
94+
except Exception as e:
95+
print('Something went wrong', e)
96+
finally:
97+
print('The try except is finished')
98+
99+
100+
101+
if __name__ == "__main__":
102+
main()

tests/test_p2tr_txs.py

Lines changed: 9 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,7 @@ def setUp(self):
5454
"347bb24adec37a88ac00000000"
5555
)
5656
self.raw_signed02 = (
57-
"02000000000101566e10098ddba743bedbe1e4b356377abb3ef106c6831e733863d5eea012"
58-
"647b0100000000ffffffff01a00f0000000000001976a9148e48a6c5108efac226d33018b5"
59-
"347bb24adec37a88ac01401107a2e9576bc4fc03c21d5752907b9043b99c03d7bb2f46a1e3"
60-
"450517e75d9bffaae5ee1e02b2b1ff48755fa94434b841770e472684f881fe6b184d6dcc9f"
61-
"7600000000"
57+
"02000000000101566e10098ddba743bedbe1e4b356377abb3ef106c6831e733863d5eea012647b0100000000ffffffff01a00f0000000000001976a9148e48a6c5108efac226d33018b5347bb24adec37a88ac01403065c743ec6261ce82abe9ea13f718702f9fb23f6d95ffe0eb59266d38416ad29b664370c8f6719a8e1354f38c58c7c6e965cec71b9b5b0f8c100d207a448bd100000000"
6258
)
6359

6460
# values for testing taproot unsigned/signed txs with privkeys that
@@ -77,41 +73,25 @@ def setUp(self):
7773
"347bb24adec37a88ac00000000"
7874
)
7975
self.raw_signed03 = (
80-
"02000000000101af13b1a8f3ed87c4a9424bd063f87d0ba3730031da90a3868a51a08bbdf8"
81-
"282a0100000000ffffffff01a00f0000000000001976a9148e48a6c5108efac226d33018b5"
82-
"347bb24adec37a88ac01409e42a9fe684abd801be742e558caeadc1a8d096f2f17660ba7b2"
83-
"64b3d1f14c7a0a3f96da1fbd413ea494562172b99c1a7c95e921299f686587578d7060b89d"
84-
"2100000000"
76+
"02000000000101af13b1a8f3ed87c4a9424bd063f87d0ba3730031da90a3868a51a08bbdf8282a0100000000ffffffff01a00f0000000000001976a9148e48a6c5108efac226d33018b5347bb24adec37a88ac0140e19f0031e545a607f5d9b3f2588cd39464be8cf845defdc15174f03d929ac8c96eef3fad46e1afc1262504fce884aa2ac520f4921c317d2b0779167f7ff33d6c00000000"
8577
)
8678

8779
# values for testing taproot signed tx with SINGLE
8880
# uses mostly values from 02 key above
8981
self.raw_signed_signle = (
90-
"02000000000101566e10098ddba743bedbe1e4b356377abb3ef106c6831e733863d5eea012"
91-
"647b0100000000ffffffff01a00f0000000000001976a9148e48a6c5108efac226d33018b5"
92-
"347bb24adec37a88ac0141a01ba79ead43b55bf732ccb75115f3f428decf128d482a2d4c1a"
93-
"dd6e2b160c0a2a1288bce076e75bc6d978030ce4b1a74f5602ae99601bad35c58418fe9333"
94-
"750300000000"
82+
"02000000000101566e10098ddba743bedbe1e4b356377abb3ef106c6831e733863d5eea012647b0100000000ffffffff01a00f0000000000001976a9148e48a6c5108efac226d33018b5347bb24adec37a88ac01414ace20f539e3bcf3de7c14655fd2b0076e08de524b750039eeea286a69b858888258820f0677240768418373c96d38f27b1a904b4dbb092e2483c2a49471f4980300000000"
9583
)
9684

9785
# values for testing taproot signed tx with NONE
9886
# uses mostly values from 02 key above
9987
self.raw_signed_none = (
100-
"02000000000101566e10098ddba743bedbe1e4b356377abb3ef106c6831e733863d5eea012"
101-
"647b0100000000ffffffff01a00f0000000000001976a9148e48a6c5108efac226d33018b5"
102-
"347bb24adec37a88ac0141fd01234cf9569112f20ed54dad777560d66b3611dcd6076bc980"
103-
"96e5d354e01556ee52a8dc35dac22b398978f2e05c9586bafe81d9d5ff8f8fa966a9e458c4"
104-
"410200000000"
88+
"02000000000101566e10098ddba743bedbe1e4b356377abb3ef106c6831e733863d5eea012647b0100000000ffffffff01a00f0000000000001976a9148e48a6c5108efac226d33018b5347bb24adec37a88ac01419ec0b0d8aec0fbf9bfd64bdd3cd4c854434bdaff6ad1a40ff21dfd1e125cb3008d0e490fc773c238654253ed4b4af55f857e67789c91e791d28a9ea3989f20290200000000"
10589
)
10690

10791
# values for testing taproot signed tx with ALL|ANYONECANPAY
10892
# uses mostly values from 02 key above
10993
self.raw_signed_all_anyonecanpay = (
110-
"02000000000101566e10098ddba743bedbe1e4b356377abb3ef106c6831e733863d5eea012"
111-
"647b0100000000ffffffff01a00f0000000000001976a9148e48a6c5108efac226d33018b5"
112-
"347bb24adec37a88ac0141530cc8246d3624f54faa50312204a89c67e1595f1b418b6da66a"
113-
"61b089195c54e853a1e2d80b3379a3ec9f9429daf9f5bc332986af6463381fe4e9f5d686f7"
114-
"468100000000"
94+
"02000000000101566e10098ddba743bedbe1e4b356377abb3ef106c6831e733863d5eea012647b0100000000ffffffff01a00f0000000000001976a9148e48a6c5108efac226d33018b5347bb24adec37a88ac0141e8acdacc3f86d8e4d046f67037a6b69423798308e0d6a1e4ff4117a703d14458b4922fb6ddd07161a64cee572cc3a9fb28a1b0b29abd0743fb757c534eb2cc5c8100000000"
11595
)
11696
self.sig_65_bytes_size = 103
11797

@@ -244,11 +224,7 @@ def setUp(self):
244224
)
245225

246226
self.signed_tx2 = (
247-
"0200000000010166fa733b552a229823b72571c3d91349ae90354926ff45e67257c6c4739d"
248-
"4c3d0000000000ffffffff01b80b000000000000225120d4213cd57207f22a9e905302007b"
249-
"99b84491534729bd5f4065bdcb42ed10fcd50140f1776ddef90a87b646a45ad4821b8dd33e"
250-
"01c5036cbe071a2e1e609ae0c0963685cb8749001944dbe686662dd7c95178c85c4f59c685"
251-
"b646ab27e34df766b7b100000000"
227+
"0200000000010166fa733b552a229823b72571c3d91349ae90354926ff45e67257c6c4739d4c3d0000000000ffffffff01b80b000000000000225120d4213cd57207f22a9e905302007b99b84491534729bd5f4065bdcb42ed10fcd50140dff6c2256f49fd03b03c0ede86e6ba5ae64c84217438f24752e5493c097983a8358b31cc821f84c63f0cbc39dae2885e669e7cfe370696dcde27bf99e712fdad00000000"
252228
)
253229

254230
self.from_amount2 = to_satoshis(0.000035)
@@ -259,13 +235,7 @@ def setUp(self):
259235

260236
# 3-same as 2 but now spend from tapleaf script
261237
self.signed_tx3 = (
262-
"0200000000010166fa733b552a229823b72571c3d91349ae90354926ff45e67257c6c4739d"
263-
"4c3d0000000000ffffffff01b80b000000000000225120d4213cd57207f22a9e905302007b"
264-
"99b84491534729bd5f4065bdcb42ed10fcd50340bf0a391574b56651923abdb25673105900"
265-
"8a08b5a3406cd81ce10ef5e7f936c6b9f7915ec1054e2a480e4552fa177aed868dc8b28c62"
266-
"63476871b21584690ef8222013f523102815e9fbbe132ffb8329b0fef5a9e4836d216dce18"
267-
"24633287b0abc6ac21c11036a7ed8d24eac9057e114f22342ebf20c16d37f0d25cfd2c900b"
268-
"f401ec09c900000000"
238+
"0200000000010166fa733b552a229823b72571c3d91349ae90354926ff45e67257c6c4739d4c3d0000000000ffffffff01b80b000000000000225120d4213cd57207f22a9e905302007b99b84491534729bd5f4065bdcb42ed10fcd503407dac0f9685e8392e29a74302beeec1a38fd0731380d96136e0a1d02d492593ed0c229031def4d6cb235213d60f631c48aa944c44a86bd56be8778aa7794bbaf3222013f523102815e9fbbe132ffb8329b0fef5a9e4836d216dce1824633287b0abc6ac21c11036a7ed8d24eac9057e114f22342ebf20c16d37f0d25cfd2c900bf401ec09c900000000"
269239
)
270240

271241
# 1-create address with single script spending path
@@ -356,14 +326,7 @@ def setUp(self):
356326
self.all_utxos_scriptPubkeys = [self.scriptPubkey]
357327

358328
self.signed_tx = (
359-
"020000000001014dc1c5b54477a18c962d5e065e69a42bd7e9244b74ea2c29f105b0b75dc8"
360-
"8e800000000000ffffffff01b80b000000000000225120d4213cd57207f22a9e905302007b"
361-
"99b84491534729bd5f4065bdcb42ed10fcd50340ab89d20fee5557e57b7cf85840721ef28d"
362-
"68e91fd162b2d520e553b71d604388ea7c4b2fcc4d946d5d3be3c12ef2d129ffb92594bc1f"
363-
"42cdaec8280d0c83ecc2222013f523102815e9fbbe132ffb8329b0fef5a9e4836d216dce18"
364-
"24633287b0abc6ac41c11036a7ed8d24eac9057e114f22342ebf20c16d37f0d25cfd2c900b"
365-
"f401ec09c9682f0e85d59cb20fd0e4503c035d609f127c786136f276d475e8321ec9e77e6c"
366-
"00000000"
329+
"020000000001014dc1c5b54477a18c962d5e065e69a42bd7e9244b74ea2c29f105b0b75dc88e800000000000ffffffff01b80b000000000000225120d4213cd57207f22a9e905302007b99b84491534729bd5f4065bdcb42ed10fcd50340dd418618603b959843a91cd6f13f52ce5992db134712b08ae54fc85fcf445726149f6d352cddc43c34ec7ed8a7c099039b06922c2ad37b01696cba4325bc02f7222013f523102815e9fbbe132ffb8329b0fef5a9e4836d216dce1824633287b0abc6ac41c11036a7ed8d24eac9057e114f22342ebf20c16d37f0d25cfd2c900bf401ec09c9682f0e85d59cb20fd0e4503c035d609f127c786136f276d475e8321ec9e77e6c00000000"
367330
)
368331

369332
# 1-spend taproot from first script path (A) of two (A,B)
@@ -449,14 +412,7 @@ def setUp(self):
449412
self.all_utxos_scriptPubkeys = [self.scriptPubkey]
450413

451414
self.signed_tx = (
452-
"02000000000101d387dafa20087c38044f3cbc2e93e1e0141e64265d304d0d44b233f3d001"
453-
"8a9b0000000000ffffffff01b80b000000000000225120d4213cd57207f22a9e905302007b"
454-
"99b84491534729bd5f4065bdcb42ed10fcd50340644e392f5fd88d812bad30e73ff9900cdc"
455-
"f7f260ecbc862819542fd4683fa9879546613be4e2fc762203e45715df1a42c65497a63edc"
456-
"e5f1dfe5caea5170273f2220e808f1396f12a253cf00efdf841e01c8376b616fb785c39595"
457-
"285c30f2817e71ac61c11036a7ed8d24eac9057e114f22342ebf20c16d37f0d25cfd2c900b"
458-
"f401ec09c9ed9f1b2b0090138e31e11a31c1aea790928b7ce89112a706e5caa703ff7e0ab9"
459-
"28109f92c2781611bb5de791137cbd40a5482a4a23fd0ffe50ee4de9d5790dd100000000"
415+
"02000000000101d387dafa20087c38044f3cbc2e93e1e0141e64265d304d0d44b233f3d0018a9b0000000000ffffffff01b80b000000000000225120d4213cd57207f22a9e905302007b99b84491534729bd5f4065bdcb42ed10fcd50340eecf48c6e3ccd3f2cead3ab5829652c6e4ed0ba7af0e7ca5ba3fc1733f34d5297bd75459da88b7189418576dbc379898e225ffa20d84037328a59db2e6df6cf82220e808f1396f12a253cf00efdf841e01c8376b616fb785c39595285c30f2817e71ac61c11036a7ed8d24eac9057e114f22342ebf20c16d37f0d25cfd2c900bf401ec09c9ed9f1b2b0090138e31e11a31c1aea790928b7ce89112a706e5caa703ff7e0ab928109f92c2781611bb5de791137cbd40a5482a4a23fd0ffe50ee4de9d5790dd100000000"
460416
)
461417

462418
# 1-spend taproot from second script path (B) of three ((A,B),C)

0 commit comments

Comments
 (0)