Skip to content

Commit

Permalink
Merge branch 'sighash-improvements'
Browse files Browse the repository at this point in the history
  • Loading branch information
prestwich committed Oct 31, 2018
2 parents 1f6efe4 + 3a8254d commit 4c1abf3
Show file tree
Hide file tree
Showing 5 changed files with 40 additions and 66 deletions.
10 changes: 0 additions & 10 deletions riemann/tests/tx/test_tx.py
Expand Up @@ -735,13 +735,3 @@ def test_sighash_forkid_all_anyone_can_pay(self):
self.assertEqual(
sighash,
helpers.SIGHASH_FORKID['all_anyone_can_pay'])

def test_get_script_code(self):
tx_ins = [tx.TxIn.from_bytes(
helpers.P2SH['ser']['ins'][0]['input'])]
t = tx.Tx(self.version, self.none_flag, tx_ins, self.tx_outs,
self.none_witnesses, self.lock_time)

self.assertEqual(
t._get_script_code(0),
helpers.P2SH['ser']['ins'][0]['redeem_script'])
13 changes: 1 addition & 12 deletions riemann/tx/decred.py
Expand Up @@ -304,25 +304,14 @@ def copy(self, version=None, tx_ins=None, tx_outs=None,
tx_witnesses=(tx_witnesses if tx_witnesses is not None
else self.tx_witnesses))

def _get_script_code(self, index):
if len(self.tx_witnesses[index].redeem_script) != 0:
script = DecredByteData()
# redeemScript in case of P2SH
script += self.tx_witnesses[index].redeem_script
return script.to_bytes()
return b''

def sighash_none(self):
raise NotImplementedError('SIGHASH_NONE is a bad idea.')

