Exemple #1
0
def main():
    parser = argparse.ArgumentParser(
        description="Add a transaction to tx cache.")
    parser.add_argument(
        "tx_id_or_path",
        nargs="+",
        help=
        'The id of the transaction to fetch from web services or the path to it.'
    )

    args = parser.parse_args()

    TX_RE = re.compile(r"^[0-9a-fA-F]{64}$")

    tx_db = get_tx_db("BTC")

    for p in args.tx_id_or_path:
        if TX_RE.match(p):
            tx = tx_db.get(h2b_rev(p))
            if not tx:
                parser.error("can't find Tx with id %s" % p)
        else:
            f = open(p, "rb")
            try:
                if f.name.endswith("hex"):
                    f = io.BytesIO(codecs.getreader("hex_codec")(f).read())
                tx = Tx.parse(f)
            except Exception:
                parser.error("can't parse %s" % f.name)

        tx_db[tx.hash()] = tx
        print("cached %s" % tx.id())
Exemple #2
0
def main():
    parser = argparse.ArgumentParser(description="Add a transaction to tx cache.")
    parser.add_argument("tx_id_or_path", nargs="+",
                        help='The id of the transaction to fetch from web services or the path to it.')

    args = parser.parse_args()

    TX_RE = re.compile(r"^[0-9a-fA-F]{64}$")

    tx_db = get_tx_db()

    for p in args.tx_id_or_path:
        if TX_RE.match(p):
            tx = tx_db.get(h2b_rev(p))
            if not tx:
                parser.error("can't find Tx with id %s" % p)
        else:
            f = open(p, "rb")
            try:
                if f.name.endswith("hex"):
                    f = io.BytesIO(codecs.getreader("hex_codec")(f).read())
                tx = Tx.parse(f)
            except Exception:
                parser.error("can't parse %s" % f.name)

        tx_db[tx.hash()] = tx
        print("cached %s" % tx.id())
Exemple #3
0
def get_tx(tx_hash):
    """
    Get a Tx by its hash.
    """
    URL = "http://btc.blockr.io/api/v1/tx/raw/%s" % b2h_rev(tx_hash)
    r = json.loads(urlopen(URL).read().decode("utf8"))
    tx = Tx.parse(io.BytesIO(h2b(r.get("data").get("tx").get("hex"))))
    return tx
Exemple #4
0
 def test_validate_multisig(self):
     # this is a transaction in the block chain
     # the unspents are included too, so it can be validated
     f = io.BytesIO(h2b("01000000025718fb915fb8b3a802bb699ddf04dd91261ef6715f5f2820a2b1b9b7e38b4f27000000004a004830450221008c2107ed4e026ab4319a591e8d9ec37719cdea053951c660566e3a3399428af502202ecd823d5f74a77cc2159d8af2d3ea5d36a702fef9a7edaaf562aef22ac35da401ffffffff038f52231b994efb980382e4d804efeadaee13cfe01abe0d969038ccb45ec17000000000490047304402200487cd787fde9b337ab87f9fe54b9fd46d5d1692aa58e97147a4fe757f6f944202203cbcfb9c0fc4e3c453938bbea9e5ae64030cf7a97fafaf460ea2cb54ed5651b501ffffffff0100093d00000000001976a9144dc39248253538b93d3a0eb122d16882b998145888ac0000000002000000000000004751210351efb6e91a31221652105d032a2508275f374cea63939ad72f1b1e02f477da782100f2b7816db49d55d24df7bdffdbc1e203b424e8cd39f5651ab938e5e4a193569e52ae404b4c00000000004751210351efb6e91a31221652105d032a2508275f374cea63939ad72f1b1e02f477da7821004f0331742bbc917ba2056a3b8a857ea47ec088dd10475ea311302112c9d24a7152ae"))
     tx = Tx.parse(f)
     tx.parse_unspents(f)
     self.assertEqual(tx.id(), "70c4e749f2b8b907875d1483ae43e8a6790b0c8397bbb33682e3602617f9a77a")
     self.assertEqual(tx.bad_signature_count(), 0)
Exemple #5
0
def get_tx(tx_hash):
    """
    Get a Tx by its hash.
    """
    URL = "http://btc.blockr.io/api/v1/tx/raw/%s" % b2h_rev(tx_hash)
    r = json.loads(urlopen(URL).read().decode("utf8"))
    tx = Tx.parse(io.BytesIO(h2b(r.get("data").get("tx").get("hex"))))
    return tx
Exemple #6
0
def get_tx(tx_hash):
    """
    Get a Tx by its hash.
    """
    URL = "%s/tx/raw/%s" % (blockrendpoint.url, b2h_rev(tx_hash))
    r = json.loads(urlopen(URL).read().decode("utf8"))
    tx = Tx.parse(io.BytesIO(h2b(r.get("data").get("tx").get("hex"))))
    return tx
Exemple #7
0
 def test_validate_multisig(self):
     # this is a transaction in the block chain
     # the unspents are included too, so it can be validated
     f = io.BytesIO(h2b("01000000025718fb915fb8b3a802bb699ddf04dd91261ef6715f5f2820a2b1b9b7e38b4f27000000004a004830450221008c2107ed4e026ab4319a591e8d9ec37719cdea053951c660566e3a3399428af502202ecd823d5f74a77cc2159d8af2d3ea5d36a702fef9a7edaaf562aef22ac35da401ffffffff038f52231b994efb980382e4d804efeadaee13cfe01abe0d969038ccb45ec17000000000490047304402200487cd787fde9b337ab87f9fe54b9fd46d5d1692aa58e97147a4fe757f6f944202203cbcfb9c0fc4e3c453938bbea9e5ae64030cf7a97fafaf460ea2cb54ed5651b501ffffffff0100093d00000000001976a9144dc39248253538b93d3a0eb122d16882b998145888ac0000000002000000000000004751210351efb6e91a31221652105d032a2508275f374cea63939ad72f1b1e02f477da782100f2b7816db49d55d24df7bdffdbc1e203b424e8cd39f5651ab938e5e4a193569e52ae404b4c00000000004751210351efb6e91a31221652105d032a2508275f374cea63939ad72f1b1e02f477da7821004f0331742bbc917ba2056a3b8a857ea47ec088dd10475ea311302112c9d24a7152ae"))
     tx = Tx.parse(f)
     tx.parse_unspents(f)
     self.assertEqual(tx.id(), "70c4e749f2b8b907875d1483ae43e8a6790b0c8397bbb33682e3602617f9a77a")
     self.assertEqual(tx.bad_signature_count(), 0)
