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 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 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 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])
def get_blockheader_with_transaction_hashes(self, block_hash): URL = "%s/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 = Block(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, bitcoin_address): """ Return a list of Spendable objects for the given bitcoin address. """ URL = "%s/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 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(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. """ 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 test_issue_39(self): """ See https://github.com/richardkiss/pycoin_grs/issues/39 and https://github.com/richardkiss/pycoin_grs/pull/40 There was a problem validating the following transactions: 315ac7d4c26d69668129cc352851d9389b4a6868f1509c6c8b66bead11e2619f dbf38261224ebff0c455c405e2435cfc69adb6b8a42d7b10674d9a4eb0464dca de744408e4198c0a39310c8106d1830206e8d8a5392bcf715c9b5ec97d784edd This codes tests this. """ TX_B64_LIST = [ # some encoded transactions (the three listed above and the three # that they depend upon) ("AQAAAALcOOk1m9faO1g4YgThhtlAhoX0J/XlE2ZttzWqimshaQAAAABqRzBE" "AiBdj+6zEkeORo0LUU5j4ROVjXIU+lcqzYcHmn8MwCb8XAIgD6duoFvyQ69t" "D5F38kHK9gbQH8/V5i1r77yiTlaeXCcDIQIQChqcosGJMtZXfFjyJVgBhNDg" "gibUGVmHSslj48Gy/v/////cOOk1m9faO1g4YgThhtlAhoX0J/XlE2ZttzWq" "imshaQEAAABrSDBFAiAIft44cp5tNeT1FVBQGOZZIiAxJztzZpIPOT7jqxe8" "HgIhAMpDFkt1fRptEjXxMgDUtfdt2P2k7J/ChUay31sSEejfAyECdZg5E+YA" "k7dn6FWXypOX+y9Bjlf5mNavu8U2EWCFscv/////AUCJlQAAAAAAGXapFPzJ" "s204z1XX1bTuTd22ssF2EvSMiKwAAAAA"), ("AQAAAAEtUf3HWib/PGE4Ag4am7QPH6tuOc6W/q4yGMmuA14AqwEAAABrSDBF" "AiEA5PGlIZB+UPxE0zEy7pjJcVpk350sKGDj4EdMUhq4U34CIDCvjTUGpTUu" "KwVkRazYVaQtNycOlKYpp7KLIYcOxtdhASEDgIxJPwYZkNK+AB5A8EiuiHAy" "C3SJXOLZZS88HHPNbyz/////AvCHSwAAAAAAGXapFPzJs204z1XX1bTuTd22" "ssF2EvSMiKzwh0sAAAAAABl2qRQzzvYXSdEboq3wkaXgRWeBd/46bYisAAAA" "AA=="), ("AQAAAAJa+fLO2OCiRk98qhSvobvRyPsY3qrl0QEZa1jcIn70rgAAAABqRzBE" "AiANKFITbLHEu93eBOx29YHRsyockZFIyF+8D9BWXTWK8wIgNvKqF87Ind6w" "A3aigYv3KMRHmSgLnyBExWkad7Dc2WwDIQIQChqcosGJMtZXfFjyJVgBhNDg" "gibUGVmHSslj48Gy/v////9a+fLO2OCiRk98qhSvobvRyPsY3qrl0QEZa1jc" "In70rgEAAABrSDBFAiEA9APIYMTjztPlIyyzWCXnk3It+vCsLwGWGpN4K0kG" "qWMCIGLdifJz5mvPrW8FqLDNJrp7Bma+/Qw9pF2feVcX2lBKAyECdZg5E+YA" "k7dn6FWXypOX+y9Bjlf5mNavu8U2EWCFscv/////AaAClAAAAAAAGXapFOUK" "XY2jOZUbBAutBFPXxAz9dNPciKwAAAAA"), ("AQAAAAGfYeIRrb5mi2ycUPFoaEqbONlRKDXMKYFmaW3C1MdaMQAAAABsSTBG" "AiEAhIisrGQ/6Sa7DAJtv+pa9nMiHuBTLNAkxlyzDjYvGEQCIQCFH27K+zjJ" "ItZHnrCORpOhrBnHvPnUX8mqXy1pGB/4ngEhAhAKGpyiwYky1ld8WPIlWAGE" "0OCCJtQZWYdKyWPjwbL+/////wKgxEoAAAAAABl2qRT8ybNtOM9V19W07k3d" "trLBdhL0jIisoMRKAAAAAAAZdqkUM872F0nRG6Kt8JGl4EVngXf+Om2IrAAA" "AAA="), ("AQAAAALCBkSoNGHOnUgtcCy8I87ODdMmW1WL56GNNOIWvaccAAAAAABrSDBF" "AiAxKffbGKLs4sDhPFwLZvQlHX+Q20uxr0hFzQqtnSQZQAIhAImY0R1z7HrT" "Tt4hR0R/3n3eS8LXk14G94/O8Pc7LDlmAyECE2UQ39BTBuo0mCvz395yuOSd" "QyqYBb9kUtOZTnkvnRn/////yRF9O6xy+bn8PWf3KNM1uywKHCYWOL0bgEe1" "Zd1jGaIAAAAAakcwRAIgRQ7h/BpT6uurhfpEmEE/Xx5OAZdUohj+Euzr3Zg8" "mbkCIDxIakZ02TMLAtt5OHKyy0VQw7uywxjyis6540zeNZdJAyED78tvrsro" "6386Jta3YJd/I64guTuYS8oof9K4PDGZeHD/////AeD9HAAAAAAAGXapFB0x" "6lo758/yr1vtc3EOtvXV9n1wiKwAAAAA"), ("AQAAAAKerCh2TFeXmFaXU1qdQUucoCL5WRFVNZdvNt1FZgp5XQAAAACMSTBG" "AiEAvLz97Qz/zSlKSDrllLRwj73G2B7RfaiR1ZspOG5Ae3kCIQD5ATZgiNvH" "X8Tn8Ib8RohgW0HGbPRi00XUcvxCTmybGgFBBCsXId9LDBz91gENMCmVXxRE" "ZI+E6QOSkToVTtny7tiOJhmHy/jci4KzQmucvUBotsK5r4CiwjhjOkAAXRD6" "SWD/////6864dM1/4fxjvltUc0HJ1da9agsSw4LV3KYhGR7FJ+MBAAAAi0gw" "RQIhAJIopjUy7dPOHa+LGTvgM4jfZ8pA522/Jx3+uFC4Lz5IAiBzLNoxejaa" "dw1CXwOUuzI4rMl0xsuYC5XQaxZNT2TFzwFBBBPpriULEjb9VdVoC8v3E4is" "RMmfQByPCJYadSwK/ZZg9TTFGyDXUwW+dQ9tScDzhMWfdLK9DyV4iAbnYh/S" "2cr/////A0BCDwAAAAAAGXapFFzGycfh13x6rrUPhNJNj2ViE7xbiKwACT0A" "AAAAABl2qRQhQVEH8cwnc3//rGPcfvakBANJxIistBcsAAAAAAAZdqkUMQV+" "QpfDgBAsCQ+ixaUK5Kgl0kOIrAAAAAA="), ("AQAAAAO1CFlm1mEB3fjCtilQEH+6TbR3UzdJyqafj3mab9Mc6gAAAACKRzBE" "AiA8rWZ4BB8YYJp3xtx8jAZdrfQ6B0zjYRdgTS7I5LZF7gIgabCjn9iu9L3n" "YvKrdXFJJygtbg6V8iMTLrPh8ghdGvwBQQQrFyHfSwwc/dYBDTAplV8URGSP" "hOkDkpE6FU7Z8u7YjiYZh8v43IuCs0JrnL1AaLbCua+AosI4YzpAAF0Q+klg" "/////8IGRKg0Yc6dSC1wLLwjzs4N0yZbVYvnoY004ha9pxwAAQAAAItIMEUC" "IDNZYWLuCV0nJL6CCGgUfQfNoh0oAACd2lMZn+zJdJCDAiEAqZafa18G1K1x" "/6yOvj8h1uAGSM8UjSJJ6479li5sos4BQQTswrqYR5m+x0vFTzgGrrM2k+Gx" "gX+hDBAvN8Kq9RRuWdqC4jVNGhGdFD63Ev1TQYXMqvp6b9ztbAZ3ED8i6sFo" "/////0Vf19DzvUs2DvFwlVW9viTF+YlXCNYNMD6yUXK9I9RBAgAAAItIMEUC" "IQCKbaQY2eH1fsXZFksstrP4B+uxPBwGRe2Wxl7rW5sYGwIgVvVEPdnJNvVj" "rh0XZdhqnOAA0Sw39Upqkejrm+yXWnwBQQQ1hDJBuzoTc1ZJ8zyVQjEfRcjW" "o8rq3lE+3x3rYZ3Q/9xBEBtsnkFAzps/N8n6C5cK2QAmRGxeGFmbYaGFT5RP" "/////wNAQg8AAAAAABl2qRSU70Qwi2d2bI+nKnCP19XGsbSnWoisVEkwAAAA" "AAAZdqkUgroT7ai54LzKPXVnWJsPoV6lJ0yIrHjrFQAAAAAAGXapFEFyZV9I" "izJXnWmTivO2n9OKDWCdiKwAAAAA"), ("AQAAAAHBHumhtHyFj2ma501AFchO/RrrfkY1sYTKsJiYe6i5pAEAAADaAEcw" "RAIgJQsMj5xe4yyGSQOseNBu7zuQNbdwYRpmu4tyOeVrDhoCIHTRJ5lHr5OH" "JsmDYl4nTEMhT2TeEN8tMNtrt/rFLMaHAUgwRQIhAObKZ2o5NubR2aoXKP7q" "oNMI3sv4u33Hnxcu1NBCilhoAiAH5OaEGAC5snVQDIWgXXVWICosFmTHHjXg" "y5fNwAO5gAFHUiECzr9qtYCUjRRrfMdx2OZGl0NJ09exHz4DKH0Jl6R307kh" "A3umUUhbeiyyIhketkpVkm5iu6v+m17SqUiKrVR7IEKCUq7/////An1KDwAA" "AAAAGXapFNxnIa33YyARGtMFwzhMdn1LmeGViKxllyYPAAAAABepFNsSg3N8" "2T68HrEpjWRKeEbFWm2WhwAAAAA="), ("AQAAAAHZI2Rm7Gvz7UMEKi20P7AIT5AOxlhwW29S0uFz9EPz1QEAAADaAEgw" "RQIhAIX1NZuYzrKUHFAxUNYI6yWMUuzCEapuZOUY6TdCspWaAiAchzgPP6if" "WNh0cmVkyW1UpygM/eVa1XrtHepCMhvArAFHMEQCIGLJtKtbyJEaH6iQS+hK" "xUlGrWwmqdJScz/JfSZ1Qln6AiBNRC+gswEEMjTNR5uVetqCGJkNL2m6fDfk" "DyICU/otoQFHUiECzr9qtYCUjRRrfMdx2OZGl0NJ09exHz4DKH0Jl6R307kh" "A3umUUhbeiyyIhketkpVkm5iu6v+m17SqUiKrVR7IEKCUq7/////Aux5CAAA" "AAAAGXapFDIKbLrYWAn/2ZTB7ToisbIaZ5DoiKzL5TUPAAAAABepFNsSg3N8" "2T68HrEpjWRKeEbFWm2WhwAAAAA="), ( # 837dea37ddc8b1e3ce646f1a656e79bbd8cc7f558ac56a169626d649ebe2a3ba "AQAAAAGsp/O0VlTCMOCIalf7mIwwRO9ej385cm0wXGHV6BiQPAAAAAD9XQEAS" "DBFAiABh6+Sjp0VXEsaycHJEYFTI5q6dndPd118H5w+EG/zPAIhAIgisPZY7e" "wiJ00LauneEOvy2gaxu9qrpOUOsHjznj14AUcwRAIgeV8PT1lBp3rgMuy54zd" "TeI1+tcsMeNgFV11rAKHZv+0CID4fStkzLRQWrgHicDjpRbydtZxzJyijg6bx" "7S+5naekAUzJUkEEkbuiUQkSpb032h+1sWcwEOQ9LG2BLFFOkb+p8usSnhwYM" "ynbVb2GjiCarC+8Assz2Y/nS/I/DCNdYSax2DNPhkEEhlxAKTpoDLnAIOex4Q" "bYwZFtPO+ZqkMaVtJT5pJW2sCe8SKxqYaBiny2JFMvBiwdH4ciCEhhxcMpHM/" "+9OxodEEEjSRV0kA+CHCPwfVWAC8bbNg/mS0IUJf5l0qwiiiDjweJb7qwjzlJ" "XhX6b61u2/sedU41+hx4RMQfMioYY9RiE1Ou/////wFAQg8AAAAAABl2qRSuV" "rTbE1VNMhxALbOWEYeu0bvtW4isAAAAAA=="), ( # 3c9018e8d5615c306d72397f8f5eef44308c98fb576a88e030c25456b4f3a7ac # input of # 837dea37ddc8b1e3ce646f1a656e79bbd8cc7f558ac56a169626d649ebe2a3ba "AQAAAAGJYyhI+ZcikVcnxcddqNstvxlDQqBCmCj2b/iPqyr31gAAAACLSDBFA" "iEAq7yKc/4gVEgL2j8ygdotDFHihBORq9TAn0+QiiA0wY0CIFvJ5NaOr7kY8+" "lmIzhkekQZwN4aZQq4mD8dIW4qMdjjAUEEb1XXre/2ARx+rClP5UDFeDC+gOk" "1XIOGnJJgpLi/R2ema6y9cLgE3GPVvusUGAKSrX87CDNysdAtejfdl/9cnv//" "//8BQEIPAAAAAAAXqRT4FbA22bu85enyoAq9G/PckelVEIcAAAAA") ] TX_LIST = [ Tx.from_hex(b2h(binascii.a2b_base64(b64.encode("utf8")))) for b64 in TX_B64_LIST ] TX_DB = dict((tx.hash(), tx) for tx in TX_LIST) for h in [ "315ac7d4c26d69668129cc352851d9389b4a6868f1509c6c8b66bead11e2619f", "dbf38261224ebff0c455c405e2435cfc69adb6b8a42d7b10674d9a4eb0464dca", "de744408e4198c0a39310c8106d1830206e8d8a5392bcf715c9b5ec97d784edd", "485716e53b422aca0fe5b1ded21360695ce5f49255d80e10db56458ed6962ff3", "837dea37ddc8b1e3ce646f1a656e79bbd8cc7f558ac56a169626d649ebe2a3ba" ]: tx = TX_DB.get(h2b_rev(h)) self.assertNotEqual(tx, None) tx.unspents_from_db(TX_DB) for idx, tx_in in enumerate(tx.txs_in): self.assertTrue(tx.is_signature_ok(tx_in_idx=idx))
def get_blockchain_tip(self): URL = "%s/status?q=getLastBlockHash" % self.base_url d = urlopen(URL).read().decode("utf8") r = json.loads(d) return h2b_rev(r.get("lastblockhash"))
def check_provider_tx_for_tx_hash(self, p, networks): for net in networks: b = p(net) for tx_id in tx_id_for_net[net]: tx = b.tx_for_tx_hash(h2b_rev(tx_id)) self.assertEqual(tx.id(), tx_id)
import threading import unittest from pycoin_grs.serialize import h2b_rev from pycoin_grs.services import providers from pycoin_grs.services.blockchain_info import BlockchainInfoProvider from pycoin_grs.services.blockcypher import BlockcypherProvider from pycoin_grs.services.blockexplorer import BlockExplorerProvider from pycoin_grs.services.chain_so import ChainSoProvider from pycoin_grs.services.insight import InsightProvider BLOCK_0_HASH = h2b_rev("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f") BLOCK_1_HASH = h2b_rev("00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048") tx_id_for_net = { "BTC": ["b958e4a3ccd5bc8fe0ff6fafd635199313e347b88a8102040c05dd123f32a4f3", "d1ef46055a84fd02ee82580d691064780def18614d98646371c3448ca20019ac", "69916297f7adde13457b8244e2d704966097e9519ec8fd6f2e7af8c2a60f70f2", "c586389e5e4b3acb9d6c8be1c19ae8ab2795397633176f5a6442a261bbdefc3a"], "XTN": ["4586e67ee5adcdbc97ed3d2a026ee8703df2ed3553854c186c216e90cd761b69"], "DOGE": ["ed7df4e7506ac8447b6983c8ad79da1af86cddda0ff012f7db83e664f61ef6cf"], "XDT": ["19dd5c3423e606b5b5dd30b070688bdf9af27fa736e8f3aeb2b68d92a50e67ef"], } class ServicesTest(unittest.TestCase): def test_env(self): CS = "blockchain.info blockexplorer.com chain.so insight:https://hostname/url"