Skip to content

Commit

Permalink
Merge 6973f1a into 20443aa
Browse files Browse the repository at this point in the history
  • Loading branch information
SomberNight committed Jan 23, 2018
2 parents 20443aa + 6973f1a commit ccdd528
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 29 deletions.
2 changes: 1 addition & 1 deletion lib/coinchooser.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ def bucketize_coins(self, coins):
buckets[key].append(coin)

def make_Bucket(desc, coins):
witness = any(Transaction.is_segwit_input(coin) for coin in coins)
witness = any(Transaction.is_segwit_input(coin, guess_for_address=True) for coin in coins)
# note that we're guessing whether the tx uses segwit based
# on this single bucket
weight = sum(Transaction.estimated_input_weight(coin, witness)
Expand Down
88 changes: 61 additions & 27 deletions lib/transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -337,19 +337,30 @@ def parse_scriptSig(d, _bytes):

# p2sh transaction, m of n
match = [ opcodes.OP_0 ] + [ opcodes.OP_PUSHDATA4 ] * (len(decoded) - 1)
if not match_decoded(decoded, match):
print_error("cannot find address in input script", bh2u(_bytes))
if match_decoded(decoded, match):
x_sig = [bh2u(x[1]) for x in decoded[1:-1]]
m, n, x_pubkeys, pubkeys, redeemScript = parse_redeemScript(decoded[-1][1])
# write result in d
d['type'] = 'p2sh'
d['num_sig'] = m
d['signatures'] = parse_sig(x_sig)
d['x_pubkeys'] = x_pubkeys
d['pubkeys'] = pubkeys
d['redeemScript'] = redeemScript
d['address'] = hash160_to_p2sh(hash_160(bfh(redeemScript)))
return

# custom partial format for imported addresses
match = [ opcodes.OP_INVALIDOPCODE, opcodes.OP_0, opcodes.OP_PUSHDATA4 ]
if match_decoded(decoded, match):
d['type'] = 'address'
d['address'] = bitcoin.script_to_address(bh2u(decoded[2][1]))
d['num_sig'] = 1
d['x_pubkeys'] = [None]
d['signatures'] = [None]
return
x_sig = [bh2u(x[1]) for x in decoded[1:-1]]
m, n, x_pubkeys, pubkeys, redeemScript = parse_redeemScript(decoded[-1][1])
# write result in d
d['type'] = 'p2sh'
d['num_sig'] = m
d['signatures'] = parse_sig(x_sig)
d['x_pubkeys'] = x_pubkeys
d['pubkeys'] = pubkeys
d['redeemScript'] = redeemScript
d['address'] = hash160_to_p2sh(hash_160(bfh(redeemScript)))

print_error("cannot find address in input script", bh2u(_bytes))


def parse_redeemScript(s):
Expand Down Expand Up @@ -433,6 +444,8 @@ def parse_witness(vds, txin):
w = list(bh2u(vds.read_bytes(vds.read_compact_size())) for i in range(n))
if txin['type'] == 'coinbase':
pass
elif txin['type'] == 'address':
pass
elif n > 2:
txin['signatures'] = parse_sig(w[1:-1])
m, n, x_pubkeys, pubkeys, witnessScript = parse_redeemScript(bfh(w[-1]))
Expand Down Expand Up @@ -664,38 +677,58 @@ def get_siglist(self, txin, estimate_size=False):
@classmethod
def serialize_witness(self, txin, estimate_size=False):
add_w = lambda x: var_int(len(x)//2) + x
if not self.is_segwit_input(txin):
if not (self.is_segwit_input(txin) or txin['type'] == 'address'):
return '00'
pubkeys, sig_list = self.get_siglist(txin, estimate_size)
if txin['type'] in ['p2wpkh', 'p2wpkh-p2sh']:
_type = txin['type']
if estimate_size and _type == 'address':
_type = self.guess_txintype_from_address(txin['address'])
if _type in ['p2wpkh', 'p2wpkh-p2sh']:
pubkeys, sig_list = self.get_siglist(txin, estimate_size)
witness = var_int(2) + add_w(sig_list[0]) + add_w(pubkeys[0])
elif txin['type'] in ['p2wsh', 'p2wsh-p2sh']:
elif _type in ['p2wsh', 'p2wsh-p2sh']:
pubkeys, sig_list = self.get_siglist(txin, estimate_size)
n = len(sig_list) + 2
witness_script = multisig_script(pubkeys, txin['num_sig'])
witness = var_int(n) + '00' + ''.join(add_w(x) for x in sig_list) + add_w(witness_script)
else:
raise BaseException('wrong txin type')
witness = '00'
if self.is_txin_complete(txin) or estimate_size:
value_field = ''
else:
value_field = var_int(0xffffffff) + int_to_hex(txin['value'], 8)
return value_field + witness

@classmethod
def is_segwit_input(cls, txin):
return cls.is_segwit_inputtype(txin['type'])
def is_segwit_input(cls, txin, guess_for_address=False):
_type = txin['type']
if _type == 'address' and guess_for_address:
_type = cls.guess_txintype_from_address(txin['address'])
return cls.is_segwit_inputtype(_type)

@classmethod
def is_segwit_inputtype(cls, txin_type):
return txin_type in ('p2wpkh', 'p2wpkh-p2sh', 'p2wsh', 'p2wsh-p2sh')

@classmethod
def guess_txintype_from_address(cls, addr):
witver, witprog = segwit_addr.decode(NetworkConstants.SEGWIT_HRP, addr)
if witprog is not None:
return 'p2wpkh'
addrtype, hash_160 = b58_address_to_hash160(addr)
if addrtype == NetworkConstants.ADDRTYPE_P2PKH:
return 'p2pkh'
elif addrtype == NetworkConstants.ADDRTYPE_P2SH:
return 'p2wpkh-p2sh'

@classmethod
def input_script(self, txin, estimate_size=False):
_type = txin['type']
if _type == 'coinbase':
return txin['scriptSig']
pubkeys, sig_list = self.get_siglist(txin, estimate_size)
script = ''.join(push_script(x) for x in sig_list)
if _type == 'address' and estimate_size:
_type = self.guess_txintype_from_address(txin['address'])
if _type == 'p2pk':
pass
elif _type == 'p2sh':
Expand All @@ -716,7 +749,7 @@ def input_script(self, txin, estimate_size=False):
scriptSig = bitcoin.p2wsh_nested_script(witness_script)
return push_script(scriptSig)
elif _type == 'address':
script += push_script(pubkeys[0])
return 'ffff00' + push_script(bitcoin.address_to_script(txin['address']))
elif _type == 'unknown':
return txin['scriptSig']
return script
Expand Down Expand Up @@ -802,8 +835,8 @@ def serialize_preimage(self, i):
preimage = nVersion + txins + txouts + nLocktime + nHashType
return preimage

def is_segwit(self):
return any(self.is_segwit_input(x) for x in self.inputs())
def is_segwit(self, guess_for_address=False):
return any(self.is_segwit_input(x, guess_for_address=guess_for_address) for x in self.inputs())

def serialize(self, estimate_size=False, witness=True):
nVersion = int_to_hex(self.version, 4)
Expand All @@ -812,7 +845,9 @@ def serialize(self, estimate_size=False, witness=True):
outputs = self.outputs()
txins = var_int(len(inputs)) + ''.join(self.serialize_input(txin, self.input_script(txin, estimate_size)) for txin in inputs)
txouts = var_int(len(outputs)) + ''.join(self.serialize_output(o) for o in outputs)
if witness and self.is_segwit():
use_segwit_serialization = estimate_size and self.is_segwit(guess_for_address=True) or \
not estimate_size and (self.is_segwit() or any(txin['type'] == 'address' for txin in inputs))
if witness and use_segwit_serialization:
marker = '00'
flag = '01'
witness = ''.join(self.serialize_witness(x, estimate_size) for x in inputs)
Expand Down Expand Up @@ -872,8 +907,7 @@ def estimated_input_weight(cls, txin, is_segwit_tx):
script = cls.input_script(txin, True)
input_size = len(cls.serialize_input(txin, script)) // 2

if cls.is_segwit_input(txin):
assert is_segwit_tx
if cls.is_segwit_input(txin, guess_for_address=True):
witness_size = len(cls.serialize_witness(txin, True)) // 2
else:
witness_size = 1 if is_segwit_tx else 0
Expand All @@ -897,10 +931,10 @@ def estimated_total_size(self):

def estimated_witness_size(self):
"""Return an estimate of witness size in bytes."""
if not self.is_segwit():
estimate = not self.is_complete()
if not self.is_segwit(guess_for_address=estimate):
return 0
inputs = self.inputs()
estimate = not self.is_complete()
witness = ''.join(self.serialize_witness(x, estimate) for x in inputs)
witness_size = len(witness) // 2 + 2 # include marker and flag
return witness_size
Expand Down
7 changes: 6 additions & 1 deletion lib/wallet.py
Original file line number Diff line number Diff line change
Expand Up @@ -1111,7 +1111,8 @@ def add_input_info(self, txin):
if self.is_mine(address):
txin['type'] = self.get_txin_type(address)
# segwit needs value to sign
if txin.get('value') is None and Transaction.is_segwit_input(txin):
if txin.get('value') is None and \
(Transaction.is_segwit_input(txin) or txin['type'] == 'address'):
received, spent = self.get_addr_io(address)
item = received.get(txin['prevout_hash']+':%d'%txin['prevout_n'])
tx_height, value, is_cb = item
Expand All @@ -1121,6 +1122,9 @@ def add_input_info(self, txin):
def can_sign(self, tx):
if tx.is_complete():
return False
for txin in tx.inputs():
if txin['type'] == 'address':
self.add_input_info(txin)
for k in self.get_keystores():
if k.can_sign(tx):
return True
Expand Down Expand Up @@ -1570,6 +1574,7 @@ def add_input_sig_info(self, txin, address):
pubkey = self.addresses[address]['pubkey']
txin['num_sig'] = 1
txin['x_pubkeys'] = [pubkey]
txin['pubkeys'] = [pubkey]
txin['signatures'] = [None]
else:
redeem_script = self.addresses[address]['redeem_script']
Expand Down

0 comments on commit ccdd528

Please sign in to comment.