Exemple #8
0
 def get_tx(self, tx_hash):
     """
     Get a Tx by its hash.
     """
     url_append = "tx/raw/%s" %(tx_hash)
     URL = self.base_url("%s" %url_append)
     r = json.loads(urlopen(URL).read().decode("utf8"))
     tx = Tx.parse(io.BytesIO(h2b(r.get("data").get("tx").get("hex"))))
     return tx
Exemple #9
0
 def test_recognize_multisig(self):
     h = '010000000139c92b102879eb95f14e7344e4dd7d481e1238b1bfb1fa0f735068d2927b231400000000910047304402208fc06d216ebb4b6a3a3e0f906e1512c372fa8a9c2a92505d04e9b451ea7acd0c0220764303bb7e514ddd77855949d941c934e9cbda8e3c3827bfdb5777477e73885b014730440220569ec6d2e81625dd18c73920e0079cdb4c1d67d3d7616759eb0c18cf566b3d3402201c60318f0a62e3ba85ca0f158d4dfe63c0779269eb6765b6fc939fc51e7a8ea901ffffffff0140787d01000000001976a914641ad5051edd97029a003fe9efb29359fcee409d88ac0000000040787d0100000000c952410496ec45f878b62c46c4be8e336dff7cc58df9b502178cc240eb3d31b1266f69f5767071aa3e017d1b82a0bb28dab5e27d4d8e9725b3e68ed5f8a2d45c730621e34104cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4410461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af53ae'
     f = io.BytesIO(h2b(h))
     tx = Tx.parse(f)
     tx.parse_unspents(f)
     self.assertEqual(tx.id(), "10c61e258e0a2b19b245a96a2d0a1538fe81cd4ecd547e0a3df7ed6fd3761ada")
     the_script = tx.unspents[0].script
     s = script_obj_from_script(tx.unspents[0].script)
     self.assertEqual(s.script(), the_script)
Exemple #10
0
 def test_recognize_multisig(self):
     h = '010000000139c92b102879eb95f14e7344e4dd7d481e1238b1bfb1fa0f735068d2927b231400000000910047304402208fc06d216ebb4b6a3a3e0f906e1512c372fa8a9c2a92505d04e9b451ea7acd0c0220764303bb7e514ddd77855949d941c934e9cbda8e3c3827bfdb5777477e73885b014730440220569ec6d2e81625dd18c73920e0079cdb4c1d67d3d7616759eb0c18cf566b3d3402201c60318f0a62e3ba85ca0f158d4dfe63c0779269eb6765b6fc939fc51e7a8ea901ffffffff0140787d01000000001976a914641ad5051edd97029a003fe9efb29359fcee409d88ac0000000040787d0100000000c952410496ec45f878b62c46c4be8e336dff7cc58df9b502178cc240eb3d31b1266f69f5767071aa3e017d1b82a0bb28dab5e27d4d8e9725b3e68ed5f8a2d45c730621e34104cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4410461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af53ae'
     f = io.BytesIO(h2b(h))
     tx = Tx.parse(f)
     tx.parse_unspents(f)
     self.assertEqual(tx.id(), "10c61e258e0a2b19b245a96a2d0a1538fe81cd4ecd547e0a3df7ed6fd3761ada")
     the_script = tx.unspents[0].script
     s = script_obj_from_script(tx.unspents[0].script)
     self.assertEqual(s.script(), the_script)
Exemple #11
0
 def tx_for_tx_hash(self, tx_hash):
     """
     returns the pycoin.tx object for tx_hash
     """
     try:
         url_append = "?token=%s&includeHex=true" % self.api_key
         url = self.base_url("txs/%s%s" % (b2h_rev(tx_hash), url_append))
         result = json.loads(urlopen(url).read().decode("utf8"))
         tx = Tx.parse(io.BytesIO(h2b(result.get("hex"))))
         return tx
     except:
         raise Exception
Exemple #12
0
 def tx_for_tx_hash(self, tx_hash):
     """
     returns the pycoin.tx object for tx_hash
     """
     try:
         url_append = "?token=%s&includeHex=true" % self.api_key
         url = self.base_url("txs/%s%s" % (b2h_rev(tx_hash), url_append))
         result = json.loads(urlopen(url).read().decode("utf8"))
         tx = Tx.parse(io.BytesIO(h2b(result.get("hex"))))
         return tx
     except:
         raise Exception
Exemple #13
0
def tx_for_id(tx_id):
    """
    Get a transaction by ID.

    Args:
        tx_id: the transaction ID.
    Returns:
        The Tx object.
    """
    result = requests.get(
        'https://api.blockcypher.com/v1/btc/main/txs/%s?token=&includeHex=true'
        % tx_id).json()
    tx = Tx.parse(io.BytesIO(unhexlify(result.get("hex"))))
    return tx
Exemple #14
0
def wallet_exclude(path, args):
    sql_db = sqlite3.Connection(os.path.join(path, "wallet.db"))
    persistence = SQLite3Persistence(sql_db)

    with open(args.path_to_tx, "rb") as f:
        if f.name.endswith("hex"):
            f = io.BytesIO(codecs.getreader("hex_codec")(f).read())
        tx = Tx.parse(f)

    for tx_in in tx.txs_in:
        spendable = persistence.spendable_for_hash_index(tx_in.previous_hash, tx_in.previous_index)
        if spendable:
            spendable.does_seem_spent = True
            persistence.save_spendable(spendable)
    persistence.commit()
