|
|
|
@ -8,9 +8,15 @@ import binascii |
|
|
|
import copy |
|
|
|
import copy |
|
|
|
import re |
|
|
|
import re |
|
|
|
import os |
|
|
|
import os |
|
|
|
|
|
|
|
import struct |
|
|
|
from jmbitcoin.secp256k1_main import * |
|
|
|
from jmbitcoin.secp256k1_main import * |
|
|
|
from jmbitcoin.bech32 import * |
|
|
|
from jmbitcoin.bech32 import * |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
P2PKH_PRE, P2PKH_POST = b'\x76\xa9\x14', b'\x88\xac' |
|
|
|
|
|
|
|
P2SH_P2WPKH_PRE, P2SH_P2WPKH_POST = b'\xa9\x14', b'\x87' |
|
|
|
|
|
|
|
P2WPKH_PRE = b'\x00\x14' |
|
|
|
|
|
|
|
P2WSH_PRE = b'\x00\x16' |
|
|
|
|
|
|
|
|
|
|
|
# Transaction serialization and deserialization |
|
|
|
# Transaction serialization and deserialization |
|
|
|
|
|
|
|
|
|
|
|
def deserialize(txinp): |
|
|
|
def deserialize(txinp): |
|
|
|
@ -108,12 +114,11 @@ def deserialize(txinp): |
|
|
|
return obj |
|
|
|
return obj |
|
|
|
|
|
|
|
|
|
|
|
def serialize(tx): |
|
|
|
def serialize(tx): |
|
|
|
"""Assumes a deserialized transaction in which all |
|
|
|
""" Accepts a dict in which the "outpoint","hash" |
|
|
|
dictionary values are decoded hex strings or numbers. |
|
|
|
"script" and "txinwitness" entries can be in binary |
|
|
|
Rationale: mixing raw bytes/hex/strings in dict objects causes |
|
|
|
or hex. If any "hash" field is in hex, the serialized |
|
|
|
complexity; since the dict tx object has to be inspected |
|
|
|
output is in hex, otherwise the serialization output |
|
|
|
in this function, avoid complicated logic elsewhere by making |
|
|
|
is in binary. Below table assumes all hex. |
|
|
|
all conversions to raw byte strings happen here. |
|
|
|
|
|
|
|
Table of dictionary keys and type for the value: |
|
|
|
Table of dictionary keys and type for the value: |
|
|
|
================================================ |
|
|
|
================================================ |
|
|
|
version: int |
|
|
|
version: int |
|
|
|
@ -129,7 +134,9 @@ def serialize(tx): |
|
|
|
outs[0]["value"]: int |
|
|
|
outs[0]["value"]: int |
|
|
|
locktime: int |
|
|
|
locktime: int |
|
|
|
================================================= |
|
|
|
================================================= |
|
|
|
Returned serialized transaction is a byte string. |
|
|
|
Returned serialized transaction is a byte string, |
|
|
|
|
|
|
|
or a hex encoded string, according to the above |
|
|
|
|
|
|
|
hash check. |
|
|
|
""" |
|
|
|
""" |
|
|
|
#Because we are manipulating the dict in-place, need |
|
|
|
#Because we are manipulating the dict in-place, need |
|
|
|
#to work on a copy |
|
|
|
#to work on a copy |
|
|
|
@ -184,6 +191,9 @@ def serialize(tx): |
|
|
|
items = inp["txinwitness"] |
|
|
|
items = inp["txinwitness"] |
|
|
|
o.write(num_to_var_int(len(items))) |
|
|
|
o.write(num_to_var_int(len(items))) |
|
|
|
for item in items: |
|
|
|
for item in items: |
|
|
|
|
|
|
|
if item is None: |
|
|
|
|
|
|
|
o.write(b'\x00') |
|
|
|
|
|
|
|
continue |
|
|
|
if isinstance(item, basestring) and not isinstance(item, bytes): |
|
|
|
if isinstance(item, basestring) and not isinstance(item, bytes): |
|
|
|
item = binascii.unhexlify(item) |
|
|
|
item = binascii.unhexlify(item) |
|
|
|
o.write(num_to_var_int(len(item)) + item) |
|
|
|
o.write(num_to_var_int(len(item)) + item) |
|
|
|
@ -203,7 +213,7 @@ SIGHASH_ANYONECANPAY = 0x80 |
|
|
|
|
|
|
|
|
|
|
|
def segwit_signature_form(txobj, i, script, amount, hashcode=SIGHASH_ALL, |
|
|
|
def segwit_signature_form(txobj, i, script, amount, hashcode=SIGHASH_ALL, |
|
|
|
decoder_func=binascii.unhexlify): |
|
|
|
decoder_func=binascii.unhexlify): |
|
|
|
"""Given a deserialized transaction txobj, an input index i, |
|
|
|
""" Given a deserialized transaction txobj, an input index i, |
|
|
|
which spends from a witness, |
|
|
|
which spends from a witness, |
|
|
|
a script for redemption and an amount in satoshis, prepare |
|
|
|
a script for redemption and an amount in satoshis, prepare |
|
|
|
the version of the transaction to be hashed and signed. |
|
|
|
the version of the transaction to be hashed and signed. |
|
|
|
@ -310,8 +320,11 @@ def segwit_txid(tx, hashcode=None): |
|
|
|
return txhash(reserialized_tx, hashcode) |
|
|
|
return txhash(reserialized_tx, hashcode) |
|
|
|
|
|
|
|
|
|
|
|
def txhash(tx, hashcode=None, check_sw=True): |
|
|
|
def txhash(tx, hashcode=None, check_sw=True): |
|
|
|
"""Creates the appropriate sha256 hash as required |
|
|
|
""" Creates the appropriate sha256 hash as required |
|
|
|
either for signing or calculating txids. |
|
|
|
either for signing or calculating txids. |
|
|
|
|
|
|
|
The hashcode argument is used to distinguish the case |
|
|
|
|
|
|
|
where we are hashing for signing (sighashing); by default |
|
|
|
|
|
|
|
it is None, and this indicates we are calculating a txid. |
|
|
|
If check_sw is True it checks the serialized format for |
|
|
|
If check_sw is True it checks the serialized format for |
|
|
|
segwit flag bytes, and produces the correct form for txid (not wtxid). |
|
|
|
segwit flag bytes, and produces the correct form for txid (not wtxid). |
|
|
|
""" |
|
|
|
""" |
|
|
|
@ -356,9 +369,7 @@ def ecdsa_tx_verify(tx, sig, pub, hashcode=SIGHASH_ALL): |
|
|
|
|
|
|
|
|
|
|
|
# Scripts |
|
|
|
# Scripts |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def mk_pubkey_script(addr): |
|
|
|
def mk_pubkey_script(addr): |
|
|
|
# Keep the auxiliary functions around for altcoins' sake |
|
|
|
|
|
|
|
return '76a914' + b58check_to_hex(addr) + '88ac' |
|
|
|
return '76a914' + b58check_to_hex(addr) + '88ac' |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -366,20 +377,36 @@ def mk_scripthash_script(addr): |
|
|
|
return 'a914' + b58check_to_hex(addr) + '87' |
|
|
|
return 'a914' + b58check_to_hex(addr) + '87' |
|
|
|
|
|
|
|
|
|
|
|
def segwit_scriptpubkey(witver, witprog): |
|
|
|
def segwit_scriptpubkey(witver, witprog): |
|
|
|
"""Construct a Segwit scriptPubKey for a given witness program.""" |
|
|
|
""" Construct a Segwit scriptPubKey for a given witness program. |
|
|
|
x = bytes([witver + 0x50 if witver else 0, len(witprog)] + witprog) |
|
|
|
""" |
|
|
|
return x |
|
|
|
return bytes([witver + 0x50 if witver else 0, len(witprog)] + witprog) |
|
|
|
|
|
|
|
|
|
|
|
def mk_native_segwit_script(addr): |
|
|
|
def mk_native_segwit_script(hrp, addr): |
|
|
|
hrp = addr[:2] |
|
|
|
""" Returns scriptPubKey as hex encoded string, |
|
|
|
|
|
|
|
given a valid bech32 address and human-readable-part, |
|
|
|
|
|
|
|
else throws an Exception if the arguments do not |
|
|
|
|
|
|
|
match this pattern. |
|
|
|
|
|
|
|
""" |
|
|
|
ver, prog = bech32addr_decode(hrp, addr) |
|
|
|
ver, prog = bech32addr_decode(hrp, addr) |
|
|
|
|
|
|
|
if ver is None: |
|
|
|
|
|
|
|
# This error cannot occur if this function is |
|
|
|
|
|
|
|
# called from `address_to_script` |
|
|
|
|
|
|
|
raise Exception("Invalid native segwit script") |
|
|
|
scriptpubkey = segwit_scriptpubkey(ver, prog) |
|
|
|
scriptpubkey = segwit_scriptpubkey(ver, prog) |
|
|
|
return binascii.hexlify(scriptpubkey).decode('ascii') |
|
|
|
return binascii.hexlify(scriptpubkey).decode('ascii') |
|
|
|
|
|
|
|
|
|
|
|
# Address representation to output script |
|
|
|
# Address representation to output script |
|
|
|
|
|
|
|
|
|
|
|
def address_to_script(addr): |
|
|
|
def address_to_script(addr): |
|
|
|
if addr[:2] in ['bc', 'tb']: |
|
|
|
""" Returns scriptPubKey as a hex-encoded string for |
|
|
|
return mk_native_segwit_script(addr) |
|
|
|
a given Bitcoin address. |
|
|
|
|
|
|
|
""" |
|
|
|
|
|
|
|
x = bech32_decode(addr) |
|
|
|
|
|
|
|
# Any failure (because it's not a bech32 address) |
|
|
|
|
|
|
|
# returns (None, None) |
|
|
|
|
|
|
|
if x[0] and x[1]: |
|
|
|
|
|
|
|
return mk_native_segwit_script(x[0], addr) |
|
|
|
|
|
|
|
|
|
|
|
if addr[0] == '3' or addr[0] == '2': |
|
|
|
if addr[0] == '3' or addr[0] == '2': |
|
|
|
return mk_scripthash_script(addr) |
|
|
|
return mk_scripthash_script(addr) |
|
|
|
else: |
|
|
|
else: |
|
|
|
@ -388,57 +415,145 @@ def address_to_script(addr): |
|
|
|
# Output script to address representation |
|
|
|
# Output script to address representation |
|
|
|
|
|
|
|
|
|
|
|
def is_p2pkh_script(script): |
|
|
|
def is_p2pkh_script(script): |
|
|
|
if script[:3] == b'\x76\xa9\x14' and script[-2:] == b'\x88\xac' and len( |
|
|
|
""" Given a script as bytes, returns True if the script |
|
|
|
|
|
|
|
matches the pay-to-pubkey-hash pattern (using opcodes), |
|
|
|
|
|
|
|
otherwise returns False. |
|
|
|
|
|
|
|
""" |
|
|
|
|
|
|
|
if not isinstance(script, bytes): |
|
|
|
|
|
|
|
script = binascii.unhexlify(script) |
|
|
|
|
|
|
|
if script[:3] == P2PKH_PRE and script[-2:] == P2PKH_POST and len( |
|
|
|
script) == 25: |
|
|
|
script) == 25: |
|
|
|
return True |
|
|
|
return True |
|
|
|
return False |
|
|
|
return False |
|
|
|
|
|
|
|
|
|
|
|
def is_segwit_native_script(script): |
|
|
|
def is_segwit_native_script(script): |
|
|
|
"""Is scriptPubkey of form P2WPKH or P2WSH""" |
|
|
|
"""Is script, as bytes, of form P2WPKH or P2WSH; |
|
|
|
if script[:2] in [b'\x00\x14', b'\x00\x20']: |
|
|
|
see BIP141 for definitions of 2 current valid scripts. |
|
|
|
|
|
|
|
""" |
|
|
|
|
|
|
|
if not isinstance(script, bytes): |
|
|
|
|
|
|
|
script = binascii.unhexlify(script) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (script[:2] == P2WPKH_PRE and len(script) == 22) or ( |
|
|
|
|
|
|
|
script[:2] == P2WSH_PRE and len(script) == 34): |
|
|
|
return True |
|
|
|
return True |
|
|
|
return False |
|
|
|
return False |
|
|
|
|
|
|
|
|
|
|
|
def script_to_address(script, vbyte=0, witver=0): |
|
|
|
def script_to_address(script, vbyte=0, witver=0): |
|
|
|
|
|
|
|
""" Given a hex or bytes script, and optionally a version byte |
|
|
|
|
|
|
|
(for P2SH) and/or a witness version (for native segwit witness |
|
|
|
|
|
|
|
programs), convert to a valid address (either bech32 or Base58CE). |
|
|
|
|
|
|
|
An important tacit assumption: anything which does not match |
|
|
|
|
|
|
|
the parsing rules of native segwit, or pay-to-pubkey-hash, is |
|
|
|
|
|
|
|
assumed to be p2sh. |
|
|
|
|
|
|
|
Note also that this translates scriptPubKeys to addresses, not |
|
|
|
|
|
|
|
redeemscripts to p2sh addresses; for that, use p2sh_scriptaddr. |
|
|
|
|
|
|
|
""" |
|
|
|
if not isinstance(script, bytes): |
|
|
|
if not isinstance(script, bytes): |
|
|
|
script = binascii.unhexlify(script) |
|
|
|
script = binascii.unhexlify(script) |
|
|
|
if is_segwit_native_script(script): |
|
|
|
if is_segwit_native_script(script): |
|
|
|
#hrp interpreted from the vbyte entry, TODO this should be cleaner. |
|
|
|
#hrp interpreted from the vbyte entry, TODO: better way? |
|
|
|
if vbyte in [0, 5]: |
|
|
|
if vbyte in [0, 5]: |
|
|
|
hrp = 'bc' |
|
|
|
hrp = 'bc' |
|
|
|
|
|
|
|
elif vbyte == 100: |
|
|
|
|
|
|
|
hrp = 'bcrt' |
|
|
|
else: |
|
|
|
else: |
|
|
|
hrp = 'tb' |
|
|
|
hrp = 'tb' |
|
|
|
return bech32addr_encode(hrp=hrp, witver=witver, |
|
|
|
return bech32addr_encode(hrp=hrp, witver=witver, |
|
|
|
witprog=[ord(x) for x in script[2:]]) |
|
|
|
witprog=struct.unpack('{}B'.format(len(script[2:])).encode( |
|
|
|
|
|
|
|
'ascii'), script[2:])) |
|
|
|
if is_p2pkh_script(script): |
|
|
|
if is_p2pkh_script(script): |
|
|
|
return bin_to_b58check(script[3:-2], vbyte) # pubkey hash addresses |
|
|
|
return bin_to_b58check(script[3:-2], vbyte) |
|
|
|
else: |
|
|
|
else: |
|
|
|
# BIP0016 scripthash addresses: requires explicit vbyte set |
|
|
|
# BIP0016 scripthash addresses: requires explicit vbyte set |
|
|
|
if vbyte == 0: raise Exception("Invalid version byte for P2SH") |
|
|
|
if vbyte == 0: raise Exception("Invalid version byte for P2SH") |
|
|
|
return bin_to_b58check(script[2:-1], vbyte) |
|
|
|
return bin_to_b58check(script[2:-1], vbyte) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def pubkey_to_script(pubkey, script_pre, script_post=b'', |
|
|
|
|
|
|
|
require_compressed=False): |
|
|
|
|
|
|
|
""" Generic conversion from a binary pubkey serialization |
|
|
|
|
|
|
|
to a corresponding binary scriptPubKey for the pubkeyhash case. |
|
|
|
|
|
|
|
""" |
|
|
|
|
|
|
|
if not is_valid_pubkey(pubkey, False, |
|
|
|
|
|
|
|
require_compressed=require_compressed): |
|
|
|
|
|
|
|
raise Exception("Invalid pubkey.") |
|
|
|
|
|
|
|
h = bin_hash160(pubkey) |
|
|
|
|
|
|
|
assert len(h) == 0x14 |
|
|
|
|
|
|
|
assert script_pre[-1:] == b'\x14' |
|
|
|
|
|
|
|
return script_pre + h + script_post |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def p2sh_scriptaddr(script, magicbyte=5): |
|
|
|
|
|
|
|
if not isinstance(script, bytes): |
|
|
|
|
|
|
|
script = binascii.unhexlify(script) |
|
|
|
|
|
|
|
return hex_to_b58check(hash160(script), magicbyte) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def pubkey_to_p2pkh_script(pub, require_compressed=False): |
|
|
|
|
|
|
|
""" Construct a pay-to-pubkey-hash scriptPubKey |
|
|
|
|
|
|
|
given a single pubkey. Script returned in binary. |
|
|
|
|
|
|
|
The require compressed flag may be used to disallow |
|
|
|
|
|
|
|
uncompressed keys, e.g. in constructing a segwit |
|
|
|
|
|
|
|
scriptCode field. |
|
|
|
|
|
|
|
""" |
|
|
|
|
|
|
|
if not isinstance(pub, bytes): |
|
|
|
|
|
|
|
pub = binascii.unhexlify(pub) |
|
|
|
|
|
|
|
return pubkey_to_script(pub, P2PKH_PRE, P2PKH_POST) |
|
|
|
|
|
|
|
|
|
|
|
def pubkey_to_p2sh_p2wpkh_script(pub): |
|
|
|
def pubkey_to_p2sh_p2wpkh_script(pub): |
|
|
|
|
|
|
|
""" Construct a nested-pay-to-witness-pubkey-hash |
|
|
|
|
|
|
|
scriptPubKey given a single pubkey. |
|
|
|
|
|
|
|
Script returned in binary. |
|
|
|
|
|
|
|
""" |
|
|
|
if not isinstance(pub, bytes): |
|
|
|
if not isinstance(pub, bytes): |
|
|
|
pub = binascii.unhexlify(pub) |
|
|
|
pub = binascii.unhexlify(pub) |
|
|
|
return "0014" + hash160(pub) |
|
|
|
wscript = pubkey_to_p2wpkh_script(pub) |
|
|
|
|
|
|
|
return P2SH_P2WPKH_PRE + bin_hash160(wscript) + P2SH_P2WPKH_POST |
|
|
|
|
|
|
|
|
|
|
|
def pubkey_to_p2sh_p2wpkh_address(pub, magicbyte=5): |
|
|
|
def pubkey_to_p2sh_p2wpkh_address(pub, magicbyte=5): |
|
|
|
|
|
|
|
""" Construct a nested-pay-to-witness-pubkey-hash |
|
|
|
|
|
|
|
address given a single pubkey; magicbyte defines |
|
|
|
|
|
|
|
network as for any p2sh address. |
|
|
|
|
|
|
|
""" |
|
|
|
if not isinstance(pub, bytes): |
|
|
|
if not isinstance(pub, bytes): |
|
|
|
pub = binascii.unhexlify(pub) |
|
|
|
pub = binascii.unhexlify(pub) |
|
|
|
script = pubkey_to_p2sh_p2wpkh_script(pub) |
|
|
|
script = pubkey_to_p2wpkh_script(pub) |
|
|
|
return p2sh_scriptaddr(script, magicbyte=magicbyte) |
|
|
|
return p2sh_scriptaddr(script, magicbyte=magicbyte) |
|
|
|
|
|
|
|
|
|
|
|
def p2sh_scriptaddr(script, magicbyte=5): |
|
|
|
def pubkey_to_p2wpkh_script(pub): |
|
|
|
if not isinstance(script, bytes): |
|
|
|
""" Construct a pay-to-witness-pubkey-hash |
|
|
|
script = binascii.unhexlify(script) |
|
|
|
scriptPubKey given a single pubkey. Note that |
|
|
|
return hex_to_b58check(hash160(script), magicbyte) |
|
|
|
this is the witness program (version 0). Script |
|
|
|
|
|
|
|
is returned in binary. |
|
|
|
|
|
|
|
""" |
|
|
|
|
|
|
|
if not isinstance(pub, bytes): |
|
|
|
|
|
|
|
pub = binascii.unhexlify(pub) |
|
|
|
|
|
|
|
return pubkey_to_script(pub, P2WPKH_PRE, |
|
|
|
|
|
|
|
require_compressed=True) |
|
|
|
|
|
|
|
|
|
|
|
scriptaddr = p2sh_scriptaddr |
|
|
|
def pubkey_to_p2wpkh_address(pub): |
|
|
|
|
|
|
|
""" Construct a pay-to-witness-pubkey-hash |
|
|
|
|
|
|
|
address (bech32) given a single pubkey. |
|
|
|
|
|
|
|
""" |
|
|
|
|
|
|
|
script = pubkey_to_p2wpkh_script(pub) |
|
|
|
|
|
|
|
return script_to_address(script) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def pubkeys_to_p2wsh_script(pubs): |
|
|
|
|
|
|
|
""" Given a list of N pubkeys, constructs an N of N |
|
|
|
|
|
|
|
multisig scriptPubKey of type pay-to-witness-script-hash. |
|
|
|
|
|
|
|
No other scripts than N-N multisig supported as of now. |
|
|
|
|
|
|
|
""" |
|
|
|
|
|
|
|
N = len(pubs) |
|
|
|
|
|
|
|
script = mk_multisig_script(pubs, N) |
|
|
|
|
|
|
|
return P2WSH_PRE + bin_sha256(binascii.unhexlify(script)) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def pubkeys_to_p2wsh_address(pubs): |
|
|
|
|
|
|
|
""" Given a list of N pubkeys, constructs an N of N |
|
|
|
|
|
|
|
multisig address of type pay-to-witness-script-hash. |
|
|
|
|
|
|
|
No other scripts than N-N multisig supported as of now. |
|
|
|
|
|
|
|
""" |
|
|
|
|
|
|
|
script = pubkeys_to_p2wsh_script(pubs) |
|
|
|
|
|
|
|
return script_to_address(script) |
|
|
|
|
|
|
|
|
|
|
|
def deserialize_script(scriptinp): |
|
|
|
def deserialize_script(scriptinp): |
|
|
|
"""Note that this is not used internally, in |
|
|
|
""" Note that this is not used internally, in |
|
|
|
the jmbitcoin package, to deserialize() transactions; |
|
|
|
the jmbitcoin package, to deserialize() transactions; |
|
|
|
its function is only to allow parsing of scripts by |
|
|
|
its function is only to allow parsing of scripts by |
|
|
|
external callers. Thus, it returns in the format used |
|
|
|
external callers. Thus, it returns in the format used |
|
|
|
@ -521,80 +636,118 @@ def serialize_script(script): |
|
|
|
return result |
|
|
|
return result |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def mk_multisig_script(*args): # [pubs],k or pub1,pub2...pub[n],k |
|
|
|
def mk_multisig_script(pubs, k): |
|
|
|
if isinstance(args[0], list): |
|
|
|
""" Given a list of pubkeys and an integer k, |
|
|
|
pubs, k = args[0], int(args[1]) |
|
|
|
construct a multisig script for k of N, where N is |
|
|
|
else: |
|
|
|
the length of the list `pubs`; script is returned |
|
|
|
pubs = list(filter(lambda x: len(str(x)) >= 32, args)) |
|
|
|
as hex string. |
|
|
|
k = int(args[len(pubs)]) |
|
|
|
""" |
|
|
|
return serialize_script([k] + pubs + [len(pubs)]) + 'ae' |
|
|
|
return serialize_script([k] + pubs + [len(pubs)]) + 'ae' |
|
|
|
|
|
|
|
|
|
|
|
# Signing and verifying |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Signing and verifying |
|
|
|
|
|
|
|
|
|
|
|
def verify_tx_input(tx, i, script, sig, pub, witness=None, amount=None): |
|
|
|
def verify_tx_input(tx, i, script, sig, pub, scriptCode=None, amount=None): |
|
|
|
|
|
|
|
""" Given a hex-serialized transaction tx, an integer index i, |
|
|
|
|
|
|
|
a script (see more on this below), signature and pubkey, and optionally |
|
|
|
|
|
|
|
a segwit scriptCode and amount in satoshis (that flags segwit usage), |
|
|
|
|
|
|
|
we return True if and only if the signature and pubkey is valid for |
|
|
|
|
|
|
|
this tx input. |
|
|
|
|
|
|
|
Note on 'script' and 'scriptCode': |
|
|
|
|
|
|
|
For p2pkh, 'script' should be the output script (scriptPubKey), and |
|
|
|
|
|
|
|
the scriptCode should be left as the default None. |
|
|
|
|
|
|
|
For p2sh non-segwit multisig, the script should be the |
|
|
|
|
|
|
|
output of mk_multisig_script (and scriptCode and amount None). |
|
|
|
|
|
|
|
For either nested (p2sh) or not p2wpkh and p2wsh, the scriptCode is the |
|
|
|
|
|
|
|
preimage of the scriptPubKey hash (the witness program, which here |
|
|
|
|
|
|
|
has been passed in in the 'script' parameter). |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Note that for the p2sh-p2wsh and p2wsh cases, a redeem script that is not |
|
|
|
|
|
|
|
multisig should still be correctly handled here; the codebase restricts |
|
|
|
|
|
|
|
the creation of such signatures to multisig only, but *verification* should |
|
|
|
|
|
|
|
function correctly with any custom script. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
TODO: Also note that the OP_CODESEPARATOR special case outlined in BIP143 |
|
|
|
|
|
|
|
is not yet covered. |
|
|
|
|
|
|
|
""" |
|
|
|
|
|
|
|
assert isinstance(i, int) |
|
|
|
|
|
|
|
assert i >= 0 |
|
|
|
if not isinstance(tx, bytes): |
|
|
|
if not isinstance(tx, bytes): |
|
|
|
tx = binascii.unhexlify(tx) |
|
|
|
tx = binascii.unhexlify(tx) |
|
|
|
if not isinstance(script, bytes): |
|
|
|
if not isinstance(script, bytes): |
|
|
|
script = binascii.unhexlify(script) |
|
|
|
script = binascii.unhexlify(script) |
|
|
|
|
|
|
|
if scriptCode is not None and not isinstance(scriptCode, bytes): |
|
|
|
|
|
|
|
scriptCode = binascii.unhexlify(scriptCode) |
|
|
|
if isinstance(sig, bytes): |
|
|
|
if isinstance(sig, bytes): |
|
|
|
sig = binascii.hexlify(sig).decode('ascii') |
|
|
|
sig = binascii.hexlify(sig).decode('ascii') |
|
|
|
if isinstance(pub, bytes): |
|
|
|
if isinstance(pub, bytes): |
|
|
|
pub = binascii.hexlify(pub).decode('ascii') |
|
|
|
pub = binascii.hexlify(pub).decode('ascii') |
|
|
|
if witness: |
|
|
|
|
|
|
|
if isinstance(witness, bytes): |
|
|
|
|
|
|
|
witness = safe_hexlify(witness) |
|
|
|
|
|
|
|
hashcode = binascii.unhexlify(sig[-2:]) |
|
|
|
hashcode = binascii.unhexlify(sig[-2:]) |
|
|
|
if witness and amount: |
|
|
|
|
|
|
|
#TODO assumes p2sh wrapped segwit input; OK for JM wallets |
|
|
|
if amount: |
|
|
|
scriptCode = binascii.unhexlify("76a914"+hash160(binascii.unhexlify(pub))+"88ac") |
|
|
|
modtx = segwit_signature_form(deserialize(tx), i, |
|
|
|
modtx = segwit_signature_form(deserialize(tx), int(i), |
|
|
|
scriptCode, amount, hashcode, decoder_func=lambda x: x) |
|
|
|
scriptCode, amount, hashcode, decoder_func=lambda x: x) |
|
|
|
|
|
|
|
else: |
|
|
|
else: |
|
|
|
modtx = signature_form(tx, int(i), script, hashcode) |
|
|
|
modtx = signature_form(tx, i, script, hashcode) |
|
|
|
return ecdsa_tx_verify(modtx, sig, pub, hashcode) |
|
|
|
return ecdsa_tx_verify(modtx, sig, pub, hashcode) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def sign(tx, i, priv, hashcode=SIGHASH_ALL, usenonce=None, amount=None): |
|
|
|
def sign(tx, i, priv, hashcode=SIGHASH_ALL, usenonce=None, amount=None, |
|
|
|
i = int(i) |
|
|
|
native=False): |
|
|
|
|
|
|
|
""" |
|
|
|
|
|
|
|
Given a serialized transaction tx, an input index i, and a privkey |
|
|
|
|
|
|
|
in bytes or hex, returns a serialized transaction, in hex always, |
|
|
|
|
|
|
|
into which the signature and/or witness has been inserted. The field |
|
|
|
|
|
|
|
`amount` flags whether segwit signing is to be done, and the field |
|
|
|
|
|
|
|
`native` flags that native segwit p2wpkh signing is to be done. Note |
|
|
|
|
|
|
|
that signing multisig is to be done with the alternative functions |
|
|
|
|
|
|
|
multisign or p2wsh_multisign (and non N of N multisig scripthash |
|
|
|
|
|
|
|
signing is not currently supported). |
|
|
|
|
|
|
|
""" |
|
|
|
if isinstance(tx, basestring) and not isinstance(tx, bytes): |
|
|
|
if isinstance(tx, basestring) and not isinstance(tx, bytes): |
|
|
|
tx = binascii.unhexlify(tx) |
|
|
|
tx = binascii.unhexlify(tx) |
|
|
|
hexout = True |
|
|
|
|
|
|
|
else: |
|
|
|
|
|
|
|
hexout = False |
|
|
|
|
|
|
|
if len(priv) <= 33: |
|
|
|
if len(priv) <= 33: |
|
|
|
priv = safe_hexlify(priv) |
|
|
|
priv = safe_hexlify(priv) |
|
|
|
if amount: |
|
|
|
if amount: |
|
|
|
return p2sh_p2wpkh_sign(tx, i, priv, amount, hashcode=hashcode, |
|
|
|
serobj = p2wpkh_sign(tx, i, priv, amount, hashcode=hashcode, |
|
|
|
usenonce=usenonce) |
|
|
|
usenonce=usenonce, native=native) |
|
|
|
pub = privkey_to_pubkey(priv, True) |
|
|
|
else: |
|
|
|
address = pubkey_to_address(pub) |
|
|
|
pub = privkey_to_pubkey(priv, True) |
|
|
|
signing_tx = signature_form(tx, i, mk_pubkey_script(address), hashcode) |
|
|
|
address = pubkey_to_address(pub) |
|
|
|
sig = ecdsa_tx_sign(signing_tx, priv, hashcode, usenonce=usenonce) |
|
|
|
signing_tx = signature_form(tx, i, mk_pubkey_script(address), hashcode) |
|
|
|
txobj = deserialize(tx) |
|
|
|
sig = ecdsa_tx_sign(signing_tx, priv, hashcode, usenonce=usenonce) |
|
|
|
txobj["ins"][i]["script"] = serialize_script([sig, pub]) |
|
|
|
txobj = deserialize(tx) |
|
|
|
serobj = serialize(txobj) |
|
|
|
txobj["ins"][i]["script"] = serialize_script([sig, pub]) |
|
|
|
if hexout and isinstance(serobj, basestring) and isinstance(serobj, bytes): |
|
|
|
serobj = serialize(txobj) |
|
|
|
|
|
|
|
if isinstance(serobj, basestring) and isinstance(serobj, bytes): |
|
|
|
return binascii.hexlify(serobj).decode('ascii') |
|
|
|
return binascii.hexlify(serobj).decode('ascii') |
|
|
|
elif not hexout and not isinstance(serobj, bytes): |
|
|
|
|
|
|
|
return binascii.unhexlify(serobj) |
|
|
|
|
|
|
|
else: |
|
|
|
else: |
|
|
|
return serobj |
|
|
|
return serobj |
|
|
|
|
|
|
|
|
|
|
|
def p2sh_p2wpkh_sign(tx, i, priv, amount, hashcode=SIGHASH_ALL, usenonce=None): |
|
|
|
def p2wpkh_sign(tx, i, priv, amount, hashcode=SIGHASH_ALL, native=False, |
|
|
|
|
|
|
|
usenonce=None): |
|
|
|
"""Given a serialized transaction, index, private key in hex, |
|
|
|
"""Given a serialized transaction, index, private key in hex, |
|
|
|
amount in satoshis and optionally hashcode, return the serialized |
|
|
|
amount in satoshis and optionally hashcode, return the serialized |
|
|
|
transaction containing a signature and witness for this input; it's |
|
|
|
transaction containing a signature and witness for this input; it's |
|
|
|
assumed that the input is of type pay-to-witness-pubkey-hash nested in p2sh. |
|
|
|
assumed that the input is of type pay-to-witness-pubkey-hash. |
|
|
|
|
|
|
|
If native is False, it's treated as p2sh nested. |
|
|
|
""" |
|
|
|
""" |
|
|
|
pub = privkey_to_pubkey(priv) |
|
|
|
pub = privkey_to_pubkey(priv) |
|
|
|
script = pubkey_to_p2sh_p2wpkh_script(pub) |
|
|
|
# Convert the input tx and script to hex so that the deserialize() |
|
|
|
scriptCode = "76a914"+hash160(binascii.unhexlify(pub))+"88ac" |
|
|
|
# call creates hex-encoded fields |
|
|
|
|
|
|
|
script = binascii.hexlify(pubkey_to_p2wpkh_script(pub)).decode('ascii') |
|
|
|
|
|
|
|
scriptCode = binascii.hexlify(pubkey_to_p2pkh_script(pub)).decode('ascii') |
|
|
|
|
|
|
|
if isinstance(tx, bytes): |
|
|
|
|
|
|
|
tx = binascii.hexlify(tx).decode('ascii') |
|
|
|
signing_tx = segwit_signature_form(deserialize(tx), i, scriptCode, amount, |
|
|
|
signing_tx = segwit_signature_form(deserialize(tx), i, scriptCode, amount, |
|
|
|
hashcode=hashcode) |
|
|
|
hashcode=hashcode) |
|
|
|
sig = ecdsa_tx_sign(signing_tx, priv, hashcode, usenonce=usenonce) |
|
|
|
sig = ecdsa_tx_sign(signing_tx, priv, hashcode, usenonce=usenonce) |
|
|
|
txobj = deserialize(tx) |
|
|
|
txobj = deserialize(tx) |
|
|
|
txobj["ins"][i]["script"] = "16"+script |
|
|
|
if not native: |
|
|
|
|
|
|
|
txobj["ins"][i]["script"] = "16" + script |
|
|
|
|
|
|
|
else: |
|
|
|
|
|
|
|
txobj["ins"][i]["script"] = "" |
|
|
|
txobj["ins"][i]["txinwitness"] = [sig, pub] |
|
|
|
txobj["ins"][i]["txinwitness"] = [sig, pub] |
|
|
|
return serialize(txobj) |
|
|
|
return serialize(txobj) |
|
|
|
|
|
|
|
|
|
|
|
@ -611,14 +764,45 @@ def signall(tx, priv): |
|
|
|
return tx |
|
|
|
return tx |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def multisign(tx, i, script, pk, hashcode=SIGHASH_ALL): |
|
|
|
def multisign(tx, i, script, pk, amount=None, hashcode=SIGHASH_ALL): |
|
|
|
|
|
|
|
""" Tx is assumed to be serialized. The script passed here is |
|
|
|
|
|
|
|
the redeemscript, for example the output of mk_multisig_script. |
|
|
|
|
|
|
|
pk is the private key, and must be passed in hex. |
|
|
|
|
|
|
|
If amount is not None, the output of p2wsh_multisign is returned. |
|
|
|
|
|
|
|
What is returned is a single signature. |
|
|
|
|
|
|
|
""" |
|
|
|
if isinstance(tx, str): |
|
|
|
if isinstance(tx, str): |
|
|
|
tx = binascii.unhexlify(tx) |
|
|
|
tx = binascii.unhexlify(tx) |
|
|
|
if isinstance(script, str): |
|
|
|
if isinstance(script, str): |
|
|
|
script = binascii.unhexlify(script) |
|
|
|
script = binascii.unhexlify(script) |
|
|
|
|
|
|
|
if amount: |
|
|
|
|
|
|
|
return p2wsh_multisign(tx, i, script, pk, amount, hashcode) |
|
|
|
modtx = signature_form(tx, i, script, hashcode) |
|
|
|
modtx = signature_form(tx, i, script, hashcode) |
|
|
|
return ecdsa_tx_sign(modtx, pk, hashcode) |
|
|
|
return ecdsa_tx_sign(modtx, pk, hashcode) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def p2wsh_multisign(tx, i, script, pk, amount, hashcode=SIGHASH_ALL): |
|
|
|
|
|
|
|
""" See note to multisign for the value to pass in as `script`. |
|
|
|
|
|
|
|
Tx is assumed to be serialized. |
|
|
|
|
|
|
|
""" |
|
|
|
|
|
|
|
modtx = segwit_signature_form(deserialize(tx), i, script, amount, |
|
|
|
|
|
|
|
hashcode, decoder_func=lambda x: x) |
|
|
|
|
|
|
|
return ecdsa_tx_sign(modtx, pk, hashcode) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def apply_p2wsh_multisignatures(tx, i, script, sigs): |
|
|
|
|
|
|
|
"""Sigs must be passed in as a list, and must be a |
|
|
|
|
|
|
|
complete list for this multisig, in the same order |
|
|
|
|
|
|
|
as the list of pubkeys when creating the scriptPubKey. |
|
|
|
|
|
|
|
""" |
|
|
|
|
|
|
|
if isinstance(script, str): |
|
|
|
|
|
|
|
script = binascii.unhexlify(script) |
|
|
|
|
|
|
|
sigs = [binascii.unhexlify(x) if x[:2] == '30' else x for x in sigs] |
|
|
|
|
|
|
|
if isinstance(tx, str): |
|
|
|
|
|
|
|
return safe_hexlify(apply_p2wsh_multisignatures( |
|
|
|
|
|
|
|
binascii.unhexlify(tx), i, script, sigs)) |
|
|
|
|
|
|
|
txobj = deserialize(tx) |
|
|
|
|
|
|
|
txobj["ins"][i]["script"] = "" |
|
|
|
|
|
|
|
txobj["ins"][i]["txinwitness"] = [None] + sigs + [script] |
|
|
|
|
|
|
|
return serialize(txobj) |
|
|
|
|
|
|
|
|
|
|
|
def apply_multisignatures(*args): |
|
|
|
def apply_multisignatures(*args): |
|
|
|
# tx,i,script,sigs OR tx,i,script,sig1,sig2...,sig[n] |
|
|
|
# tx,i,script,sigs OR tx,i,script,sig1,sig2...,sig[n] |
|
|
|
@ -636,21 +820,8 @@ def apply_multisignatures(*args): |
|
|
|
txobj["ins"][i]["script"] = serialize_script([None] + sigs + [script]) |
|
|
|
txobj["ins"][i]["script"] = serialize_script([None] + sigs + [script]) |
|
|
|
return serialize(txobj) |
|
|
|
return serialize(txobj) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def mktx(ins, outs, version=1): |
|
|
|
def is_inp(arg): |
|
|
|
txobj = {"locktime": 0, "version": version, "ins": [], "outs": []} |
|
|
|
return len(arg) > 64 or "output" in arg or "outpoint" in arg |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def mktx(*args): |
|
|
|
|
|
|
|
# [in0, in1...],[out0, out1...] or in0, in1 ... out0 out1 ... |
|
|
|
|
|
|
|
ins, outs = [], [] |
|
|
|
|
|
|
|
for arg in args: |
|
|
|
|
|
|
|
if isinstance(arg, list): |
|
|
|
|
|
|
|
for a in arg: |
|
|
|
|
|
|
|
(ins if is_inp(a) else outs).append(a) |
|
|
|
|
|
|
|
else: |
|
|
|
|
|
|
|
(ins if is_inp(arg) else outs).append(arg) |
|
|
|
|
|
|
|
txobj = {"locktime": 0, "version": 1, "ins": [], "outs": []} |
|
|
|
|
|
|
|
for i in ins: |
|
|
|
for i in ins: |
|
|
|
if isinstance(i, dict) and "outpoint" in i: |
|
|
|
if isinstance(i, dict) and "outpoint" in i: |
|
|
|
txobj["ins"].append(i) |
|
|
|
txobj["ins"].append(i) |
|
|
|
|