def _create_oracle_request(self, input_chain_paths, output_chain_paths, spend_id, tx, verifications=None, callback=None): """:nodoc:""" tx = deepcopy( tx ) # keep original Tx object intact, so that it is not mutated by fix_input_scripts below. # Have the Oracle sign the tx chain_paths = [] input_scripts = [] input_txs = [] for i, inp in enumerate(tx.txs_in): input_tx = self.tx_db.get(inp.previous_hash) if input_tx is None: raise Error("could not look up tx for %s" % (b2h(inp.previous_hash))) input_txs.append(input_tx) if input_chain_paths[i] is not None: redeem_script = self._account.script_for_path( input_chain_paths[i]).script() input_scripts.append(redeem_script) chain_paths.append(input_chain_paths[i]) fix_input_script(inp, redeem_script) else: input_scripts.append(None) chain_paths.append(None) req = { "walletAgent": self._wallet_agent, "transaction": { "bytes": b2h(stream_to_bytes(tx.stream)), "inputScripts": [(b2h(script) if script else None) for script in input_scripts], "inputTransactions": [b2h(stream_to_bytes(tx.stream)) for tx in input_txs], "chainPaths": chain_paths, "outputChainPaths": output_chain_paths, "masterKeys": self._account.public_keys[0:-self.num_oracle_keys], } } if spend_id: req['spendId'] = spend_id if callback: req['callback'] = callback if verifications: req['verifications'] = verifications return req
def dump_block(block, network): blob = stream_to_bytes(block.stream) print("%d bytes block hash %s" % (len(blob), block.id())) print("version %d" % block.version) print("prior block hash %s" % b2h_rev(block.previous_block_hash)) print("merkle root %s" % binascii.hexlify(block.merkle_root).decode("utf8")) print("timestamp %s" % datetime.datetime.utcfromtimestamp(block.timestamp).isoformat()) print("difficulty %d" % block.difficulty) print("nonce %s" % block.nonce) print("%d transaction%s" % (len(block.txs), "s" if len(block.txs) != 1 else "")) for idx, tx in enumerate(block.txs): print("Tx #%d:" % idx) dump_tx(tx, netcode=network)
def dump_header(tx): tx_bin = stream_to_bytes(tx.stream) print("Version: %2d tx hash %s %d bytes" % (tx.version, tx.id(), len(tx_bin))) print("TxIn count: %d; TxOut count: %d" % (len(tx.txs_in), len(tx.txs_out))) if tx.lock_time == 0: meaning = "valid anytime" elif tx.lock_time < LOCKTIME_THRESHOLD: meaning = "valid after block index %d" % tx.lock_time else: when = datetime.datetime.utcfromtimestamp(tx.lock_time) meaning = "valid on or after %s utc" % when.isoformat() print("Lock time: %d (%s)" % (tx.lock_time, meaning)) print("Input%s:" % ('s' if len(tx.txs_in) != 1 else ''))
def dump_block(block, network): blob = stream_to_bytes(block.stream) print("%d bytes block hash %s" % (len(blob), block.id())) print("version %d" % block.version) print("prior block hash %s" % b2h_rev(block.previous_block_hash)) print("merkle root %s" % b2h(block.merkle_root)) print("timestamp %s" % datetime.datetime.utcfromtimestamp(block.timestamp).isoformat()) print("difficulty %d" % block.difficulty) print("nonce %s" % block.nonce) print("%d transaction%s" % (len(block.txs), "s" if len(block.txs) != 1 else "")) for idx, tx in enumerate(block.txs): print("Tx #%d:" % idx) dump_tx(tx, network=network, verbose_signature=False, disassembly_level=0, do_trace=False, use_pdb=False)
def _create_oracle_request(self, input_chain_paths, output_chain_paths, spend_id, tx, verifications=None, callback=None): """:nodoc:""" tx = deepcopy(tx) # keep original Tx object intact, so that it is not mutated by fix_input_scripts below. # Have the Oracle sign the tx chain_paths = [] input_scripts = [] input_txs = [] for i, inp in enumerate(tx.txs_in): input_tx = self.tx_db.get(inp.previous_hash) if input_tx is None: raise Error("could not look up tx for %s" % (b2h(inp.previous_hash))) input_txs.append(input_tx) if input_chain_paths[i] is not None: redeem_script = self._account.script_for_path(input_chain_paths[i]).script() input_scripts.append(redeem_script) chain_paths.append(input_chain_paths[i]) fix_input_script(inp, redeem_script) else: input_scripts.append(None) chain_paths.append(None) req = { "walletAgent": self._wallet_agent, "transaction": { "bytes": b2h(stream_to_bytes(tx.stream)), "inputScripts": [(b2h(script) if script else None) for script in input_scripts], "inputTransactions": [b2h(stream_to_bytes(tx.stream)) for tx in input_txs], "chainPaths": chain_paths, "outputChainPaths": output_chain_paths, "masterKeys": self._account.public_keys[0:-self.num_oracle_keys], } } if spend_id: req['spendId'] = spend_id if callback: req['callback'] = callback if verifications: req['verifications'] = verifications return req
def dump_header(tx): tx_bin = stream_to_bytes(tx.stream) print("Version: %2d tx hash %s %d bytes" % (tx.version, tx.id(), len(tx_bin))) if tx.has_witness_data(): print(" segwit tx hash %s" % tx.w_id()) print("TxIn count: %d; TxOut count: %d" % (len(tx.txs_in), len(tx.txs_out))) if tx.lock_time == 0: meaning = "valid anytime" elif tx.lock_time < LOCKTIME_THRESHOLD: meaning = "valid after block index %d" % tx.lock_time else: when = datetime.datetime.utcfromtimestamp(tx.lock_time) meaning = "valid on or after %s utc" % when.isoformat() print("Lock time: %d (%s)" % (tx.lock_time, meaning)) print("Input%s:" % ('s' if len(tx.txs_in) != 1 else ''))
def dump_block(block, netcode=None): if netcode is None: netcode = get_current_netcode() blob = stream_to_bytes(block.stream) print("%d bytes block hash %s" % (len(blob), block.id())) print("version %d" % block.version) print("prior block hash %s" % b2h_rev(block.previous_block_hash)) print("merkle root %s" % b2h(block.merkle_root)) print("timestamp %s" % datetime.datetime.utcfromtimestamp(block.timestamp).isoformat()) print("difficulty %d" % block.difficulty) print("nonce %s" % block.nonce) print("%d transaction%s" % (len(block.txs), "s" if len(block.txs) != 1 else "")) for idx, tx in enumerate(block.txs): print("Tx #%d:" % idx) dump_tx(tx, netcode=netcode, verbose_signature=False, disassembly_level=0, do_trace=False, use_pdb=False)
def dump_block(block, netcode=None): if netcode is None: netcode = get_current_netcode() blob = stream_to_bytes(block.stream) print("%d bytes block hash %s" % (len(blob), block.id())) print("version %d" % block.version) print("prior block hash %s" % b2h_rev(block.previous_block_hash)) print("merkle root %s" % b2h(block.merkle_root)) print("timestamp %s" % datetime.datetime.utcfromtimestamp(block.timestamp).isoformat()) print("difficulty %d" % block.difficulty) print("nonce %s" % block.nonce) print("%d transaction%s" % (len(block.txs), "s" if len(block.txs) != 1 else "")) for idx, tx in enumerate(block.txs): print("Tx #%d:" % idx) dump_tx(tx, netcode=netcode)
def dump_tx(tx, netcode='BTC'): address_prefix = address_prefix_for_netcode(netcode) tx_bin = stream_to_bytes(tx.stream) print("Version: %2d tx hash %s %d bytes " % (tx.version, tx.id(), len(tx_bin))) print("TxIn count: %d; TxOut count: %d" % (len(tx.txs_in), len(tx.txs_out))) if tx.lock_time == 0: meaning = "valid anytime" elif tx.lock_time < LOCKTIME_THRESHOLD: meaning = "valid after block index %d" % tx.lock_time else: when = datetime.datetime.utcfromtimestamp(tx.lock_time) meaning = "valid on or after %s utc" % when.isoformat() print("Lock time: %d (%s)" % (tx.lock_time, meaning)) print("Input%s:" % ('s' if len(tx.txs_in) != 1 else '')) missing_unspents = tx.missing_unspents() for idx, tx_in in enumerate(tx.txs_in): if tx.is_coinbase(): print("%4d: COINBASE %12.5f mBTC" % (idx, satoshi_to_mbtc(tx.total_in()))) else: suffix = "" if tx.missing_unspent(idx): address = tx_in.bitcoin_address(address_prefix=address_prefix) else: tx_out = tx.unspents[idx] sig_result = " sig ok" if tx.is_signature_ok( idx) else " BAD SIG" suffix = " %12.5f mBTC %s" % (satoshi_to_mbtc( tx_out.coin_value), sig_result) address = tx_out.bitcoin_address(netcode=netcode) t = "%4d: %34s from %s:%-4d%s" % (idx, address, b2h_rev(tx_in.previous_hash), tx_in.previous_index, suffix) print(t.rstrip()) print("Output%s:" % ('s' if len(tx.txs_out) != 1 else '')) for idx, tx_out in enumerate(tx.txs_out): amount_mbtc = satoshi_to_mbtc(tx_out.coin_value) address = tx_out.bitcoin_address(netcode=netcode) or "(unknown)" print("%4d: %34s receives %12.5f mBTC" % (idx, address, amount_mbtc)) if not missing_unspents: print("Total input %12.5f mBTC" % satoshi_to_mbtc(tx.total_in())) print("Total output %12.5f mBTC" % satoshi_to_mbtc(tx.total_out())) if not missing_unspents: print("Total fees %12.5f mBTC" % satoshi_to_mbtc(tx.fee()))
def dump_tx(tx, netcode='BTC'): address_prefix = address_prefix_for_netcode(netcode) tx_bin = stream_to_bytes(tx.stream) print("Version: %2d tx hash %s %d bytes " % (tx.version, tx.id(), len(tx_bin))) print("TxIn count: %d; TxOut count: %d" % (len(tx.txs_in), len(tx.txs_out))) if tx.lock_time == 0: meaning = "valid anytime" elif tx.lock_time < LOCKTIME_THRESHOLD: meaning = "valid after block index %d" % tx.lock_time else: when = datetime.datetime.utcfromtimestamp(tx.lock_time) meaning = "valid on or after %s utc" % when.isoformat() print("Lock time: %d (%s)" % (tx.lock_time, meaning)) print("Input%s:" % ('s' if len(tx.txs_in) != 1 else '')) missing_unspents = tx.missing_unspents() for idx, tx_in in enumerate(tx.txs_in): if tx.is_coinbase(): print("%3d: COINBASE %12.5f mBTC" % (idx, satoshi_to_mbtc(tx.total_in()))) else: suffix = "" if tx.missing_unspent(idx): address = tx_in.bitcoin_address(address_prefix=address_prefix) else: tx_out = tx.unspents[idx] sig_result = " sig ok" if tx.is_signature_ok(idx) else " BAD SIG" suffix = " %12.5f mBTC %s" % (satoshi_to_mbtc(tx_out.coin_value), sig_result) address = tx_out.bitcoin_address(netcode=netcode) print("%3d: %34s from %s:%d%s" % (idx, address, b2h_rev(tx_in.previous_hash), tx_in.previous_index, suffix)) print("Output%s:" % ('s' if len(tx.txs_out) != 1 else '')) for idx, tx_out in enumerate(tx.txs_out): amount_mbtc = satoshi_to_mbtc(tx_out.coin_value) address = tx_out.bitcoin_address(netcode=netcode) or "(unknown)" print("%3d: %34s receives %12.5f mBTC" % (idx, address, amount_mbtc)) if not missing_unspents: print("Total input %12.5f mBTC" % satoshi_to_mbtc(tx.total_in())) print( "Total output %12.5f mBTC" % satoshi_to_mbtc(tx.total_out())) if not missing_unspents: print("Total fees %12.5f mBTC" % satoshi_to_mbtc(tx.fee()))
def dump_tx(tx, netcode, verbose_signature, disassembly_level, do_trace): address_prefix = address_prefix_for_netcode(netcode) tx_bin = stream_to_bytes(tx.stream) print("Version: %2d tx hash %s %d bytes " % (tx.version, tx.id(), len(tx_bin))) print("TxIn count: %d; TxOut count: %d" % (len(tx.txs_in), len(tx.txs_out))) if tx.lock_time == 0: meaning = "valid anytime" elif tx.lock_time < LOCKTIME_THRESHOLD: meaning = "valid after block index %d" % tx.lock_time else: when = datetime.datetime.utcfromtimestamp(tx.lock_time) meaning = "valid on or after %s utc" % when.isoformat() print("Lock time: %d (%s)" % (tx.lock_time, meaning)) print("Input%s:" % ('s' if len(tx.txs_in) != 1 else '')) missing_unspents = tx.missing_unspents() traceback_f = trace_script if do_trace else None for idx, tx_in in enumerate(tx.txs_in): if disassembly_level > 0: signature_for_hash_type_f = lambda hash_type, script: tx.signature_hash( script, idx, hash_type) if tx.is_coinbase(): print("%4d: COINBASE %12.5f mBTC" % (idx, satoshi_to_mbtc(tx.total_in()))) else: suffix = "" if tx.missing_unspent(idx): tx_out = None address = tx_in.bitcoin_address(address_prefix=address_prefix) else: tx_out = tx.unspents[idx] sig_result = " sig ok" if tx.is_signature_ok(idx, traceback_f=traceback_f) else " BAD SIG" suffix = " %12.5f mBTC %s" % (satoshi_to_mbtc(tx_out.coin_value), sig_result) address = tx_out.bitcoin_address(netcode=netcode) t = "%4d: %34s from %s:%-4d%s" % (idx, address, b2h_rev(tx_in.previous_hash), tx_in.previous_index, suffix) print(t.rstrip()) if disassembly_level > 0: out_script = b'' if tx_out: out_script = tx_out.script for (pre_annotations, pc, opcode, instruction, post_annotations) in \ disassemble_scripts(tx_in.script, out_script, signature_for_hash_type_f): for l in pre_annotations: print(" %s" % l) print( " %4x: %02x %s" % (pc, opcode, instruction)) for l in post_annotations: print(" %s" % l) if verbose_signature: signatures = [] for opcode in opcode_list(tx_in.script): if not opcode.startswith("OP_"): try: signatures.append(parse_signature_blob(h2b(opcode))) except UnexpectedDER: pass if signatures: sig_types_identical = (zip(*signatures)[1]).count(signatures[0][1]) == len(signatures) i = 1 if len(signatures) > 1 else '' for sig_pair, sig_type in signatures: print(" r{0}: {1:#x}\n s{0}: {2:#x}".format(i, *sig_pair)) if not sig_types_identical and tx_out: print(" z{}: {:#x} {}".format(i, tx.signature_hash(tx_out.script, idx, sig_type), sighash_type_to_string(sig_type))) if i: i += 1 if sig_types_identical and tx_out: print(" z:{} {:#x} {}".format(' ' if i else '', tx.signature_hash(tx_out.script, idx, sig_type), sighash_type_to_string(sig_type))) print("Output%s:" % ('s' if len(tx.txs_out) != 1 else '')) for idx, tx_out in enumerate(tx.txs_out): amount_mbtc = satoshi_to_mbtc(tx_out.coin_value) address = tx_out.bitcoin_address(netcode=netcode) or "(unknown)" print("%4d: %34s receives %12.5f mBTC" % (idx, address, amount_mbtc)) if disassembly_level > 0: for (pre_annotations, pc, opcode, instruction, post_annotations) in \ disassemble_scripts(b'', tx_out.script, signature_for_hash_type_f): for l in pre_annotations: print(" %s" % l) print( " %4x: %02x %s" % (pc, opcode, instruction)) for l in post_annotations: print(" %s" % l) if not missing_unspents: print("Total input %12.5f mBTC" % satoshi_to_mbtc(tx.total_in())) print( "Total output %12.5f mBTC" % satoshi_to_mbtc(tx.total_out())) if not missing_unspents: print("Total fees %12.5f mBTC" % satoshi_to_mbtc(tx.fee()))
def dump_tx(tx, netcode, verbose_signature, disassembly_level, do_trace, use_pdb): address_prefix = address_prefix_for_netcode(netcode) tx_bin = stream_to_bytes(tx.stream) # print("Tx_type:%2d" % tx.tx_type) if TransactionUtils.isCFTransation(tx): print("original_hash : %s" % tx.cf_header.original_hash) print("target_amount : %s" % tx.cf_header.target_amount) print("pubkey : %s" % tx.cf_header.pubkey) print("end_time : %s" % tx.cf_header.end_time) print("pre_hash : %s" % tx.cf_header.pre_hash) print("lack_amount : %s" % tx.cf_header.lack_amount) print("Version: %2d tx hash %s %d bytes " % (tx.version, tx.id(), len(tx_bin))) print("TransactionIn count: %d; TransactionOut count: %d" % (len(tx.txs_in), len(tx.txs_out))) if tx.lock_time == 0: meaning = "valid anytime" elif tx.lock_time < LOCKTIME_THRESHOLD: meaning = "valid after block index %d" % tx.lock_time else: when = datetime.datetime.utcfromtimestamp(tx.lock_time) meaning = "valid on or after %s utc" % when.isoformat() print("Lock time: %d (%s)" % (tx.lock_time, meaning)) print("Input%s:" % ('s' if len(tx.txs_in) != 1 else '')) missing_unspents = tx.missing_unspents() def trace_script(old_pc, opcode, data, stack, altstack, if_condition_stack, is_signature): from pycoin.tx.script.tools import disassemble_for_opcode_data print("%3d : %02x %s" % (old_pc, opcode, disassemble_for_opcode_data(opcode, data))) if use_pdb: import pdb from pycoin.serialize import b2h print("stack: [%s]" % ', '.join(b2h(s) for s in stack)) if len(altstack) > 0: print("altstack: %s" % altstack) if len(if_condition_stack) > 0: print("condition stack: %s" % ', '.join(int(s) for s in if_condition_stack)) pdb.set_trace() traceback_f = trace_script if do_trace or use_pdb else None for idx, tx_in in enumerate(tx.txs_in): if disassembly_level > 0: def signature_for_hash_type_f(hash_type, script): return tx.signature_hash(script, idx, hash_type) if tx.is_coinbase(): print("%4d: COINBASE %12.5f mBTC" % (idx, satoshi_to_mbtc(tx.total_in()))) else: suffix = "" if tx.missing_unspent(idx): tx_out = None address = tx_in.bitcoin_address(address_prefix=address_prefix) else: tx_out = tx.unspents[idx] sig_result = " sig ok" if tx.is_signature_ok( idx, traceback_f=traceback_f) else " BAD SIG" suffix = " %12.5f mBTC %s" % (satoshi_to_mbtc( tx_out.coin_value), sig_result) address = tx_out.bitcoin_address(netcode=netcode) t = "%4d: %34s from %s:%-4d%s" % (idx, address, b2h_rev(tx_in.previous_hash), tx_in.previous_index, suffix) print(t.rstrip()) if disassembly_level > 0: out_script = b'' if tx_out: out_script = tx_out.script for (pre_annotations, pc, opcode, instruction, post_annotations) in \ disassemble_scripts( tx_in.script, out_script, tx.lock_time, signature_for_hash_type_f): for l in pre_annotations: print(" %s" % l) print(" %4x: %02x %s" % (pc, opcode, instruction)) for l in post_annotations: print(" %s" % l) if verbose_signature: signatures = [] for opcode in opcode_list(tx_in.script): if not opcode.startswith("OP_"): try: signatures.append(parse_signature_blob( h2b(opcode))) except UnexpectedDER: pass if signatures: sig_types_identical = (zip(*signatures)[1]).count( signatures[0][1]) == len(signatures) i = 1 if len(signatures) > 1 else '' for sig_pair, sig_type in signatures: print(" r{0}: {1:#x}\n s{0}: {2:#x}".format( i, *sig_pair)) if not sig_types_identical and tx_out: print(" z{}: {:#x} {}".format( i, tx.signature_hash(tx_out.script, idx, sig_type), sighash_type_to_string(sig_type))) if i: i += 1 if sig_types_identical and tx_out: print(" z:{} {:#x} {}".format( ' ' if i else '', tx.signature_hash(tx_out.script, idx, sig_type), sighash_type_to_string(sig_type))) print("Output%s:" % ('s' if len(tx.txs_out) != 1 else '')) for idx, tx_out in enumerate(tx.txs_out): amount_mbtc = satoshi_to_mbtc(tx_out.coin_value) address = tx_out.bitcoin_address(netcode=netcode) or "(unknown)" print("%4d: %34s receives %12.5f mBTC" % (idx, address, amount_mbtc)) if disassembly_level > 0: for (pre_annotations, pc, opcode, instruction, post_annotations) in \ disassemble_scripts(b'', tx_out.script, tx.lock_time, signature_for_hash_type_f): for l in pre_annotations: print(" %s" % l) print(" %4x: %02x %s" % (pc, opcode, instruction)) for l in post_annotations: print(" %s" % l) if not missing_unspents: print("Total input %12.5f mBTC" % satoshi_to_mbtc(tx.total_in())) print("Total output %12.5f mBTC" % satoshi_to_mbtc(tx.total_out())) if not missing_unspents: print("Total fees %12.5f mBTC" % satoshi_to_mbtc(tx.fee()))
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)
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)