Exemple #15
0
def wallet_exclude(path, args):
    sql_db = sqlite3.Connection(os.path.join(path, "wallet.db"))
    persistence = SQLite3Persistence(sql_db)

    with open(args.path_to_tx, "rb") as f:
        if f.name.endswith("hex"):
            f = io.BytesIO(codecs.getreader("hex_codec")(f).read())
        tx = Tx.parse(f)

    for tx_in in tx.txs_in:
        spendable = persistence.spendable_for_hash_index(
            tx_in.previous_hash, tx_in.previous_index)
        if spendable:
            spendable.does_seem_spent = True
            persistence.save_spendable(spendable)
    persistence.commit()
Exemple #16
0
def cache_tx(args, parser):
    TX_RE = re.compile(r"^[0-9a-fA-F]{64}$")

    tx_db = get_tx_db()

    for p in args.tx_id_or_path:
        if TX_RE.match(p):
            tx = tx_db.get(h2b_rev(p))
            if not tx:
                parser.error("can't find Tx with id %s" % p)
        else:
            f = open(p, "rb")
            try:
                if f.name.endswith("hex"):
                    f = io.BytesIO(codecs.getreader("hex_codec")(f).read())
                tx = Tx.parse(f)
            except Exception:
                parser.error("can't parse %s" % f.name)

        tx_db[tx.hash()] = tx
        print("cached %s" % tx.id())
Exemple #17
0
 def tx_for_tx_hash(self, tx_hash):
     "Get a Tx by its hash."
     url = self.base_url("get_tx", b2h_rev(tx_hash))
     r = json.loads(urlopen(url).read().decode("utf8"))
     tx = Tx.parse(io.BytesIO(h2b(r.get("data").get("tx_hex"))))
     return tx
def main():
    parser = argparse.ArgumentParser(
        description="DigitalOracle HD multisig command line utility",
        formatter_class=argparse.RawDescriptionHelpFormatter,
    )
    parser.add_argument("-e", "--email", help="e-mail for create")
    parser.add_argument("-p", "--phone", help="phone number for create - in the format +14155551212")
    parser.add_argument("-n", "--network", default="BTC", choices=NETWORK_NAMES)
    parser.add_argument("-i", "--inputpath", nargs="+", help="HD subkey path for each input (example: 0/2/15)")
    parser.add_argument(
        "-c",
        "--changepath",
        nargs="+",
        help="HD subkey path for each change output (example: 0/2/15) or a dash for non-change outputs",
    )
    parser.add_argument("-s", "--spendid", help="an additional hex string to disambiguate spends to the same address")
    parser.add_argument(
        "-u", "--baseurl", help="the API endpoint, defaults to the sandbox - https://s.digitaloracle.co/"
    )
    parser.add_argument(
        "-v", "--verbose", default=0, action="count", help="Verbosity, use more -v flags for more verbosity"
    )
    parser.add_argument("command", help="""a command""")
    parser.add_argument("item", nargs="+", help="""a key""")
    parser.epilog = textwrap.dedent(
        """
    Items:
     * P:wallet_passphrase - a secret for deriving an HD hierarchy with private keys
     * xpub - an account extended public key for deriving an HD hierarchy with public keys only
     * FILE.bin - unsigned transaction binary
     * FILE.hex - unsigned transaction hex

    Commands:
     * dump - dump the public subkeys
     * create - create Oracle account based on the supplied leading key with with any additional keys
     * address - get the deposit address for a subkey path
     * sign - sign a transaction, tx.bin or tx.hex must be supplied. Only one subkey path is supported.

    Notes:
     * --subkey is applicable for the address and sign actions, but not the create action
    """
    )
    args = parser.parse_args()

    keys = []
    txs = []
    for item in args.item:
        key = None
        tx = None
        if item.startswith("P:"):
            s = item[2:]
            key = BIP32Node.from_master_secret(s.encode("utf8"), netcode=args.network)
            keys.append(key)
        else:
            try:
                key = Key.from_text(item)
                keys.append(key)
            except encoding.EncodingError:
                pass
        if key is None:
            if os.path.exists(item):
                try:
                    with open(item, "rb") as f:
                        if f.name.endswith("hex"):
                            f = io.BytesIO(codecs.getreader("hex_codec")(f).read())
                        tx = Tx.parse(f)
                        txs.append(tx)
                        try:
                            tx.parse_unspents(f)
                        except Exception as ex:
                            pass
                        continue
                except Exception as ex:
                    print("could not parse %s %s" % (item, ex), file=sys.stderr)
                    pass
        if tx is None and key is None:
            print("could not understand item %s" % (item,))

    account = MultisigAccount(keys, complete=False)
    oracle = Oracle(account, tx_db=get_tx_db(), base_url=args.baseurl)
    oracle.verbose = args.verbose

    if args.command == "dump":
        sub_keys = [key.subkey_for_path(path or "") for key, path in izip_longest(keys, args.inputpath)]
        for key in sub_keys:
            print(key.wallet_key(as_private=False))
    elif args.command == "create":
        calls = []
        if args.email:
            calls.append("email")
        if args.phone:
            calls.append("phone")
        parameters = {"levels": [{"asset": "BTC", "period": 60, "value": 0.001}, {"delay": 0, "calls": calls}]}
        oracle.create(parameters, email=args.email, phone=args.phone)
    elif args.command == "address":
        oracle.get()
        path = args.inputpath[0] if args.inputpath else ""
        payto = account.payto_for_path(path)
        if args.verbose > 0:
            sub_keys = [key.subkey_for_path(path) for key in account.keys]
            print("* account keys")
            print(account.keys)
            print("* child keys")
            for key in sub_keys:
                print(key.wallet_key(as_private=False))
            print("* address")
        print(payto.address(netcode=args.network))
    elif args.command == "sign":
        oracle.get()
        scripts = [account.script_for_path(path) for path in args.inputpath]
        change_paths = [None if path == "-" else path for path in (args.changepath or [])]
        for tx in txs:
            print(tx.id())
            print(b2h(stream_to_bytes(tx.stream)))
            sub_keys = [keys[0].subkey_for_path(path) for path in args.inputpath]
            # sign locally
            local_sign(tx, scripts, sub_keys)
            # have Oracle sign
            result = oracle.sign_with_paths(tx, args.inputpath, change_paths, spend_id=args.spendid)
            print("Result:")
            print(result)
            if "transaction" in result:
                print("Hex serialized transaction:")
                print(b2h(stream_to_bytes(result["transaction"].stream)))
    else:
        print("unknown command %s" % (args.command,), file=sys.stderr)
