def test_eight_bytes(self): res = tx.VarInt(0xffffffffffffffff) self.assertEqual(res, b'\xff' + (b'\xff' * 8)) self.assertIsInstance(res, tx.VarInt) res = tx.VarInt(0x0123456789abcdef) self.assertEqual(res, b'\xff' + b'\xef\xcd\xab\x89\x67\x45\x23\x01') res = tx.VarInt(0x234000000000) # 6 bytes to test padding self.assertEqual(res, b'\xff' + b'\x00\x00\x00\x00\x40\x23\x00\x00')
def signed_refund_htlc_transaction(secret_hash: bytes, redeemer_pkh: bytes, timeout: int, funder_pkh: bytes, tx_id: str, index: int, prevout_value: int, address: str, privkey: bytes, fee: int = 0) -> tx.Tx: ''' Builds an entire Refund HTLC spend from scratch. ''' # build the unsigned version of the transaction t = spend_htlc_transaction(tx_id, index, prevout_value - fee, address, timeout) # Prep the witness program s = build_htlc_script(secret_hash, redeemer_pkh, timeout, funder_pkh) serialized_script = script.serialize(s) script_len = len(serialized_script) prepended_script = tx.VarInt(script_len).to_bytes() + serialized_script # calculate sighash using the witness program sighash = t.sighash_all(index=index, script=prepended_script, prevout_value=rutils.i2le_padded(prevout_value, 8)) # sign it and make the witness signature = crypto.sign_digest(sighash, privkey) witness = htlc_refund_witness(s, signature, crypto.priv_to_pub(privkey)) # insert the witness into the tx return t.copy(tx_witnesses=[witness])
def _packetize_input_for_signing(tx_in: tx.TxIn, prevout_info: PrevoutInfo) -> List[bytes]: '''Turn an input into a set of packets for the last step of signing''' chunks = [] if prevout_info['witness_script'] is None: raise ValueError('Packet for signing must have a script') script = cast(bytes, prevout_info['witness_script']) le_value = rutils.i2le_padded(prevout_info['value'], 8) script_len_bytes = tx.VarInt(len(script)).to_bytes() # the first packt is the outpoint and value chunks.append(b'\x02' # 02 is ledger-speak for segwit input + tx_in.outpoint.to_bytes() + le_value + script_len_bytes) # Chunk into 50-byte chunks chunks.extend([script[i:i + 50] for i in range(0, len(script), 50)]) # append the sequence to the last one chunks[-1] = chunks[-1] + tx_in.sequence return [_transaction_continue_packet(chunk) for chunk in chunks]
def _packetize_vout(tx_outs: Tuple[tx.TxOut]) -> List[bytes]: '''Converts the output vector into adpu packets''' # first get the whole length-prefixed vector data_to_be_chunked = bytearray() data_to_be_chunked.extend(tx.VarInt(len(tx_outs)).to_bytes()) for tx_out in tx_outs: data_to_be_chunked.extend(tx_out.to_bytes()) # chunk it into 50 byte chunks chunks = [ data_to_be_chunked[i:i + 50] # chunk the data for i in range(0, len(data_to_be_chunked), 50) ] # make continue packets for all but the last one packets = [] packets.extend([_output_continue_packet(chunk) for chunk in chunks[:-1]]) # the last one is a final packet packets.append(_output_final_packet(chunks[-1])) # return all the adpu packets return packets
def _packetize_version_and_vin_length(t: tx.Tx) -> bytes: '''The first packet sent to UNTRUSTED HASH TRANSACTION INPUT START''' # will break on bullshit like non-compact VarInts chunk = t.version + tx.VarInt(len(t.tx_ins)).to_bytes() return _transaction_start_packet(chunk)
def length_prepend(byte_string): ''' bytes -> bytes ''' length = tx.VarInt(len(byte_string)) return length.to_bytes() + byte_string
def test_four_bytes(self): res = tx.VarInt(0xffffffff) self.assertEqual(res, b'\xfe' + (b'\xff' * 4)) self.assertIsInstance(res, tx.VarInt)
def test_two_bytes(self): res = tx.VarInt(0xffff) self.assertEqual(res, b'\xfd' + (b'\xff' * 2)) self.assertIsInstance(res, tx.VarInt)
def test_one_byte_boundary(self): res = tx.VarInt(0xff) self.assertEqual(res, b'\xfd' + b'\xff\x00') self.assertIsInstance(res, tx.VarInt)
def test_one_byte(self): res = tx.VarInt(0xfb) self.assertEqual(res, b'\xfb') self.assertIsInstance(res, tx.VarInt)
def test_copy(self): res = tx.VarInt(0xffffffffffffffff) copy = res.copy() self.assertEqual(res, copy) self.assertIsNot(res, copy)
def test_too_high(self): with self.assertRaises(ValueError) as context: tx.VarInt(2**64 + 1) self.assertIn('VarInt cannot be greater than (2 ** 64) - 1.', str(context.exception))
def test_negative(self): with self.assertRaises(ValueError) as context: tx.VarInt(-5) self.assertIn('VarInt cannot be less than 0.', str(context.exception))