def test_Bitcoin(raw_header, header_hash, version, prev_hash, merkle_root, timestamp, bits, nonce): header_hash = hex_str_to_hash(header_hash) prev_hash = hex_str_to_hash(prev_hash) merkle_root = hex_str_to_hash(merkle_root) assert Bitcoin.header_hash(raw_header) == header_hash assert Bitcoin.header_prev_hash(raw_header) == prev_hash assert Bitcoin.header_work(raw_header) == bits_to_work(bits) assert Bitcoin.header_timestamp(raw_header) == timestamp header = Bitcoin.deserialized_header(raw_header, 0) assert header.version == version assert header.prev_hash == prev_hash assert header.merkle_root == merkle_root assert header.timestamp == timestamp assert header.bits == bits assert header.nonce == nonce assert header.raw == raw_header assert header.hash == header_hash assert header.height == 0 assert header.work() == Bitcoin.header_work(raw_header) assert header.target() == bits_to_target(bits) assert header.hash_value() == hash_to_value(header_hash) assert header.hex_str() == hash_to_hex_str(header_hash) assert 'height=0' in str(header)
def test_pickle(self, tmpdir): testnet_genesis_checkpoint = CheckPoint(BitcoinTestnet.genesis_header, 0, 0) headers_obj = create_headers(tmpdir, testnet_genesis_checkpoint) header1 = bytes.fromhex( '0100000043497fd7f826957108f4a30fd9cec3aeba79972084e90ead01ea330900000000bac8b0fa' '927c0ac8234287e33c5f74d38d354820e24756ad709d7038fc5f31f020e7494dffff001d03e4b672' ) header2 = bytes.fromhex( '0100000006128e87be8b1b4dea47a7247d5528d2702c96826c7a648497e773b800000000e241352e' '3bec0a95a6217e10c3abb54adfa05abb12c126695595580fb92e222032e7494dffff001d00d23534' ) headers_obj.connect(header1) headers_obj.connect(header2) pickle_bytes = pickle.dumps(headers_obj) headers_obj2 = pickle.loads(pickle_bytes) assert headers_obj._short_hashes == headers_obj2._short_hashes assert headers_obj._heights == headers_obj2._heights assert headers_obj._chain_indices == headers_obj2._chain_indices assert len(headers_obj._chains) == len(headers_obj2._chains) # Chain objects cannot be directly compared, so we need to do the legwork. # This goes beyond what is needed here as it might be reused for a wider variety of # cases if necessary. for i in range(len(headers_obj._chains)): original_chain = headers_obj._chains[i] unpickled_chain = headers_obj2._chains[i] assert original_chain.tip == unpickled_chain.tip assert original_chain.work == unpickled_chain.work assert original_chain.first_height == unpickled_chain.first_height assert original_chain._header_indices == unpickled_chain._header_indices if original_chain.parent is None: assert unpickled_chain.parent is None else: assert unpickled_chain.parent is not None original_index = headers_obj._chains.index(original_chain.parent) unpickled_index = headers_obj2._chains.index(unpickled_chain.parent) assert original_index == unpickled_index header_1_hash_hex = '00000000b873e79784647a6c82962c70d228557d24a747ea4d1b8bbe878e1206' header_1_hash = hex_str_to_hash(header_1_hash_hex) headers_obj2.common_setup(headers_obj.network, storage_filename(tmpdir), testnet_genesis_checkpoint) original_header, original_chain = headers_obj.lookup(header_1_hash) unpickled_header, unpickled_chain = headers_obj2.lookup(header_1_hash) assert original_header == unpickled_header header_2_hash_hex = '000000006c02c8ea6e4ff69651f7fcde348fb9d557a06e6957b65552002a7820' header_2_hash = hex_str_to_hash(header_2_hash_hex) original_header, original_chain = headers_obj.lookup(header_2_hash) unpickled_header, unpickled_chain = headers_obj2.lookup(header_2_hash) assert original_header == unpickled_header
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 parse_label_sync_json(klass, account: AbstractAccount, text: str) -> LabelImportResult: addresses = klass._get_addresses(account) updates: Dict[str, str] = json.loads(text).items() results = LabelImportResult(LabelImportFormat.LABELSYNC) for label_reference, label_text in updates: if len(label_reference ) == 64: # length of the transaction id (hex of hash) try: tx_hash = hex_str_to_hash(label_reference) except (TypeError, ValueError): pass else: results.transaction_labels[tx_hash] = label_text continue else: try: address = Address.from_string(label_reference, Net.COIN) except (Base58Error, ValueError): pass else: keyinstance_id = addresses.get(address) if keyinstance_id is not None: results.key_labels[keyinstance_id] = label_text continue results.unknown_labels[label_reference] = label_text return results
def test_import_labelsync_format() -> None: account = MockAccount() results = LabelImport.parse_label_sync_json(account, labelsync_text) assert 7 == len(results.transaction_labels) tx_hash = hex_str_to_hash("44e14e2eef1482bc803d1a32d9348b4914922990dbb730fe70b8a40753aeea03") assert "Return funding to 'mainnet_cash'" == results.transaction_labels[tx_hash]
def _fetch_transaction_dto(self, account: AbstractAccount, tx_id) -> Optional[Dict]: tx_hash = hex_str_to_hash(tx_id) tx = account.get_transaction(tx_hash) if not tx: raise Fault(Errors.TRANSACTION_NOT_FOUND_CODE, Errors.TRANSACTION_NOT_FOUND_MESSAGE) return {"tx_hex": tx.to_hex()}
def utxo_from_dict(self, d): return UTXO(value=d['value'], script_pubkey=bitcoinx.Script.from_hex(d['script_pubkey']), script_type=d['script_type'], tx_hash=bitcoinx.hex_str_to_hash(d['tx_hash']), out_index=d['out_index'], keyinstance_id=d['keyinstance_id'], address=bitcoinx.Address.from_string(d['address'], coin=Net.COIN), is_coinbase=d['is_coinbase'], flags=d['flags'])
async def remove_txs(self, request): # follows this spec https://opensource.zalando.com/restful-api-guidelines/#152 """This might be used to clean up after creating many transactions that were never sent.""" try: vars = await self.argparser(request, required_vars=[ VNAME.WALLET_NAME, VNAME.ACCOUNT_ID, VNAME.TXIDS ]) wallet_name = vars[VNAME.WALLET_NAME] account_id = vars[VNAME.ACCOUNT_ID] txids = vars[VNAME.TXIDS] account = self._get_account(wallet_name, account_id) results = [] if txids: for txid in txids: try: self.remove_transaction(bitcoinx.hex_str_to_hash(txid), account) results.append({"id": txid, "result": 200}) except Fault as e: if e.code == Errors.DISABLED_FEATURE_CODE: results.append({ "id": txid, "result": 400, "description": Errors.DISABLED_FEATURE_MESSAGE }) if e.code == Errors.TRANSACTION_NOT_FOUND_CODE: results.append({ "id": txid, "result": 400, "description": Errors.TRANSACTION_NOT_FOUND_MESSAGE }) return self.batch_response({"items": results}) except Fault as e: return fault_to_http_response(e)
def _transaction_state_dto(self, wallet: AbstractAccount, tx_ids: Optional[Iterable[str]]=None) -> Union[Fault, Dict[Any, Any]]: chain = self.app_state.daemon.network.chain() result = {} for tx_id in tx_ids: tx_hash = hex_str_to_hash(tx_id) if wallet.has_received_transaction(tx_hash): # height, conf, timestamp height, conf, timestamp = wallet.get_tx_height(tx_hash) block_id = None if timestamp: block_id = self.app_state.headers.header_at_height(chain, height).hex_str() result[tx_id] = { "block_id": block_id, "height": height, "conf": conf, "timestamp": timestamp, } return result
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 to_tx_input(txin): prev_hash, prev_idx = txin['output'].split(':') x_pubkeys = [] value = txin.get('value') sec = txin.get('privkey') threshold = 1 if sec: privkey = PrivateKey.from_text(sec) privkey, compressed = privkey.to_bytes(), privkey.is_compressed() x_pubkey = XPublicKey(privkey.public_key.to_hex()) keypairs[x_pubkey] = privkey, compressed x_pubkeys = [x_pubkey] return XTxInput( prev_hash=hex_str_to_hash(prev_hash), prev_idx=int(prev_idx), script_sig=Script(), sequence=0xffffffff, value=value, x_pubkeys=x_pubkeys, address=None, threshold=threshold, signatures=[NO_SIGNATURE] * len(x_pubkeys), )
def parse_label_export_json(klass, account: AbstractAccount, text: str) -> LabelImportResult: updates: Dict[str, Any] = json.loads(text) results = LabelImportResult(LabelImportFormat.ACCOUNT) for tx_id, label_text in updates.get("transactions", []): if len(tx_id) == 64: # length of the transaction id (hex of hash) try: tx_hash = hex_str_to_hash(tx_id) except (TypeError, ValueError): pass else: results.transaction_labels[tx_hash] = label_text continue results.unknown_labels[tx_id] = label_text keydata: Optional[Dict[str, Any]] = updates.get("keys") if keydata is not None: account_fingerprint = account.get_fingerprint().hex() if isinstance(keydata.get("account_fingerprint"), str): results.account_fingerprint = keydata["account_fingerprint"] derivations = klass._get_derivations(account) for derivation_path_text, label_text in keydata["entries"]: try: derivation_path = tuple( bip32_decompose_chain_string(derivation_path_text)) except (TypeError, ValueError): pass else: # We never import key descriptions if the account does not match. if account_fingerprint == results.account_fingerprint: keyinstance_id = derivations.get(derivation_path) if keyinstance_id is not None: results.key_labels[keyinstance_id] = label_text continue results.unknown_labels[derivation_path_text] = label_text return results
unlock_key_priv = PrivateKey.from_WIF( '****************************************************') unlock_key_pub = unlock_key_priv.public_key addr_dest = '1Pc1iF4g8iVmnu1puvGasSyDcwv2FS1VcH' 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)
class Net(metaclass=_CurrentNetMeta): _net = SVTestnet SPENDABLE_UTXOS = [ UTXO(address=Address.from_string('miz93i75XiTdnvzkU6sDddvGcCr4ZrCmou', Net.COIN), is_coinbase=False, out_index=0, script_pubkey=Script( b'v\xa9\x14&\x0c\x95\x8e\x81\xc8o\xe3.\xc3\xd4\x1d7\x1cy' b'\x0e\xed\x9a\xb4\xf3\x88\xac'), tx_hash=hex_str_to_hash( '76d5bfabe40ca6cbd315b04aa24b68fdd8179869fd1c3501d5a88a980c61c1bf' ), value=100000, script_type=ScriptType.P2PKH, keyinstance_id=0, flags=TransactionOutputFlag.NONE), UTXO(address=Address.from_string('msccMGHunfHANQWXMZragRggHMkJaBWSFr', Net.COIN), is_coinbase=False, out_index=0, script_pubkey=Script( b'v\xa9\x14\x84\xb3[1i\xe4+"}+\x9d\x85s!\t\xa1y\xab\xff' b'\x12\x88\xac'), tx_hash=hex_str_to_hash( '76d5bfabe40ca6cbd315b04aa24b68fdd8179869fd1c3501d5a88a980c61c1bf' ),
def reverse_hash(hex_string): hex_string = bitcoinx.hex_str_to_hash(hex_string)[::-1] return bitcoinx.hash_to_hex_str(hex_string)