Exemple #19
0
 def tx_for_tx_hash(self, tx_hash):
     "Get a Tx by its hash."
     URL = "%s/tx/raw/%s" % (self.url, b2h_rev(tx_hash))
     r = json.loads(url_open(URL).read().decode("utf8"))
     tx = Tx.parse(io.BytesIO(h2b(r.get("data").get("tx").get("hex"))))
     return tx
def main():
    parser = argparse.ArgumentParser(
        description='DigitalOracle HD multisig command line utility',
        formatter_class=argparse.RawDescriptionHelpFormatter,
    )
    parser.add_argument('-e', '--email', help='e-mail for create')
    parser.add_argument(
        '-p',
        '--phone',
        help='phone number for create - in the format +14155551212')
    parser.add_argument('-n',
                        '--network',
                        default='BTC',
                        choices=NETWORK_NAMES)
    parser.add_argument('-i',
                        "--inputpath",
                        nargs='+',
                        help='HD subkey path for each input (example: 0/2/15)')
    parser.add_argument(
        '-c',
        "--changepath",
        nargs='+',
        help=
        'HD subkey path for each change output (example: 0/2/15) or a dash for non-change outputs'
    )
    parser.add_argument(
        '-s',
        "--spendid",
        help=
        'an additional hex string to disambiguate spends to the same address')
    parser.add_argument(
        '-u',
        "--baseurl",
        help=
        'the API endpoint, defaults to the sandbox - https://s.digitaloracle.co/'
    )
    parser.add_argument('-v',
                        "--verbose",
                        default=0,
                        action="count",
                        help="Verbosity, use more -v flags for more verbosity")
    parser.add_argument('command', help="""a command""")
    parser.add_argument('item', nargs='+', help="""a key""")
    parser.epilog = textwrap.dedent("""
    Items:
     * P:wallet_passphrase - a secret for deriving an HD hierarchy with private keys
     * xpub - an account extended public key for deriving an HD hierarchy with public keys only
     * FILE.bin - unsigned transaction binary
     * FILE.hex - unsigned transaction hex

    Commands:
     * dump - dump the public subkeys
     * create - create Oracle account based on the supplied leading key with with any additional keys
     * address - get the deposit address for a subkey path
     * sign - sign a transaction, tx.bin or tx.hex must be supplied. Only one subkey path is supported.

    Notes:
     * --subkey is applicable for the address and sign actions, but not the create action
    """)
    args = parser.parse_args()

    keys = []
    txs = []
    for item in args.item:
        key = None
        tx = None
        if item.startswith('P:'):
            s = item[2:]
            key = BIP32Node.from_master_secret(s.encode('utf8'),
                                               netcode=args.network)
            keys.append(key)
        else:
            try:
                key = Key.from_text(item)
                keys.append(key)
            except encoding.EncodingError:
                pass
        if key is None:
            if os.path.exists(item):
                try:
                    with open(item, "rb") as f:
                        if f.name.endswith("hex"):
                            f = io.BytesIO(
                                codecs.getreader("hex_codec")(f).read())
                        tx = Tx.parse(f)
                        txs.append(tx)
                        try:
                            tx.parse_unspents(f)
                        except Exception as ex:
                            pass
                        continue
                except Exception as ex:
                    print('could not parse %s %s' % (item, ex),
                          file=sys.stderr)
                    pass
        if tx is None and key is None:
            print('could not understand item %s' % (item, ))

    account = MultisigAccount(keys, complete=False)
    oracle = Oracle(account, tx_db=get_tx_db(), base_url=args.baseurl)
    oracle.verbose = args.verbose

    if args.command == 'dump':
        sub_keys = [
            key.subkey_for_path(path or "")
            for key, path in izip_longest(keys, args.inputpath)
        ]
        for key in sub_keys:
            print(key.wallet_key(as_private=False))
    elif args.command == 'create':
        calls = []
        if args.email:
            calls.append('email')
        if args.phone:
            calls.append('phone')
        parameters = {
            "levels": [{
                "asset": "BTC",
                "period": 60,
                "value": 0.001
            }, {
                "delay": 0,
                "calls": calls
            }]
        }
        oracle.create(parameters, email=args.email, phone=args.phone)
    elif args.command == 'address':
        oracle.get()
        path = args.inputpath[0] if args.inputpath else ""
        payto = account.payto_for_path(path)
        if args.verbose > 0:
            sub_keys = [key.subkey_for_path(path) for key in account.keys]
            print("* account keys")
            print(account.keys)
            print("* child keys")
            for key in sub_keys:
                print(key.wallet_key(as_private=False))
            print("* address")
        print(payto.address(netcode=args.network))
    elif args.command == 'sign':
        oracle.get()
        scripts = [account.script_for_path(path) for path in args.inputpath]
        change_paths = [
            None if path == '-' else path for path in (args.changepath or [])
        ]
        for tx in txs:
            print(tx.id())
            print(b2h(stream_to_bytes(tx.stream)))
            sub_keys = [
                keys[0].subkey_for_path(path) for path in args.inputpath
            ]
            # sign locally
            local_sign(tx, scripts, sub_keys)
            # have Oracle sign
            result = oracle.sign_with_paths(tx,
                                            args.inputpath,
                                            change_paths,
                                            spend_id=args.spendid)
            print("Result:")
            print(result)
            if 'transaction' in result:
                print("Hex serialized transaction:")
                print(b2h(stream_to_bytes(result['transaction'].stream)))
    else:
        print('unknown command %s' % (args.command, ), file=sys.stderr)
