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 test_P2SH(self): script_hex = 'a9143e4501f9f212cb6813b3815edbc7013d6a3f0f1087' s = Script.from_hex(script_hex) sc = classify_output_script(s, Bitcoin) assert isinstance(sc, P2SH_Address) suffix = push_item(b'foobar') + pack_byte(OP_DROP) + pack_byte(OP_NOP) s2 = Script.from_hex(script_hex + suffix.hex()) sc2 = classify_output_script(s2, Bitcoin) assert s2 != s assert isinstance(sc2, P2SH_Address)
def test_P2PKH(self): script_hex = '76a914a6dbba870185ab6689f386a40522ae6cb5c7b61a88ac' s = Script.from_hex(script_hex) sc = classify_output_script(s, Bitcoin) assert isinstance(sc, P2PKH_Address) prefix = push_item(b'foobar') + pack_byte(OP_DROP) + pack_byte(OP_NOP) s2 = Script.from_hex(prefix.hex() + script_hex) sc2 = classify_output_script(s2, Bitcoin) assert s2 != s assert isinstance(sc2, P2PKH_Address)
def test_P2PK(self): script_hex = '210363f75554e05e05a04551e59d78d78965ec6789f42199f7cbaa9fa4bd2df0a4b4ac' s = Script.from_hex(script_hex) sc = classify_output_script(s, Bitcoin) assert isinstance(sc, P2PK_Output) assert (sc.public_key.to_hex() == '0363f75554e05e05a04551e59d78d78965ec6789f42199f7cbaa9fa4bd2df0a4b4') suffix = push_item(b'foo') + push_item(b'bar') + pack_byte(OP_2DROP) s2 = Script.from_hex(script_hex + suffix.hex()) sc2 = classify_output_script(s2, Bitcoin) assert sc2.public_key == sc.public_key assert s2 != s assert isinstance(sc2, P2PK_Output)
def test_P2MultiSig(self): script_hex = ('5221022812701688bc76ef3610b46c8e97f4b385241d5ed6eab6269b8af5f9bfd5a89c210' '3fa0879c543ac97f34daffdaeed808f3500811aa5070e4a1f7e2daed3dd22ef2052ae') s = Script.from_hex(script_hex) sc = classify_output_script(s, Bitcoin) assert isinstance(sc, P2MultiSig_Output) assert len(sc.public_keys) == 2 assert sc.threshold == 2 # Confirm suffix fails to match s = Script.from_hex(script_hex + 'a0') assert isinstance(classify_output_script(s, Bitcoin), Unknown_Output) # Confirm prefix fails to match s = Script.from_hex('a0' + script_hex) assert isinstance(classify_output_script(s, Bitcoin), Unknown_Output)
def test_unknown(self): # Modified final pubkey byte; not a curve point script_hex = '210363f75554e05e05a04551e59d78d78965ec6789f42199f7cbaa9fa4bd2df0a4b3ac' s = Script.from_hex(script_hex) sc = classify_output_script(s, Bitcoin) assert isinstance(sc, Unknown_Output) # Truncated script script_hex = '210363f7' s = Script.from_hex(script_hex) sc = classify_output_script(s, Bitcoin) assert isinstance(sc, Unknown_Output) # Unknown script script_hex = pack_byte(OP_1).hex() s = Script.from_hex(script_hex) sc = classify_output_script(s, Bitcoin) assert isinstance(sc, Unknown_Output)
def hex(self): self.bind() res_buff = [] flat_struct = utils.flatten_struct(self, '') for elem in flat_struct: res_buff.append(elem['value'].hex) return Script.from_hex(''.join(res_buff)).to_hex()
def from_dict(cls, data: dict) -> 'Output': if 'script' not in data: raise Bip270Exception("Missing required 'script' field") script_hex = data['script'] amount = data.get('amount') if amount is not None and type(amount) is not int: raise Bip270Exception("Invalid 'amount' field") description = data.get('description') if description is not None and type(description) is not str: raise Bip270Exception("Invalid 'description' field") return cls(Script.from_hex(script_hex), amount, description)
def encode_pub_function_call(self, contract, name, *args): for entity in self.abi: if entity['name'] == name: if len(entity['params']) != len(args): raise Exception('Wrong number of arguments passed to function call "{}", ' \ 'expected {}, but got {}.'.format(name, len(entity['params']), len(args))) hex_script = self.encode_params(args, entity['params']) if len(self.abi) > 2 and 'index' in entity: pub_func_index = entity['index'] hex_script += '{}'.format( types.Int(pub_func_index).hex) # TODO unlocking_script = Script.from_hex(hex_script) return FunctionCall(name, args, contract, unlocking_script=unlocking_script)
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)
"2df53273de1b740e6f566eba00d90366e53afd2c6af896a9488515f8ef5abbd8": [], # 5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ (P2PK) "7149d82068249701104cd662bfe8ebc0af131ce6e781b124cc6297f45f7f6de5": [ { "height": 50000, "value": 1804376, "tx_hash": "3f5a1badfe1beb42b650f325b20935f09f3ab43a3c473c5be18f58308fc7eff1", "tx_pos": 3, }, ], } result_S = ([ UTXO(value=45318048, script_pubkey=Script.from_hex( '76a914cb3e86e38ce37d5add87d3da753adc04a04bf60c88ac'), tx_hash= '9f2c45a12db0144909b5db269415f7319179105982ac70ed80d76ea79d923ebf', out_index=0, height=437146, address=address_from_string('1KXf5PUHNaV42jE9NbJFPKhGGN1fSSGJNK'), is_coinbase=False) ], { XPublicKey('04e7dd15b4271f8308ff52ad3d3e472b652e78a2c5bc6ed10250a543d28c0128894ae863d086488e6773c4589be93a1793f685dd3f1e8a1f1b390b23470f7d1095'): (b'\x98\xe3\x15\xc3%j\x97\x17\xd4\xdd\xea0\xeb*\n-V\xa1d\x93yN\xb0SSf\xea"\xd8i\xa3 ', False), XPublicKey('03e7dd15b4271f8308ff52ad3d3e472b652e78a2c5bc6ed10250a543d28c012889'): (b'\x98\xe3\x15\xc3%j\x97\x17\xd4\xdd\xea0\xeb*\n-V\xa1d\x93yN\xb0SSf\xea"\xd8i\xa3 ', True), XPublicKey('fd76a914cb3e86e38ce37d5add87d3da753adc04a04bf60c88ac'): (b'\x98\xe3\x15\xc3%j\x97\x17\xd4\xdd\xea0\xeb*\n-V\xa1d\x93yN\xb0SSf\xea"\xd8i\xa3 ',
prev_txid = 'c1543650beafbf646e75aeeae9b091e4c477362db4a18e740d3f9d2ae250c013' miner_fee = 120 contract = '../test/res/p2pkh.scrypt' compiler_result = scryptlib.utils.compile_contract(contract) desc = compiler_result.to_desc() 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)
def _script_to_address(script_hex): result = classify_output_script(Script.from_hex(script_hex)) assert isinstance(result, Address) return result
def get_ls_code_part(self, contract, hex_script, *args): abi_constructor = self.abi_constructor() c_params = abi_constructor.get('params', []) if len(args) != len(c_params): raise Exception('Wrong number of arguments passed to constructor. ' \ 'Expected {}, but got {}.'.format(len(c_params), len(args))) _c_params = [] _args = [] for idx, param in enumerate(c_params): arg = args[idx] arg = utils.primitives_to_scrypt_types(arg) resolved_type = utils.resolve_type(param['type'], self.aliases) is_param_statefull = param['state'] if utils.is_array_type(resolved_type): elem_type, array_sizes = utils.factorize_array_type_str( resolved_type) if not utils.check_array(arg, elem_type, array_sizes): raise Exception( 'Constructors parameter with index {} should be array of type "{}".' .format(idx, resolved_type)) flattened_arr = utils.flatten_array(arg, param['name'], resolved_type) for obj in flattened_arr: _c_params.append({ 'name': obj['name'], 'type': obj['type'], 'state': is_param_statefull }) _args.append(obj['value']) elif utils.is_struct_type(resolved_type): if arg.final_type != resolved_type: raise Exception('Constructors parameter with index {} should be struct of type "{}". ' \ 'Got struct of type "{}" instead.'.format(idx, param['type'], arg.type_str)) flattened_struct = utils.flatten_struct(arg, param['name']) for obj in flattened_struct: _c_params.append({ 'name': obj['name'], 'type': obj['type'], 'state': is_param_statefull }) _args.append(obj['value']) else: _c_params.append(param) _args.append(arg) if is_param_statefull: # If a statefull variable, set the passed value as a member of the contract object. setattr(contract, param['name'], arg) finalized_hex_script = hex_script for idx, param in enumerate(_c_params): if not '<{}>'.format(param['name']) in hex_script: raise Exception( 'Missing "{}" contract constructor parameter in passed args.' .format(param['name'])) param_regex = re.compile( escape_str_for_regex('<{}>'.format(param['name']))) if param['state']: # State variables need only a placeholder value as they will get replaced during script execution. #finalized_hex_script = re.sub(param_regex, '0100', finalized_hex_script) finalized_hex_script = re.sub( param_regex, self.encode_param(_args[idx], param), finalized_hex_script) else: finalized_hex_script = re.sub( param_regex, self.encode_param(_args[idx], param), finalized_hex_script) finalized_hex_script = re.sub('<__codePart__>', '00', finalized_hex_script) # Replace inline assembly variable placeholders in locking script with the actual arguments. # TODO: Check if each value is instance of ScryptType if contract.inline_asm_vars: for key, val in contract.inline_asm_vars.items(): param_regex = re.compile( escape_str_for_regex('<{}>'.format(key))) finalized_hex_script = re.sub(param_regex, val.hex, finalized_hex_script) return Script.from_hex(finalized_hex_script)
def get_ls_data_part(self, contract, custom_vals_dict=None, first_call=None): abi_constructor = self.abi_constructor() c_params = abi_constructor.get('params', []) state_buff = [] first_call = first_call if first_call is not None else contract.first_call if first_call: state_buff.append('01') else: state_buff.append('00') for param in c_params: if not param['state']: continue param_name = param['name'] resolved_type = utils.resolve_type(param['type'], self.aliases) if custom_vals_dict: val = custom_vals_dict[param_name] else: val = getattr(contract, param_name, None) if not val: raise Exception( 'Statefull variable "{}" has no value.'.format( param_name)) val = utils.primitives_to_scrypt_types(val) # Do type checking. if utils.is_array_type(resolved_type): elem_type, array_sizes = utils.factorize_array_type_str( resolved_type) if not utils.check_array(val, elem_type, array_sizes): raise Exception( 'Statefull variable "{}" should be array of type "{}".' .format(param_name, resolved_type)) elif utils.is_struct_type(resolved_type): if val.final_type != resolved_type: raise Exception('Statefull variable "{}" should be struct of type "{}". ' \ 'Got struct of type "{}" instead.'.format(param_name, param['type'], val.type_str)) else: if val.final_type != resolved_type: raise Exception('Statefull variable "{}" should be of type "{}". ' \ 'Got object of type "{}" instead.'.format(param_name, param['type'], val.type_str)) state_buff.append(serializer.serialize(val).hex()) # State length and state version. state_len = 0 for elem in state_buff: state_len += len(elem) // 2 if state_len > 0: size_bytes = state_len.to_bytes(4, 'little') state_buff.append(size_bytes.hex()) state_buff.append( CONTRACT_STATE_VERSION.to_bytes(1, 'little').hex()) return Script.from_hex(''.join(state_buff))