def _sighash_prep(self, index, script=None):
sub_script = self._get_script_code(index)
if sub_script == b'':
sub_script = script
copy_tx_witnesses = [w.copy(stack_script=b'', redeem_script=b'')
for w in self.tx_witnesses]
copy_tx_witnesses[index] = \
copy_tx_witnesses[index].copy(stack_script=sub_script,
copy_tx_witnesses[index].copy(stack_script=script,
redeem_script=b'')

return self.copy(tx_witnesses=copy_tx_witnesses)
Expand Down
16 changes: 1 addition & 15 deletions riemann/tx/sprout.py
Expand Up @@ -216,17 +216,6 @@ def copy(self, version=None, tx_ins=None, tx_outs=None, lock_time=None,
joinsplit_sig=(joinsplit_sig if joinsplit_sig is not None
else self.joinsplit_sig))

def _get_script_code(self, index):
'''
SproutTx, int -> bytes
'''
if len(self.tx_ins) > 0 and len(self.tx_ins[index].redeem_script) > 0:
script = z.ZcashByteData()
# redeemScript in case of P2SH
script += self.tx_ins[index].redeem_script
return script.to_bytes()
return b''

def _sighash_prep(self, index, script):
'''
SproutTx, int, byte-like -> SproutTx
Expand All @@ -236,9 +225,6 @@ def _sighash_prep(self, index, script):
https://bitcoin.stackexchange.com/questions/3374/how-to-redeem-a-basic-tx
We save on complexity by refusing to support OP_CODESEPARATOR
'''
sub_script = self._get_script_code(index=index)
if sub_script == b'':
sub_script = script

if len(self.tx_ins) == 0:
return self.copy(joinsplit_sig=b'')
Expand All @@ -249,7 +235,7 @@ def _sighash_prep(self, index, script):
# NB: The script for the current transaction input in txCopy is set to
# subScript (lead in by its length as a var-integer encoded!)
copy_tx_ins[index] = \
copy_tx_ins[index].copy(stack_script=b'', redeem_script=sub_script)
copy_tx_ins[index].copy(stack_script=b'', redeem_script=script)

return self.copy(tx_ins=copy_tx_ins, joinsplit_sig=b'')

Expand Down
65 changes: 37 additions & 28 deletions riemann/tx/tx.py
Expand Up @@ -410,7 +410,7 @@ def no_witness(self):
return bytes(tx)

def is_witness(self):
return self.flag is not None
return self.flag is not None or self.tx_witnesses is not None

def calculate_fee(self, input_values):
'''
Expand Down Expand Up @@ -450,17 +450,14 @@ def _sighash_prep(self, index, script):
https://bitcoin.stackexchange.com/questions/3374/how-to-redeem-a-basic-tx
We save on complexity by refusing to support OP_CODESEPARATOR
'''
sub_script = self._get_script_code(index=index)
if sub_script == b'':
sub_script = script
# 0 out scripts in tx_ins
copy_tx_ins = [tx_in.copy(stack_script=b'', redeem_script=b'')
for tx_in in self.tx_ins]

# NB: The script for the current transaction input in txCopy is set to
# subScript (lead in by its length as a var-integer encoded!)
copy_tx_ins[index] = \
copy_tx_ins[index].copy(stack_script=b'', redeem_script=sub_script)
copy_tx_ins[index].copy(stack_script=b'', redeem_script=script)

return self.copy(tx_ins=copy_tx_ins)

Expand All @@ -470,7 +467,7 @@ def sighash_all(self, index, script=None,
Tx, int, byte-like, byte-like, bool -> bytearray
Sighashes suck
Generates the hash to be signed with shared.SIGHASH_ALL
https://en.bitcoin.it/wiki/OP_CHECKSIG#Hashtype_shared.SIGHASH_ALL_.28default.29
https://en.bitcoin.it/wiki/OP_CHECKSIG#Hashtype_SIGHASH_ALL_.28default.29
'''

if riemann.network.FORKID is not None:
Expand Down Expand Up @@ -572,9 +569,7 @@ def segwit_sighash(self, index, script, prevout_value=None,
data += self.tx_ins[index].outpoint

# 5. scriptCode of the input (serialized as scripts inside CTxOuts)
data += self._adjusted_script_code(
index=index,
script=script)
data += self._adjusted_script_code(script=script)

# 6. value of the output spent by this input (8-byte little endian)
data += prevout_value
Expand Down Expand Up @@ -636,6 +631,14 @@ def _hash_prevouts(self, anyone_can_pay):
return hash_prevouts

def _hash_sequence(self, sighash_type, anyone_can_pay):
'''BIP143 hashSequence implementation
Args:
sighash_type (int): SIGHASH_SINGLE or SIGHASH_ALL
anyone_can_pay (bool): true if ANYONECANPAY should be set
Returns:
(bytes): the hashSequence, a 32 byte hash
'''
if anyone_can_pay or sighash_type == shared.SIGHASH_SINGLE:
# If any of ANYONECANPAY, SINGLE sighash type is set,
# hashSequence is a uint256 of 0x0000......0000.
Expand All @@ -647,26 +650,34 @@ def _hash_sequence(self, sighash_type, anyone_can_pay):
sequences += tx_in.sequence
return utils.hash256(sequences.to_bytes())

def _get_script_code(self, index):
if len(self.tx_ins[index].redeem_script) != 0:
script = ByteData()
# redeemScript in case of P2SH
script += self.tx_ins[index].redeem_script
return script.to_bytes()
return b''

def _adjusted_script_code(self, index, script):
def _adjusted_script_code(self, script):
'''
Checks if the script code pased in to the sighash function is already
length-prepended
This will break if there's a redeem script that's just a pushdata
That won't happen in practice
Args:
script (bytes): the spend script
Returns:
(bytes): the length-prepended script (if necessary)
'''
script_code = ByteData()
tx_in_redeem_script = self._get_script_code(index=index)
if tx_in_redeem_script == b'':
script_code += VarInt(len(script))
script_code += script
return script_code
script_code += VarInt(len(tx_in_redeem_script))
script_code += tx_in_redeem_script
if script[0] == len(script) - 1:
return script
script_code += VarInt(len(script))
script_code += script
return script_code

def _hash_outputs(self, index, sighash_type):
'''BIP143 hashOutputs implementation
Args:
index (int): index of input being signed
sighash_type (int): SIGHASH_SINGLE or SIGHASH_ALL
Returns:
(bytes): the hashOutputs, a 32 byte hash
'''
if sighash_type == shared.SIGHASH_ALL:
# If the sighash type is ALL,
# hashOutputs is the double SHA256 of all output amounts
Expand Down Expand Up @@ -725,9 +736,7 @@ def _sighash_forkid(self, index, script, prevout_value,
data += self.tx_ins[index].outpoint

# 5. scriptCode of the input (serialized as scripts inside CTxOuts)
data += self._adjusted_script_code(
index=index,
script=script)
data += self._adjusted_script_code(script=script)

# 6. value of the output spent by this input (8-byte little endian)
data += prevout_value
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Expand Up @@ -4,7 +4,7 @@

setup(
name='riemann-tx',
version='1.1.0',
version='1.1.1',
description=('Transaction creation library for Bitcoin-like coins'),
url='https://github.com/summa-tx/riemann',
author='James Prestwich',
Expand Down

0 comments on commit 4c1abf3

Please sign in to comment.