def increment_counter(counter_obj, prev_txid, prev_out_idx, funding_txid, funding_out_idx, unlock_key_priv, miner_fee): # Get data from previous counter tx r = requests.get('https://api.whatsonchain.com/v1/bsv/main/tx/{}'.format( prev_txid)).json() prev_locking_script = Script.from_hex( r['vout'][prev_out_idx]['scriptPubKey']['hex']) prev_counter_bytes = list(prev_locking_script.ops())[-1] prev_counter_val = int.from_bytes(prev_counter_bytes, 'little') unlocked_satoshis_counter = int(r['vout'][prev_out_idx]['value'] * 10**8) # Get data from funding tx r = requests.get('https://api.whatsonchain.com/v1/bsv/main/tx/{}'.format( funding_txid)).json() funding_locking_script = Script.from_hex( r['vout'][funding_out_idx]['scriptPubKey']['hex']) unlocked_satoshis_funding = int(r['vout'][funding_out_idx]['value'] * 10**8) # Set data for next iteration counter_obj.set_data_part( scryptlib.utils.get_push_int(prev_counter_val + 1)) ## Construct tx n_sequence = 0xffffffff # Counter input and output prev_tx_hash = hex_str_to_hash(prev_txid) counter_in = TxInput(prev_tx_hash, prev_out_idx, None, n_sequence) out_satoshis = unlocked_satoshis_counter + unlocked_satoshis_funding - miner_fee contract_out = TxOutput(out_satoshis, counter_obj.locking_script) # Funding input funding_tx_hash = hex_str_to_hash(funding_txid) funding_in = TxInput(funding_tx_hash, funding_out_idx, None, n_sequence) tx = Tx(2, [counter_in, funding_in], [contract_out], 0x00000000) # Set input script to unlock previous counter sighash_flag = SigHash(SigHash.ALL | SigHash.FORKID) preimage = scryptlib.utils.get_preimage(tx, 0, unlocked_satoshis_counter, prev_locking_script, sighash_flag) increment_func_call = counter_obj.increment(SigHashPreimage(preimage), Int(out_satoshis)) tx.inputs[0].script_sig = increment_func_call.script # Set input script to unlock funding output unlock_key_pub = unlock_key_priv.public_key sighash = tx.signature_hash(1, unlocked_satoshis_funding, funding_locking_script, sighash_flag) sig = unlock_key_priv.sign(sighash, hasher=None) sig = sig + pack_byte(sighash_flag) unlock_script = Script() << sig << unlock_key_pub.to_bytes() tx.inputs[1].script_sig = unlock_script broadcast_tx(tx)
def estimated_size(self): '''Return an estimated of serialized input size in bytes.''' saved_script_sig = self.script_sig x_pubkeys = [x_pubkey.to_public_key() for x_pubkey in self.x_pubkeys] signatures = [dummy_signature] * self.threshold self.script_sig = self._realize_script_sig(x_pubkeys, signatures) size = len(TxInput.to_bytes(self)) # base class implementation self.script_sig = saved_script_sig return size
def test_merge(input_idx, balance0, balance1): context = scryptlib.utils.create_dummy_input_context() context.utxo.value = in_sats context.input_index = input_idx tx_in = TxInput(context.tx.inputs[0].prev_hash, 1, Script(), 0xffffffff) context.tx.inputs.append(tx_in) prev_txid = context.tx.inputs[0].prev_hash prevouts = prev_txid + b'\x00\x00\x00\x00' + prev_txid + b'\x01\x00\x00\x00' new_locking_script = Script(token.code_part.to_bytes() + b'\x23' + key_pub_2.to_bytes() + scryptlib.utils.get_push_int(balance0)[1:] + scryptlib.utils.get_push_int(balance1)[1:]) tx_out = TxOutput(value=out_sats, script_pubkey=new_locking_script) context.tx.outputs.append(tx_out) if input_idx == 0: balance = balance1 context.utxo.script_pubkey = locking_script_0 key_to_sign = key_priv_0 token.set_data_part(b'\x23' + data_part_0) else: balance = balance0 context.utxo.script_pubkey = locking_script_1 key_to_sign = key_priv_1 token.set_data_part(b'\x23' + data_part_1) sighash_flag = SigHash(SigHash.ALL | SigHash.FORKID) #preimage = scryptlib.utils.get_preimage_from_input_context(context, sighash_flag) if input_idx == 0: preimage = scryptlib.utils.get_preimage(context.tx, input_idx, in_sats, locking_script_0, sighash_flag=sighash_flag) else: preimage = scryptlib.utils.get_preimage(context.tx, input_idx, in_sats, locking_script_1, sighash_flag=sighash_flag) if input_idx == 0: sighash = context.tx.signature_hash(input_idx, in_sats, locking_script_0, sighash_flag) else: sighash = context.tx.signature_hash(input_idx, in_sats, locking_script_1, sighash_flag) sig = key_to_sign.sign(sighash, hasher=None) sig = sig + pack_byte(sighash_flag) return token.merge( Sig(sig), PubKey(key_pub_2), Bytes(prevouts), balance, out_sats, SigHashPreimage(preimage) ).verify(context)
def create_input_context(utxo_satoshis, utxo_locking_script, new_out): tx_version = 2 tx_locktime = 0x00000000 utxo = TxOutput(utxo_satoshis, utxo_locking_script) prev_tx = Tx(tx_version, [], [utxo], tx_locktime) prev_txid = prev_tx.hash() utxo_idx = 0 n_sequence = 0xffffffff unlocking_script = Script() curr_in = TxInput(prev_txid, utxo_idx, unlocking_script, n_sequence) curr_tx = Tx(tx_version, [curr_in], [new_out], tx_locktime) input_idx = 0 return TxInputContext(curr_tx, input_idx, utxo, is_utxo_after_genesis=True)
def create_dummy_input_context(): ''' Creates dummy instance of bitoinx.TxInputContext with empty locking and unlocking scripts. ''' tx_version = 2 tx_locktime = 0x00000000 utxo_satoshis = 0 script_pubkey = Script() utxo = TxOutput(utxo_satoshis, script_pubkey) prev_tx = Tx(tx_version, [], [utxo], tx_locktime) prev_txid = prev_tx.hash() utxo_idx = 0 script_sig = Script() n_sequence = 0xffffffff curr_in = TxInput(prev_txid, utxo_idx, script_sig, n_sequence) curr_tx = Tx(tx_version, [curr_in], [], tx_locktime) input_idx = 0 return TxInputContext(curr_tx, input_idx, utxo, is_utxo_after_genesis=True)
def initialize_counter(counter_obj, counter_initial_val, funding_txid, funding_out_idx, \ unlock_key_priv, miner_fee, contract_out_sats, change_addr): counter_obj.set_data_part( scryptlib.utils.get_push_int(counter_initial_val)) # Funding TX funding_tx_hash = hex_str_to_hash(funding_txid) unlock_key_pub = unlock_key_priv.public_key r = requests.get('https://api.whatsonchain.com/v1/bsv/main/tx/{}'.format( funding_txid)).json() funding_locking_script = Script.from_hex( r['vout'][funding_out_idx]['scriptPubKey']['hex']) unlocked_satoshis = int(r['vout'][funding_out_idx]['value'] * 10**8) n_sequence = 0xffffffff tx_input = TxInput(funding_tx_hash, funding_out_idx, None, n_sequence) # Output with counter script code contract_out = TxOutput(contract_out_sats, counter_obj.locking_script) # Change output tx_output_script = P2PKH_Address.from_string(change_addr, Bitcoin).to_script() change_out = TxOutput(unlocked_satoshis - miner_fee - contract_out_sats, tx_output_script) tx = Tx(2, [tx_input], [contract_out, change_out], 0x00000000) # Create signature for input sighash_flag = SigHash(SigHash.ALL | SigHash.FORKID) sighash = tx.signature_hash(0, unlocked_satoshis, funding_locking_script, sighash_flag) sig = unlock_key_priv.sign(sighash, hasher=None) sig = sig + pack_byte(sighash_flag) # Set script for input unlock_script = Script() << sig << unlock_key_pub.to_bytes() tx.inputs[0].script_sig = unlock_script broadcast_tx(tx)
def size(self) -> int: return len(TxInput.to_bytes(self))
def random_input(): sequence = SEQUENCE_FINAL if random_bool() else randrange(0, SEQUENCE_FINAL) return TxInput(urandom(32), randrange(0, 4), random_script(), sequence)
P2PKH = scryptlib.contract.build_contract_class(desc) p2pkh_obj = P2PKH(Ripemd160(addr_dest)) prev_tx_hash = hex_str_to_hash(prev_txid) prev_out_idx = 0 r = requests.get( 'https://api.whatsonchain.com/v1/bsv/main/tx/{}'.format(prev_txid)).json() prev_locking_script = Script.from_hex( r['vout'][prev_out_idx]['scriptPubKey']['hex']) unlocked_satoshis = int(r['vout'][prev_out_idx]['value'] * 10**8) out_satoshis = unlocked_satoshis - miner_fee n_sequence = 0xffffffff tx_input = TxInput(prev_tx_hash, prev_out_idx, None, n_sequence) tx_output = TxOutput(out_satoshis, p2pkh_obj.locking_script) tx = Tx(2, [tx_input], [tx_output], 0x00000000) sighash_flag = SigHash(SigHash.ALL | SigHash.FORKID) sighash = tx.signature_hash(0, unlocked_satoshis, prev_locking_script, sighash_flag) sig = unlock_key_priv.sign(sighash, hasher=None) sig = sig + pack_byte(sighash_flag) unlock_script = Script() << sig << unlock_key_pub.to_bytes() tx.inputs[0].script_sig = unlock_script ######## Broadcast transaction ######## import json