def tx_from_json_dict(r): version = r.get("version") lock_time = r.get("locktime") txs_in = [] for vin in r.get("vin"): if "coinbase" in vin: previous_hash = b'\0' * 32 script = h2b(vin.get("coinbase")) previous_index = 4294967295 else: previous_hash = h2b_rev(vin.get("txid")) scriptSig = vin.get("scriptSig") if "hex" in scriptSig: script = h2b(scriptSig.get("hex")) else: script = tools.compile(scriptSig.get("asm")) previous_index = vin.get("vout") sequence = vin.get("sequence") txs_in.append(TxIn(previous_hash, previous_index, script, sequence)) txs_out = [] for vout in r.get("vout"): coin_value = btc_to_satoshi(decimal.Decimal(vout.get("value"))) script = tools.compile(vout.get("scriptPubKey").get("asm")) txs_out.append(TxOut(coin_value, script)) tx = Tx(version, txs_in, txs_out, lock_time) bh = r.get("blockhash") if bh: bh = h2b_rev(bh) tx.confirmation_block_hash = bh return tx
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())
def parse_tx(arg, parser, tx_db, network): # hex transaction id tx = None if TX_ID_RE.match(arg): if tx_db is None: tx_db = create_tx_db(network) tx = tx_db.get(h2b_rev(arg)) if not tx: parser.error("can't find Tx with id %s" % arg) return tx, tx_db # hex transaction data try: return Tx.from_hex(arg), tx_db 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) tx.parse_unspents(f) except Exception: pass return tx, tx_db
def populate_unspents(nodes): global balance global unspents if nodes['used'] == {}: return addresses = nodes['used'].keys() response = json.load( urlopen("http://%s.blockr.io/api/v1/address/unspent/" % BLOCKR + \ ','.join(addresses)) ) data = response['data'] if type(data) == type({}): data = [data] for entry in data: if entry['unspent'] == []: continue for unspent in entry['unspent']: balance += Decimal(unspent['amount']) amount = convention.btc_to_satoshi(unspent['amount']) script = serialize.h2b(unspent['script']) txid = serialize.h2b_rev(unspent['tx']) unspent_spendable = Spendable( amount, script, txid, unspent['n'] ) unspents.append(unspent_spendable) time.sleep(1) # Don't overload blockr.io API
def test_h2b(self): h = "000102" b = b"\x00\x01\x02" self.assertEqual(h2b(h), b) self.assertEqual(b2h(b), h) self.assertEqual(h2b_rev(h), b[::-1]) self.assertEqual(b2h_rev(b), "020100")
def decode_spendables(address, results): spendables = [ Spendable(r['value'], ScriptPayToAddress(address), h2b_rev(r['tx_hash']), r['tx_pos']) for r in results['result'] ] return spendables
def get_tx(tx_hash): """ Get a Tx by its hash. """ # TODO: fix this j = get_json_for_hash(tx_hash) txs_in = [] for j_in in j.get("in"): if j_in.get("coinbase"): txs_in.append( TxIn.coinbase_tx_in(binascii.unhexlify(j_in["coinbase"]))) else: txs_in.append( TxIn(h2b_rev(j_in["prev_out"]["hash"]), int(j_in["prev_out"]["n"]), tools.compile(j_in["scriptSig"]))) txs_out = [] for j_out in j.get("out"): txs_out.append( TxOut(int(btc_to_satoshi(j_out["value"])), tools.compile(j_out["scriptPubKey"]))) tx = Tx(int(j["ver"]), txs_in, txs_out, int(j["lock_time"])) assert tx.hash() == tx_hash return tx
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 Transact ion 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 = Transaction.parse(f) except Exception: parser.error("can't parse %s" % f.name) tx_db[tx.hash()] = tx print("cached %s" % tx.id())
def main(): logging.basicConfig( level=logging.DEBUG, format=('%(asctime)s [%(process)d] [%(levelname)s] ' '%(filename)s:%(lineno)d %(message)s')) from pycoinnet.helpers.networks import MAINNET from pycoinnet.util.BlockChainView import BlockChainView from pycoinnet.bloom import BloomFilter from pycoin.tx import Spendable from pycoin.serialize import h2b_rev, h2b network = MAINNET initial_blockchain_view = BlockChainView() bloom_filter = BloomFilter(2048, hash_function_count=8, tweak=3) bloom_filter.add_address("14gZfnEn8Xd3ofkjr5s7rKoC3bi8J4Yfyy") # bloom_filter.add_address("1GL6i1ty44RnERgqYLKS1CrnhrahW4JhQZ") bloom_filter.add_item(h2b("0478abb18c0c7c95348fa77eb5fd43ce963e450d797cf4878894230ca528e6c8e866c3" "8ad93746e04f2161a01787c82a858ee24940e9a06e41fddb3494dfe29380")) spendable = Spendable( 0, b'', h2b_rev("0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c9"), 0) bloom_filter.add_spendable(spendable) merkle_block_index_queue = asyncio.Queue() spv = SPVClient( network, initial_blockchain_view, bloom_filter, merkle_block_index_queue, host_port_q=None) def fetch(merkle_block_index_queue): while True: merkle_block, index = yield from merkle_block_index_queue.get() logging.info( "block #%d %s with %d transactions", index, merkle_block.id(), len(merkle_block.txs)) t = asyncio.Task(fetch(merkle_block_index_queue)) asyncio.get_event_loop().run_forever()
def make_bare_tx(candidate, change_address, rs_asm, version=1): # <Tx> components spendables = [] ins = [] outs = [] # estimate the final (signed) bytesize per input based on the redeemscript redeem_script = tools.compile(rs_asm) in_size = estimate_input_size(redeem_script) # initialize size and amount counters in_amount = Decimal(0) est_size = TX_COMPONENTS.version + TX_COMPONENTS.out_count + TX_COMPONENTS.in_count # add output size est_size += OUTSIZE * 2 # iterate over unspents for utxo in candidate.utxos: value = Decimal(utxo.amount) * COIN in_amount += value script = h2b(utxo.script) # for now: test if the in_script we figured we would need, actually matches the in script :D # reverse that tx hash prevtx = h2b_rev(utxo.hash) # output index outnum = utxo.outpoint # create "spendable" spdbl = Spendable(value, script, prevtx, outnum) spendables.append(spdbl) # also create this as input as_input = spdbl.tx_in() as_input.sigs = [] ins.append(as_input) # add the estimated size per input est_size += in_size # calc fee and out amount fee = (Decimal(math.ceil(est_size / 1000)) * COIN * NETWORK_FEE) + FEE_MARGIN change_amount = Decimal( math.floor(in_amount - (candidate.amount * COIN) - fee)) # create outputs outs.append( TxOut(int(candidate.amount * COIN), make_payto(candidate.address))) outs.append(TxOut(int(change_amount), make_payto_script(change_address))) # create bare tx without sigs tx = Tx(version, ins, outs, 0, spendables) return tx
def request_blocks(self, block_ids): invs = [] for id in block_ids: inv = bitcoin.net.CInv() inv.type = self.INV_BLOCK inv.hash = h2b_rev(id) invs.append(inv) self.request_objects(invs)
def spendable_for_row(r): return Spendable(coin_value=r[2], script=h2b(r[3]), tx_hash=h2b_rev(r[0]), tx_out_index=r[1], block_index_available=r[4], does_seem_spent=r[5], block_index_spent=r[6])
async def get_block(self, blockhash: str, peers=None, timeout=None, privileged_peers=False) -> Dict: inv_item = InvItem(ITEM_TYPE_SEGWIT_BLOCK, h2b_rev(blockhash)) response = await self.pool.get(inv_item, peers=peers, timeout=timeout, privileged=privileged_peers) return response and { "block_hash": str(blockhash), "header_bytes": response[:80], "block_bytes": response }
def get_speendable(address): URL = "%s%saddr/%s/utxo" % ('https://test-insight.bitpay.com', '/api/', from_address) utxos = json.loads(urlopen(URL).read().decode("utf8")) utxo = utxos[0] return Spendable(utxo['amount'], h2b(utxo.get("scriptPubKey")), h2b_rev(utxo.get("txid")), utxo.get("vout"))
def op_return_this(privatekey, text, prefix = "KEYSTAMP:", bitcoin_fee = 10000): bitcoin_keyobj = get_key(privatekey) bitcoin_address = bitcoin_keyobj.bitcoin_address() ## Get the spendable outputs we are going to use to pay the fee all_spendables = get_spendables_blockcypher(bitcoin_address) spendables = [] value = 0 for unspent in all_spendables: while value < bitcoin_fee + 10000: coin_value = unspent.get("value") script = h2b(unspent.get("script_hex")) previous_hash = h2b_rev(unspent.get("tx_hash")) previous_index = unspent.get("index") spendables.append(Spendable(coin_value, script, previous_hash, previous_index)) value += coin_value bitcoin_sum = sum(spendable.coin_value for spendable in spendables) if(bitcoin_sum < bitcoin_fee): print "ERROR: not enough balance: available: %s - fee: %s" %(bitcoin_sum, bitcoin_fee) return False ## Create the inputs we are going to use inputs = [spendable.tx_in() for spendable in spendables] ## If we will have change left over create an output to send it back outputs = [] if (bitcoin_sum > bitcoin_fee): change_output_script = standard_tx_out_script(bitcoin_address) total_amout = bitcoin_sum - bitcoin_fee outputs.append(TxOut(total_amout, change_output_script)) # home_address = standard_tx_out_script(bitcoin_address) # #TODO: it needs some love and IQ on input mananagement stuff # outputs.append(TxOut((bitcoin_sum - bitcoin_fee), home_address)) ## Build the OP_RETURN output with our message if prefix is not None and (len(text) + len(prefix) <= 80): text = prefix + text message = hexlify(text.encode()).decode('utf8') op_return_output_script = tools.compile("OP_RETURN %s" % message) outputs.append(TxOut(0, op_return_output_script)) ## Create the transaction and sign it with the private key tx = Tx(version=1, txs_in=inputs, txs_out=outputs) # print tx.as_hex() # print spendables tx.set_unspents(spendables) sign_tx(tx, wifs=[privatekey]) print "singed_tx: %s" %tx.as_hex() #TODO: uncomment this when its ready to push data to blockchian tx_hash = broadcast_tx_blockr(tx.as_hex()) return tx_hash
def get_blockheader_with_transaction_hashes(self, block_hash): URL = "%s/api/block/%s" % (self.base_url, b2h_rev(block_hash)) r = json.loads(urlopen(URL).read().decode("utf8")) version = r.get("version") previous_block_hash = h2b_rev(r.get("previousblockhash")) merkle_root = h2b_rev(r.get("merkleroot")) timestamp = r.get("time") difficulty = int(r.get("bits"), 16) nonce = int(r.get("nonce")) tx_hashes = [h2b_rev(tx_hash) for tx_hash in r.get("tx")] blockheader = BlockHeader(version, previous_block_hash, merkle_root, timestamp, difficulty, nonce) if blockheader.hash() != block_hash: return None, None calculated_hash = merkle(tx_hashes, double_sha256) if calculated_hash != merkle_root: return None, None blockheader.height = r.get("height") return blockheader, tx_hashes
def spendables_for_address(self, address, amount=None): """ Return a list of Spendable objects for the given bitcoin address. """ if amount is None: spendables = [] url_append = "?unspentOnly=true&token=%s&includeScript=true" % self.api_key url = self.base_url("addrs/%s%s" % (address, url_append)) result = json.loads(urlopen(url).read().decode("utf8")) for txn in result.get("txrefs", []): coin_value = txn.get("value") script = h2b(txn.get("script")) previous_hash = h2b_rev(txn.get("tx_hash")) previous_index = txn.get("tx_output_n") spendables.append( Spendable(coin_value, script, previous_hash, previous_index)) return spendables else: spendables = [] url_append = "?unspentOnly=true&token=%s&includeScript=true" % self.api_key url = self.base_url("addrs/%s%s" % (address, url_append)) result = json.loads(urlopen(url).read().decode("utf8")) total_amount = 0 list_spend = result.get("txrefs", []) if len(list_spend) == 0: raise Exception("No spendable outputs found") unspents = sorted(list_spend, key=lambda d: d['value'], reverse=True) for txn in unspents: coin_value = txn.get("value") total_amount = total_amount + coin_value script = h2b(txn.get("script")) previous_hash = h2b_rev(txn.get("tx_hash")) previous_index = txn.get("tx_output_n") spendables.append( Spendable(coin_value, script, previous_hash, previous_index)) if total_amount > amount: break return [spendables, total_amount]
def spendables_for_address(self, address, amount=None): """ Return a list of Spendable objects for the given bitcoin address. """ if amount == None: spendables = [] r = json.loads( urlopen(self.base_url('get_tx_unspent', address)).read().decode("utf8")) for u in r['data']['txs']: coin_value = int(float(u['value']) * 100000000) script = h2b(u["script_hex"]) previous_hash = h2b_rev(u["txid"]) previous_index = u["output_no"] spendables.append( Spendable(coin_value, script, previous_hash, previous_index)) return spendables else: spendables = [] r = json.loads( urlopen(self.base_url('get_tx_unspent', address)).read().decode("utf8")) list_spend = r['data']['txs'] total_amount = 0 if len(list_spend) == 0: raise Exception("No spendable outputs found") unspents = sorted(list_spend, key=lambda d: d['value'], reverse=True) for u in unspents: coin_value = int(float(u['value']) * 100000000) script = h2b(u["script_hex"]) previous_hash = h2b_rev(u["txid"]) previous_index = u["output_no"] spendables.append( Spendable(coin_value, script, previous_hash, previous_index)) total_amount = total_amount + coin_value if total_amount >= amount: break return [spendables, total_amount]
async def get_block(self, blockhash: str, peers=None, timeout=None) -> Dict: inv_item = InvItem(ITEM_TYPE_SEGWIT_BLOCK, h2b_rev(blockhash)) response = await self.pool.get(inv_item, peers=peers, timeout=timeout) return response and { "block_hash": str(response.hash()), "prev_block_hash": str(response.previous_block_hash), "timestamp": int(response.timestamp), "header_bytes": bytes(response.as_blockheader().as_bin()), "block_object": response, "block_bytes": bytes(response.as_bin()) }
def spendables_for_address(self, bitcoin_address): URL = "%s/api/addr/%s/utxo" % (self.base_url, bitcoin_address) r = json.loads(urlopen(URL).read().decode("utf8")) spendables = [] for u in r: value = btc_to_satoshi(str(u.get("amount"))) script = h2b(u.get("scriptPubKey")) prev_hash = h2b_rev(u.get("txid")) prev_index = u.get("vout") spendable = Spendable(value, script, prev_hash, prev_index) spendables.append(spendable) return spendables
def spendables_for_address(self, bitcoin_address): url = "{0}/addr/{1}/utxo".format(self.base_url, bitcoin_address) result = json.loads(urlopen(url).read().decode("utf8")) spendables = [] for utxo in result: value = btc_to_satoshi(str(utxo["amount"])) prev_index = utxo["vout"] prev_hash = h2b_rev(utxo["txid"]) script = h2b(utxo["scriptPubKey"]) spendable = Spendable(value, script, prev_hash, prev_index) spendables.append(spendable) return spendables
def spend_sh_fund(tx_ins, wif_keys, tx_outs): """ spend script hash fund the key point of an input comes from multisig address is that, its sign script is combined with several individual signs :param tx_ins: list with tuple(tx_id, idx, balance, address, redeem_script) :param wif_keys: private keys in wif format, technical should be the same order with the pubkey in redeem script, but pycoin has inner control, so here order is not mandatory :param tx_outs: balance, receiver_address :return: raw hex and tx id """ _txs_in = [] _un_spent = [] for tx_id, idx, balance, address, _ in tx_ins: # must h2b_rev NOT h2b tx_id_b = h2b_rev(tx_id) _txs_in.append(TxIn(tx_id_b, idx)) _un_spent.append( Spendable( balance, script_obj_from_address(address, netcodes=[NET_CODE]).script(), tx_id_b, idx)) _txs_out = [] for balance, receiver_address in tx_outs: _txs_out.append( TxOut( balance, script_obj_from_address(receiver_address, netcodes=[NET_CODE]).script())) version, lock_time = 1, 0 tx = Tx(version, _txs_in, _txs_out, lock_time) tx.set_unspents(_un_spent) # construct hash160_lookup[hash160] = (secret_exponent, public_pair, compressed) for each individual key hash160_lookup = build_hash160_lookup( [Key.from_text(wif_key).secret_exponent() for wif_key in wif_keys]) for i in range(0, len(tx_ins)): # you can add some conditions that if the input script is not p2sh type, not provide p2sh_lookup, # so that all kinds of inputs can work together p2sh_lookup = build_p2sh_lookup([binascii.unhexlify(tx_ins[i][-1])]) tx.sign_tx_in(hash160_lookup, i, tx.unspents[i].script, hash_type=SIGHASH_ALL, p2sh_lookup=p2sh_lookup) return tx.as_hex(), tx.id()
def spendables_for_address(self, bitcoin_address, amount=None): """ Return a list of Spendable objects for the given bitcoin address. """ URL = "%s/addr/%s/utxo" % (self.base_url, bitcoin_address) print(URL) r = json.loads(urlopen(URL).read().decode("utf8")) r = sorted(r, key=lambda d: d['amount'], reverse=True) if len(r) == 0: raise Exception("No spendable outputs found") if amount == None: spendables = [] for u in r: coin_value = btc_to_satoshi(str(u.get("amount"))) script = h2b(u.get("scriptPubKey")) previous_hash = h2b_rev(u.get("txid")) previous_index = u.get("vout") spendables.append( Spendable(coin_value, script, previous_hash, previous_index)) return spendables else: total_amount = 0 spendables = [] for u in r: coin_value = btc_to_satoshi(str(u.get("amount"))) script = h2b(u.get("scriptPubKey")) previous_hash = h2b_rev(u.get("txid")) previous_index = u.get("vout") spendables.append( Spendable(coin_value, script, previous_hash, previous_index)) total_amount = total_amount + coin_value if total_amount >= amount: break return [spendables, total_amount]
def validate_unspents(self, tx_db): """ Spendable objects returned from blockchain.info or similar services contain coin_value information that must be trusted on faith. Mistaken coin_value data can result in coins being wasted to fees. This function solves this problem by iterating over the incoming transactions, fetching them from the tx_db in full, and verifying that the coin_values are as expected. Returns the fee for this transaction. If any of the spendables set by tx.set_unspents do not match the authenticated transactions, a ValidationFailureError is raised. """ tx_hashes = set((tx_in.previous_hash for tx_in in self.txs_in)) # build a local copy of the DB tx_lookup = {} for h in tx_hashes: if h == ZERO32: continue the_tx = tx_db.get(h) if the_tx is None: raise KeyError("hash id %s not in tx_db" % b2h_rev(h)) if the_tx.hash() != h: raise KeyError( "attempt to load Tx %s yielded a Tx with id %s" % (h2b_rev(h), the_tx.id())) tx_lookup[h] = the_tx for idx, tx_in in enumerate(self.txs_in): if tx_in.previous_hash == ZERO32: continue txs_out = tx_lookup[tx_in.previous_hash].txs_out if tx_in.previous_index > len(txs_out): raise BadSpendableError( "tx_out index %d is too big for Tx %s" % (tx_in.previous_index, b2h_rev(tx_in.previous_hash))) tx_out1 = txs_out[tx_in.previous_index] tx_out2 = self.unspents[idx] if tx_out1.coin_value != tx_out2.coin_value: raise BadSpendableError( "unspents[%d] coin value mismatch (%d vs %d)" % (idx, tx_out1.coin_value, tx_out2.coin_value)) if tx_out1.script != tx_out2.script: raise BadSpendableError("unspents[%d] script mismatch!" % idx) return self.fee()
def spendables_for_address(bitcoin_address): """ Return a list of Spendable objects for the given bitcoin address. """ URL = "http://btc.blockr.io/api/v1/address/unspent/%s" % bitcoin_address r = json.loads(urlopen(URL).read().decode("utf8")) spendables = [] for u in r.get("data", {}).get("unspent", []): coin_value = btc_to_satoshi(u.get("amount")) script = h2b(u.get("script")) previous_hash = h2b_rev(u.get("tx")) previous_index = u.get("n") spendables.append(Spendable(coin_value, script, previous_hash, previous_index)) return spendables
def spendables_for_address(self, bitcoin_address): """ Return a list of Spendable objects for the given bitcoin address. """ URL = "%s/api/addr/%s/utxo" % (self.base_url, bitcoin_address) r = json.loads(urlopen(URL).read().decode("utf8")) spendables = [] for u in r: coin_value = btc_to_satoshi(u.get("amount")) script = h2b(u.get("scriptPubKey")) previous_hash = h2b_rev(u.get("txid")) previous_index = u.get("vout") spendables.append(Spendable(coin_value, script, previous_hash, previous_index)) return spendables
def spendables_for_address(self, bitcoin_address): """ Return a list of Spendable objects for the given bitcoin address. """ URL = "%s/api/addr/%s/utxo" % (self.base_url, bitcoin_address) r = json.loads(urlopen(URL).read().decode("utf8")) spendables = [] for u in r: coin_value = btc_to_satoshi(str(u.get("amount"))) script = h2b(u.get("scriptPubKey")) previous_hash = h2b_rev(u.get("txid")) previous_index = u.get("vout") spendables.append(Spendable(coin_value, script, previous_hash, previous_index)) return spendables
def get_unspents(self, address): """ Return a list of Spendable objects for the given bitcoin address. """ URL = "%s/addr/%s/utxo" % (self.base_url, address) r = requests.get(URL) r.raise_for_status() spendables = [] for u in r.json(): coin_value = btc_to_satoshi(str(u.get("amount"))) script = h2b(u.get("scriptPubKey")) previous_hash = h2b_rev(u.get("txid")) previous_index = u.get("vout") spendables.append(Spendable(coin_value, script, previous_hash, previous_index)) return spendables
def search(target_xfp): k = BIP32Node.from_hwif( "tprv8ZgxMBicQKsPeXJHL3vPPgTAEqQ5P2FD9qDeCQT4Cp1EMY5QkwMPWFxHdxHrxZhhcVRJ2m7BNWTz9Xre68y7mX5vCdMJ5qXMUfnrZ2si2X4" ) pid = os.getpid() target_xfp = h2b_rev(target_xfp) # test by going -33 here. #sec_exp = k._secret_exponent - 33 sec_exp = k._secret_exponent + (pid * int(1e40)) i = 0 last_xfp = None while 1: i += 1 sec_exp += 1 public_pair = ecdsa.public_pair_for_secret_exponent( ecdsa.generator_secp256k1, sec_exp) xfp = public_pair_to_hash160_sec(public_pair, compressed=True)[:4] if i <= 5: # checking code (slow) b = BIP32Node(netcode='BTC', chain_code=bytes(32), secret_exponent=sec_exp) chk = b.fingerprint() assert b._secret_exponent == sec_exp assert xfp == chk, (xfp, chk) assert xfp != last_xfp, 'repeat xfp!' last_xfp = xfp if xfp == target_xfp: print(f"\n\nFOUND: sec_exp = {sec_exp}\n") b = BIP32Node(netcode='BTC', chain_code=bytes(32), secret_exponent=sec_exp) chk = b.fingerprint() assert b._secret_exponent == sec_exp assert xfp == chk, (xfp, chk) print(b.hwif(), end='\n\n') return if not (i % 27): print(' %6d %9d' % (pid, i), end='\r')
def txs_from_json(path): """ Read tests from ./data/tx_??valid.json Format is an array of arrays Inner arrays are either [ "comment" ] or [[[prevout hash, prevout index, prevout scriptPubKey], [input 2], ...], serializedTransaction, verifyFlags] ... where all scripts are stringified scripts. verifyFlags is a comma separated list of script verification flags to apply, or "NONE" """ with open(path, 'r') as f: for tvec in json.load(f): if len(tvec) == 1: continue assert len(tvec) == 3 prevouts = tvec[0] for prevout in prevouts: assert len(prevout) == 3 tx_hex = tvec[1] flags = set() for flag in tvec[2].split(','): assert flag in FLAGS flags.add(flag) try: tx = Tx.from_hex(tx_hex) except: print("Cannot parse tx_hex: %s" % tx_hex) raise spendable_db = {} blank_spendable = Spendable(0, b'', b'\0' * 32, 0) for prevout in prevouts: spendable = Spendable(coin_value=1000000, script=compile_script(prevout[2]), tx_hash=h2b_rev(prevout[0]), tx_out_index=prevout[1]) spendable_db[(spendable.tx_hash, spendable.tx_out_index)] = spendable unspents = [ spendable_db.get((tx_in.previous_hash, tx_in.previous_index), blank_spendable) for tx_in in tx.txs_in ] tx.set_unspents(unspents) yield (tx, flags)
def unspents_for_address(self, address): """ Return a list of Spendable objects for the given bitcoin address. """ spendables = [] r = json.loads(urlopen(self.base_url('get_tx_unspent', address)).read().decode("utf8")) for u in r['data']['txs']: coin_value = int (float (u['value']) * 100000000) script = h2b(u["script_hex"]) previous_hash = h2b_rev(u["txid"]) previous_index = u["output_no"] spendables.append(Spendable(coin_value, script, previous_hash, previous_index)) return spendables
def spendables_for_address(self, address): """ Return a list of Spendable objects for the given bitcoin address. """ url_append = "unspent/%s" % address URL = "%s/address/%s" % (self.url, url_append) r = json.loads(url_open(URL).read().decode("utf8")) spendables = [] for u in r.get("data", {}).get("unspent", []): coin_value = btc_to_satoshi(u.get("amount")) script = h2b(u.get("script")) previous_hash = h2b_rev(u.get("tx")) previous_index = u.get("n") spendables.append(Spendable(coin_value, script, previous_hash, previous_index)) return spendables
def spendables_for_address(bitcoin_address): """ Return a list of Spendable objects for the given bitcoin address. """ URL = "http://btc.blockr.io/api/v1/address/unspent/%s" % bitcoin_address r = json.loads(urlopen(URL).read().decode("utf8")) spendables = [] for u in r.get("data", {}).get("unspent", []): coin_value = btc_to_satoshi(u.get("amount")) script = binascii.unhexlify(u.get("script")) previous_hash = h2b_rev(u.get("tx")) previous_index = u.get("n") spendables.append( Spendable(coin_value, script, previous_hash, previous_index)) return spendables
def spendables_for_address(self, address): """ Return a list of Spendable objects for the given bitcoin address. """ spendables = [] url_append = "?unspentOnly=true&token=%s&includeScript=true" % self.api_key url = self.base_url("addrs/%s%s" % (address, url_append)) result = json.loads(urlopen(url).read().decode("utf8")) for txn in result.get("txrefs", []): coin_value = txn.get("value") script = h2b(txn.get("script")) previous_hash = h2b_rev(txn.get("tx_hash")) previous_index = txn.get("tx_output_n") spendables.append(Spendable(coin_value, script, previous_hash, previous_index)) return spendables
def spendables_for_address(self, address): """ Return a list of Spendable objects for the given bitcoin address. """ url_append = "unspent/%s" % address URL = "%s/address/%s" % (self.url, url_append) r = json.loads(urlopen(URL).read().decode("utf8")) spendables = [] for u in r.get("data", {}).get("unspent", []): coin_value = btc_to_satoshi(u.get("amount")) script = h2b(u.get("script")) previous_hash = h2b_rev(u.get("tx")) previous_index = u.get("n") spendables.append(Spendable(coin_value, script, previous_hash, previous_index)) return spendables
def spendables_for_address(bitcoin_address): """ Return a list of Spendable objects for the given bitcoin address. """ json_response = fetch_json("addresses/%s/unspent-outputs" % bitcoin_address) spendables = [] for tx_out_info in json_response.get("data", {}).get("outputs"): if tx_out_info.get("to_address") == bitcoin_address: coin_value = tx_out_info["value"] script = tools.compile(tx_out_info.get("script_pub_key")) previous_hash = h2b_rev(tx_out_info.get("transaction_hash")) previous_index = tx_out_info.get("transaction_index") spendables.append( Spendable(coin_value, script, previous_hash, previous_index)) return spendables
def spendables_for_address(bitcoin_address): """ Return a list of Spendable objects for the given bitcoin address. """ json_response = fetch_json( "addresses/%s/unspent-outputs" % bitcoin_address) spendables = [] for tx_out_info in json_response.get("data", {}).get("outputs"): if tx_out_info.get("to_address") == bitcoin_address: coin_value = tx_out_info["value"] script = tools.compile(tx_out_info.get("script_pub_key")) previous_hash = h2b_rev(tx_out_info.get("transaction_hash")) previous_index = tx_out_info.get("transaction_index") spendables.append( Spendable(coin_value, script, previous_hash, previous_index)) return spendables
def createTransaction(self, address, amount, keychain, fee="standard", confirms=0): unspents_result = yield self.unspents() spendables = [] p2sh = [] chain_paths = [] # Strip leading / keychain_path = keychain['path'][1:] for unspent in unspents_result["unspents"]: if unspent["confirmations"] < confirms: continue p2sh.append(h2b(unspent["redeemScript"])) spendable = Spendable(unspent["value"], h2b(unspent["script"]), h2b_rev(unspent["tx_hash"]), unspent["tx_output_n"]) spendables.append(spendable) chain_paths.append(keychain_path + unspent['chainPath']) p2sh_lookup = build_p2sh_lookup(p2sh) address_result = yield self.createAddress(1) change = address_result["address"] tx = tx_utils.create_tx(spendables, [(address, amount), change], fee) # address_keys = [BIP32Node.from_hwif(keychain["xprv"]).subkey_for_path("0/0/0/0"), # BIP32Node.from_hwif(keychain["xprv"]).subkey_for_path(address_result['path'])] spendable_keys = [ BIP32Node.from_hwif(keychain["xprv"]).subkey_for_path(path) for path in chain_paths ] # all_keys = address_keys + spendable_keys hash160_lookup = build_hash160_lookup( [key.secret_exponent() for key in spendable_keys]) pprint(tx) tx.sign(hash160_lookup=hash160_lookup, p2sh_lookup=p2sh_lookup) pprint(tx) returnValue({'tx': tx.as_hex(), 'fee': tx.fee()})
async def get_block(self, blockhash: str, peers=None, timeout=None, privileged_peers=False, segwit=True) -> Dict: Logger.p2p.debug('Downloading block %s' % blockhash) block_type = segwit and ITEM_TYPE_SEGWIT_BLOCK or ITEM_TYPE_BLOCK inv_item = InvItem(block_type, h2b_rev(blockhash)) response = await self.pool.get(inv_item, peers=peers, timeout=timeout, privileged=privileged_peers) return response and { "block_hash": str(blockhash), "header_bytes": response[:80], "block_bytes": response }
def spendables_for_addresses(self, bitcoin_addresses): """ Return a list of Spendable objects for the given bitcoin address. """ r = [] for i in xrange(0, len(bitcoin_addresses), CHUNK_SIZE): addresses = bitcoin_addresses[i:i+CHUNK_SIZE] url = "%s/api/addrs/%s/utxo" % (self.base_url, ",".join(addresses)) r.extend(json.loads(urlopen(url).read().decode("utf8"))) spendables = [] for u in r: coin_value = btc_to_satoshi(str(u.get("amount"))) script = h2b(u.get("scriptPubKey")) previous_hash = h2b_rev(u.get("txid")) previous_index = u.get("vout") spendables.append(Spendable(coin_value, script, previous_hash, previous_index)) return spendables
def txs_from_json(path): """ Read tests from ./data/tx_??valid.json Format is an array of arrays Inner arrays are either [ "comment" ] or [[[prevout hash, prevout index, prevout scriptPubKey], [input 2], ...], serializedTransaction, verifyFlags] ... where all scripts are stringified scripts. verifyFlags is a comma separated list of script verification flags to apply, or "NONE" """ comments = None with open(path, 'r') as f: for tvec in json.load(f): if len(tvec) == 1: comments = tvec[0] continue assert len(tvec) == 3 prevouts = tvec[0] for prevout in prevouts: assert len(prevout) in (3, 4) tx_hex = tvec[1] flag_mask = parse_flags(tvec[2]) try: tx = Tx.from_hex(tx_hex) except: print("Cannot parse tx_hex: %s" % tx_hex) raise spendable_db = {} blank_spendable = Spendable(0, b'', b'\0' * 32, 0) for prevout in prevouts: coin_value = 1000000 if len(prevout) == 4: coin_value = prevout[3] spendable = Spendable(coin_value=coin_value, script=compile(prevout[2]), tx_hash=h2b_rev(prevout[0]), tx_out_index=prevout[1]) spendable_db[(spendable.tx_hash, spendable.tx_out_index)] = spendable unspents = [ spendable_db.get((tx_in.previous_hash, tx_in.previous_index), blank_spendable) for tx_in in tx.txs_in] tx.set_unspents(unspents) yield (tx, flag_mask, comments)
def spendables_for_address(bitcoin_address): """ Return a list of Spendable objects for the given bitcoin address. """ URL = "https://api.biteasy.com/blockchain/v1/addresses/%s/unspent-outputs" % bitcoin_address r = Request(URL, headers={"content-type": "application/json", "accept": "*/*", "User-Agent": "curl/7.29.0"}) d = urlopen(r).read() json_response = json.loads(d.decode("utf8")) spendables = [] for tx_out_info in json_response.get("data", {}).get("outputs"): if tx_out_info.get("to_address") == bitcoin_address: coin_value = tx_out_info["value"] script = tools.compile(tx_out_info.get("script_pub_key")) previous_hash = h2b_rev(tx_out_info.get("transaction_hash")) previous_index = tx_out_info.get("transaction_index") spendables.append(Spendable(coin_value, script, previous_hash, previous_index)) return spendables
def test_is_invalid(self): for (prevouts, tx_hex, flags) in txs_from_json(TX_INVALID_JSON): try: tx = Tx.from_hex(tx_hex) if not check_transaction(tx): continue unspents = [Spendable(coin_value=1000000, script=compile_script(prevout[2]), tx_hash=h2b_rev(prevout[0]), tx_out_index=prevout[1]) for prevout in prevouts] tx.set_unspents(unspents) bs = tx.bad_signature_count() self.assertEqual(bs, 0) except: continue self.fail("Invalid transaction: " + tx.id() + " appears to be valid.")
def json_to_tx(json_text): # transactions with non-standard lock time are not decoded properly # for example, d1ef46055a84fd02ee82580d691064780def18614d98646371c3448ca20019ac # there is no way to fix this until biteasy add a lock_time field to their output d = json.loads(json_text).get("data") txs_in = [] for d1 in d.get("inputs"): previous_hash = h2b_rev(d1.get("outpoint_hash")) previous_index = d1.get("outpoint_index") script = h2b(d1.get("script_sig")) sequence = 4294967295 # BRAIN DAMAGE txs_in.append(TxIn(previous_hash, previous_index, script, sequence)) txs_out = [] for d1 in d.get("outputs"): coin_value = d1.get("value") script = h2b(d1.get("script_pub_key")) txs_out.append(TxOut(coin_value, script)) version = d.get("version") lock_time = 0 # BRAIN DAMAGE return Tx(version, txs_in, txs_out, lock_time)
def createTransaction(self, address, amount, keychain, fee="standard", confirms=0): unspents_result = yield self.unspents() spendables = [] p2sh = [] chain_paths = [] # Strip leading / keychain_path = keychain['path'][1:] for unspent in unspents_result["unspents"]: if unspent["confirmations"] < confirms: continue p2sh.append(h2b(unspent["redeemScript"])) spendable = Spendable(unspent["value"], h2b(unspent["script"]), h2b_rev(unspent["tx_hash"]), unspent["tx_output_n"]) spendables.append(spendable) chain_paths.append(keychain_path + unspent['chainPath']) p2sh_lookup = build_p2sh_lookup(p2sh) address_result = yield self.createAddress(1) change = address_result["address"] tx = tx_utils.create_tx(spendables, [(address, amount), change], fee) # address_keys = [BIP32Node.from_hwif(keychain["xprv"]).subkey_for_path("0/0/0/0"), # BIP32Node.from_hwif(keychain["xprv"]).subkey_for_path(address_result['path'])] spendable_keys = [BIP32Node.from_hwif(keychain["xprv"]).subkey_for_path(path) for path in chain_paths] # all_keys = address_keys + spendable_keys hash160_lookup = build_hash160_lookup([key.secret_exponent() for key in spendable_keys]) pprint(tx) tx.sign(hash160_lookup=hash160_lookup, p2sh_lookup=p2sh_lookup) pprint(tx) returnValue({'tx': tx.as_hex(), 'fee': tx.fee()})
def get_tx(tx_hash): """ Get a Tx by its hash. """ # TODO: fix this j = get_json_for_hash(tx_hash) txs_in = [] for j_in in j.get("in"): if j_in.get("coinbase"): txs_in.append(TxIn.coinbase_tx_in(binascii.unhexlify(j_in["coinbase"]))) else: txs_in.append(TxIn( h2b_rev(j_in["prev_out"]["hash"]), int(j_in["prev_out"]["n"]), tools.compile(j_in["scriptSig"]))) txs_out = [] for j_out in j.get("out"): txs_out.append(TxOut(int(btc_to_satoshi(j_out["value"])), tools.compile(j_out["scriptPubKey"]))) tx = Tx(int(j["ver"]), txs_in, txs_out, int(j["lock_time"])) assert tx.hash() == tx_hash return tx
def send(self, wallet_id, passcode, address, amount, message='', fee=10000): """ Send bitcoins to address :param wallet_id: bitgo wallet id :param address: bitcoin address :param amount: btc amount in satoshis :return: boolean """ wallet = self.get_wallet(wallet_id) if not wallet['spendingAccount']: raise NotSpendableWallet() if not wallet['isActive']: raise NotActiveWallet() if amount < 10000: raise Exception('amount to small') if wallet['confirmedBalance'] < amount: raise NotEnoughFunds('Not enough funds: balance %s amount %s' % (wallet['confirmedBalance'], amount) ) change_address = self.create_address(wallet_id, chain=1) usableKeychain = False spendables = [] chain_paths = [] p2sh = [] payables = [(address, amount)] keychain_path = "" for keychain in wallet['private']['keychains']: keychain_path = keychain['path'][1:] keychain = self.get_keychain(keychain['xpub']) if 'encryptedXprv' not in keychain: continue usableKeychain = True break if not usableKeychain: raise BitGoError("didn't found a spendable keychain") data = json.loads(keychain['encryptedXprv']) #add base64 paddings for k in ['iv', 'salt', 'ct']: data[k] = data[k] + "==" cipher = sjcl.SJCL() xprv = cipher.decrypt(data, passcode) unspents = self.get_unspents(wallet_id) total_value = 0 for d in unspents['unspents'][::-1]: path = keychain_path + d['chainPath'] chain_paths.append(path) p2sh.append(h2b(d["redeemScript"])) spendables.append(Spendable(d["value"], h2b(d["script"]), h2b_rev(d["tx_hash"]), d["tx_output_n"])) total_value += d['value'] if total_value > amount: break if total_value > (amount + fee): #add a change address #TODO: create address payables.append(change_address) p2sh_lookup = build_p2sh_lookup(p2sh) spendable_keys = [] priv_key = BIP32Node.from_hwif(xprv) spendable_keys = [priv_key.subkey_for_path(path) for path in chain_paths] hash160_lookup = build_hash160_lookup([key.secret_exponent() for key in spendable_keys]) tx = create_tx(spendables, payables) tx.sign(hash160_lookup=hash160_lookup, p2sh_lookup=p2sh_lookup) r = requests.post(self.url + '/tx/send', { 'tx': tx.as_hex(), 'message': message }, headers={ 'Authorization': 'Bearer %s' % self.access_token, }) return r.json()
alice_masked_pcode_nosuffix = payment_code_no_suffix(alice_sign, masked_xval, masked_cc) alice_masked_pcode = pcode_b58( payment_code(alice_sign, masked_xval, masked_cc) ) print "Alice's Masked Payment Code:\n", alice_masked_pcode, '\n' from pycoin.tx.TxOut import * from pycoin.tx import Tx, tx_utils, Spendable, pay_to from pycoin import convention amount = convention.btc_to_satoshi(first_nondust['amount']) dustbtc = convention.btc_to_satoshi(DUST) feebtc = convention.btc_to_satoshi(FEE) unspent = Spendable( amount, standard_tx_out_script(first_address), \ serialize.h2b_rev(first_nondust['tx']), first_nondust['n'] ) txout = TxOut( dustbtc, standard_tx_out_script(bob_notif.address()) ) change = TxOut( amount - (dustbtc + feebtc), standard_tx_out_script(change_addresses.pop()) ) op_return_script = pay_to.ScriptNulldata(alice_masked_pcode_nosuffix) op_return_txout = TxOut(0, op_return_script.script()) notif_tx = Tx( 1, [unspent.tx_in()], [txout, change, op_return_txout], unspents=[unspent] ) tx_utils.sign_tx( notif_tx, [first_node.wif()] ) print "Signed Notification TX as hex:\n", notif_tx.as_hex() data = urlencode(dict( hex=notif_tx.as_hex() )) response = json.load( urlopen(url="http://tbtc.blockr.io/api/v1/tx/decode", data=data) ) print "Decoded:\n", pretty_json(response), '\n'
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)
def get_blockchain_tip(self): URL = "%s/api/status?q=getLastBlockHash" % self.base_url d = urlopen(URL).read().decode("utf8") r = json.loads(d) return h2b_rev(r.get("lastblockhash"))
def test_validate(self): # block 80971 block_80971_cs = h2b('00000000001126456C67A1F5F0FF0268F53B4F22E0531DC70C7B69746AF69DAC') block_80971_data = h2b('01000000950A1631FB9FAC411DFB173487B9E18018B7C6F7147E78C06258410000000000A881352F97F14B'\ 'F191B54915AE124E051B8FE6C3922C5082B34EAD503000FC34D891974CED66471B4016850A040100'\ '0000010000000000000000000000000000000000000000000000000000000000000000FFFFFFFF080'\ '4ED66471B02C301FFFFFFFF0100F2052A01000000434104CB6B6B4EADC96C7D08B21B29D0ADA5F29F937'\ '8978CABDB602B8B65DA08C8A93CAAB46F5ABD59889BAC704925942DD77A2116D10E0274CAD944C71D3D1A'\ '670570AC0000000001000000018C55ED829F16A4E43902940D3D33005264606D5F7D555B5F67EE4C033390'\ 'C2EB010000008A47304402202D1BF606648EDCDB124C1254930852D99188E1231715031CBEAEA80CCFD2B39A'\ '02201FA9D6EE7A1763580E342474FC1AEF59B0468F98479953437F525063E25675DE014104A01F763CFBF5E518'\ 'C628939158AF3DC0CAAC35C4BA7BC1CE8B7E634E8CDC44E15F0296B250282BD649BAA8398D199F2424FCDCD88'\ 'D3A9ED186E4FD3CB9BF57CFFFFFFFFF02404B4C00000000001976A9148156FF75BEF24B35ACCE3C05289A241'\ '1E1B0E57988AC00AA38DF010000001976A914BC7E692A5FFE95A596712F5ED83393B3002E452E88AC000000'\ '0001000000019C97AFDF6C9A31FFA86D71EA79A079001E2B59EE408FD418498219400639AC0A010000008B4'\ '830450220363CFFAE09599397B21E6D8A8073FB1DFBE06B6ACDD0F2F7D3FEA86CA9C3F605022100FA255A6ED'\ '23FD825C759EF1A885A31CAD0989606CA8A3A16657D50FE3CEF5828014104FF444BAC08308B9EC97F56A652A'\ 'D8866E0BA804DA97868909999566CB377F4A2C8F1000E83B496868F3A282E1A34DF78565B65C15C3FA21A076'\ '3FD81A3DFBBB6FFFFFFFF02C05EECDE010000001976A914588554E6CC64E7343D77117DA7E01357A6111B798'\ '8AC404B4C00000000001976A914CA6EB218592F289999F13916EE32829AD587DBC588AC00000000010000000'\ '1BEF5C9225CB9FE3DEF929423FA36AAD9980B9D6F8F3070001ACF3A5FB389A69F000000004A493046022100F'\ 'B23B1E2F2FB8B96E04D220D385346290A9349F89BBBC5C225D5A56D931F8A8E022100F298EB28294B90C1BAF'\ '319DAB713E7CA721AAADD8FCC15F849DE7B0A6CF5412101FFFFFFFF0100F2052A010000001976A9146DDEA80'\ '71439951115469D0D2E2B80ECBCDD48DB88AC00000000'); # block 80974 block_80974_cs = h2b('0000000000089F7910F6755C10EA2795EC368A29B435D80770AD78493A6FECF1') block_80974_data = h2b('010000007480150B299A16BBCE5CCDB1D1BBC65CFC5893B01E6619107C55200000000000790'\ '0A2B203D24C69710AB6A94BEB937E1B1ADD64C2327E268D8C3E5F8B41DBED8796974CED66471B204C3247030'\ '1000000010000000000000000000000000000000000000000000000000000000000000000FFFFFFFF0804ED6'\ '6471B024001FFFFFFFF0100F2052A010000004341045FEE68BAB9915C4EDCA4C680420ED28BBC369ED84D48A'\ 'C178E1F5F7EEAC455BBE270DABA06802145854B5E29F0A7F816E2DF906E0FE4F6D5B4C9B92940E4F0EDAC000'\ '000000100000001F7B30415D1A7BF6DB91CB2A272767C6799D721A4178AA328E0D77C199CB3B57F010000008'\ 'A4730440220556F61B84F16E637836D2E74B8CB784DE40C28FE3EF93CCB7406504EE9C7CAA5022043BD4749D'\ '4F3F7F831AC696748AD8D8E79AEB4A1C539E742AA3256910FC88E170141049A414D94345712893A828DE57B4C'\ '2054E2F596CDCA9D0B4451BA1CA5F8847830B9BE6E196450E6ABB21C540EA31BE310271AA00A49ED0BA930743'\ 'D1ED465BAD0FFFFFFFF0200E1F505000000001976A914529A63393D63E980ACE6FA885C5A89E4F27AA08988AC'\ 'C0ADA41A000000001976A9145D17976537F308865ED533CCCFDD76558CA3C8F088AC000000000100000001651'\ '48D894D3922EF5FFDA962BE26016635C933D470C8B0AB7618E869E3F70E3C000000008B48304502207F5779EB'\ 'F4834FEAEFF4D250898324EB5C0833B16D7AF4C1CB0F66F50FCF6E85022100B78A65377FD018281E77285EFC3'\ '1E5B9BA7CB7E20E015CF6B7FA3E4A466DD195014104072AD79E0AA38C05FA33DD185F84C17F611E58A8658CE'\ '996D8B04395B99C7BE36529CAB7606900A0CD5A7AEBC6B233EA8E0FE60943054C63620E05E5B85F0426FFFFF'\ 'FFF02404B4C00000000001976A914D4CAA8447532CA8EE4C80A1AE1D230A01E22BFDB88AC8013A0DE0100000'\ '01976A9149661A79AE1F6D487AF3420C13E649D6DF3747FC288AC00000000') block_80971 = Block.parse(io.BytesIO(block_80971_data)) block_80974 = Block.parse(io.BytesIO(block_80974_data)) tx_db = dict((tx.hash(), tx) for tx in block_80971.txs) tx_to_validate = block_80974.txs[2] self.assertEqual("OP_DUP OP_HASH160 d4caa8447532ca8ee4c80a1ae1d230a01e22bfdb OP_EQUALVERIFY OP_CHECKSIG", tools.disassemble(tx_to_validate.txs_out[0].script)) self.assertEqual(tx_to_validate.hash(), h2b_rev("7c4f5385050c18aa8df2ba50da566bbab68635999cc99b75124863da1594195b")) tx_to_validate.validate(tx_db) # now, let's corrupt the Tx and see what happens tx_out = tx_to_validate.txs_out[1] disassembly = tools.disassemble(tx_out.script) tx_out.script = tools.compile(disassembly) tx_to_validate.validate(tx_db) disassembly = disassembly.replace("9661a79ae1f6d487af3420c13e649d6df3747fc2", "9661a79ae1f6d487af3420c13e649d6df3747fc3") tx_out.script = tools.compile(disassembly) with self.assertRaises(ValidationFailureError) as cm: tx_to_validate.validate(tx_db) exception = cm.exception self.assertEqual(exception.args[0], "Tx 3c0ef7e369e81876abb0c870d433c935660126be62a9fd5fef22394d898d1465 TxIn index 0 did not verify")
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)