Exemple #21
0
def parse_context(args, parser):
    # defaults

    txs = []
    spendables = []
    payables = []

    key_iters = []

    TX_ID_RE = re.compile(r"^[0-9a-fA-F]{64}$")

    # there are a few warnings we might optionally print out, but only if
    # they are relevant. We don't want to print them out multiple times, so we
    # collect them here and print them at the end if they ever kick in.

    warning_tx_cache = None
    warning_tx_for_tx_hash = None
    warning_spendables = None

    if args.private_key_file:
        wif_re = re.compile(r"[1-9a-km-zA-LMNP-Z]{51,111}")
        # address_re = re.compile(r"[1-9a-kmnp-zA-KMNP-Z]{27-31}")
        for f in args.private_key_file:
            if f.name.endswith(".gpg"):
                gpg_args = ["gpg", "-d"]
                if args.gpg_argument:
                    gpg_args.extend(args.gpg_argument.split())
                gpg_args.append(f.name)
                popen = subprocess.Popen(gpg_args, stdout=subprocess.PIPE)
                f = popen.stdout
            for line in f.readlines():
                # decode
                if isinstance(line, bytes):
                    line = line.decode("utf8")
                # look for WIFs
                possible_keys = wif_re.findall(line)

                def make_key(x):
                    try:
                        return Key.from_text(x)
                    except Exception:
                        return None

                keys = [make_key(x) for x in possible_keys]
                for key in keys:
                    if key:
                        key_iters.append((k.wif() for k in key.subkeys("")))

                # if len(keys) == 1 and key.hierarchical_wallet() is None:
                #    # we have exactly 1 WIF. Let's look for an address
                #   potential_addresses = address_re.findall(line)

    # update p2sh_lookup
    p2sh_lookup = {}
    if args.pay_to_script:
        for p2s in args.pay_to_script:
            try:
                script = h2b(p2s)
                p2sh_lookup[hash160(script)] = script
            except Exception:
                print("warning: error parsing pay-to-script value %s" % p2s)

    if args.pay_to_script_file:
        hex_re = re.compile(r"[0-9a-fA-F]+")
        for f in args.pay_to_script_file:
            count = 0
            for l in f:
                try:
                    m = hex_re.search(l)
                    if m:
                        p2s = m.group(0)
                        script = h2b(p2s)
                        p2sh_lookup[hash160(script)] = script
                        count += 1
                except Exception:
                    print("warning: error parsing pay-to-script file %s" % f.name)
            if count == 0:
                print("warning: no scripts found in %s" % f.name)

    # we create the tx_db lazily
    tx_db = None

    for arg in args.argument:

        # hex transaction id
        if TX_ID_RE.match(arg):
            if tx_db is None:
                warning_tx_cache = message_about_tx_cache_env()
                warning_tx_for_tx_hash = message_about_tx_for_tx_hash_env(args.network)
                tx_db = get_tx_db(args.network)
            tx = tx_db.get(h2b_rev(arg))
            if not tx:
                for m in [warning_tx_cache, warning_tx_for_tx_hash, warning_spendables]:
                    if m:
                        print("warning: %s" % m, file=sys.stderr)
                parser.error("can't find Tx with id %s" % arg)
            txs.append(tx)
            continue

        # hex transaction data
        try:
            tx = Tx.from_hex(arg)
            txs.append(tx)
            continue
        except Exception:
            pass

        is_valid = is_address_valid(arg, allowable_netcodes=[args.network])
        if is_valid:
            payables.append((arg, 0))
            continue

        try:
            key = Key.from_text(arg)
            # TODO: check network
            if key.wif() is None:
                payables.append((key.address(), 0))
                continue
            # TODO: support paths to subkeys
            key_iters.append((k.wif() for k in key.subkeys("")))
            continue
        except Exception:
            pass

        if os.path.exists(arg):
            try:
                with open(arg, "rb") as f:
                    if f.name.endswith("hex"):
                        f = io.BytesIO(codecs.getreader("hex_codec")(f).read())
                    tx = Tx.parse(f)
                    txs.append(tx)
                    try:
                        tx.parse_unspents(f)
                    except Exception as ex:
                        pass
                    continue
            except Exception:
                pass

        parts = arg.split("/")
        if len(parts) == 4:
            # spendable
            try:
                spendables.append(Spendable.from_text(arg))
                continue
            except Exception:
                pass

        if len(parts) == 2 and is_address_valid(parts[0], allowable_netcodes=[args.network]):
            try:
                payables.append(parts)
                continue
            except ValueError:
                pass

        parser.error("can't parse %s" % arg)

    if args.fetch_spendables:
        warning_spendables = message_about_spendables_for_address_env(args.network)
        for address in args.fetch_spendables:
            spendables.extend(spendables_for_address(address))

    for tx in txs:
        if tx.missing_unspents() and args.augment:
            if tx_db is None:
                warning_tx_cache = message_about_tx_cache_env()
                warning_tx_for_tx_hash = message_about_tx_for_tx_hash_env(args.network)
                tx_db = get_tx_db(args.network)
            tx.unspents_from_db(tx_db, ignore_missing=True)

    return (txs, spendables, payables, key_iters, p2sh_lookup, tx_db, warning_tx_cache,
            warning_tx_for_tx_hash, warning_spendables)
