def test_bip39_multisig_seed_bip45_standard(self): seed_words = 'treat dwarf wealth gasp brass outside high rent blood crowd make initial' self.assertEqual(keystore.bip39_is_checksum_valid(seed_words), (True, True)) ks1 = keystore.from_bip39_seed(seed_words, '', "m/45'/0") self.assertTrue(isinstance(ks1, keystore.BIP32_KeyStore)) self.assertEqual( ks1.xpub, 'xpub69xafV4YxC6o8Yiga5EiGLAtqR7rgNgNUGiYgw3S9g9pp6XYUne1KxdcfYtxwmA3eBrzMFuYcNQKfqsXCygCo4GxQFHfywxpUbKNfYvGJka' ) ks2 = keystore.from_xpub( 'xpub6Bco9vrgo8rNUSi8Bjomn8xLA41DwPXeuPcgJamNRhTTyGVHsp8fZXaGzp9ypHoei16J6X3pumMAP1u3Dy4jTSWjm4GZowL7Dcn9u4uZC9W' ) self._check_xpub_keystore_sanity(ks2) self.assertTrue(isinstance(ks2, keystore.BIP32_KeyStore)) w = self._create_multisig_wallet(ks1, ks2) self.assertEqual( w.get_receiving_addresses()[0], Address.from_string('3H3iyACDTLJGD2RMjwKZcCwpdYZLwEZzKb')) self.assertEqual( w.get_change_addresses()[0], Address.from_string('31hyfHrkhNjiPZp1t7oky5CGNYqSqDAVM9'))
def test_child_safe(self): '''Test child derivations agree with Electrum.''' # Also tests WIF, address rec_master = mprivkey.child_safe(0) assert rec_master.public_key.to_address() == Address.from_string( '18zW4D1Vxx9jVPGzsFzgXj8KrSLHt7w2cg', Bitcoin) chg_master = mprivkey.child_safe(1) assert chg_master.public_key.to_address() == Address.from_string( '1G8YpbkZd7bySHjpdQK3kMcHhc6BvHr5xy', Bitcoin) rec0 = rec_master.child_safe(0) assert rec0.to_WIF( ) == 'L2M6WWMdu3YfWxvLGF76HZgHCA6idwVQx5QL91vfdqeZi8XAgWkz' rec19 = rec_master.child_safe(19) assert rec19.to_WIF( ) == 'KwMHa1fynU2J2iBGCuBZxumM2qDXHe5tVPU9VecNGQv3UCqnET7X' chg0 = chg_master.child_safe(0) assert chg0.to_WIF( ) == 'L4J1esD4rYuBHXwjg72yi7Rw4G3iF2yUHt7LN9trpC3snCppUbq8' with pytest.raises(ValueError): mprivkey.child_safe(-1) with pytest.raises(ValueError): mprivkey.child_safe(1 << 32) # OK mprivkey.child_safe((1 << 32) - 1)
def _mktx(self, outputs, fee=None, change_addr=None, domain=None, nocheck=False, unsigned=False, password=None, locktime=None): self.nocheck = nocheck change_addr = None if change_addr is None else Address.from_string( change_addr) domain = None if domain is None else [ Address.from_string(x) for x in domain ] final_outputs = [] for address, amount in outputs: address = Address.from_string(address) amount = satoshis(amount) final_outputs.append(TxOutput(amount, address.to_script())) coins = self.wallet.get_spendable_coins(domain, self.config) tx = self.wallet.make_unsigned_transaction(coins, final_outputs, self.config, fee, change_addr) if locktime is not None: tx.locktime = locktime if not unsigned: self.wallet.sign_transaction(tx, password) return tx
def test_electrum_multisig_seed_standard(self): seed_words = 'blast uniform dragon fiscal ensure vast young utility dinosaur abandon rookie sure' self.assertEqual(bitcoin.seed_type(seed_words), 'standard') ks1 = keystore.from_seed(seed_words, '', True) self._check_seeded_keystore_sanity(ks1) self.assertTrue(isinstance(ks1, keystore.BIP32_KeyStore)) self.assertEqual( ks1.xpub, 'xpub661MyMwAqRbcGNEPu3aJQqXTydqR9t49Tkwb4Esrj112kw8xLthv8uybxvaki4Ygt9xiwZUQGeFTG7T2TUzR3eA4Zp3aq5RXsABHFBUrq4c' ) ks2 = keystore.from_xpub( 'xpub661MyMwAqRbcGfCPEkkyo5WmcrhTq8mi3xuBS7VEZ3LYvsgY1cCFDbenT33bdD12axvrmXhuX3xkAbKci3yZY9ZEk8vhLic7KNhLjqdh5ec' ) self._check_xpub_keystore_sanity(ks2) self.assertTrue(isinstance(ks2, keystore.BIP32_KeyStore)) w = self._create_multisig_wallet(ks1, ks2) self.assertEqual( w.get_receiving_addresses()[0], Address.from_string('32ji3QkAgXNz6oFoRfakyD3ys1XXiERQYN')) self.assertEqual( w.get_change_addresses()[0], Address.from_string('36XWwEHrrVCLnhjK5MrVVGmUHghr9oWTN1'))
def test_address_to_pubkey(self): addr = Address.from_string('1KXf5PUHNaV42jE9NbJFPKhGGN1fSSGJNK') pubkey = imported_keystore.address_to_pubkey(addr) assert pubkey.to_hex() == ( '04e7dd15b4271f8308ff52ad3d3e472b652e78a2c5bc6ed10250a543d28c0128894ae' '863d086488e6773c4589be93a1793f685dd3f1e8a1f1b390b23470f7d1095') addr = Address.from_string('1JYPZWn7YbJJ8LpNknU1EVxs3JXuVzc7Rd') assert imported_keystore.address_to_pubkey(addr) is None
def test_tx_signed(self): expected = { 'inputs': [{ 'address': Address.from_string('13Vp8Y3hD5Cb6sERfpxePz5vGJizXbWciN'), 'num_sig': 1, 'prevout_hash': 'ed6a4d07e546b677abf6ba1257c2546128c694f23f4b9ebbd822fdfe435ef349', 'prevout_n': 1, 'pubkeys': [ '03b5bbebceeb33c1b61f649596b9c3611c6b2853a1f6b48bce05dd54f667fa2166' ], 'scriptSig': '473044022025bdc804c6fe30966f6822dc25086bc6bb0366016e68e880cf6efd2468921f3202200e665db0404f6d6d9f86f73838306ac55bb0d0f6040ac6047d4e820f24f46885412103b5bbebceeb33c1b61f649596b9c3611c6b2853a1f6b48bce05dd54f667fa2166', 'sequence': 4294967294, 'signatures': [ '3044022025bdc804c6fe30966f6822dc25086bc6bb0366016e68e880cf6efd2468921f3202200e665db0404f6d6d9f86f73838306ac55bb0d0f6040ac6047d4e820f24f4688541' ], 'type': 'p2pkh', 'x_pubkeys': [ '03b5bbebceeb33c1b61f649596b9c3611c6b2853a1f6b48bce05dd54f667fa2166' ] }], 'lockTime': 507231, 'outputs': [ TxOutput( 20112408, Address.from_string( '1MYXdf4moacvaEKZ57ozerpJ3t9xSeN6LK').to_script()) ], 'version': 1 } tx = transaction.Transaction(signed_blob) self.assertEqual(tx.deserialize(), expected) self.assertEqual(tx.deserialize(), None) self.assertEqual(tx.as_dict(), { 'hex': signed_blob, 'complete': True, 'final': True }) self.assertEqual(tx.serialize(), signed_blob) tx.update_signatures(signed_blob) self.assertEqual(tx.estimated_size(), 191)
def test_tx_unsigned(self): outputs = [ TxOutput( 20112408, Address.from_string( '1MYXdf4moacvaEKZ57ozerpJ3t9xSeN6LK').to_script()) ] expected = { 'inputs': [{ 'address': Address.from_string('13Vp8Y3hD5Cb6sERfpxePz5vGJizXbWciN'), 'num_sig': 1, 'prevout_hash': 'ed6a4d07e546b677abf6ba1257c2546128c694f23f4b9ebbd822fdfe435ef349', 'prevout_n': 1, 'pubkeys': [ '03b5bbebceeb33c1b61f649596b9c3611c6b2853a1f6b48bce05dd54f667fa2166' ], 'scriptSig': '01ff4c53ff0488b21e0000000000000000004f130d773e678a58366711837ec2e33ea601858262f8eaef246a7ebd19909c9a03c3b30e38ca7d797fee1223df1c9827b2a9f3379768f520910260220e0560014600002300', 'sequence': 4294967294, 'signatures': [None], 'type': 'p2pkh', 'value': 20112600, 'x_pubkeys': [ 'ff0488b21e0000000000000000004f130d773e678a58366711837ec2e33ea601858262f8eaef246a7ebd19909c9a03c3b30e38ca7d797fee1223df1c9827b2a9f3379768f520910260220e0560014600002300' ] }], 'lockTime': 507231, 'outputs': outputs, 'version': 1 } tx = transaction.Transaction(unsigned_blob) calc = tx.deserialize() self.assertEqual(calc, expected) self.assertEqual(tx.deserialize(), None) self.assertEqual(tx.as_dict(), { 'hex': unsigned_blob, 'complete': False, 'final': True }) self.assertEqual(tx.outputs(), outputs) self.assertEqual(tx.serialize(), unsigned_blob)
def test_bip39_seed_bip44_standard(self, mock_write): seed_words = 'treat dwarf wealth gasp brass outside high rent blood crowd make initial' self.assertEqual(keystore.bip39_is_checksum_valid(seed_words), (True, True)) ks = keystore.from_bip39_seed(seed_words, '', "m/44'/0'/0'") self.assertTrue(isinstance(ks, keystore.BIP32_KeyStore)) self.assertEqual(ks.xpub, 'xpub6DFh1smUsyqmYD4obDX6ngaxhd53Zx7aeFjoobebm7vbkT6f9awJWFuGzBT9FQJEWFBL7UyhMXtYzRcwDuVbcxtv9Ce2W9eMm4KXLdvdbjv') w = self._create_standard_wallet(ks) self.assertEqual(w.get_receiving_addresses()[0], Address.from_string('16j7Dqk3Z9DdTdBtHcCVLaNQy9MTgywUUo')) self.assertEqual(w.get_change_addresses()[0], Address.from_string('1GG5bVeWgAp5XW7JLCphse14QaC4qiHyWn'))
def serialize(self, jsontx): """Create a transaction from json inputs. Inputs must have a redeemPubkey. Outputs must be a list of {'address':address, 'value':satoshi_amount}. """ keypairs = {} inputs = jsontx.get('inputs') outputs = jsontx.get('outputs') locktime = jsontx.get('locktime', 0) for txin in inputs: if txin.get('output'): prevout_hash, prevout_n = txin['output'].split(':') txin['prevout_n'] = int(prevout_n) txin['prevout_hash'] = prevout_hash sec = txin.get('privkey') if sec: privkey = PrivateKey.from_text(sec) txin_type, privkey, compressed = ('p2pkh', privkey.to_bytes(), privkey.is_compressed()) pubkey = privkey.public_key.to_hex() keypairs[pubkey] = privkey, compressed txin['type'] = txin_type txin['x_pubkeys'] = [pubkey] txin['signatures'] = [None] txin['num_sig'] = 1 outputs = [ TxOutput(output['value'], Address.from_string(output['address']).to_script()) for output in outputs ] tx = Transaction.from_io(inputs, outputs, locktime=locktime) tx.sign(keypairs) return tx.as_dict()
def getaddresshistory(self, address): """Return the transaction history of any address. Note: This is a walletless server query, results are not checked by SPV. """ sh = scripthash_hex(Address.from_string(address)) return self.network.request_and_wait( 'blockchain.scripthash.get_history', [sh])
def getaddressunspent(self, address): """Returns the UTXO list of any address. Note: This is a walletless server query, results are not checked by SPV. """ sh = scripthash_hex(Address.from_string(address)) return self.network.request_and_wait( 'blockchain.scripthash.listunspent', [sh])
def signmessage(self, account_id, address, message, password=None): """Sign a message with a key. Use quotes if your message contains whitespaces""" address = Address.from_string(address) wallet = self.parent_wallet.get_wallet_for_account(account_id) sig = wallet.sign_message(address, message, password) return base64.b64encode(sig).decode('ascii')
def test_parse_xpub(self): res = xpubkey_to_address( 'fe4e13b0f311a55b8a5db9a32e959da9f011b131019d4cebe6141b9e2c93edcbfc0954c358b062a9f94111548e50bde5847a3096b8b7872dcffadb0e9579b9017b01000200' ) self.assertEqual(res, ( '04ee98d63800824486a1cf5b4376f2f574d86e0a3009a6448105703453f3368e8e1d8d090aaecdd626a45cc49876709a3bbb6dc96a4311b3cac03e225df5f63dfc', Address.from_string('19h943e4diLc68GXW7G75QNe2KWuMu7BaJ')))
def string_to_script_template(text: str) -> ScriptTemplate: # raises bip276.ChecksumMismatchError if text.startswith(PREFIX_SCRIPT): prefix, version, network, data = bip276_decode(text, Net.BIP276_VERSION) assert network == Net.BIP276_VERSION, "incompatible network" return classify_output_script(Script(data), Net.COIN) return Address.from_string(text, Net.COIN)
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 getrequest(self, key): """Return a payment request""" r = self.wallet.get_payment_request(Address.from_string(key), self.config) if not r: raise Exception("Request not found") return self._format_request(r)
def getrequest(self, account_id: int, key: str) -> Dict[str, Any]: """Return a payment request""" wallet = self.parent_wallet.get_wallet_for_account(account_id) r = wallet.get_payment_request(Address.from_string(key), self.config) if not r: raise Exception("Request not found") return self._format_request(r)
def test_electrum_seed_old(self, mock_write): seed_words = 'powerful random nobody notice nothing important anyway look away hidden message over' self.assertEqual(bitcoin.seed_type(seed_words), 'old') ks = keystore.from_seed(seed_words, '', False) self._check_seeded_keystore_sanity(ks) self.assertTrue(isinstance(ks, keystore.Old_KeyStore)) self.assertEqual(ks.mpk, 'e9d4b7866dd1e91c862aebf62a49548c7dbf7bcc6e4b7b8c9da820c7737968df9c09d5a3e271dc814a29981f81b3faaf2737b551ef5dcc6189cf0f8252c442b3') w = self._create_standard_wallet(ks) self.assertEqual(w.get_receiving_addresses()[0], Address.from_string('1FJEEB8ihPMbzs2SkLmr37dHyRFzakqUmo')) self.assertEqual(w.get_change_addresses()[0], Address.from_string('1KRW8pH6HFHZh889VDq6fEKvmrsmApwNfe'))
def test_electrum_seed_standard(self, mock_write): seed_words = 'cycle rocket west magnet parrot shuffle foot correct salt library feed song' self.assertEqual(bitcoin.seed_type(seed_words), 'standard') ks = keystore.from_seed(seed_words, '', False) self._check_seeded_keystore_sanity(ks) self.assertTrue(isinstance(ks, keystore.BIP32_KeyStore)) self.assertEqual(ks.xpub, 'xpub661MyMwAqRbcFWohJWt7PHsFEJfZAvw9ZxwQoDa4SoMgsDDM1T7WK3u9E4edkC4ugRnZ8E4xDZRpk8Rnts3Nbt97dPwT52CwBdDWroaZf8U') w = self._create_standard_wallet(ks) self.assertEqual(w.get_receiving_addresses()[0], Address.from_string('1NNkttn1YvVGdqBW4PR6zvc3Zx3H5owKRf')) self.assertEqual(w.get_change_addresses()[0], Address.from_string('1KSezYMhAJMWqFbVFB2JshYg69UpmEXR4D'))
def getaddressbalance(self, address): """Return the balance of any address. Note: This is a walletless server query, results are not checked by SPV. """ sh = scripthash_hex(Address.from_string(address)) out = self.network.request_and_wait('blockchain.scripthash.get_balance', [sh]) out["confirmed"] = str(Decimal(out["confirmed"])/COIN) out["unconfirmed"] = str(Decimal(out["unconfirmed"])/COIN) return out
def test_addresses(self, raw_hex, address, coin): address = Address.from_string(address) address._coin = coin x_pubkey = XPublicKey(raw_hex) assert x_pubkey.to_bytes() == bytes.fromhex(raw_hex) assert x_pubkey.to_hex() == raw_hex assert not x_pubkey.is_bip32_key() assert x_pubkey.to_public_key() == address assert x_pubkey.to_address() == address assert x_pubkey.to_address().coin() is coin
def test_pubkeys_to_address(self, tmp_storage, network): coin = network.COIN privkey = PrivateKey.from_random() WIF = privkey.to_WIF(coin=coin) wallet = ImportedPrivkeyWallet.from_text(tmp_storage, WIF, None) public_key = privkey.public_key pubkey_hex = public_key.to_hex() address = public_key.to_address(coin=coin).to_string() assert wallet.pubkeys_to_address(pubkey_hex) == Address.from_string( address)
def test_tx_unsigned(self): tx = Transaction.from_hex(unsigned_blob) assert tx.version == 1 assert len(tx.inputs) == 1 txin = tx.inputs[0] assert txin.prev_hash.hex() == '49f35e43fefd22d8bb9e4b3ff294c6286154c25712baf6ab77b646e5074d6aed' assert txin.prev_idx == 1 assert txin.script_sig.to_hex() == '01ff4c53ff0488b21e0000000000000000004f130d773e678a58366711837ec2e33ea601858262f8eaef246a7ebd19909c9a03c3b30e38ca7d797fee1223df1c9827b2a9f3379768f520910260220e0560014600002300' assert txin.sequence == 4294967294 assert txin.value == 20112600 assert txin.signatures == [NO_SIGNATURE] assert txin.x_pubkeys == [XPublicKey('ff0488b21e0000000000000000004f130d773e678a58366711837ec2e33ea601858262f8eaef246a7ebd19909c9a03c3b30e38ca7d797fee1223df1c9827b2a9f3379768f520910260220e0560014600002300')] assert txin.address == Address.from_string('13Vp8Y3hD5Cb6sERfpxePz5vGJizXbWciN') assert txin.threshold == 1 assert tx.outputs == [TxOutput(20112408, Address.from_string( '1MYXdf4moacvaEKZ57ozerpJ3t9xSeN6LK').to_script())] assert tx.locktime == 507231 assert tx.as_dict() == {'hex': unsigned_blob, 'complete': False}
def sweep(self, privkey, destination, fee=None, nocheck=False, imax=100): """Sweep private keys. Returns a transaction that spends UTXOs from privkey to a destination address. The transaction is not broadcasted.""" from .wallet import sweep tx_fee = satoshis(fee) privkeys = privkey.split() self.nocheck = nocheck addr = Address.from_string(destination) tx = sweep(privkeys, self.network, self.config, addr, tx_fee, imax) return tx.as_dict() if tx else None
def test_pubkeys_to_address(self, tmp_storage, network): coin = network.COIN privkey = PrivateKey.from_random() WIF = privkey.to_WIF(coin=coin) parent_wallet = _TestableParentWallet.as_legacy_wallet_container( tmp_storage) wallet = ImportedPrivkeyWallet.from_text(parent_wallet, WIF) public_key = privkey.public_key pubkey_hex = public_key.to_hex() address = public_key.to_address(coin=coin).to_string() assert wallet.pubkeys_to_address(pubkey_hex) == Address.from_string( address)
def test_tx_signed(self): tx = Transaction.from_hex(signed_blob) assert tx.version == 1 assert len(tx.inputs) == 1 txin = tx.inputs[0] assert txin.prev_hash.hex() == '49f35e43fefd22d8bb9e4b3ff294c6286154c25712baf6ab77b646e5074d6aed' assert txin.prev_idx == 1 assert txin.script_sig.to_hex() == '473044022025bdc804c6fe30966f6822dc25086bc6bb0366016e68e880cf6efd2468921f3202200e665db0404f6d6d9f86f73838306ac55bb0d0f6040ac6047d4e820f24f46885412103b5bbebceeb33c1b61f649596b9c3611c6b2853a1f6b48bce05dd54f667fa2166' assert txin.sequence == 4294967294 assert txin.signatures == [bytes.fromhex('3044022025bdc804c6fe30966f6822dc25086bc6bb0366016e68e880cf6efd2468921f3202200e665db0404f6d6d9f86f73838306ac55bb0d0f6040ac6047d4e820f24f4688541')] assert txin.x_pubkeys == [XPublicKey('03b5bbebceeb33c1b61f649596b9c3611c6b2853a1f6b48bce05dd54f667fa2166')] assert txin.address == Address.from_string('13Vp8Y3hD5Cb6sERfpxePz5vGJizXbWciN') assert txin.threshold == 1 assert tx.outputs == [TxOutput(20112408, Address.from_string( '1MYXdf4moacvaEKZ57ozerpJ3t9xSeN6LK').to_script())] assert tx.locktime == 507231 assert tx.as_dict() == {'hex': signed_blob, 'complete': True} assert tx.serialize() == signed_blob tx.update_signatures(signed_blob) assert tx.estimated_size() == 192
def _parse_output(self, text: str) -> Script: try: address = Address.from_string(text, Net.COIN) self._show_cashaddr_warning(text) return address.to_script() except ValueError: pass try: return string_to_bip276_script(text) except ValueError: pass return Script.from_asm(text)
def notify(self, address, URL): """Watch an address. Everytime the address changes, a http POST is sent to the URL.""" def callback(x): import urllib.request headers = {'content-type':'application/json'} data = {'address':address, 'status':x.get('result')} serialized_data = to_bytes(json.dumps(data)) try: req = urllib.request.Request(URL, serialized_data, headers) response_stream = urllib.request.urlopen(req, timeout=5) logger.debug('Got Response for %s', address) except Exception as e: logger.error("exception processing response %s", e) h = scripthash_hex(Address.from_string(address)) self.network.send([('blockchain.scripthash.subscribe', [h])], callback) return True
def serialize(self, jsontx): """Create a transaction from json inputs. Inputs must have a redeemPubkey. Outputs must be a list of {'address':address, 'value':satoshi_amount}. """ keypairs = {} inputs = jsontx.get('inputs') outputs = jsontx.get('outputs') locktime = jsontx.get('locktime', 0) # This might need work 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), ) inputs = [to_tx_input(txin) for txin in inputs] outputs = [ TxOutput(output['value'], Address.from_string(output['address']).to_script()) for output in outputs ] tx = Transaction.from_io(inputs, outputs, locktime=locktime) tx.sign(keypairs) return tx.as_dict()
def set_filter_match(self, text: Optional[str]) -> None: self._filter_type = MatchType.UNKNOWN if text is not None: try: address = Address.from_string(text, Net.COIN) except ValueError: pass else: self._filter_type = MatchType.ADDRESS self._filter_match = address if self._filter_type == MatchType.UNKNOWN: self._filter_type = MatchType.TEXT self._filter_match = text.lower() else: self._filter_match = None self.invalidateFilter()