def test_everything(self): version = utils.i2le_padded(1, 4) outpoint_index = utils.i2le_padded(0, 4) outpoint_tx_id = bytearray( bytearray.fromhex('10399b3f20cbdd4b5ac3f823afdba28b' '9f70e21437a59b312a1b62c42c5cd101'))[::-1] outpoint = tx.Outpoint(outpoint_tx_id, outpoint_index) sequence = utils.i2le_padded(0, 4) script = bytearray( bytearray.fromhex( '473044022000e02ea97289a35181a9bfabd324f12439410db11c4e94978cdade6a665bf1840220458b87c34d8bb5e4d70d01041c7c2d714ea8bfaca2c2d2b1f9e5749c3ee17e3d012102ed0851f0b4c4458f80e0310e57d20e12a84642b8e097fe82be229edbd7dbd53920f6665740b1f950eb58d646b1fae9be28cef842da5e51dc78459ad2b092e7fd6e514c5163a914bb408296de2420403aa79eb61426bb588a08691f8876a91431b31321831520e346b069feebe6e9cf3dd7239c670400925e5ab17576a9140d22433293fe9652ea00d21c5061697aef5ddb296888ac' )) # noqa: E501 tx_in = tx.TxIn(outpoint, script, bytearray(), sequence) tx_ins = [tx_in] tx_outs = [ tx.TxOut( value=bytearray(utils.i2le_padded(2000, 8)), output_script=bytearray( bytearray.fromhex( '76a914f2539f42058da784a9d54615ad074436cf3eb85188ac') )) # noqa: E501 ] lock_time = utils.i2le_padded(0, 4) res = tx.Tx(version, None, tx_ins, tx_outs, None, lock_time) self.assertEqual(res, helpers.RAW_P2SH_TO_P2PKH)
def test_txhash(self): ''' https://github.com/decred/dcrd/blob/master/wire/msgtx_test.go#L139-L140 ''' outpoint = tx.DecredOutpoint(tx_id=b'\x00' * 32, index=b'\xff' * 4, tree=b'\x00') tx_ins = [tx.DecredTxIn(outpoint=outpoint, sequence=b'\xff' * 4)] tx_outs = [ tx.DecredTxOut(value=utils.i2le_padded(5000000000, 8), version=b'\xf0\xf0', output_script=helpers.DCR['ser']['hash_pk']) ] tx_witnesses = [ tx.DecredInputWitness( value=utils.i2le_padded(5000000000, 8), height=b'\x34' * 4, index=b'\x2E' * 4, stack_script=bytes([0x04, 0x31, 0xdc, 0x00, 0x1b, 0x01, 0x62]), redeem_script=b'') ] version = helpers.DCR['ser']['version'] t = tx.DecredTx(version=version, tx_ins=tx_ins, tx_outs=tx_outs, lock_time=b'\x00' * 4, expiry=b'\x00' * 4, tx_witnesses=tx_witnesses) self.assertEqual(t.tx_id, helpers.DCR['ser']['tx']['expected_hash'])
def make_tx(version, tx_ins, tx_outs, lock_time, expiry=None, value_balance=None, tx_shielded_spends=None, tx_shielded_outputs=None, tx_witnesses=None, tx_joinsplits=None, joinsplit_pubkey=None, joinsplit_sig=None, binding_sig=None): ''' int, list(TxIn), list(TxOut), int, list(InputWitness) -> Tx ''' n = riemann.get_current_network_name() if 'decred' in n: return tx.DecredTx( version=utils.i2le_padded(version, 4), tx_ins=tx_ins, tx_outs=tx_outs, lock_time=utils.i2le_padded(lock_time, 4), expiry=utils.i2le_padded(expiry, 4), tx_witnesses=[tx_witnesses]) if 'sprout' in n and tx_joinsplits is not None: return tx.SproutTx( version=version, tx_ins=tx_ins, tx_outs=tx_outs, lock_time=utils.i2le_padded(lock_time, 4), tx_joinsplits=tx_joinsplits if tx_joinsplits is not None else [], joinsplit_pubkey=joinsplit_pubkey, joinsplit_sig=joinsplit_sig) if 'overwinter' in n: return tx.OverwinterTx( tx_ins=tx_ins, tx_outs=tx_outs, lock_time=utils.i2le_padded(lock_time, 4), expiry_height=utils.i2le_padded(expiry, 4), tx_joinsplits=tx_joinsplits if tx_joinsplits is not None else [], joinsplit_pubkey=joinsplit_pubkey, joinsplit_sig=joinsplit_sig) if 'sapling' in n: return tx.SaplingTx( tx_ins=tx_ins, tx_outs=tx_outs, lock_time=utils.i2le_padded(lock_time, 4), expiry_height=utils.i2le_padded(expiry, 4), value_balance=utils.i2le_padded[value_balance], tx_shielded_spends=(tx_shielded_spends if tx_shielded_spends is not None else []), tx_shielded_outputs=(tx_shielded_outputs if tx_shielded_outputs is not None else []), tx_joinsplits=tx_joinsplits if tx_joinsplits is not None else [], joinsplit_pubkey=joinsplit_pubkey, joinsplit_sig=joinsplit_sig, binding_sig=binding_sig) flag = riemann.network.SEGWIT_TX_FLAG \ if tx_witnesses is not None else None return tx.Tx(version=utils.i2le_padded(version, 4), flag=flag, tx_ins=tx_ins, tx_outs=tx_outs, tx_witnesses=tx_witnesses, lock_time=utils.i2le_padded(lock_time, 4))
def make_outpoint(tx_id_le, index, tree=None): ''' byte-like, int, int -> Outpoint ''' if 'decred' in riemann.get_current_network_name(): return tx.DecredOutpoint(tx_id=tx_id_le, index=utils.i2le_padded(index, 4), tree=utils.i2le_padded(tree, 1)) return tx.Outpoint(tx_id=tx_id_le, index=utils.i2le_padded(index, 4))
def make_legacy_input(outpoint, stack_script, redeem_script, sequence): ''' Outpoint, byte-like, byte-like, int -> TxIn ''' if 'decred' in riemann.get_current_network_name(): return tx.DecredTxIn( outpoint=outpoint, sequence=utils.i2le_padded(sequence, 4)) return tx.TxIn(outpoint=outpoint, stack_script=stack_script, redeem_script=redeem_script, sequence=utils.i2le_padded(sequence, 4))
def make_wpkh_witness( t: tx.Tx, input_index: int, prevout_value: int, privkey: bytes ) -> tx.InputWitness: ''' Make a witness for a transaction spending a native-Segwith WPKH output. Args: t: A transaction. You can use the output of `spend_utxo_to_address` input_index: Which input should this function sign? prevout_value: What is the size of the UTXO being spent? privkey: The 32-byte private key to sign with Return: A SegWit witness signing `t` ''' pubkey = crypto.priv_to_pub(privkey) sighash = t.sighash_all( index=input_index, script=b'\x16\x00\x14' + rutils.hash160(pubkey), prevout_value=rutils.i2le_padded(prevout_value, 8)) signature = crypto.sign_digest(sighash, privkey) return tx_builder.make_witness([signature, pubkey])
def partial(tx_id: str, index: int, prevout_value: int, recipient_addr: str, output_value: int, lock_time: int, keypair: KeyPair) -> Tx: ''' Makes a partial_tx from human readable information Args: tx_id (str): txid of parent tx index (int): index of input in parent tx prevout_value (int): value in satoshi of the input recipient_addr (str): address of the recipient output_value (int): value in satoshi of the output lock_time (int): desired lock_time in bitcoin format keypair (tuple(str, str)): privkey as hex, pubkey as hex Returns: (riemann.tx.Tx): The signed transaction ''' outpoint = simple.outpoint(tx_id, index) pub = bytes.fromhex(keypair[1]) pkh = rutils.hash160(pub) output_script = b'\x19\x76\xa9\x14' + pkh + b'\x88\xac' # Assume PKH unsigned = make_partial_tx(outpoint=outpoint, output_value=output_value, output_address=recipient_addr, lock_time=lock_time) signed = sign_partial_tx(partial_tx=unsigned, keypair=keypair, prevout_script=output_script, prevout_value=rutils.i2le_padded( prevout_value, 8)) return signed
def create_dummy_transaction(prev_tx_id=None, prev_out_index=None): """ Creates a 1-1 transaction that is structurally correct, but spends from probably not valid outputs. If ``prev_tx_id`` is passed, the previous transaction id is set to the received value, otherwise it it randomly generated. In a similar way, if ``prev_out_index`` is passed, the previous index is set to it, otherwise it it set to ``0``. Args: prev_tx_id (:obj:`str`): the previous transaction id from where the new transaction will spend. If ``None``, a random one is generated. prev_out_index (:obj:`int`): the previous output index from where the new transaction will spend. If ``None``, ``0`` is set. Returns: :obj:`Tx <riemann.tx.tx.Tx>`: a ``Tx`` object representing the new generated transaction. """ if prev_tx_id is None or len(prev_tx_id) != 64: prev_tx_id = utils.get_random_value_hex(32) idx = prev_out_index if prev_out_index is not None else 0 prev_out_index_bytes = rutils.i2le_padded(idx, 4).hex() dummy_hex = f"0100000001{prev_tx_id}{prev_out_index_bytes}4847304402204e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d624c6c61548ab5fb8cd410220181522ec8eca07de4860a4acdd12909d831cc56cbbac4622082221a8768d1d0901ffffffff0100f2052a01000000434104ae1a62fe09c5f51b13905f07f06b99a2f7159b2225f374cd378d71302fa28414e7aab37397f554a7df5f142c21c1b7303b8a0626f1baded5c72a704f7e6cd84cac00000000" return tx.Tx.from_hex(dummy_hex)
def make_sh_output(value, output_script, witness=False): ''' int, str -> TxOut ''' return _make_output( value=utils.i2le_padded(value, 8), output_script=make_sh_output_script(output_script, witness))
def make_pkh_output(value, pubkey, witness=False): ''' int, bytearray -> TxOut ''' return _make_output( value=utils.i2le_padded(value, 8), output_script=make_pkh_output_script(pubkey, witness))
def createFundingTx(self, funder_amount, funder_pubkey, fundee_pubkey, change_address=None): if not self.input_utxo_specified: sys.exit("Did not specify source of funds.") msig_addr, funding_redeem_script = createMultiSigAddress(funder_pubkey, fundee_pubkey, verbose=self.verbose) # create tx input tx_ins = [tb.make_legacy_input(outpoint=self.outpoint, stack_script=b"", redeem_script=self.scriptsig, sequence=0xFFFFFFFF)] if self.verbose: print("Input: ", tx_ins) tx_outs = [simple.output(address=msig_addr, value=funder_amount)] if self.verbose: print("Output: ", tx_outs) need_change_output = True if self.utxo_amount > funder_amount else False if need_change_output: change_output_amount = self.utxo_amount - funder_amount - self.network_fee tx_outs += [simple.output(address=self.change_address, value=change_output_amount)] if BITCOIN in self.network: unsigned_tx = simple.unsigned_legacy_tx(tx_ins, tx_outs) elif ZCASH in self.network: unsigned_tx = simple.unsigned_legacy_tx(tx_ins, tx_outs) script_code1 = b'\x19' + addresses.to_output_script(msig_addr) # TODO: computing sighash_all => verify that this is done correctly sighash = unsigned_tx.sighash_all(index=0, script=script_code1, prevout_value=utils.i2le_padded(funder_amount, 8)) # NOTE: for dual-funded channel, funder_bal = funder_amount + fundee_amount funding_tx = {'funding_tx_id': unsigned_tx.tx_id.hex(), 'funding_bal': funder_amount, 'funding_address': str(msig_addr), 'funding_witness_script': funding_redeem_script} return funding_tx, unsigned_tx.hex()
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 check_for_known_outpoints(outpoint_list: List[Outpoint]) -> List[Outpoint]: ''' Finds all prevouts we know of from a list of outpoints Useful for checking whether the DB already knows about specific prevouts ''' # NB: We want to flatten the outpoint to look it up in the DB flattened_list: List[str] = [] for o in outpoint_list: flat_outpoint = '{tx_id}{index}'.format( tx_id=utils.reverse_hex(o['tx_id']), index=rutils.i2le_padded(o['index'], 4).hex()) flattened_list.append(flat_outpoint) c = connection.get_cursor() try: question_marks = ', '.join(['?' for _ in range(len(outpoint_list))]) cursor = c.execute( ''' SELECT tx_id, idx FROM prevouts WHERE outpoint IN ({question_marks}) '''.format(question_marks=question_marks), flattened_list) res = [Outpoint(tx_id=p['tx_id'], index=p['idx']) for p in cursor] return res finally: c.close()
def make_op_return_output(data: bytes) -> tx.TxOut: ''' Generates OP_RETURN output for data of up to 77 bytes. OP_RETURN outputs are data carriers with no impact on the UTXO set. They are comonly used to create on-chain commitments to some off-chain information. There are few consensus constraints on their content or structure, however they become non-standard above 77 bytes. Args: data (bytes): data to be included in output Returns: (TxOut): TxOut object with OP_RETURN output ''' if len(data) > 77: # 77 bytes is the limit raise ValueError('Data is too long. Expected <= 77 bytes') pk_script = bytearray() pk_script.extend(b'\x6a') # OP_RETURN # OP_PUSHDATA1 only used if data is greater than 75 bytes if len(data) in [76, 77]: pk_script.extend(b'\x4c') # OP_PUSHDATA1 pk_script.extend([len(data)]) # One byte for length of data pk_script.extend(data) # Data return _make_output(utils.i2le_padded(0, 8), pk_script)
def _segwit_sighash_adjustment( self, sighash_type: int, anyone_can_pay: bool) -> bytes: # sighash type altered to include ANYONECANPAY if anyone_can_pay: sighash_type = sighash_type | shared.SIGHASH_ANYONECANPAY return utils.i2le_padded(sighash_type, 4)
def make_witness_input(outpoint, sequence): # noqa: F811 ''' Make a Segwit input. This is clearly superior to `make_legacy_input` and you should use witness always. Args: outpoint: The Outpoint object sequence: The 4-byte LE-encoded sequence number Returns: A Segwit TxIn object. ''' if 'decred' in riemann.get_current_network_name(): return decred.DecredTxIn(outpoint=outpoint, sequence=utils.i2le_padded(sequence, 4)) return tx.TxIn(outpoint=outpoint, stack_script=b'', redeem_script=b'', sequence=utils.i2le_padded(sequence, 4))
def make_outpoint(tx_id_le, index, tree=None): # noqa: F811 ''' Instantiate an Outpoint object from a transaction id and an index. Args: tx_id_le: The 32-byte LE hash of the transaction that created the prevout being referenced. index: The index of the TxOut that created the prevout in its transaction's output vector tree: Only in Decred transactions. Specifies the commitment tree. Returns: An Outpoint object. If network is set to Decred, a DecredOutpoint ''' if 'decred' in riemann.get_current_network_name(): tree_bytes = b'\x00' if tree is None else utils.i2le_padded(tree, 1) return decred.DecredOutpoint(tx_id=tx_id_le, index=utils.i2le_padded(index, 4), tree=tree_bytes) return tx.Outpoint(tx_id=tx_id_le, index=utils.i2le_padded(index, 4))
def _sighash_final_hashing(self, copy_tx, sighash_type): ''' SproutTx, int -> bytes Returns the hash that should be signed https://en.bitcoin.it/wiki/OP_CHECKSIG#Procedure_for_Hashtype_SIGHASH_ANYONECANPAY ''' sighash = z.ZcashByteData() sighash += copy_tx.to_bytes() sighash += utils.i2le_padded(sighash_type, 4) return utils.hash256(sighash.to_bytes())
def _flatten_prevout(prevout: Prevout) -> PrevoutEntry: outpoint = '{tx_id}{index}'.format( tx_id=utils.reverse_hex(prevout['outpoint']['tx_id']), index=rutils.i2le_padded(prevout['outpoint']['index'], 4).hex()) return { 'outpoint': outpoint, 'tx_id': prevout['outpoint']['tx_id'], 'idx': prevout['outpoint']['index'], 'value': prevout['value'], 'spent_at': prevout['spent_at'], 'spent_by': prevout['spent_by'], 'address': prevout['address'] }