Exemple #22
0
def parse_context(args, parser):
    # defaults

    txs = []
    spendables = []
    payables = []

    key_iters = []

    TX_ID_RE = re.compile(r"^[0-9a-fA-F]{64}$")

    # there are a few warnings we might optionally print out, but only if
    # they are relevant. We don't want to print them out multiple times, so we
    # collect them here and print them at the end if they ever kick in.

    warning_tx_cache = None
    warning_tx_for_tx_hash = None
    warning_spendables = None

    if args.private_key_file:
        wif_re = re.compile(r"[1-9a-km-zA-LMNP-Z]{51,111}")
        # address_re = re.compile(r"[1-9a-kmnp-zA-KMNP-Z]{27-31}")
        for f in args.private_key_file:
            if f.name.endswith(".gpg"):
                gpg_args = ["gpg", "-d"]
                if args.gpg_argument:
                    gpg_args.extend(args.gpg_argument.split())
                gpg_args.append(f.name)
                popen = subprocess.Popen(gpg_args, stdout=subprocess.PIPE)
                f = popen.stdout
            for line in f.readlines():
                # decode
                if isinstance(line, bytes):
                    line = line.decode("utf8")
                # look for WIFs
                possible_keys = wif_re.findall(line)

                def make_key(x):
                    try:
                        return Key.from_text(x)
                    except Exception:
                        return None

                keys = [make_key(x) for x in possible_keys]
                for key in keys:
                    if key:
                        key_iters.append((k.wif() for k in key.subkeys("")))

                # if len(keys) == 1 and key.hierarchical_wallet() is None:
                #    # we have exactly 1 WIF. Let's look for an address
                #   potential_addresses = address_re.findall(line)

    # update p2sh_lookup
    p2sh_lookup = {}
    if args.pay_to_script:
        for p2s in args.pay_to_script:
            try:
                script = h2b(p2s)
                p2sh_lookup[hash160(script)] = script
            except Exception:
                print("warning: error parsing pay-to-script value %s" % p2s)

    if args.pay_to_script_file:
        hex_re = re.compile(r"[0-9a-fA-F]+")
        for f in args.pay_to_script_file:
            count = 0
            for l in f:
                try:
                    m = hex_re.search(l)
                    if m:
                        p2s = m.group(0)
                        script = h2b(p2s)
                        p2sh_lookup[hash160(script)] = script
                        count += 1
                except Exception:
                    print("warning: error parsing pay-to-script file %s" %
                          f.name)
            if count == 0:
                print("warning: no scripts found in %s" % f.name)

    # we create the tx_db lazily
    tx_db = None

    for arg in args.argument:

        # hex transaction id
        if TX_ID_RE.match(arg):
            if tx_db is None:
                warning_tx_cache = message_about_tx_cache_env()
                warning_tx_for_tx_hash = message_about_tx_for_tx_hash_env(
                    args.network)
                tx_db = get_tx_db(args.network)
            tx = tx_db.get(h2b_rev(arg))
            if not tx:
                for m in [
                        warning_tx_cache, warning_tx_for_tx_hash,
                        warning_spendables
                ]:
                    if m:
                        print("warning: %s" % m, file=sys.stderr)
                parser.error("can't find Tx with id %s" % arg)
            txs.append(tx)
            continue

        # hex transaction data
        try:
            tx = Tx.from_hex(arg)
            txs.append(tx)
            continue
        except Exception:
            pass

        is_valid = is_address_valid(arg, allowable_netcodes=[args.network])
        if is_valid:
            payables.append((arg, 0))
            continue

        try:
            key = Key.from_text(arg)
            # TODO: check network
            if key.wif() is None:
                payables.append((key.address(), 0))
                continue
            # TODO: support paths to subkeys
            key_iters.append((k.wif() for k in key.subkeys("")))
            continue
        except Exception:
            pass

        if os.path.exists(arg):
            try:
                with open(arg, "rb") as f:
                    if f.name.endswith("hex"):
                        f = io.BytesIO(codecs.getreader("hex_codec")(f).read())
                    tx = Tx.parse(f)
                    txs.append(tx)
                    try:
                        tx.parse_unspents(f)
                    except Exception as ex:
                        pass
                    continue
            except Exception:
                pass

        parts = arg.split("/")
        if len(parts) == 4:
            # spendable
            try:
                spendables.append(Spendable.from_text(arg))
                continue
            except Exception:
                pass

        if len(parts) == 2 and is_address_valid(
                parts[0], allowable_netcodes=[args.network]):
            try:
                payables.append(parts)
                continue
            except ValueError:
                pass

        parser.error("can't parse %s" % arg)

    if args.fetch_spendables:
        warning_spendables = message_about_spendables_for_address_env(
            args.network)
        for address in args.fetch_spendables:
            spendables.extend(spendables_for_address(address))

    for tx in txs:
        if tx.missing_unspents() and args.augment:
            if tx_db is None:
                warning_tx_cache = message_about_tx_cache_env()
                warning_tx_for_tx_hash = message_about_tx_for_tx_hash_env(
                    args.network)
                tx_db = get_tx_db(args.network)
            tx.unspents_from_db(tx_db, ignore_missing=True)

    return (txs, spendables, payables, key_iters, p2sh_lookup, tx_db,
            warning_tx_cache, warning_tx_for_tx_hash, warning_spendables)
 def tx_from_b64(h):
     f = io.BytesIO(binascii.a2b_base64(h.encode("utf8")))
     return Tx.parse(f)
Exemple #24
0
 def init(self):
     self.transaction = PycoinTx.parse(self.tx_bytes)
Exemple #25
0
 def tx_from_b64(h):
     f = io.BytesIO(binascii.a2b_base64(h.encode("utf8")))
     return Tx.parse(f)
Exemple #26
0
 def tx_for_tx_hash(self, tx_hash):
     "Get a Tx by its hash."
     url = self.base_url("get_tx", b2h_rev(tx_hash))
     r = json.loads(urlopen(url).read().decode("utf8"))
     tx = Tx.parse(io.BytesIO(h2b(r.get("data").get("tx_hex"))))
     return tx
