def check_bip143_tx(self, tx_u_hex, tx_s_hex, txs_out_value_scripthex_pair, tx_in_count, tx_out_count, version, lock_time): tx_u = Tx.from_hex(tx_u_hex) tx_s = Tx.from_hex(tx_s_hex) txs_out = [ TxOut(int(coin_value * 1e8), h2b(script_hex)) for coin_value, script_hex in txs_out_value_scripthex_pair ] for tx in (tx_u, tx_s): self.assertEqual(len(tx.txs_in), tx_in_count) self.assertEqual(len(tx.txs_out), tx_out_count) self.assertEqual(tx.version, version) self.assertEqual(tx.lock_time, lock_time) tx.set_unspents(txs_out) self.check_unsigned(tx_u) self.check_signed(tx_s) tx_hex = tx_u.as_hex() self.assertEqual(tx_hex, tx_u_hex) tx_hex = tx_s.as_hex() self.assertEqual(tx_hex, tx_s_hex) tx_u_prime = self.unsigned_copy(tx_s) tx_hex = tx_u_prime.as_hex() self.assertEqual(tx_hex, tx_u_hex) self.assertEqual(b2h_rev(double_sha256(h2b(tx_s_hex))), tx_s.w_id()) self.assertEqual(b2h_rev(double_sha256(h2b(tx_u_hex))), tx_u.w_id()) self.assertEqual(b2h_rev(double_sha256(h2b(tx_u_hex))), tx_u.id()) return tx_u, tx_s
def _fetch_loop(self, next_message, getdata_loop_future): try: while True: name, data = yield from next_message() ITEM_LOOKUP = dict(tx="tx", block="block", merkleblock="header") if name in ITEM_LOOKUP: item = data[ITEM_LOOKUP[name]] the_hash = item.hash() TYPE_DB = { "tx": ITEM_TYPE_TX, "block": ITEM_TYPE_BLOCK, "merkleblock": ITEM_TYPE_MERKLEBLOCK } the_type = TYPE_DB[name] inv_item = InvItem(the_type, the_hash) if name == "merkleblock": txs = [] for h in data["tx_hashes"]: name, data = yield from next_message() if name != "tx": logging.error( "insufficient tx messages after merkleblock message: missing %s", b2h_rev(h)) del self.futures[inv_item] future.set_result(None) break tx = data["tx"] if tx.hash() != h: logging.error( "missing tx message after merkleblock message: missing %s", b2h_rev(h)) del self.futures[inv_item] future.set_result(None) break txs.append(tx) item.txs = txs future = self.futures.get(inv_item) if future: del self.futures[inv_item] if not future.done(): future.set_result(item) else: logging.info("got %s unsolicited", item.id()) if name == "notfound": for inv_item in data["items"]: the_hash = inv_item.data future = self.futures.get(inv_item) if future: del self.futures[inv_item] future.set_result(None) except EOFError: getdata_loop_future.cancel()
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 reformat(spendable): return { "txid": b2h_rev(spendable.tx_hash), "index": spendable.tx_out_index, "value": spendable.coin_value, "script": b2h(spendable.script), }
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 electrumx_script_hash(self, bech32: bool = False) -> str: addr = self.bech32_p2wpkh_address() if bech32 \ else self.p2sh_p2wpkh_address() # type: str script = standard_tx_out_script(addr) # type: bytes h = SHA256.new() h.update(script) return b2h_rev(h.digest())
def dump_inputs(tx, network, verbose_signature, traceback_f, disassembly_level): 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()))) continue suffix = "" if tx.missing_unspent(idx): tx_out = None address = tx_in.address(ui_context=network.ui) 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 = network.ui.address_for_script(tx_out.puzzle_script()) 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: dump_disassembly(tx, idx, network.extras.annotate) if verbose_signature: dump_signatures(tx, tx_in, tx_out, idx, network, traceback_f)
def dump_inputs(tx, netcode, verbose_signature, address_prefix, traceback_f, disassembly_level): def signature_for_hash_type_f(hash_type, script): return tx.signature_hash(script, idx, hash_type) 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()))) continue 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: dump_disassembly(tx_in, tx_out, tx.lock_time, signature_for_hash_type_f) if verbose_signature: dump_signatures(tx, tx_in, tx_out, idx, netcode, address_prefix, traceback_f, disassembly_level)
def query_links (self, proxy): if self.previous_hash != 0: tx = ProxyTx.from_txhash(proxy, b2h_rev(self.previous_hash), do_query_links=False) txout = tx.txs_out[self.previous_index] self._address = txout.address(addrversion=proxy.addrversion) self.coin_value = txout.coin_value self.is_shallow = False
def dump_inputs(tx, netcode, verbose_signature, address_prefix, traceback_f, disassembly_level): def signature_for_hash_type_f(hash_type, script): return tx.signature_hash(script, idx, hash_type) 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()))) continue 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: dump_disassembly(tx_in, tx_out, tx.lock_time, signature_for_hash_type_f) if verbose_signature: dump_signatures(tx, tx_in, tx_out, idx, netcode, address_prefix, traceback_f, disassembly_level)
def reformat(spendable): return { "txid": b2h_rev(spendable.tx_hash), "index": spendable.tx_out_index, "value": spendable.coin_value, "script": b2h(spendable.script) }
def tx_for_tx_hash(self, tx_hash): URL = "%s/api/tx/%s" % (self.base_url, b2h_rev(tx_hash)) r = json.loads(urlopen(URL).read().decode("utf8")) tx = tx_from_json_dict(r) if tx.hash() == tx_hash: return tx return None
def get_tx(self, tx_hash): URL = "%s/api/rawtx/%s" % (self.base_url, b2h_rev(tx_hash)) r = json.loads(urlopen(URL).read().decode("utf8")) tx = Tx.tx_from_hex(r['rawtx']) if tx.hash() == tx_hash: return tx return None
def save_spendable(self, spendable): tx_hash = b2h_rev(spendable.tx_hash) script = b2h(spendable.script) c = self._exec_sql("insert or replace into Spendable values (?, ?, ?, ?, ?, ?, ?)", tx_hash, spendable.tx_out_index, spendable.coin_value, script, spendable.block_index_available, spendable.does_seem_spent, spendable.block_index_spent)
def get_tx(self, tx_hash): url = "%s/rawtx/%s" % (self.base_url, b2h_rev(tx_hash)) result = json.loads(urlopen(url).read().decode("utf8")) tx = Tx.from_hex(result["rawtx"]) if tx.hash() == tx_hash: return tx return None
def get_tx(self, tx_hash): URL = "%s/api/rawtx/%s" % (self.base_url, b2h_rev(tx_hash)) r = json.loads(urlopen(URL).read().decode("utf8")) tx = Tx.from_hex(r['rawtx']) if tx.hash() == tx_hash: return tx return None
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
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
def tx_for_tx_hash(self, tx_hash): URL = "%s/getrawtransaction?txid=%s&decrypt=0" % (self.base_url, b2h_rev(tx_hash)) r = urlopen(URL).read().decode("utf8") tx = Tx.from_hex(r) if tx.hash() == tx_hash: return tx return None
def __str__(self): BLOCK = "Block" TX = "Tx" INV_TYPES = {0: "?", 1: TX, 2: BLOCK, 3: "Merkle", 1073741825: TX, 1073741826: BLOCK} idx = self.item_type if idx not in INV_TYPES.keys(): idx = 0 return "InvItem %s [%s]" % (INV_TYPES[idx], b2h_rev(self.data))
def tx_for_tx_hash(tx_hash): URL = "https://api.biteasy.com/blockchain/v1/transactions/%s" % b2h_rev(tx_hash) r = Request(URL, headers={"content-type": "application/json", "accept": "*/*", "User-Agent": "curl/7.29.0" }) d = urlopen(r).read() tx = json_to_tx(d.decode("utf8")) if tx.hash() == tx_hash: return tx return None
def put(self, tx): name = b2h_rev(tx.hash()) if self.writable_cache_path: try: path = os.path.join(self.writable_cache_path, "%s_tx.bin" % name) with open(path, "wb") as f: tx.stream(f) except IOError: pass
def confirms(self, txid): try: url = "%s/tx/%s" % (self.base_url, b2h_rev(txid)) result = json.loads(urlopen(url).read().decode("utf8")) return result.get("confirmations", 0) except HTTPError as ex: if ex.code == 404: # unpublished tx return None else: raise ex
def tx_for_tx_hash(self, tx_hash): """ Get a Tx by its hash. """ url = "%s/rawtx/%s" % (self.url, b2h_rev(tx_hash)) d = urlopen(url).read() j = json.loads(d.decode("utf8")) tx = Tx.from_hex(j.get("rawtx", "")) if tx.hash() == tx_hash: return tx
def verify_tx_in(self, tx_in_idx, tx_out_script, expected_hash_type=None): tx_in = self.txs_in[tx_in_idx] def signature_for_hash_type_f(hash_type, script): return self.signature_hash(script, tx_in_idx, hash_type) if not tx_in.verify( tx_out_script, signature_for_hash_type_f, expected_hash_type, tx_version=self.version): raise ValidationFailureError( "just signed script Tx %s TransactionIn index %d did not verify" % ( b2h_rev(tx_in.previous_hash), tx_in_idx))
def post_unpack_merkleblock(d, f): """ A post-processing "post_unpack" to merkleblock messages. It validates the merkle proofs (throwing an exception if there's an error), and returns the list of transaction hashes in "tx_hashes". The transactions are supposed to be sent immediately after the merkleblock message. """ level_widths = [] count = d["total_transactions"] while count > 1: level_widths.append(count) count += 1 count //= 2 level_widths.append(1) level_widths.reverse() tx_acc = [] flags = d["flags"] hashes = list(reversed(d["hashes"])) left_hash, flag_index = _recurse(level_widths, 0, 0, hashes, flags, 0, tx_acc) if len(hashes) > 0: raise ValueError("extra hashes: %s" % hashes) idx, r = divmod(flag_index - 1, 8) if idx != len(flags) - 1: raise ValueError("not enough flags consumed") if flags[idx] > (1 << (r + 1)) - 1: raise ValueError("unconsumed 1 flag bits set") if left_hash != d["header"].merkle_root: raise ValueError( "merkle root %s does not match calculated hash %s" % (b2h_rev(d["header"].merkle_root), b2h_rev(left_hash))) d["tx_hashes"] = tx_acc return d
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
def spendable_for_hash_index(self, tx_hash, tx_out_index): tx_hash_hex = b2h_rev(tx_hash) SQL = ("select coin_value, script, block_index_available, " "does_seem_spent, block_index_spent from Spendable where " "tx_hash = ? and tx_out_index = ?") c = self._exec_sql(SQL, tx_hash_hex, tx_out_index) r = c.fetchone() if r is None: return r return Spendable(coin_value=r[0], script=h2b(r[1]), tx_hash=tx_hash, tx_out_index=tx_out_index, block_index_available=r[2], does_seem_spent=r[3], block_index_spent=r[4])
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_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 load_tx(get_txs_func, rawtx): Tx.ALLOW_SEGWIT = False # FIXME remove on next pycoin version tx = Tx.from_hex(rawtx) unspent_info = {} # txid -> index for txin in tx.txs_in: unspent_info[b2h_rev(txin.previous_hash)] = txin.previous_index utxo_rawtxs = get_txs_func(list(unspent_info.keys())) for utxo_txid, utxo_rawtx in utxo_rawtxs.items(): utxo_tx = Tx.from_hex(utxo_rawtx) prev_index = unspent_info[utxo_txid] tx.unspents.append(utxo_tx.txs_out[prev_index]) return tx
def _fetch_loop(self, next_message, getdata_loop_future): try: while True: name, data = yield from next_message() ITEM_LOOKUP = dict(tx="tx", block="block", merkleblock="header") if name in ITEM_LOOKUP: item = data[ITEM_LOOKUP[name]] the_hash = item.hash() TYPE_DB = {"tx": ITEM_TYPE_TX, "block": ITEM_TYPE_BLOCK, "merkleblock": ITEM_TYPE_MERKLEBLOCK} the_type = TYPE_DB[name] inv_item = InvItem(the_type, the_hash) future = self.futures.get(inv_item) if name == "merkleblock": txs = [] for h in data["tx_hashes"]: name, data = yield from next_message() if name != "tx": logging.error( "insufficient tx messages after merkleblock message: missing %s", b2h_rev(h)) del self.futures[inv_item] future.set_result(None) break tx = data["tx"] if tx.hash() != h: logging.error( "missing tx message after merkleblock message: missing %s", b2h_rev(h)) del self.futures[inv_item] future.set_result(None) break txs.append(tx) item.txs = txs if future is not None: del self.futures[inv_item] if not future.done(): future.set_result(item) else: logging.info("got %s unsolicited", item.id()) if name == "notfound": for inv_item in data["items"]: the_hash = inv_item.data future = self.futures.get(inv_item) if future: del self.futures[inv_item] future.set_result(None) except EOFError: getdata_loop_future.cancel()
def unspents_from_db(txs_in, ignore_missing=False): unspents = [] for tx_in in txs_in: if tx_in.is_coinbase(): unspents.append(None) continue tx = searchByHash(tx_in.previous_hash) if tx and tx.hash() == tx_in.previous_hash: unspents.append(tx.txs_out[tx_in.previous_index]) elif ignore_missing: unspents.append(None) else: raise KeyError( "can't find tx_out for %s:%d" % (b2h_rev(tx_in.previous_hash), tx_in.previous_index)) return unspents
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_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_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 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 from_hex (klass, proxy, hexv): STANDARD_SCRIPT_OUT = "OP_DUP OP_HASH160 %s OP_EQUALVERIFY OP_CHECKSIG" tx = ProxyTx.parse(StringIO(binascii.unhexlify(hexv))) uto = [] for txi in tx.txs_in: if proxy is None: pscript = '' else: thistx = proxy.getTx(b2h_rev(txi.previous_hash), shallow_txins=True) pscript = thistx.txs_out[txi.previous_index].script uto.append(UnsignedTxOut(txi.previous_hash, txi.previous_index, None, pscript)) uti = [] for txo in tx.txs_out: script_text = STANDARD_SCRIPT_OUT % b2h(txo.address_h160()) script_bin = compile(script_text) uti.append(ProxyTxOut(txo.coin_value, script_bin)) return klass(tx.version, uto, uti, tx.lock_time)
def test_block(self): expected_checksum = '0000000000089F7910F6755C10EA2795EC368A29B435D80770AD78493A6FECF1'.lower() block_data = h2b( "010000007480150B299A16BBCE5CCDB1D1BBC65CFC5893B01E6619107C552000000000" "007900A2B203D24C69710AB6A94BEB937E1B1ADD64C2327E268D8C3E5F8B41DBED8796" "974CED66471B204C324703010000000100000000000000000000000000000000000000" "00000000000000000000000000FFFFFFFF0804ED66471B024001FFFFFFFF0100F2052A" "010000004341045FEE68BAB9915C4EDCA4C680420ED28BBC369ED84D48AC178E1F5F7E" "EAC455BBE270DABA06802145854B5E29F0A7F816E2DF906E0FE4F6D5B4C9B92940E4F0" "EDAC000000000100000001F7B30415D1A7BF6DB91CB2A272767C6799D721A4178AA328" "E0D77C199CB3B57F010000008A4730440220556F61B84F16E637836D2E74B8CB784DE4" "0C28FE3EF93CCB7406504EE9C7CAA5022043BD4749D4F3F7F831AC696748AD8D8E79AE" "B4A1C539E742AA3256910FC88E170141049A414D94345712893A828DE57B4C2054E2F5" "96CDCA9D0B4451BA1CA5F8847830B9BE6E196450E6ABB21C540EA31BE310271AA00A49" "ED0BA930743D1ED465BAD0FFFFFFFF0200E1F505000000001976A914529A63393D63E9" "80ACE6FA885C5A89E4F27AA08988ACC0ADA41A000000001976A9145D17976537F30886" "5ED533CCCFDD76558CA3C8F088AC00000000010000000165148D894D3922EF5FFDA962" "BE26016635C933D470C8B0AB7618E869E3F70E3C000000008B48304502207F5779EBF4" "834FEAEFF4D250898324EB5C0833B16D7AF4C1CB0F66F50FCF6E85022100B78A65377F" "D018281E77285EFC31E5B9BA7CB7E20E015CF6B7FA3E4A466DD195014104072AD79E0A" "A38C05FA33DD185F84C17F611E58A8658CE996D8B04395B99C7BE36529CAB7606900A0" "CD5A7AEBC6B233EA8E0FE60943054C63620E05E5B85F0426FFFFFFFF02404B4C000000" "00001976A914D4CAA8447532CA8EE4C80A1AE1D230A01E22BFDB88AC8013A0DE010000" "001976A9149661A79AE1F6D487AF3420C13E649D6DF3747FC288AC00000000") # try to parse a block block = Block.parse(io.BytesIO(block_data)) print(block) assert b2h_rev(block.hash()) == expected_checksum for tx in block.txs: print(tx) for t in tx.txs_in: print(" %s" % t) for t in tx.txs_out: print(" %s" % t) block.check_merkle_hash()
def do_headers_improve_path(self, headers): """ Raises ValueError if headers path don't extend from anywhere in this view. Returns False if the headers don't improve the path. If the headers DO improve the path, return the value of the block index of the first header. So you need to rewind to "new_start_idx" before applying the new blocks. """ tuples = [] if len(self.node_tuples) == 0: if headers[0].previous_block_hash != HASH_INITIAL_BLOCK: return False the_tuple = GENESIS_TUPLE else: the_tuple = self.tuple_for_hash(headers[0].previous_block_hash) if the_tuple is None: return False new_start_idx = the_tuple[0] + 1 total_work = the_tuple[-1] expected_prior_hash = the_tuple[1] for idx, h in enumerate(headers): if h.previous_block_hash != expected_prior_hash: raise ValueError( "headers are not properly linked: no known block with hash %s" % b2h_rev( h.previous_block_hash)) total_work += 1 # TODO: make this difficulty/work instead of path size expected_prior_hash = h.hash() tuples.append((idx + new_start_idx, expected_prior_hash, total_work)) if total_work <= self.last_block_tuple()[-1]: return False # the headers DO improve things old_tuples = self.node_tuples self._set_tuples(t for t in old_tuples if t[0] < new_start_idx) self._add_tuples(tuples) return new_start_idx
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 delete_spendable(self, tx_hash, tx_out_index): c = self._exec_sql("delete from Spendable where tx_hash = ? and tx_out_index = ?", b2h_rev(tx_hash), tx_out_index)
def post_unpack_merkleblock(d, f): """ A post-processing "post_unpack" to merkleblock messages. It validates the merkle proofs (throwing an exception if there's an error), and returns the list of transaction hashes in "tx_hashes". The transactions are supposed to be sent immediately after the merkleblock message. """ def recurse(level_widths, level_index, node_index, hashes, flags, flag_index, tx_acc): idx, r = divmod(flag_index, 8) mask = (1 << r) flag_index += 1 if flags[idx] & mask == 0: h = hashes.pop() return h, flag_index if level_index == len(level_widths) - 1: h = hashes.pop() tx_acc.append(h) return h, flag_index # traverse the left left_hash, flag_index = recurse( level_widths, level_index+1, node_index*2, hashes, flags, flag_index, tx_acc) # is there a right? if node_index*2+1 < level_widths[level_index+1]: right_hash, flag_index = recurse( level_widths, level_index+1, node_index*2+1, hashes, flags, flag_index, tx_acc) if left_hash == right_hash: raise ValueError("merkle hash has same left and right value at node %d" % node_index) else: right_hash = left_hash return double_sha256(left_hash + right_hash), flag_index level_widths = [] count = d["total_transactions"] while count > 1: level_widths.append(count) count += 1 count //= 2 level_widths.append(1) level_widths.reverse() tx_acc = [] flags = d["flags"] hashes = list(reversed(d["hashes"])) left_hash, flag_index = recurse(level_widths, 0, 0, hashes, flags, 0, tx_acc) if len(hashes) > 0: raise ValueError("extra hashes: %s" % hashes) idx, r = divmod(flag_index-1, 8) if idx != len(flags) - 1: raise ValueError("not enough flags consumed") if flags[idx] > (1 << (r+1))-1: raise ValueError("unconsumed 1 flag bits set") if left_hash != d["header"].merkle_root: raise ValueError( "merkle root %s does not match calculated hash %s" % ( b2h_rev(d["header"].merkle_root), b2h_rev(left_hash))) d["tx_hashes"] = tx_acc return d
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 broadcast_tx(self, transaction): as_hex = transaction.as_hex() transaction = CTransaction.deserialize(h2b(as_hex)) tx_id = bitcoin.rpc.Proxy().sendrawtransaction(transaction) # reverse endianness for bitcoind return b2h_rev(tx_id)
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()))