|
@ -33,6 +33,8 @@ import struct |
|
|
import StringIO |
|
|
import StringIO |
|
|
import mmap |
|
|
import mmap |
|
|
|
|
|
|
|
|
|
|
|
NO_SIGNATURE = 'ff' |
|
|
|
|
|
|
|
|
class SerializationError(Exception): |
|
|
class SerializationError(Exception): |
|
|
""" Thrown when there's a problem deserializing or serializing """ |
|
|
""" Thrown when there's a problem deserializing or serializing """ |
|
|
|
|
|
|
|
@ -303,12 +305,21 @@ def parse_sig(x_sig): |
|
|
if sig[-2:] == '01': |
|
|
if sig[-2:] == '01': |
|
|
s.append(sig[:-2]) |
|
|
s.append(sig[:-2]) |
|
|
else: |
|
|
else: |
|
|
assert sig == 'ff' |
|
|
assert sig == NO_SIGNATURE |
|
|
|
|
|
s.append(None) |
|
|
return s |
|
|
return s |
|
|
|
|
|
|
|
|
def is_extended_pubkey(x_pubkey): |
|
|
def is_extended_pubkey(x_pubkey): |
|
|
return x_pubkey[0:2] in ['fe', 'ff'] |
|
|
return x_pubkey[0:2] in ['fe', 'ff'] |
|
|
|
|
|
|
|
|
|
|
|
def x_to_xpub(x_pubkey): |
|
|
|
|
|
if x_pubkey[0:2] == 'ff': |
|
|
|
|
|
from account import BIP32_Account |
|
|
|
|
|
xpub, s = BIP32_Account.parse_xpubkey(x_pubkey) |
|
|
|
|
|
return xpub |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def parse_xpub(x_pubkey): |
|
|
def parse_xpub(x_pubkey): |
|
|
if x_pubkey[0:2] == 'ff': |
|
|
if x_pubkey[0:2] == 'ff': |
|
|
from account import BIP32_Account |
|
|
from account import BIP32_Account |
|
@ -385,6 +396,7 @@ def parse_scriptSig(d, bytes): |
|
|
d['x_pubkeys'] = x_pubkeys |
|
|
d['x_pubkeys'] = x_pubkeys |
|
|
pubkeys = map(parse_xpub, x_pubkeys) |
|
|
pubkeys = map(parse_xpub, x_pubkeys) |
|
|
d['pubkeys'] = pubkeys |
|
|
d['pubkeys'] = pubkeys |
|
|
|
|
|
|
|
|
redeemScript = Transaction.multisig_script(pubkeys,2) |
|
|
redeemScript = Transaction.multisig_script(pubkeys,2) |
|
|
d['redeemScript'] = redeemScript |
|
|
d['redeemScript'] = redeemScript |
|
|
d['address'] = hash_160_to_bc_address(hash_160(redeemScript.decode('hex')), 5) |
|
|
d['address'] = hash_160_to_bc_address(hash_160(redeemScript.decode('hex')), 5) |
|
@ -415,6 +427,8 @@ def get_address_from_output_script(bytes): |
|
|
return False, "(None)" |
|
|
return False, "(None)" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Transaction: |
|
|
class Transaction: |
|
|
|
|
|
|
|
|
def __init__(self, raw): |
|
|
def __init__(self, raw): |
|
@ -510,8 +524,6 @@ class Transaction: |
|
|
@classmethod |
|
|
@classmethod |
|
|
def serialize( klass, inputs, outputs, for_sig = None ): |
|
|
def serialize( klass, inputs, outputs, for_sig = None ): |
|
|
|
|
|
|
|
|
NO_SIGNATURE = 'ff' |
|
|
|
|
|
|
|
|
|
|
|
push_script = lambda x: op_push(len(x)/2) + x |
|
|
push_script = lambda x: op_push(len(x)/2) + x |
|
|
s = int_to_hex(1,4) # version |
|
|
s = int_to_hex(1,4) # version |
|
|
s += var_int( len(inputs) ) # number of inputs |
|
|
s += var_int( len(inputs) ) # number of inputs |
|
@ -522,38 +534,34 @@ class Transaction: |
|
|
s += int_to_hex(txin['prevout_n'],4) # prev index |
|
|
s += int_to_hex(txin['prevout_n'],4) # prev index |
|
|
|
|
|
|
|
|
p2sh = txin.get('redeemScript') is not None |
|
|
p2sh = txin.get('redeemScript') is not None |
|
|
n_sig = 2 if p2sh else 1 |
|
|
num_sig = txin['num_sig'] |
|
|
|
|
|
|
|
|
pubkeys = txin['pubkeys'] # pubkeys should always be known |
|
|
|
|
|
address = txin['address'] |
|
|
address = txin['address'] |
|
|
|
|
|
|
|
|
if for_sig is None: |
|
|
x_signatures = txin['signatures'] |
|
|
|
|
|
signatures = filter(lambda x: x is not None, x_signatures) |
|
|
|
|
|
is_complete = len(signatures) == num_sig |
|
|
|
|
|
|
|
|
# list of signatures |
|
|
if for_sig is None: |
|
|
signatures = txin.get('signatures',[]) |
|
|
# if we have enough signatures, we use the actual pubkeys |
|
|
|
|
|
# use extended pubkeys (with bip32 derivation) |
|
|
sig_list = [] |
|
|
sig_list = [] |
|
|
|
|
|
if is_complete: |
|
|
|
|
|
pubkeys = txin['pubkeys'] |
|
|
for signature in signatures: |
|
|
for signature in signatures: |
|
|
sig_list.append(signature + '01') |
|
|
sig_list.append(signature + '01') |
|
|
if len(sig_list) > n_sig: |
|
|
|
|
|
sig_list = sig_list[:n_sig] |
|
|
|
|
|
while len(sig_list) < n_sig: |
|
|
|
|
|
sig_list.append(NO_SIGNATURE) |
|
|
|
|
|
sig_list = ''.join( map( lambda x: push_script(x), sig_list)) |
|
|
|
|
|
|
|
|
|
|
|
if len(signatures) < n_sig: |
|
|
|
|
|
# extended pubkeys (with bip32 derivation) |
|
|
|
|
|
x_pubkeys = txin['x_pubkeys'] |
|
|
|
|
|
else: |
|
|
else: |
|
|
# if we have enough signatures, we use the actual pubkeys |
|
|
pubkeys = txin['x_pubkeys'] |
|
|
x_pubkeys = txin['pubkeys'] |
|
|
for signature in x_signatures: |
|
|
|
|
|
sig_list.append((signature + '01') if signature is not None else NO_SIGNATURE) |
|
|
|
|
|
|
|
|
|
|
|
sig_list = ''.join( map( lambda x: push_script(x), sig_list)) |
|
|
if not p2sh: |
|
|
if not p2sh: |
|
|
script = sig_list |
|
|
script = sig_list |
|
|
script += push_script(x_pubkeys[0]) |
|
|
script += push_script(pubkeys[0]) |
|
|
else: |
|
|
else: |
|
|
script = '00' # op_0 |
|
|
script = '00' # op_0 |
|
|
script += sig_list |
|
|
script += sig_list |
|
|
redeem_script = klass.multisig_script(x_pubkeys,2) |
|
|
redeem_script = klass.multisig_script(pubkeys,2) |
|
|
script += push_script(redeem_script) |
|
|
script += push_script(redeem_script) |
|
|
|
|
|
|
|
|
elif for_sig==i: |
|
|
elif for_sig==i: |
|
@ -585,17 +593,13 @@ class Transaction: |
|
|
return Hash(self.raw.decode('hex') )[::-1].encode('hex') |
|
|
return Hash(self.raw.decode('hex') )[::-1].encode('hex') |
|
|
|
|
|
|
|
|
def add_signature(self, i, pubkey, sig): |
|
|
def add_signature(self, i, pubkey, sig): |
|
|
|
|
|
print_error("adding signature for", pubkey) |
|
|
txin = self.inputs[i] |
|
|
txin = self.inputs[i] |
|
|
signatures = txin.get("signatures",[]) |
|
|
pubkeys = txin['pubkeys'] |
|
|
if sig not in signatures: |
|
|
ii = pubkeys.index(pubkey) |
|
|
signatures.append(sig) |
|
|
txin['signatures'][ii] = sig |
|
|
txin["signatures"] = signatures |
|
|
txin['x_pubkeys'][ii] = pubkey |
|
|
self.inputs[i] = txin |
|
|
self.inputs[i] = txin |
|
|
print_error("adding signature for", pubkey) |
|
|
|
|
|
# replace x_pubkey |
|
|
|
|
|
i = txin['pubkeys'].index(pubkey) |
|
|
|
|
|
txin['x_pubkeys'][i] = pubkey |
|
|
|
|
|
|
|
|
|
|
|
self.raw = self.serialize(self.inputs, self.outputs) |
|
|
self.raw = self.serialize(self.inputs, self.outputs) |
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -603,7 +607,7 @@ class Transaction: |
|
|
r = 0 |
|
|
r = 0 |
|
|
s = 0 |
|
|
s = 0 |
|
|
for txin in self.inputs: |
|
|
for txin in self.inputs: |
|
|
signatures = txin.get("signatures",[]) |
|
|
signatures = filter(lambda x: x is not None, txin['signatures']) |
|
|
s += len(signatures) |
|
|
s += len(signatures) |
|
|
r += txin['num_sig'] |
|
|
r += txin['num_sig'] |
|
|
return s, r |
|
|
return s, r |
|
@ -619,15 +623,13 @@ class Transaction: |
|
|
|
|
|
|
|
|
for i, txin in enumerate(self.inputs): |
|
|
for i, txin in enumerate(self.inputs): |
|
|
|
|
|
|
|
|
redeem_pubkeys = txin['pubkeys'] |
|
|
|
|
|
num = len(redeem_pubkeys) |
|
|
|
|
|
|
|
|
|
|
|
# get list of already existing signatures |
|
|
|
|
|
signatures = txin.get("signatures",{}) |
|
|
|
|
|
# continue if this txin is complete |
|
|
# continue if this txin is complete |
|
|
|
|
|
signatures = filter(lambda x: x is not None, txin['signatures']) |
|
|
|
|
|
num = txin['num_sig'] |
|
|
if len(signatures) == num: |
|
|
if len(signatures) == num: |
|
|
continue |
|
|
continue |
|
|
|
|
|
|
|
|
|
|
|
redeem_pubkeys = txin['pubkeys'] |
|
|
for_sig = Hash(self.tx_for_sig(i).decode('hex')) |
|
|
for_sig = Hash(self.tx_for_sig(i).decode('hex')) |
|
|
for pubkey in redeem_pubkeys: |
|
|
for pubkey in redeem_pubkeys: |
|
|
if pubkey in keypairs.keys(): |
|
|
if pubkey in keypairs.keys(): |
|
|