Exemple #27
0
def main():
    parser = argparse.ArgumentParser(
        description="Manipulate bitcoin (or alt coin) transactions.",
        epilog=EPILOG)

    parser.add_argument('-t', "--transaction-version", type=int,
                        help='Transaction version, either 1 (default) or 3 (not yet supported).')

    parser.add_argument('-l', "--lock-time", type=parse_locktime, help='Lock time; either a block'
                        'index, or a date/time (example: "2014-01-01T15:00:00"')

    parser.add_argument('-n', "--network", default="BTC",
                        help='Define network code (M=Bitcoin mainnet, T=Bitcoin testnet).')

    parser.add_argument('-a', "--augment", action='store_true',
                        help='augment tx by adding any missing spendable metadata by fetching'
                             ' inputs from cache and/or web services')

    parser.add_argument("-i", "--fetch-spendables", metavar="address", action="append",
                        help='Add all unspent spendables for the given bitcoin address. This information'
                        ' is fetched from web services.')

    parser.add_argument('-f', "--private-key-file", metavar="path-to-private-keys", action="append",
                        help='file containing WIF or BIP0032 private keys. If file name ends with .gpg, '
                        '"gpg -d" will be invoked automatically. File is read one line at a time, and if '
                        'the file contains only one WIF per line, it will also be scanned for a bitcoin '
                        'address, and any addresses found will be assumed to be public keys for the given'
                        ' private key.',
                        type=argparse.FileType('r'))

    parser.add_argument('-g', "--gpg-argument", help='argument to pass to gpg (besides -d).', default='')

    parser.add_argument("--remove-tx-in", metavar="tx_in_index_to_delete", action="append", type=int,
                        help='remove a tx_in')

    parser.add_argument("--remove-tx-out", metavar="tx_out_index_to_delete", action="append", type=int,
                        help='remove a tx_out')

    parser.add_argument('-F', "--fee", help='fee, in satoshis, to pay on transaction, or '
                        '"standard" to auto-calculate. This is only useful if the "split pool" '
                        'is used; otherwise, the fee is automatically set to the unclaimed funds.',
                        default="standard", metavar="transaction-fee", type=parse_fee)

    parser.add_argument('-C', "--cache", help='force the resultant transaction into the transaction cache.'
                        ' Mostly for testing.', action='store_true'),

    parser.add_argument('-u', "--show-unspents", action='store_true',
                        help='show TxOut items for this transaction in Spendable form.')

    parser.add_argument('-b', "--bitcoind-url",
                        help='URL to bitcoind instance to validate against (http://user:pass@host:port).')

    parser.add_argument('-o', "--output-file", metavar="path-to-output-file", type=argparse.FileType('wb'),
                        help='file to write transaction to. This supresses most other output.')

    parser.add_argument("argument", nargs="+", help='generic argument: can be a hex transaction id '
                        '(exactly 64 characters) to be fetched from cache or a web service;'
                        ' a transaction as a hex string; a path name to a transaction to be loaded;'
                        ' a spendable 4-tuple of the form tx_id/tx_out_idx/script_hex/satoshi_count '
                        'to be added to TxIn list; an address/satoshi_count to be added to the TxOut '
                        'list; an address to be added to the TxOut list and placed in the "split'
                        ' pool".')

    args = parser.parse_args()

    # defaults

    txs = []
    spendables = []
    payables = []

    key_iters = []

    TX_ID_RE = re.compile(r"^[0-9a-fA-F]{64}$")

    # there are a few warnings we might optionally print out, but only if
    # they are relevant. We don't want to print them out multiple times, so we
    # collect them here and print them at the end if they ever kick in.

    warning_tx_cache = None
    warning_get_tx = None
    warning_spendables = None

    if args.private_key_file:
        wif_re = re.compile(r"[1-9a-km-zA-LMNP-Z]{51,111}")
        # address_re = re.compile(r"[1-9a-kmnp-zA-KMNP-Z]{27-31}")
        for f in args.private_key_file:
            if f.name.endswith(".gpg"):
                gpg_args = ["gpg", "-d"]
                if args.gpg_argument:
                    gpg_args.extend(args.gpg_argument.split())
                gpg_args.append(f.name)
                popen = subprocess.Popen(gpg_args, stdout=subprocess.PIPE)
                f = popen.stdout
            for line in f.readlines():
                # decode
                if isinstance(line, bytes):
                    line = line.decode("utf8")
                # look for WIFs
                possible_keys = wif_re.findall(line)

                def make_key(x):
                    try:
                        return Key.from_text(x)
                    except Exception:
                        return None

                keys = [make_key(x) for x in possible_keys]
                for key in keys:
                    if key:
                        key_iters.append((k.wif() for k in key.subkeys("")))

                # if len(keys) == 1 and key.hierarchical_wallet() is None:
                #    # we have exactly 1 WIF. Let's look for an address
                #   potential_addresses = address_re.findall(line)

    # we create the tx_db lazily
    tx_db = None

    for arg in args.argument:

        # hex transaction id
        if TX_ID_RE.match(arg):
            if tx_db is None:
                warning_tx_cache = message_about_tx_cache_env()
                warning_get_tx = message_about_get_tx_env()
                tx_db = get_tx_db()
            tx = tx_db.get(h2b_rev(arg))
            if not tx:
                for m in [warning_tx_cache, warning_get_tx, warning_spendables]:
                    if m:
                        print("warning: %s" % m, file=sys.stderr)
                parser.error("can't find Tx with id %s" % arg)
            txs.append(tx)
            continue

        # hex transaction data
        try:
            tx = Tx.tx_from_hex(arg)
            txs.append(tx)
            continue
        except Exception:
            pass

        try:
            key = Key.from_text(arg)
            # TODO: check network
            if key.wif() is None:
                payables.append((key.address(), 0))
                continue
            # TODO: support paths to subkeys
            key_iters.append((k.wif() for k in key.subkeys("")))
            continue
        except Exception:
            pass

        if os.path.exists(arg):
            try:
                with open(arg, "rb") as f:
                    if f.name.endswith("hex"):
                        f = io.BytesIO(codecs.getreader("hex_codec")(f).read())
                    tx = Tx.parse(f)
                    txs.append(tx)
                    try:
                        tx.parse_unspents(f)
                    except Exception as ex:
                        pass
                    continue
            except Exception:
                pass

        parts = arg.split("/")
        if len(parts) == 4:
            # spendable
            try:
                spendables.append(Spendable.from_text(arg))
                continue
            except Exception:
                pass

        # TODO: fix allowable_prefixes
        allowable_prefixes = b'\0'
        if len(parts) == 2 and encoding.is_valid_bitcoin_address(
                parts[0], allowable_prefixes=allowable_prefixes):
            try:
                payables.append(parts)
                continue
            except ValueError:
                pass

        parser.error("can't parse %s" % arg)

    if args.fetch_spendables:
        warning_spendables = message_about_spendables_for_address_env()
        for address in args.fetch_spendables:
            spendables.extend(spendables_for_address(address))

    for tx in txs:
        if tx.missing_unspents() and args.augment:
            if tx_db is None:
                warning_tx_cache = message_about_tx_cache_env()
                warning_get_tx = message_about_get_tx_env()
                tx_db = get_tx_db()
            tx.unspents_from_db(tx_db, ignore_missing=True)

    txs_in = []
    txs_out = []
    unspents = []
    # we use a clever trick here to keep each tx_in corresponding with its tx_out
    for tx in txs:
        smaller = min(len(tx.txs_in), len(tx.txs_out))
        txs_in.extend(tx.txs_in[:smaller])
        txs_out.extend(tx.txs_out[:smaller])
        unspents.extend(tx.unspents[:smaller])
    for tx in txs:
        smaller = min(len(tx.txs_in), len(tx.txs_out))
        txs_in.extend(tx.txs_in[smaller:])
        txs_out.extend(tx.txs_out[smaller:])
        unspents.extend(tx.unspents[smaller:])
    for spendable in spendables:
        txs_in.append(spendable.tx_in())
        unspents.append(spendable)
    for address, coin_value in payables:
        script = standard_tx_out_script(address)
        txs_out.append(TxOut(coin_value, script))

    lock_time = args.lock_time
    version = args.transaction_version

    # if no lock_time is explicitly set, inherit from the first tx or use default
    if lock_time is None:
        if txs:
            lock_time = txs[0].lock_time
        else:
            lock_time = DEFAULT_LOCK_TIME

    # if no version is explicitly set, inherit from the first tx or use default
    if version is None:
        if txs:
            version = txs[0].version
        else:
            version = DEFAULT_VERSION

    if args.remove_tx_in:
        s = set(args.remove_tx_in)
        txs_in = [tx_in for idx, tx_in in enumerate(txs_in) if idx not in s]

    if args.remove_tx_out:
        s = set(args.remove_tx_out)
        txs_out = [tx_out for idx, tx_out in enumerate(txs_out) if idx not in s]

    tx = Tx(txs_in=txs_in, txs_out=txs_out, lock_time=lock_time, version=version, unspents=unspents)

    fee = args.fee
    try:
        distribute_from_split_pool(tx, fee)
    except ValueError as ex:
        print("warning: %s" % ex.args[0], file=sys.stderr)

    unsigned_before = tx.bad_signature_count()
    if unsigned_before > 0 and key_iters:
        def wif_iter(iters):
            while len(iters) > 0:
                for idx, iter in enumerate(iters):
                    try:
                        wif = next(iter)
                        yield wif
                    except StopIteration:
                        iters = iters[:idx] + iters[idx+1:]
                        break

        print("signing...", file=sys.stderr)
        sign_tx(tx, wif_iter(key_iters))

    unsigned_after = tx.bad_signature_count()
    if unsigned_after > 0 and key_iters:
        print("warning: %d TxIn items still unsigned" % unsigned_after, file=sys.stderr)

    if len(tx.txs_in) == 0:
        print("warning: transaction has no inputs", file=sys.stderr)

    if len(tx.txs_out) == 0:
        print("warning: transaction has no outputs", file=sys.stderr)

    include_unspents = (unsigned_after > 0)
    tx_as_hex = tx.as_hex(include_unspents=include_unspents)

    if args.output_file:
        f = args.output_file
        if f.name.endswith(".hex"):
            f.write(tx_as_hex.encode("utf8"))
        else:
            tx.stream(f)
            if include_unspents:
                tx.stream_unspents(f)
        f.close()
    elif args.show_unspents:
        for spendable in tx.tx_outs_as_spendable():
            print(spendable.as_text())
    else:
        if not tx.missing_unspents():
            check_fees(tx)
        dump_tx(tx, args.network)
        if include_unspents:
            print("including unspents in hex dump since transaction not fully signed")
        print(tx_as_hex)

    if args.cache:
        if tx_db is None:
            warning_tx_cache = message_about_tx_cache_env()
            warning_get_tx = message_about_get_tx_env()
            tx_db = get_tx_db()
        tx_db.put(tx)

    if args.bitcoind_url:
        if tx_db is None:
            warning_tx_cache = message_about_tx_cache_env()
            warning_get_tx = message_about_get_tx_env()
            tx_db = get_tx_db()
        validate_bitcoind(tx, tx_db, args.bitcoind_url)

    if tx.missing_unspents():
        print("\n** can't validate transaction as source transactions missing", file=sys.stderr)
    else:
        try:
            if tx_db is None:
                warning_tx_cache = message_about_tx_cache_env()
                warning_get_tx = message_about_get_tx_env()
                tx_db = get_tx_db()
            tx.validate_unspents(tx_db)
            print('all incoming transaction values validated')
        except BadSpendableError as ex:
            print("\n**** ERROR: FEES INCORRECTLY STATED: %s" % ex.args[0], file=sys.stderr)
        except Exception as ex:
            print("\n*** can't validate source transactions as untampered: %s" %
                  ex.args[0], file=sys.stderr)

    # print warnings
    for m in [warning_tx_cache, warning_get_tx, warning_spendables]:
        if m:
            print("warning: %s" % m, file=sys.stderr)
Exemple #28
0
 def tx_for_tx_hash(self, tx_hash):
     "Get a Tx by its hash."
     URL = "%s/tx/raw/%s" % (self.url, b2h_rev(tx_hash))
     r = json.loads(urlopen(URL).read().decode("utf8"))
     tx = Tx.parse(io.BytesIO(h2b(r.get("data").get("tx").get("hex"))))
     return tx