def on_receive(self, keyhash, message): self.print_error("signal arrived for", keyhash) for key, _hash, window in self.keys: if _hash == keyhash: break else: self.print_error("keyhash not found") return wallet = window.wallet if wallet.has_keystore_encryption(): password = window.password_dialog('An encrypted transaction was retrieved from cosigning pool.\nPlease enter your password to decrypt it.') if not password: return else: password = None if not window.question(_("An encrypted transaction was retrieved from cosigning pool.\nDo you want to open it now?")): return xprv = wallet.keystore.get_master_private_key(password) if not xprv: return try: k = bh2u(bitcoin.deserialize_xprv(xprv)[-1]) EC = bitcoin.EC_KEY(bfh(k)) message = bh2u(EC.decrypt_message(message)) except Exception as e: traceback.print_exc(file=sys.stdout) window.show_message(str(e)) return self.listener.clear(keyhash) tx = transaction.Transaction(message) show_transaction(tx, window, prompt_if_unsaved=True)
def on_receive(self): if self.wallet.use_encryption: password = self.win.password_dialog('An encrypted transaction was retrieved from cosigning pool.\nPlease enter your password to decrypt it.') if not password: return else: password = None if not self.win.question(_("An encrypted transaction was retrieved from cosigning pool.\nDo you want to open it now?")): return message = self.listener.message key = self.listener.keyname xprv = self.wallet.get_master_private_key(key, password) if not xprv: return try: k = bitcoin.deserialize_xkey(xprv)[-1].encode('hex') EC = bitcoin.EC_KEY(k.decode('hex')) message = EC.decrypt_message(message) except Exception as e: traceback.print_exc(file=sys.stdout) self.win.show_message(str(e)) return self.listener.clear() tx = transaction.Transaction(message) show_transaction(tx, self.win, prompt_if_unsaved=True)
def test_tx_signed_segwit(self): tx = transaction.Transaction(signed_segwit_blob) self.assertEqual(tx.estimated_total_size(), 223) self.assertEqual(tx.estimated_base_size(), 182) self.assertEqual(tx.estimated_witness_size(), 41) self.assertEqual(tx.estimated_weight(), 769) self.assertEqual(tx.estimated_size(), 193)
def test_estimated_tx_size(self): tx = transaction.Transaction(signed_blob) self.assertEqual(tx.estimated_total_size(), 193) self.assertEqual(tx.estimated_base_size(), 193) self.assertEqual(tx.estimated_witness_size(), 0) self.assertEqual(tx.estimated_weight(), 772) self.assertEqual(tx.estimated_size(), 193)
def on_receive(self, keyhash, message): self.print_error("signal arrived for", keyhash) for key, _hash, window in self.keys: if _hash == keyhash: break else: self.print_error("keyhash not found") return wallet = window.wallet if isinstance(wallet.keystore, keystore.Hardware_KeyStore): window.show_warning( _('An encrypted transaction was retrieved from cosigning pool.' ) + '\n' + _('However, hardware wallets do not support message decryption, ' 'which makes them not compatible with the current design of cosigner pool.' )) return elif wallet.has_keystore_encryption(): password = window.password_dialog( _('An encrypted transaction was retrieved from cosigning pool.' ) + '\n' + _('Please enter your password to decrypt it.')) if not password: return else: password = None if not window.question( _("An encrypted transaction was retrieved from cosigning pool." ) + '\n' + _("Do you want to open it now?")): return xprv = wallet.keystore.get_master_private_key(password) if not xprv: return try: k = bitcoin.deserialize_xprv(xprv)[-1] EC = ecc.ECPrivkey(k) message = bh2u(EC.decrypt_message(message)) except Exception as e: traceback.print_exc(file=sys.stdout) window.show_error(_('Error decrypting message') + ':\n' + str(e)) return self.listener.clear(keyhash) tx = transaction.Transaction(message, window.network.get_server_height()) show_transaction(tx, window, prompt_if_unsaved=True)
def on_receive(self, keyhash, message): self.logger.info(f"signal arrived for {keyhash}") for key, _hash, window in self.keys: if _hash == keyhash: break else: self.logger.info("keyhash not found") return wallet = window.wallet if isinstance(wallet.keystore, keystore.Hardware_KeyStore): window.show_warning( _('An encrypted transaction was retrieved from cosigning pool.' ) + '\n' + _('However, hardware wallets do not support message decryption, ' 'which makes them not compatible with the current design of cosigner pool.' )) return elif wallet.has_keystore_encryption(): password = window.password_dialog( _('An encrypted transaction was retrieved from cosigning pool.' ) + '\n' + _('Please enter your password to decrypt it.')) if not password: return else: password = None if not window.question( _("An encrypted transaction was retrieved from cosigning pool." ) + '\n' + _("Do you want to open it now?")): return xprv = wallet.keystore.get_master_private_key(password) if not xprv: return try: privkey = BIP32Node.from_xkey(xprv).eckey message = bh2u(privkey.decrypt_message(message)) except Exception as e: self.logger.exception('') window.show_error(_('Error decrypting message') + ':\n' + str(e)) return self.listener.clear(keyhash) tx = transaction.Transaction(message) show_transaction(tx, window, prompt_if_unsaved=True)
def verify_tx_input(tx, i, script, sig, pub): pub, sig, script = (binascii.unhexlify(x) for x in [pub, sig, script]) t = etr.Transaction(tx) t.deserialize() #to prepare for verification (to do the txhash for modtx) #we need to have the "address" field set in the input. typ, addr = etr.get_address_from_output_script(script) if not typ == ebt.TYPE_ADDRESS: #Don't support non-p2sh, non-p2pkh for now log.debug("Invalid script") return False t.inputs()[i]["address"] = addr t.inputs()[i]["type"] = 'p2pkh' txforsig = etr.Hash(t.serialize_preimage(i).decode('hex')) ecdsa_pub = get_ecdsa_verifying_key(pub) if not ecdsa_pub: return False try: verified = ecdsa_pub.verify_digest(sig, txforsig, sigdecode = sigdecode_der) except BadSignatureError, BadDigestError: return False
def test_tx_deserialize_for_signed_network_tx(self): tx = transaction.Transaction(signed_blob) tx.deserialize() self.assertEqual(1, tx.version) self.assertEqual(0, tx.locktime) self.assertEqual(1, len(tx.inputs())) self.assertEqual(4294967295, tx.inputs()[0].nsequence) self.assertEqual( bfh('493046022100a82bbc57a0136751e5433f41cf000b3f1a99c6744775e76ec764fb78c54ee100022100f9e80b7de89de861dc6fb0c1429d5da72c2b6b2ee2406bc9bfb1beedd729d985012102e61d176da16edd1d258a200ad9759ef63adf8e14cd97f53227bae35cdb84d2f6' ), tx.inputs()[0].script_sig) self.assertEqual(None, tx.inputs()[0].witness) self.assertEqual( '3140eb24b43386f35ba69e3875eb6c93130ac66201d01c58f598defc949a5c2a:0', tx.inputs()[0].prevout.to_str()) self.assertEqual(1, len(tx.outputs())) self.assertEqual( bfh('76a914230ac37834073a42146f11ef8414ae929feaafc388ac'), tx.outputs()[0].scriptpubkey) self.assertEqual('k112pHc1rbsM4fQDiyUFzkUdtdzr4AQBa2r', tx.outputs()[0].address) self.assertEqual(1000000, tx.outputs()[0].value) self.assertEqual(tx.serialize(), signed_blob)
def test_btc(): #Sign and verify test (for message signing in joinmarket handshake) print("Using interface " + interface) priv = dbl_sha256("hello") + "01" x = ecdsa_sign("helloxxx", priv) log.debug("Got: " + x) y = ecdsa_verify("helloxxx", x, privkey_to_pubkey(priv)) log.debug("Sig ver: " + str(y)) assert y #address/script conversion test test_addr = "1LT6rwv26bV7mgvRosoSCyGM7ttVRsYidP" #Electrum has no support for testnet! #test_test_addr = "mgvipZr8kX7fZFQU7QsKTCJT9QCfaiswV7" assert script_to_address(address_to_script(test_addr)) == test_addr assert get_version_byte(test_addr) == 0 #Transaction creation test. raw_valid_tx = "01000000064cdfe43ad43b187b738644363144784a09bf6d408012409cf9934591109a789b060000006b483045022100d4309edbb8253e62fb59462f2ff5c3445923e0299bf1a15ac5f7db3da5752bee022066f3f219de7e6ee56c3d600da757ec1051cbd11b42969b8935ae35642b6a2e84012102e94b49525342110266a1dc7651221507318c4cb914ede004b3098650e9b951b6ffffffffc2a9b3e8285c2e7aaee2ea50f792172c920c43a675fa8e8d70976727c8752adf030000006a47304402202763d8ad9e41c99c5af587c69d267493773dc9567519a64db8b707af5daf07f0022011729c6d241ad5abe48687d084644bd442b5f9038db04fb28da674126183aca5012102d2cbeb9386fd201bc6eecf27b2858f7bc27462cd9b43ae464e9ef3281f97a3e0ffffffffa787e89792a93111ff08f5a083234c7c2410bd69b6eef42be0fc5f026a3a1cf0030000006b483045022100c3b86d7acadf1be3d8ea6706daedb842b09732621e830440481370d423703741022009fd0f90a07babd481f1011ec883b2aa248c6a4a433599c5b203c6b93fc03b67012103f9a47d3958281b6749921fdf6d9edde0176342c00ced7caacab9ab3a64795086ffffffff23fb90cebcb1784a7a4a0a35489356ba64cf95c0afdc5a0f0184dc22668ff41f050000006b483045022100ea698e5952e23ffdf6d58bdc73e91c555867e3ad99ac9b583f492882395ace9a0220705abe597972d45923fe0515695dd7b99dcfa50e69d49c03a8126180fd263bc70121036532aa886851548a5b62bff29b4c36bfdc33e68c7dbee8efb4b440e50c5ebc6effffffffd401de8afd8fd323ab6abd9db1d261ac69e7c1d2be7f1a40004e7659b7d6cd9b030000006b483045022100b09c4e7f227f2f86d1965edbc4c92b9058243300f3bc62a3169591aacb60ca4d0220390d0d7ae2ee7dab200e166337c65d4a62b576dc4fa138ce40efd240c57346fc0121034cd59665d736d927d9613c7624f8d616d483b06ab8993446f6119f18e22731feffffffff38b8b3ae5fe9ef09c9f1583c9d6cc128bbd2639d49aca97b7686a74ba91bb32a040000006a4730440220105d93aba953edf008cc5b16ac81c10d97db6e59a3e13062ceef7cc1fbffd2ad022027b14b4162d70c4448bec7cb086b4e52880b51b282de98019ec3038153e25ed0012102cdbfb52b3e164203845f72391a3a58205834a3ad473a9d9878488dc1594aa0d4ffffffff087edb0000000000001976a914a1e5f40c6171e91183533f16bbda35e45182bcfa88ac80d97800000000001976a91482985ea6f877d70692072af967af305005fc86fd88ac80d97800000000001976a914a698b206b9f654974afd2056c85c52f88e4c2b2488ac9970af05000000001976a914b05dbb0ede1191e2871209affd8a5922e0a3275288ac80d97800000000001976a914619b3b22b7b66220d22907b8600724aecc49f03488acabc80000000000001976a914911c8c57eb12aa2c1cdce92f82c7e0405a2f3c6988ac80d97800000000001976a91464cd0ed04862f2b7101e9394285d2b3066e5e4dc88ac13b14100000000001976a9143f81fa4fd890845882fbb5226539d9643c99f0f488ac00000000" rvtxid = "4489a8cc933cb4e94915ead5b57b4aa707212c1f7b317187b500491e068c7887" if interface == "joinmarket-electrum": t = etr.Transaction(raw_valid_tx) assert rvtxid == t.hash() #Transaction deserialization/serialization test #Electrum requires this call to fill out Transactionfields t.deserialize() #log.debug("Got inputs: " + str(t.inputs)) ourdeser = deserialize(t.raw) ourraw = serialize(ourdeser) #log.debug("Recreated: \n" + ourraw) assert ourraw == raw_valid_tx #double check round trip too assert deserialize(ourraw) == ourdeser txinslist = t.inputs() elif interface == "joinmarket-joinmarket": assert serialize(deserialize(raw_valid_tx)) == raw_valid_tx t = deserialize(raw_valid_tx) txinslist = t["ins"] else: raise NotImplementedError("No such interface?") #Transaction signature verification tests. #All currently assuming 100% p2pkh. for i, tin in enumerate(txinslist): if interface == "joinmarket-electrum": script = address_to_script(tin["address"]) sig = tin["signatures"][0] pub = tin["pubkeys"][0] elif interface == "joinmarket-joinmarket": log.debug("Joinmarket working with this script: " + tin["script"]) scriptSig = tin["script"] #We need to parse out the pubkey, convert to address, then convert #to a pubkeyscript; this assumes p2pkh. Note that this is handled #internally by the joinmarket blockchain/maker/taker code, so only #for tests. pub = scriptSig[-66:] script = address_to_script(pubkey_to_address(pub)) log.debug("Converted to this addr script: " + script) #drop the length bytes from the start of sig and pub sig = scriptSig[2:-68] else: raise NotImplementedError("No such interface?") log.debug("Got sig, script, pub: " + " ".join([sig, script, pub])) assert verify_tx_input(raw_valid_tx, i, script, sig, pub) log.debug("Sig at: " + str(i) + " OK.") #Note there are no transaction signing tests, as #this is done by the wallet in this interface. log.debug("All tests passed.")
def txhash(txhex): t = etr.Transaction(txhex) return t.txid()
def test_tx_unsigned(self): self.maxDiff = None expected = { 'inputs': [{ 'type': 'p2pkh', 'address': '14iRdacqJ95JffkUFUTUoZmHCUkq21UMAZ', 'issuance': None, 'num_sig': 1, 'prevout_hash': '25554b1cb7c28ca28188066312f66524bf1b241a120dec8bd39e81699aebddf8', 'prevout_n': 1, 'pubkeys': [ '031ec67b31750c9ca58b859200267625681d4c9849f8fb163207c4186a273e0b0a' ], 'scriptSig': '01ff4c53ff0488b21e000000000000000000350138c626aac760ea9eedb47287f12c4d783910821c5602d5f8ed933a8f0d95025fb1f45ecb87f2089dc8b0257fc23cc5fd13ae9d4e14c08b0398002d68eae14c00000000', 'sequence': 4294967294, 'signatures': [None], 'x_pubkeys': [ 'ff0488b21e000000000000000000350138c626aac760ea9eedb47287f12c4d783910821c5602d5f8ed933a8f0d95025fb1f45ecb87f2089dc8b0257fc23cc5fd13ae9d4e14c08b0398002d68eae14c00000000' ] }], 'lockTime': 3, 'outputs': [{ 'address': '1BvbZykUE5oS5ACH5U4mhwE5KdJPHson7', 'asset': '6718fdfa571f3f3d091cf57f03ceac534ee5a4f78f80880dc97ee1b4f5c21da4', 'asset_version': 1, 'nonce': None, 'nonce_version': 0, 'prevout_n': 0, 'scriptPubKey': '76a9140210e63973f9feddf155e5e73ac8f7289549b5f788ac', 'range_proof': None, 'surjection_proof': None, 'type': TYPE_ADDRESS, 'value': 100000000000000, 'value_version': 1 }, { 'address': '1FRUENS6LR8JdwEoptZwjRA1c64WDgcsac', 'asset': '6718fdfa571f3f3d091cf57f03ceac534ee5a4f78f80880dc97ee1b4f5c21da4', 'asset_version': 1, 'nonce': None, 'nonce_version': 0, 'prevout_n': 1, 'scriptPubKey': '76a9149e327995acc97229c07ce5e75789dab5eb3b689188ac', 'range_proof': None, 'surjection_proof': None, 'type': TYPE_ADDRESS, 'value': 399999999965500, 'value_version': 1 }, { 'address': '', 'asset': '6718fdfa571f3f3d091cf57f03ceac534ee5a4f78f80880dc97ee1b4f5c21da4', 'asset_version': 1, 'nonce': None, 'nonce_version': 0, 'prevout_n': 2, 'scriptPubKey': '', 'range_proof': None, 'surjection_proof': None, 'type': TYPE_SCRIPT, 'value': 34500, 'value_version': 1 }], 'partial': True, 'segwit_ser': False, 'version': 1, } tx = transaction.Transaction(unsigned_blob) self.assertEqual(tx.deserialize(), expected) self.assertEqual(tx.deserialize(), None) self.assertEqual(tx.as_dict(), { 'hex': unsigned_blob, 'complete': False, 'final': True }) self.assertEqual(tx.get_outputs(), [ ('1BvbZykUE5oS5ACH5U4mhwE5KdJPHson7', 100000000000000, '6718fdfa571f3f3d091cf57f03ceac534ee5a4f78f80880dc97ee1b4f5c21da4' ), ('1FRUENS6LR8JdwEoptZwjRA1c64WDgcsac', 399999999965500, '6718fdfa571f3f3d091cf57f03ceac534ee5a4f78f80880dc97ee1b4f5c21da4' ), ('SCRIPT ', 34500, '6718fdfa571f3f3d091cf57f03ceac534ee5a4f78f80880dc97ee1b4f5c21da4' ) ]) self.assertEqual(tx.get_output_addresses(), [ '1BvbZykUE5oS5ACH5U4mhwE5KdJPHson7', '1FRUENS6LR8JdwEoptZwjRA1c64WDgcsac', 'SCRIPT ' ]) self.assertTrue(tx.has_address('1BvbZykUE5oS5ACH5U4mhwE5KdJPHson7')) self.assertTrue(tx.has_address('1FRUENS6LR8JdwEoptZwjRA1c64WDgcsac')) self.assertFalse(tx.has_address('1FRUENS6LR8JdwEoptZwjRA1c64WDgcsab')) self.assertEqual(tx.serialize(), unsigned_blob) tx.update_signatures(signed_blob_signatures) self.assertEqual(tx.raw, signed_blob) tx.update(unsigned_blob) tx.raw = None blob = str(tx) self.assertEqual(transaction.deserialize(blob), expected)
def _run_naive_tests_on_tx(self, raw_tx, txid): tx = transaction.Transaction(raw_tx) self.assertEqual(txid, tx.txid()) self.assertEqual(raw_tx, tx.serialize()) self.assertTrue(tx.estimated_size() >= 0)
def test_version_field(self): tx = transaction.Transaction(v2_blob) self.assertEqual( tx.txid(), "7201a219a30af1303e4c17ab15a02e2d9c6fbfcd162403d5d171f293fa7901ce")
def test_tx_signed(self): self.maxDiff = None expected = { 'inputs': [{ 'type': 'unknown', 'address': None, 'issuance': None, 'num_sig': 0, 'prevout_hash': '25554b1cb7c28ca28188066312f66524bf1b241a120dec8bd39e81699aebddf8', 'prevout_n': 1, 'scriptSig': '483045022100c055b7b07847ee98bce64b22058356efca5b81f8a69f8c2b285669081c58361c02202d14691a6909888fc09e6fb2ab37949de87e0c7d1e72db10d6a2bfbec35fe61b0121031ec67b31750c9ca58b859200267625681d4c9849f8fb163207c4186a273e0b0a', 'sequence': 4294967294 }], 'lockTime': 3, 'outputs': [ { 'address': '1BvbZykUE5oS5ACH5U4mhwE5KdJPHson7', 'asset': '6718fdfa571f3f3d091cf57f03ceac534ee5a4f78f80880dc97ee1b4f5c21da4', 'asset_version': 1, 'nonce': None, 'nonce_version': 0, 'prevout_n': 0, 'scriptPubKey': '76a9140210e63973f9feddf155e5e73ac8f7289549b5f788ac', 'range_proof': None, 'surjection_proof': None, 'type': TYPE_ADDRESS, 'value': 100000000000000, 'value_version': 1 }, { 'address': '1FRUENS6LR8JdwEoptZwjRA1c64WDgcsac', 'asset': '6718fdfa571f3f3d091cf57f03ceac534ee5a4f78f80880dc97ee1b4f5c21da4', 'asset_version': 1, 'nonce': None, 'nonce_version': 0, 'prevout_n': 1, 'scriptPubKey': '76a9149e327995acc97229c07ce5e75789dab5eb3b689188ac', 'range_proof': None, 'surjection_proof': None, 'type': TYPE_ADDRESS, 'value': 399999999965500, 'value_version': 1 }, { 'address': '', 'asset': '6718fdfa571f3f3d091cf57f03ceac534ee5a4f78f80880dc97ee1b4f5c21da4', 'asset_version': 1, 'nonce': None, 'nonce_version': 0, 'prevout_n': 2, 'scriptPubKey': '', 'range_proof': None, 'surjection_proof': None, 'type': TYPE_SCRIPT, 'value': 34500, 'value_version': 1 }, ], 'partial': False, 'segwit_ser': False, '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_signatures) self.assertEqual(tx.estimated_total_size(), 341) self.assertEqual(tx.estimated_base_size(), 341) self.assertEqual(tx.estimated_witness_size(), 0) self.assertEqual(tx.estimated_weight(), 1364) self.assertEqual(tx.estimated_size(), 341)
def test_version_field(self): tx = transaction.Transaction(v2_blob) self.assertEqual( tx.txid(), "dd01e590bad4e115e1894c3c98894fbbdb7a76884b1bb2bfc57e137f9cc8d462")