def find_all_valid_decks(provider: Provider, deck_version: int, prod: bool=True) -> Generator: ''' Scan the blockchain for PeerAssets decks, returns list of deck objects. : provider - provider instance : version - deck protocol version (0, 1, 2, ...) : test True/False - test or production P2TH ''' pa_params = param_query(provider.network) if prod: p2th = pa_params.P2TH_addr else: p2th = pa_params.test_P2TH_addr if isinstance(provider, RpcNode): deck_spawns = (provider.getrawtransaction(i, 1) for i in find_deck_spawns(provider)) else: try: deck_spawns = (provider.getrawtransaction(i, 1) for i in provider.listtransactions(p2th)) except TypeError as err: # it will except if no transactions are found on this P2TH raise EmptyP2THDirectory(err) with concurrent.futures.ThreadPoolExecutor(max_workers=2) as th: for result in th.map(deck_parser, ((provider, rawtx, deck_version, p2th) for rawtx in deck_spawns)): if result: yield result
def find_deck_spawns(provider: Provider, prod: bool = True) -> Iterable[str]: '''find deck spawn transactions via Provider, it requires that Deck spawn P2TH were imported in local node or that remote API knows about P2TH address.''' pa_params = param_query(provider.network) if isinstance(provider, RpcNode): if prod: decks = (i["txid"] for i in provider.listtransactions("PAPROD")) else: decks = (i["txid"] for i in provider.listtransactions("PATEST")) if isinstance(provider, Mintr): if prod: decks = (i["txid"] for i in provider.listtransactions(pa_params.P2TH_addr)) else: raise NotImplementedError if isinstance(provider, Cryptoid) or isinstance(provider, Explorer): if prod: decks = (i for i in provider.listtransactions(pa_params.P2TH_addr)) else: decks = ( i for i in provider.listtransactions(pa_params.test_P2TH_addr)) return decks
def find_card_bundles(provider: Provider, deck: Deck) -> Optional[Iterator]: '''each blockchain transaction can contain multiple cards, wrapped in bundles. This method finds and returns those bundles.''' if isinstance(provider, RpcNode): if deck.id is None: raise Exception("deck.id required to listtransactions") p2th_account = provider.getaccount(deck.p2th_address) batch_data = [('getrawtransaction', [i["txid"], 1]) for i in provider.listtransactions(p2th_account)] result = provider.batch(batch_data) if result is not None: raw_txns = [i['result'] for i in result if result] else: raise EmptyP2THDirectory({'error': 'No cards found on this deck.'}) else: if deck.p2th_address is None: raise Exception("deck.p2th_address required to listtransactions") try: raw_txns = (provider.getrawtransaction(i, 1) for i in provider.listtransactions(deck.p2th_address)) except TypeError: raise EmptyP2THDirectory({'error': 'No cards found on this deck.'}) return (card_bundler(provider, deck, i) for i in raw_txns)
def find_vote_casts(provider: Provider, vote: Vote, choice_index: int) -> Iterable[VoteCast]: '''find and verify vote_casts on this vote_choice_address''' vote_casts = provider.listtransactions( vote.vote_choice_address[choice_index]) for tx in vote_casts: raw_tx = provider.getrawtransaction(tx, 1) sender = find_tx_sender(provider, raw_tx) confirmations = raw_tx["confirmations"] blocknum = provider.getblock(raw_tx["blockhash"])["height"] yield VoteCast(vote, sender, blocknum, confirmations, raw_tx["blocktime"])
def find_vote_inits(provider: Provider, deck: Deck) -> Iterable[Vote]: '''find vote_inits on this deck''' vote_ints = provider.listtransactions(deck_vote_tag(deck)) for txid in vote_ints: try: raw_vote = provider.getrawtransaction(txid) vote = parse_vote_info(read_tx_opreturn(raw_vote)) vote["vote_id"] = txid vote["sender"] = find_tx_sender(provider, raw_vote) vote["deck"] = deck yield Vote(**vote) except AssertionError: pass
def find_tx_sender(provider: Provider, raw_tx: dict) -> str: '''find transaction sender, vin[0] is used in this case.''' vin = raw_tx["vin"][0] txid = vin["txid"] index = vin["vout"] return provider.getrawtransaction(txid, 1)["vout"][index]["scriptPubKey"]["addresses"][0]
def find_parent_outputs(provider: Provider, utxo: TxIn) -> TxOut: '''due to design of the btcpy library, TxIn object must be converted to TxOut object before signing''' network_params = net_query(provider.network) index = utxo.txout # utxo index return TxOut.from_json(provider.getrawtransaction(utxo.txid, 1)['vout'][index], network=network_params)
def get_card_transfer(provider: Provider, deck: Deck, txid: str, debug: bool = False) -> Iterator: '''get a single card transfer by it's id''' rawtx = provider.getrawtransaction(txid, 1) bundle = card_bundler(provider, deck, rawtx) return card_bundle_parser(bundle, debug)
def find_deck(provider: Provider, key: str, version: int, prod: bool=True) -> Optional[Deck]: '''Find specific deck by deck id.''' pa_params = param_query(provider.network) if prod: p2th = pa_params.P2TH_addr else: p2th = pa_params.test_P2TH_addr rawtx = provider.getrawtransaction(key, 1) deck = deck_parser((provider, rawtx, 1, p2th)) return deck
def find_card_bundles(provider: Provider, deck: Deck) -> Optional[Iterator]: '''each blockchain transaction can contain multiple cards, wrapped in bundles. This method finds and returns those bundles.''' if isinstance(provider, RpcNode): if deck.id is None: raise Exception("deck.id required to listtransactions") batch_data = [('getrawtransaction', [i["txid"], 1]) for i in provider.listtransactions(deck.id)] result = provider.batch(batch_data) if result is not None: raw_txns = [i['result'] for i in result if result] else: raise EmptyP2THDirectory({'error': 'No cards found on this deck.'}) else: if deck.p2th_address is None: raise Exception("deck.p2th_address required to listtransactions") try: raw_txns = (provider.getrawtransaction(i, 1) for i in provider.listtransactions(deck.p2th_address)) except TypeError: raise EmptyP2THDirectory({'error': 'No cards found on this deck.'}) return (CardBundle(deck=deck, blockhash=i['blockhash'], txid=i['txid'], timestamp=i['time'], blockseq=tx_serialization_order(provider, i["blockhash"], i["txid"]), blocknum=provider.getblock(i["blockhash"])["height"], sender=find_tx_sender(provider, i), vouts=i['vout'], tx_confirmations=i['confirmations'] ) for i in raw_txns)
def card_bundler(provider: Provider, deck: Deck, tx: dict) -> CardBundle: '''each blockchain transaction can contain multiple cards, wrapped in bundles. This method finds and returns those bundles.''' return CardBundle(deck=deck, blockhash=tx['blockhash'], txid=tx['txid'], timestamp=tx['time'], blockseq=tx_serialization_order(provider, tx["blockhash"], tx["txid"]), blocknum=provider.getblock(tx["blockhash"])["height"], sender=find_tx_sender(provider, tx), vouts=tx['vout'], tx_confirmations=tx['confirmations'])
def tx_serialization_order(provider: Provider, blockhash: str, txid: str) -> int: '''find index of this tx in the blockid''' return provider.getblock(blockhash)["tx"].index(txid)
def get_card_transfers(provider: Provider, deck: Deck) -> Generator: '''get all <deck> card transfers, if cards match the protocol''' if isinstance(provider, RpcNode): if deck.id is None: raise Exception("deck.id required to listtransactions") batch_data = [('getrawtransaction', [i["txid"], 1]) for i in provider.listtransactions(deck.id)] result = provider.batch(batch_data) if result is not None: card_transfers = [i['result'] for i in result if result] else: if deck.p2th_address is None: raise Exception("deck.p2th_address required to listtransactions") if provider.listtransactions(deck.p2th_address): card_transfers = (provider.getrawtransaction( i, 1) for i in provider.listtransactions(deck.p2th_address)) else: raise EmptyP2THDirectory({'error': 'No cards found on this deck.'}) def card_parser(args: Tuple[Provider, Deck, dict]) -> list: '''this function wraps all the card transfer parsing''' provider = args[0] deck = args[1] raw_tx = args[2] try: validate_card_transfer_p2th(deck, raw_tx) # validate P2TH first card_metainfo = parse_card_transfer_metainfo( read_tx_opreturn(raw_tx), deck.version) vouts = raw_tx["vout"] sender = find_tx_sender(provider, raw_tx) try: # try to get block seq number blockseq = tx_serialization_order(provider, raw_tx["blockhash"], raw_tx["txid"]) except KeyError: blockseq = 0 try: # try to get block number of block when this tx was written blocknum = provider.getblock(raw_tx["blockhash"])["height"] except KeyError: blocknum = 0 try: # try to get tx confirmation count tx_confirmations = raw_tx["confirmations"] except KeyError: tx_confirmations = 0 cards = postprocess_card(card_metainfo, raw_tx, sender, vouts, blockseq, blocknum, tx_confirmations, deck) cards = [CardTransfer(**card) for card in cards] except (InvalidCardTransferP2TH, CardVersionMismatch, CardNumberOfDecimalsMismatch, InvalidVoutOrder, RecieverAmountMismatch, DecodeError, TypeError, InvalidNulldataOutput) as e: return [] return cards with concurrent.futures.ThreadPoolExecutor(max_workers=2) as th: for result in th.map(card_parser, ((provider, deck, i) for i in card_transfers)): if result: yield result