def parse_block(self, block_num): """ 解析区块的交易详情 :return: 返回待推送信息列表 """ push_list = [] block = self.rpc.get_block(block_num) timestamp = block["Header"]["Timestamp"] txs = self.rpc.get_smart_code_event(block_num) for tx in txs: # 不成功的不推送 if tx["State"] != 1: continue for n in tx["Notify"]: # 只支持ont和ong的推送, 且只推送类型为transfer的交易 if n["ContractAddress"] not in ["0100000000000000000000000000000000000000", "0200000000000000000000000000000000000000"] or \ n["States"][0] != "transfer": continue mq_tx = G_PUSH_TEMPLATE.copy() mq_tx["BlockNumber"] = block_num mq_tx["Time"] = timestamp mq_tx["Txid"] = tx["TxHash"] mq_tx["Type"] = "ONT" mq_tx["From"] = n["States"][1] mq_tx["To"] = n["States"][2] mq_tx["Amount"] = n["States"][3] mq_tx["Contract"] = n["ContractAddress"] mq_tx["status"] = "true" push_list.append(mq_tx) return push_list
def parse_block(self, block_num): """ 解析区块的交易详情 :return: 返回待推送信息列表 """ while True: try: if isinstance(block_num, int): block_num = hex(block_num) if isinstance(block_num, str) and not block_num.startswith('0x'): block_num = hex(int(block_num)) block = self.rpc.get_block(block_num) txs = block.get("transactions", []) push_list = [] for tx in txs: tx_receipt = self.rpc.get_tx_receipt(tx.get("hash")) mq_tx = G_PUSH_TEMPLATE.copy() mq_tx["Txid"] = tx.get("hash") mq_tx["Type"] = "TOMO" mq_tx["From"] = tx.get("from") mq_tx["To"] = tx.get("to") mq_tx["Amount"] = int(tx.get("value"), 16) mq_tx["Fee"] = int(tx["gasPrice"], 16) * int( tx_receipt["gasUsed"], 16) status = int(tx_receipt["status"], 16) mq_tx["Valid"] = True if status else False mq_tx["status"] = "true" if status else "false" mq_tx["Time"] = int(block.get("timestamp"), 16) mq_tx["BlockNumber"] = int(tx.get("blockNumber"), 16) push_list.append(mq_tx) return push_list except Exception as e: G_LOGGER.info(f"出现异常,尝试重新获取。异常原因:{str(e)}") time.sleep(1)
def parse_block(self, block_num): """ 解析区块的交易详情 :return: 返回待推送信息列表 """ block_num -= 1 block = self.http.get_block(block_num) current_block = block["data"][0] if "data" in block.keys() and len(block["data"]) > 0 else None push_list = [] if current_block: txs = current_block["txes"] block_num = current_block["block"]["height"] for tx in txs: tx_type = tx["tx"]["type"] if tx_type == 257: mq_tx = G_PUSH_TEMPLATE.copy() mq_tx["Txid"] = tx["hash"] mq_tx["Type"] = "NEM" # 根据公钥得到地址 signer = tx["tx"]["signer"] result = self.http.get_from_public_key(signer) mq_tx["From"] = result["account"]["address"] mq_tx["To"] = tx["tx"]["recipient"] mq_tx["Amount"] = tx["tx"]["amount"] mq_tx["Time"] = tx["tx"]["timeStamp"] mq_tx["BlockNumber"] = block_num mq_tx["status"] = "true" push_list.append(mq_tx) return push_list
def parse_block(self, block_num): """ 解析区块的交易列表 :return: 返回待推送信息列表 """ block = self.rpc.get_block(block_num) txs = block["result"]["transactions"] if block else [] tx_index = 0 push_list = [] for tx in txs: if tx.get("type") != "binary" or tx.get("status") != 1: continue mq_tx = G_PUSH_TEMPLATE.copy() mq_tx["BlockNumber"] = tx["block_height"] mq_tx["Time"] = tx["timestamp"] mq_tx["Txid"] = tx["hash"] mq_tx["Type"] = "NAS" mq_tx["From"] = tx["from"] mq_tx["To"] = tx["to"] mq_tx["Amount"] = tx["value"] mq_tx["TxIndex"] = tx_index push_list.append(mq_tx) tx_index += 1 return push_list
def parse_block(self, block_num): """ 解析区块的交易详情 :return: 返回待推送信息列表 """ while True: try: block = self.rpc.get_block(block_num) txs = block.get("tx", []) push_list = [] for tx in txs: G_LOGGER.info("tx={}".format(tx)) mq_tx = G_PUSH_TEMPLATE.copy() mq_tx["Txid"] = tx.get("txHash") mq_tx["Type"] = "BNB" mq_tx["From"] = tx.get("fromAddr") mq_tx["To"] = tx.get("toAddr") mq_tx["Amount"] = int( float(tx.get("value") if tx.get("value") else 0) * 100000000) mq_tx["Fee"] = int( float(tx.get("txFee") if tx.get("txFee") else 0) * 100000000) mq_tx["Valid"] = True mq_tx["Time"] = utcstr_to_timestamp( tx.get("timeStamp")) if tx.get("timeStamp") else 0 mq_tx["BlockNumber"] = tx.get("blockHeight") mq_tx["TxIndex"] = 0 mq_tx["Memo"] = tx.get("memo") if tx.get("memo") else "" G_LOGGER.info("mq_tx={}".format(mq_tx)) push_list.append(mq_tx) return push_list except Exception as e: G_LOGGER.info(f"出现异常,尝试重新获取。异常原因:{str(e)}") time.sleep(1)
def parse_block(self, block_num): """ 解析区块的交易列表 :return: 返回待推送信息列表 """ txs = self.rpc.get_block(block_num) push_list = [] for tx in txs: mq_tx = G_PUSH_TEMPLATE.copy() tx_type = tx.get('type', '') if tx_type not in ('payment', 'create_account'): continue mq_tx["BlockNumber"] = block_num mq_tx["Time"] = date_z_to_timestamp(tx.get('created_at')) mq_tx["Txid"] = tx.get('transaction_hash') mq_tx["Type"] = "XLM" mq_tx["status"] = "true" if tx_type == 'payment': mq_tx['From'] = tx.get('from') mq_tx['To'] = tx.get('to') mq_tx['Amount'] = tx.get('amount') elif tx_type == 'create_account': mq_tx['From'] = tx.get('funder') mq_tx['To'] = tx.get('account') mq_tx['Amount'] = tx.get('starting_balance') push_list.append(mq_tx) return push_list
def parse_block(self, block_num): """ 解析区块的交易列表 :return: 返回待推送信息列表 """ block = self.rpc.get_block(block_num) block = block.get("block", {}) txs = block.get("transactions", []) if block else [] push_list = [] for tx in txs: tx_receipt = tx.get("tx_receipt", {}) actions = tx.get("actions", []) for i, act in enumerate(actions): contract, action_name, data = act.get("contract", ""), act.get( "action_name", ""), act.get("data", "") if action_name in self.supported: data = loads(data) d_len = len(data) _from, _to, _amount, _memo = "", "", 0, "" if action_name == "transfer" and contract == "token.iost": _token = data[0] if d_len > 0 else "" if _token and _token == "iost": contract = "iost" else: contract = _token _from = data[1] if d_len > 1 else "" _to = data[2] if d_len > 2 else "" _amount = data[3] if d_len > 3 else "" _memo = data[4] if d_len > 4 else "" elif action_name in ["buy", "sell", "pledge", "unpledge"]: if d_len < 4: continue contract = "iost" _from = data[0] if d_len > 0 else "" _to = data[1] if d_len > 1 else "" _amount = data[2] if d_len > 2 else "" else: continue mq_tx = G_PUSH_TEMPLATE.copy() mq_tx["BlockNumber"] = block.get("number", "-1") mq_tx["Time"] = block.get("time", "0")[:10] mq_tx["Txid"] = tx.get("hash", "") mq_tx["Type"] = "IOST" mq_tx["From"] = _from mq_tx["To"] = _to mq_tx["Amount"] = _amount mq_tx["Contract"] = contract mq_tx["Memo"] = _memo mq_tx["Fee"] = 0 mq_tx["Action"] = self.action_map.get(action_name, "") mq_tx["VoutsIndex"] = i status = tx_receipt.get("status_code", True) mq_tx["Valid"] = True if status == "SUCCESS" else False mq_tx[ "status"] = "true" if status == "SUCCESS" else "false" push_list.append(mq_tx) return push_list
def parse_block(self, block_num): """ 解析区块的交易列表 :return: 返回待推送信息列表 """ block = self.rpc.get_block(block_num) txs = block["tx"] if block else [] push_list = [] for tx in txs: tx_type = tx.get("tx_type") if tx_type in ["BCOIN_TRANSFER_TX", "UCOIN_TRANSFER_TX"]: transfers = tx.get("transfers", []) for i, tran in enumerate(transfers): if tran.get("coin_symbol") != "WICC": continue mq_tx = G_PUSH_TEMPLATE.copy() mq_tx["BlockNumber"] = tx["confirmed_height"] mq_tx["Time"] = block["time"] mq_tx["Txid"] = tx["txid"] mq_tx["Type"] = "WICC" mq_tx["From"] = tx["from_addr"] mq_tx["To"] = tran["to_addr"] mq_tx["Amount"] = tran["coin_amount"] mq_tx["Fee"] = tx.get("fees", "") mq_tx["Memo"] = tx.get("memo", "") mq_tx["Action"] = tx_type mq_tx["VoutsIndex"] = i mq_tx["status"] = "true" push_list.append(mq_tx) else: mq_tx = G_PUSH_TEMPLATE.copy() mq_tx["BlockNumber"] = tx["confirmed_height"] mq_tx["Time"] = block["time"] mq_tx["Txid"] = tx["txid"] mq_tx["Type"] = "WICC" mq_tx["From"] = tx.get("from_addr", "") mq_tx["To"] = tx.get("to_addr", "") mq_tx["Amount"] = tx.get("coin_amount", "") mq_tx["Fee"] = tx.get("fees", "") mq_tx["Memo"] = tx.get("memo", "") mq_tx["Action"] = tx_type mq_tx["status"] = "true" push_list.append(mq_tx) return push_list
def parse_normal_tx(self, tx_ids, mempool_tx=False, push_cache=None): """ 解析交易通过USDT节点 """ push_list = [] tx_details = self.rpc.omni_get_transactions(tx_ids) for idx, tx_detail in enumerate(tx_details): if tx_detail is None: # 不过USDT交易 continue mq_tx = G_PUSH_TEMPLATE.copy() mq_tx["Txid"] = tx_ids[idx] if self.rollback and push_cache and (mq_tx["Txid"] in push_cache): continue mq_tx["Type"] = self.coin_type mq_tx['Valid'] = tx_detail.get('valid', False) mq_tx['status'] = str(tx_detail.get('valid', "false")).lower() mq_tx['Fee'] = self.real_number(tx_detail.get("fee", "0"), self.wei, eng_string=True) if mempool_tx: mq_tx["Time"] = int(time.time()) mq_tx["BlockNumber"] = 0 else: mq_tx["Time"] = int(tx_detail.get("blocktime", time.time())) mq_tx["BlockNumber"] = self.current_height mq_tx['From'] = send_addr = tx_detail.get('sendingaddress') mq_tx['To'] = to_addr = tx_detail.get('referenceaddress') if send_addr is None or to_addr is None: continue subsends = tx_detail.get('subsends') if not subsends: mq_tx['Amount'] = self.real_number(tx_detail.get('amount', "0"), self.wei, eng_string=True) op_id = tx_detail.get('propertyid') if self.verify_op_id(op_id): mq_tx['Contract'] = str(op_id) else: continue push_list.append(mq_tx) else: for out_idx, sub in enumerate(subsends): if not isinstance(sub, dict): continue mq_tx['Amount'] = sub.get('amount', "0") op_id = sub.get('propertyid') if self.verify_op_id(op_id): mq_tx['Contract'] = str(op_id) else: continue push_list.append(mq_tx) return push_list
def parse_block(self, block_number): """ 解析区块的交易列表 :return: 返回待推送信息列表 """ txs = self.rpc.get_block_transactions(block_number) push_list = [] for tx in txs: mq_tx = G_PUSH_TEMPLATE.copy() tx_details = self.rpc.get_transactions(tx) mq_tx["BlockNumber"] = block_number mq_tx["Time"] = tx_details.get('meta', {}).get('blockTimestamp') mq_tx["Txid"] = tx mq_tx["Type"] = "VET" mq_tx['From'] = tx_details.get('origin') mq_tx['To'] = tx_details.get('clauses', [])[0].get('to') mq_tx['Amount'] = int( tx_details.get('clauses', [])[0].get('value'), 16) push_list.append(mq_tx)
def parse_block(self, block_num): """ 解析区块的交易列表 :return: 返回待推送信息列表 """ block = self.rpc.get_block(block_num) txs = block["data"]["transactions"] push_list = [] for tx in txs: mq_tx = G_PUSH_TEMPLATE.copy() mq_tx["BlockNumber"] = block["data"]["height"] mq_tx["Time"] = block["data"]["timestamp"] mq_tx["Txid"] = tx["id"] mq_tx["Type"] = "BTM" vins = tx["inputs"] is_coinbase = False # 是否挖矿交易 input_amount = 0 for i, vin in enumerate(vins): input_amount += vin.get("amount", 0) if vin["type"] == "coinbase": is_coinbase = True break if i == 0: # 取第一个input中的address作为from地址 mq_tx["From"] = vin["address"] if "address" in vin.keys() else "" vouts = tx["outputs"] output_amount = 0 for i, vout in enumerate(vouts): output_amount += vout.get("amount", 0) # 如果是挖矿交易,且交易类型不为control则不推送 if is_coinbase or vout['type'] != 'control' or "address" not in vout.keys(): continue mq_tx = mq_tx.copy() mq_tx["To"] = vout["address"] mq_tx["Amount"] = vout["amount"] mq_tx["Contract"] = vout["asset_id"] mq_tx["VoutsIndex"] = i mq_tx["status"] = "true" push_list.append(mq_tx) for _push in push_list: _push["Fee"] = input_amount - output_amount return push_list
def parse_block(self, block_num): """ 解析区块的交易详情 :return: 返回待推送信息列表 """ block = None while True: try: block = self.rpc.get_block(block_num) ledger = block.get("ledger", []) if block else [] txs = ledger.get("transactions", []) if ledger else [] push_list = [] for tx in txs: if tx["TransactionType"] == "Payment" and isinstance( tx['Amount'], str): # 交易类型为Payment且状态为成功 mq_tx = G_PUSH_TEMPLATE.copy() mq_tx["Txid"] = tx.get("hash", "") mq_tx["Type"] = "XRP" mq_tx["From"] = tx.get("Account", "") mq_tx["To"] = tx.get("Destination", "") mq_tx["Fee"] = tx.get("Fee", "") mq_tx["Memo"] = tx.get("DestinationTag", "") meta_data = tx.get("metaData", {}) status = meta_data.get("TransactionResult") mq_tx["Valid"] = True if ( status and status == "tesSUCCESS") else False mq_tx["status"] = "true" if ( status and status == "tesSUCCESS") else "false" mq_tx["Amount"] = meta_data.get( "delivered_amount", "0") mq_tx["Time"] = ledger.get("close_time", 0) + self.time_diff mq_tx["BlockNumber"] = ledger.get("ledger_index", 0) push_list.append(mq_tx) return push_list except Exception as e: G_LOGGER.info( f"区块{block_num}出现异常,区块内容:{block},尝试重新获取。异常原因:{str(e)}") time.sleep(1)
def parse_coinbase_tx(self, tx_id, mempool_tx=False, push_cache=None): """ 解析封装币基交易 """ if self.rollback and push_cache and (tx_id in push_cache): return [] coinbase_list = [] tx_detail = self.rpc.get_transaction(tx_id) mq_tx = G_PUSH_TEMPLATE.copy() mq_tx["Txid"] = tx_id mq_tx["Type"] = self.coin_type mq_tx["From"] = None mq_tx["Time"] = int(tx_detail["time"]) mq_tx["BlockNumber"] = self.current_height total_out_value = 0 coinbase_v_outs = tx_detail['vout'] for v_out in coinbase_v_outs: tx_type = self.get_transaction_type(v_out) if not tx_type: continue v_out_address = self.get_vout_address(tx_type, v_out) if not v_out_address: continue v_out_value = self.get_handle_value(v_out) total_out_value += v_out_value tmp = mq_tx.copy() tmp["To"] = v_out_address tmp["Amount"] = str(v_out_value) tmp["VoutsIndex"] = v_out['n'] tmp["status"] = "true" coinbase_list.append(tmp) return coinbase_list
def parse_block(self, block_num): """ 解析区块的交易列表(支持多发送) :return: 返回待推送信息列表 """ while True: try: block = self.rpc.eth_get_block_by_number(block_num) if block is None: raise Exception(f"获取到最新高度{block_num}区块详情为None,触发异常,尝试重新获取") G_LOGGER.info( f"当前推送的区块:高度{int(block.get('number', '0x0'), 16)},哈希{block.get('hash', '')}" ) self._check_uncle(block) # 遍历交易 push_list = [] if block.get("transactions") is None: time.sleep(3) continue tx_hashes = [tx["hash"] for tx in block["transactions"]] receipts = self.rpc.eth_get_transaction_receipt( tx_hashes) if tx_hashes else [] assert len(receipts) == len(block["transactions"]) for i, tx in enumerate(block["transactions"]): _input = tx.get('input', '') _to = tx.get('to', '') _from = tx.get('from', '') multi_send_token = self.is_multi_send_token(_input) mq_tx = G_PUSH_TEMPLATE.copy() mq_tx["BlockNumber"] = hex_to_int(tx["blockNumber"]) mq_tx["Txid"] = tx["hash"] mq_tx["Type"] = self.coin_type mq_tx["From"] = tx["from"] mq_tx["Time"] = block["timestamp"] is_token_transfer = self._is_token(_input) # 交易状态 mq_tx["Valid"] = self.get_status(receipts[i], is_token_transfer) mq_tx["status"] = self.get_tx_status( receipts[i], is_token_transfer) # 手续费 gas_price = tx.get("gasPrice", "0x0") gas_used = receipts[i].get("gasUsed", "0x0") if receipts[i] else "0x0" mq_tx["Fee"] = hex(int(gas_price, 16) * int(gas_used, 16)) if is_token_transfer: if _from != _to: tx['contract'] = _to _to = self._get_token_to_address(_input) _value = self._get_token_to_value(_input) tx["to"] = _to if _to != "0x" else f"0x{'0'*40}" tx["value"] = _value if _value != "0x" else "0x0" elif multi_send_token: continue _len = self.multi_abi_len _del_0x_input = del_0x(_input) if multi_send_token == 'create': # 多发送合约创建的交易,保存到数据库存起来 data = dict() data['contract'] = tx['contract'] = tx['creates'] data['blockNumber'] = hex_to_int(tx["blockNumber"]) data['timestamp'] = block["timestamp"] data['hash'] = tx["hash"] # 保存合约 self.db.mysql.contract.insert_contract(data) G_LOGGER.info(f"发现新的多发送合约{data['contract']}并保存") continue else: token = '' address_list = amount_list = [] # 不是我们自己的多发送合约则不处理 contract = self.db.mysql.contract.get_by_contract( tx["to"]) if not contract: continue if multi_send_token == 'multisend': _multisend_abi = del_0x( self.multi_abi_deal['multisend']) result = self.parse_multi_data( _del_0x_input[len(_multisend_abi):]) address_count = hex_to_int(result[2]) address_list = result[3:3 + address_count] amount_count = hex_to_int( result[3 + address_count]) amount_list = result[4 + address_count:4 + address_count + amount_count] elif multi_send_token == 'multisendToken': _multisend_token_abi = del_0x( self.multi_abi_deal['multisendToken']) token = add_0x( self.parse_multi_data_pos( _del_0x_input, _multisend_token_abi, 0)) result = self.parse_multi_data( _del_0x_input[len(_multisend_token_abi):]) address_count = hex_to_int(result[3]) address_list = result[4:4 + address_count] amount_count = hex_to_int( result[4 + address_count]) amount_list = result[5 + address_count:5 + address_count + amount_count] for k, v in enumerate(address_list): mq_tx = mq_tx.copy() mq_tx["To"] = v mq_tx["Amount"] = int_to_hex( hex_to_int(amount_list[k])) mq_tx["Contract"] = token push_list.append(mq_tx) return push_list if tx["to"]: mq_tx["To"] = tx["to"] mq_tx["Amount"] = tx["value"] if 'contract' in tx.keys(): mq_tx["Contract"] = tx["contract"] push_list.append(mq_tx) return push_list except ForkError as ex: raise ForkError(ex.height, ex.msg) except Exception as ex: traceback.print_exc() G_LOGGER.info(f"获取块出现异常,尝试重新获取。异常原因:{str(ex)}") time.sleep(10)
def parse_block(self, block_num): """ 解析区块的交易列表 :return: 返回待推送信息列表 """ block = self.rpc.get_block(block_num) blocknumber = block.get("block_header", {}).get("raw_data", {}).get("number", -1) block_timestamp = block.get("block_header", {}).get("raw_data", {}).get("timestamp", "") txs = block.get("transactions", []) if block else [] push_list = [] for tx in txs: # ret = tx.get("ret", []) status = tx.get("ret", [])[0].get("contractRet", "") if status != "SUCCESS": continue txid = tx.get("txID", "") raw_data = tx.get("raw_data", {}) # timestamp_update = raw_data.get("timestamp", "") timestamp = block_timestamp contract = raw_data.get("contract", []) if not contract: continue else: contract = contract[0] transaction_type = contract.get("type", "") if transaction_type not in self.supported: continue transaction_data = contract.get("parameter", {}).get("value", {}) _from = b58encode_check( bytes.fromhex(transaction_data.get("owner_address", ""))).decode() # contract_type = self.contract.get(transaction_type) if transaction_type == "TransferContract": _to = b58encode_check( bytes.fromhex(transaction_data.get("to_address", ""))).decode() _amount = transaction_data.get("amount", 0) contract_type = 0 elif transaction_type == "FreezeBalanceContract": receiver_address = transaction_data.get("receiver_address", "") _to = b58encode_check(bytes.fromhex( receiver_address)).decode() if receiver_address else _from _amount = transaction_data.get("frozen_balance", 0) contract_type = "" elif transaction_type == "UnfreezeBalanceContract": receiver_address = transaction_data.get("receiver_address", "") _to = b58encode_check(bytes.fromhex( receiver_address)).decode() if receiver_address else _from _amount = transaction_data.get("frozen_balance", 0) contract_type = "" elif transaction_type == "TransferAssetContract": _from = b58encode_check( bytes.fromhex( transaction_data.get("owner_address"))).decode() _to = b58encode_check( bytes.fromhex( transaction_data.get("to_address"))).decode() asset_name = transaction_data.get("asset_name") contract_type = bytes.fromhex(asset_name).decode("utf-8") _amount = transaction_data.get("amount", 0) else: data = transaction_data.get("data") if data and data[:8] == "a9059cbb": _amount = int(data[72:], 16) _to = b58encode_check( bytes.fromhex(f"41{data[32:72]}")).decode() # after b58encode_check the legal address start with 0x41(mainnet) or 0xa0(testnet) contract_address = contract.get("parameter", {}).get( "value", {}).get("contract_address") contract_type = b58encode_check( bytes.fromhex(contract_address)).decode() else: continue mq_tx = G_PUSH_TEMPLATE.copy() mq_tx["BlockNumber"] = blocknumber mq_tx["Time"] = int(timestamp) // 1000 mq_tx["Txid"] = txid mq_tx["Type"] = "TRX" mq_tx["From"] = _from mq_tx["To"] = _to mq_tx["Amount"] = int(_amount) mq_tx["Contract"] = str(contract_type) mq_tx["Fee"] = 0 mq_tx["Action"] = self.action_map[transaction_type] mq_tx["Valid"] = True if status == "SUCCESS" else False mq_tx["status"] = "true" if status == "SUCCESS" else "false" push_list.append(mq_tx) return push_list
def parse_block(self, block_height): """ 解析区块的交易详情 :return: 返回待推送信息列表 """ accounts = self.get_xmr_accounts() block_detail = self.rpc.get_block(block_height) if not block_detail: return [] tx_ids = block_detail.get("tx_hashes", []) if not tx_ids: return [] tx_details = self.rpc.get_transactions(tx_ids) push_list = [] for tx_detail in tx_details: tx_as_json = tx_detail.get("as_json") block_timestamp = tx_detail.get("block_timestamp") tx_global_indexes = tx_detail['output_indices'] txid = tx_detail.get("tx_hash") if not txid: continue tx_json = json.loads(tx_as_json) extra_list = tx_json['extra'] extra = ''.join('{:02x}'.format(x) for x in extra_list) extra_mem = ctypes.c_char_p() extra_mem.value = extra.encode() tx_pub_key_mem = ctypes.create_string_buffer(100) tx_pub_key_result = self.monero_lib.monero_from_extra_get_tx_pub_key( extra_mem, tx_pub_key_mem) if not tx_pub_key_result: continue # G_LOGGER.info(f"height:{block_height} txid:{txid}") tx_vin = tx_json.get("vin", []) v_in_keys = [i['key'] for i in tx_vin] key_images = [i.get("k_image") for i in v_in_keys] tx_vout = tx_json.get("vout", []) rct_signatures = tx_json.get("rct_signatures") if not rct_signatures: continue ecdhInfos = rct_signatures.get("ecdhInfo") if not ecdhInfos: continue outPks = rct_signatures.get("outPk") if not outPks: continue tx_fee = rct_signatures.get("txnFee", '0') mq_tx = G_PUSH_TEMPLATE.copy() mq_tx["Txid"] = txid mq_tx["Type"] = "XMR" mq_tx["From"] = "" mq_tx["To"] = "" mq_tx["Amount"] = "" mq_tx["Memo"] = "" mq_tx["Fee"] = str(tx_fee) mq_tx["Valid"] = True mq_tx["status"] = "true" mq_tx["Time"] = block_timestamp mq_tx["BlockNumber"] = block_height mq_tx["VoutsIndex"] = 0 v_in_address = [] for key in key_images: if not key: continue redis_key = key[:6] + key[18:24] + key[-6:] addr_info = self.redis.get_and_delete(redis_key) if not addr_info: continue addr_info = json.loads(addr_info.decode()) G_LOGGER.info( f"height:{block_height}, txid:{txid}, key_image: {key}, addr_info: {addr_info}" ) v_in_address.append(addr_info) v_out_infos = [] for v_out_index, vout in enumerate(tx_vout): rct = outPks[v_out_index] global_index = tx_global_indexes[v_out_index] target = vout.get("target") if not target: continue v_out_address = target.get("key") if not v_out_address: continue random_utxo = { "global_index": str(global_index), "public_key": v_out_address, "rct": rct } self.redis.push_utxo_cache(self.random_utxo_redis_key, random_utxo) for account in accounts: address = account.get("addr") private_view_key = account.get("pr_vk") public_spend_key = account.get("pb_sk") private_view_key_mem = ctypes.c_char_p() private_view_key_mem.value = private_view_key.encode() public_spend_key_mem = ctypes.c_char_p() public_spend_key_mem.value = public_spend_key.encode() derivation_mem = ctypes.create_string_buffer(256) one_time_address_mem = ctypes.create_string_buffer(128) amount_echo_init = ecdhInfos[v_out_index] if not amount_echo_init: continue amount_echo = amount_echo_init.get("amount") if not amount_echo: continue if address and str(address).startswith("8"): additional_tx_pub_key_mem = ctypes.create_string_buffer( 100) additional_tx_pub_key_result = self.monero_lib.monero_from_extra_get_additional_tx_pub_key( extra_mem, 0, v_out_index, additional_tx_pub_key_mem) if not additional_tx_pub_key_result: der_result = self.monero_lib.monero_generate_derivation_byte32( tx_pub_key_mem, private_view_key_mem, derivation_mem) else: der_result = self.monero_lib.monero_generate_derivation_byte32( additional_tx_pub_key_mem, private_view_key_mem, derivation_mem) else: der_result = self.monero_lib.monero_generate_derivation_byte32( tx_pub_key_mem, private_view_key_mem, derivation_mem) if not der_result: continue address_result = self.monero_lib.monero_generate_one_time_public_key( derivation_mem, public_spend_key_mem, v_out_index, one_time_address_mem) if not address_result: continue one_time_addr = (one_time_address_mem.value).decode() # 判断是否命中 if v_out_address.strip() != one_time_addr.strip(): # G_LOGGER.info(f"Addr:{address}, height:{block_height}, txid:{txid}, out:{v_out_index} No") continue G_LOGGER.info( f"Addr:{address}, height:{block_height}, txid:{txid}, out:{v_out_index} Yes" ) amount_echo_mem = ctypes.c_char_p() amount_echo_mem.value = amount_echo.encode() real_amount_mem = ctypes.create_string_buffer(128) # payment_id_mem = ctypes.create_string_buffer(32) amount_result = self.monero_lib.monero_decrypt_amount( derivation_mem, v_out_index, amount_echo_mem, 0, real_amount_mem) amount = '0' if amount_result: amount = (real_amount_mem.value).decode() amount = int("0x" + amount, 0) mq_tx["Amount"] = amount # 暂时不推送memo信息,子地址不需要memo # pyamentid_result = self.monero_lib.monero_from_extra_get_payment_id(extra_mem, derivation_mem, payment_id_mem) # payment_id = '0' # if pyamentid_result: # payment_id = (payment_id_mem.value).decode() # tmp_tx["Memo"] = payment_id tmp_tx = mq_tx.copy() tmp_tx["Memo"] = "" tmp_tx["VoutsIndex"] = v_out_index tmp_tx["To"] = address v_out_infos.append(tmp_tx) break # if (not v_in_address) or (not v_out_infos): # continue v_in_length = len(v_in_address) v_out_length = len(v_out_infos) G_LOGGER.info( f"height:{block_height}, txid:{txid}, v_in_length:{v_in_length}, v_out_length:{v_out_length}" ) G_LOGGER.info(f"v_in_address: {v_in_address}") G_LOGGER.info(f"v_out_infos: {v_out_infos}") v_out_sort_infos = sorted(v_out_infos, key=lambda x: x["VoutsIndex"]) max_len = max(v_out_length, v_in_length) for num in range(max_len): v_in_idx = num if num < v_in_length else -1 v_out_idx = num if num < v_out_length else -1 tx_tp = v_out_sort_infos[ v_out_idx] if v_out_length - 1 >= v_out_idx else v_out_sort_infos[ -1] tmp = tx_tp.copy() tmp['From'] = v_in_address[v_in_idx] if v_in_address else '' push_list.append(tmp) return push_list
def parse_block(self, block_num): """ 解析区块的交易详情 :return: 返回待推送信息列表 """ t1 = time.time() push_list = [] node, block = self.http.get_block(block_num) t2 = time.time() if isinstance(block, str): # 出现大块,比如110588067,api.eossweden.org超级节点偶发性返回部分数据,不能被json反序列化,需要多次重试才能获得结果,造成消息积压 # eos.greymass.com超级节点可以正确拿到数据,但是访问频率受限,所以异常以后要重试获取数据 G_LOGGER.info( "eos_pass, block_num={}, node={}, lenght={}, head={}, tail={}". format(block_num, node, len(block), block[:38], block[-30:])) # self.redis.client.rpush("eos_pass_block", block_num) # return push_list timestamp = str(date_to_timestamp(block['timestamp'].split(".")[0])) txs = block.get("transactions", []) for tx in txs: tx_status = tx.get("status") if (not tx_status) or (tx_status not in ("executed")): continue trx = tx.get("trx", "") if not isinstance(trx, dict): continue tx_id = trx['id'] transaction = trx["transaction"] acts = transaction.get('actions', []) act_index = 0 get_amount_and_symbol = self.get_amount_and_symbol for act in acts: account, name, Contract = act.get('account'), act['name'], "" # if name in self.SUPPORTED and self.action_account.get(name) == account: if name in self.SUPPORTED: data = act['data'] if not isinstance(data, dict): continue mq_tx = G_PUSH_TEMPLATE.copy() if name == "delegatebw" and self.action_account.get( name) != account: from_addr = account to_addr = data.get("to") amount, symbol = get_amount_and_symbol( "other_delegatebw", data) elif name == "undelegatebw" and self.action_account.get( name) != account: from_addr = data.get("from") to_addr = account amount, symbol = get_amount_and_symbol( "other_undelegatebw", data) else: from_addr = data.get(self.action_convert[name]['from']) to_addr = data.get(self.action_convert[name]['to']) if not from_addr or not to_addr: continue amount, symbol = get_amount_and_symbol(name, data) # 无金额或者金额小于0.0001, 略过 if (not amount) or (float(amount) <= 0.0001): continue if account and str( account) == "eosio.token" and symbol in ["EOS"]: Contract = "eosio.token" else: Contract = str(account) + "|" + str(symbol).upper() memo_info = data.get("memo", "") mq_tx["BlockNumber"] = block_num mq_tx["Time"] = timestamp mq_tx["Txid"] = tx_id mq_tx["Type"] = "EOS" mq_tx['From'] = from_addr mq_tx['To'] = to_addr mq_tx['Amount'] = amount mq_tx["Action"] = name mq_tx['Contract'] = Contract mq_tx["VoutsIndex"] = act_index mq_tx["Memo"] = memo_info mq_tx["Valid"] = True mq_tx["status"] = "true" push_list.append(mq_tx) act_index += 1 t3 = time.time() http_time = int((t2 - t1) * 1000) / 1000 parse_time = int((t3 - t2) * 1000) / 1000 G_LOGGER.info( "eos_parse_block, block_num={}, length={}, node={}, 网络耗时={}, 处理耗时={}" .format(block_num, len(push_list), node, http_time, parse_time)) return push_list
def parse_tx(self, tx, push_cache=None): """ 解析交易详情 :param tx: 交易详情 :return: """ if not isinstance(tx, dict): return [] mq_tx = G_PUSH_TEMPLATE.copy() mq_tx["Txid"] = del_0x(tx["txid"]) if self.rollback and push_cache and (mq_tx["Txid"] in push_cache): return [] mq_tx["Type"] = "NEO" push_list = [] # 待推送交易列表 vins = tx["vin"] vin_address = [] tx_index = 0 # 同笔交易中待推送的交易索引 for vin in vins: tx_id = vin["txid"] vout = vin["vout"] vin_transactions = self.rpc.get_transaction(tx_id) for tran in vin_transactions["vout"]: address = tran["address"] n = tran["n"] if vout == n: vin_address.append(address) break vouts = tx["vout"] for i, vout in enumerate(vouts): mq_tx = mq_tx.copy() mq_tx["From"] = vin_address[0] if len(vin_address) > 0 else "" mq_tx["To"] = vout["address"] mq_tx["status"] = "true" mq_tx["Amount"] = vout["value"] mq_tx["Contract"] = del_0x(vout["asset"]) if vout["address"] not in vin_address: mq_tx["TxIndex"] = tx_index push_list.append(mq_tx) tx_index += 1 # 如果是NEP5资产,还需要调用getapplicationlog查询交易详情 if tx["type"] == "InvocationTransaction": result = self.rpc.get_application_log(tx["txid"]) if result: result = result.get("executions") result = result[0] if result and len(result) > 0 else {} vmstate = result.get("vmstate") notifications = result.get("notifications", []) if vmstate and vmstate.find("FAULT") == -1: for ns in notifications: contract = ns.get("contract") state_type = ns["state"]["type"] state_value = ns["state"]["value"] if state_type == "Array": event_type = state_value[0]["type"] event_value = state_value[0]["value"] if event_type == "ByteArray": event_value = bytes.fromhex(event_value).decode() if event_value == "transfer": from_type = state_value[1]['type'] from_value = state_value[1]['value'] to_type = state_value[2]["type"] to_value = state_value[2]["value"] amount_type = state_value[3]["type"] amount_value = state_value[3]["value"] if from_type == 'ByteArray': from_value = b58encode_check(b'\x17' + hex_to_bytes(from_value)).decode() if to_type == "ByteArray": to_value = b58encode_check(b"\x17" + hex_to_bytes(to_value)).decode() if amount_type == "ByteArray": amount_value = str(hex_to_int(size_convert(amount_value))) mq_tx = mq_tx.copy() mq_tx["From"] = from_value mq_tx["To"] = to_value mq_tx["status"] = "true" mq_tx["Amount"] = amount_value mq_tx["Contract"] = del_0x(contract) if to_value not in vin_address: mq_tx["TxIndex"] = tx_index push_list.append(mq_tx) tx_index += 1 return push_list
def parse_normal_tx(self, tx_details, mempool_tx=False, push_cache=None): """ 解析封装普通交易 """ push_list = [] total_fees = 0 for tx_detail in tx_details: mq_tx = G_PUSH_TEMPLATE.copy() mq_tx["Txid"] = tx_detail['txid'] if self.rollback and push_cache and (mq_tx["Txid"] in push_cache): continue mq_tx["Type"] = self.coin_type mq_tx["status"] = "true" if mempool_tx: mq_tx["Time"] = int(time.time()) mq_tx["BlockNumber"] = 0 else: mq_tx["Time"] = int(tx_detail["time"]) mq_tx["BlockNumber"] = self.current_height v_in_list = tx_detail['vin'] v_in_addresses = [] v_in_infos = [] for v_in in v_in_list: scriptSig = v_in.get('scriptSig') txid = v_in.get('txid') if txid: v_in_infos.append(dict(txid=txid, index=v_in.get('vout', 0))) if not scriptSig: continue v_in_address = self.get_address_from_scriptsig(scriptSig['hex']) if not v_in_address: continue v_in_addresses.append(v_in_address) v_out_list = tx_detail['vout'] out_infos = [] total_out_value = 0 for v_out in v_out_list: info = {} v_out_value = self.get_handle_value(v_out) total_out_value += v_out_value tx_type = self.get_transaction_type(v_out) if not tx_type: continue v_out_address = self.get_vout_address(tx_type, v_out) if not v_out_address: continue v_out_index = v_out['n'] if v_out_address in v_in_addresses: # 标记找零 info['Charge'] = True info['To'] = v_out_address info['Amount'] = str(v_out_value) info["VoutsIndex"] = v_out_index out_infos.append(info) # 计算交易手续费 tx_fee = self.get_tx_fee(v_in_infos, total_out_value) total_fees += tx_fee # 到此只缺FROM if not out_infos: continue v_in_length = len(v_in_addresses) v_out_length = len(out_infos) v_out_infos = sorted(out_infos, key=lambda x: x["VoutsIndex"]) max_len = max(v_out_length, v_in_length) for num in range(max_len): v_in_idx = num if num < v_in_length else -1 v_out_idx = num if num < v_out_length else -1 tx_tp = mq_tx.copy() tx_tp.update(v_out_infos[v_out_idx]) if v_out_length - 1 >= v_out_idx else tx_tp.update(v_out_infos[-1]) tx_tp['From'] = v_in_addresses[v_in_idx] if v_in_addresses else '' tx_tp['Fee'] = str(tx_fee) push_list.append(tx_tp) return push_list, total_fees
def parse_tx(self, tx_ids): """ 解析交易详情 :param tx_ids: 交易ids :return: """ push_list = [] for tx_id in tx_ids: # 根据tx_id查询交易详情 tx = self.rpc.get_transaction(tx_id) # 获取operations下的第一个type字段(交易类型) tx_type = tx[1]["trx"]["operations"][0]["type"] asset_id = tx[1]["trx"]["alp_inport_asset"]["asset_id"] if tx_type == "transaction_op_type": # 合约交易 tx = self.rpc.get_pretty_contract_transaction(tx_id) elif asset_id == 0: """ 已知tx_type withdraw_op_type: ACT Transfer (1f279cf35e8ec5cc785642bf06c57b1ba33a645b) deposit_op_type: ACT Transfer define_slate_op_type: ACT Transfer (09749dbf3aba3a54a5a5ac6f1723869becaf2b32) withdraw_pay_op_type: Agent Gets Paid (d81726e747a4e7c14ef12839cfb0dbd68117757f) register_account_op_type: Account Registration (f2de809451b084e8d80758efa7949d7ec8e80036) """ # ACT交易 tx = self.rpc.get_pretty_transaction(tx_id) else: G_LOGGER.info( f"unkown asset_id:{str(id)}, tx_type:{tx_type}, hash:{tx_id}" ) continue tx["tx_type"] = tx_type mq_tx = G_PUSH_TEMPLATE.copy() mq_tx["Type"] = "ACT" mq_tx["BlockNumber"] = tx["block_num"] mq_tx["Time"] = date_to_timestamp(tx["timestamp"]) mq_tx["TxIndex"] = 0 # 只要转账的合约交易 if tx["tx_type"] == "transaction_op_type": reserved = tx["reserved"] if reserved and tx["reserved"][0] == "transfer_to": # 区块链浏览器普遍使用该交易ID mq_tx["Txid"] = tx["orig_trx_id"] # 发起转账的地址 mq_tx["From"] = tx['to_contract_ledger_entry'][ 'from_account'] # 转账金额 mq_tx["Amount"] = tx["to_contract_ledger_entry"]["amount"][ "amount"] # 合约地址 mq_tx["Contract"] = tx["to_contract_ledger_entry"][ "to_account"] # 合约执行函数 if reserved[1].find("|") > -1: # 合约转账金额 mq_tx["Amount"] = reserved[1].split("|")[1] # 实际合约转账地址 mq_tx["To"] = reserved[1].split("|")[0] else: mq_tx["To"] = reserved[1] else: # 交易id mq_tx["Txid"] = tx["trx_id"] # 发起转账的地址 mq_tx["From"] = tx['ledger_entries'][0]['from_account'] # 接收转账的地址 mq_tx["To"] = tx["ledger_entries"][0]["to_account"] # 转账金额 mq_tx["Amount"] = tx["ledger_entries"][0]["amount"]["amount"] mq_tx["status"] = "true" push_list.append(mq_tx) return push_list
def parse_block(self, block_num): """ 解析区块的交易列表 """ try: block = self.rpc.eth_get_block_by_number(block_num) if block is None: raise Exception(f"获取区块高度{block_num}区块详情为None,触发异常,尝试重新获取") G_LOGGER.info( f"当前推送的区块:高度{int(block.get('number', '0x0'), 16)},哈希{block.get('hash', '')}" ) self._check_uncle(block) # 遍历交易 push_list = [] if block.get("transactions") is None: raise Exception( f"获取区块高度{block_num}区块transactions为None,触发异常,尝试重新获取") tx_hashes = [tx["hash"] for tx in block["transactions"]] receipts = self.rpc.eth_get_transaction_receipt( tx_hashes) if tx_hashes else [] assert len(receipts) == len(block["transactions"]) for i, tx in enumerate(block["transactions"]): _input = tx.get('input', '') _to = tx.get('to', '') _from = tx.get('from', '') is_token_transfer = self._is_token(_input) if is_token_transfer: if _from != _to: tx['contract'] = _to _to = self._get_token_to_address(_input) _value = self._get_token_to_value(_input) tx["to"] = _to if _to != "0x" else f"0x{'0'*40}" tx["value"] = _value if _value != "0x" else "0x0" mq_tx = G_PUSH_TEMPLATE.copy() mq_tx["BlockNumber"] = int(tx["blockNumber"], 16) mq_tx["Txid"] = tx["hash"] mq_tx["Type"] = self.coin_type mq_tx["From"] = tx["from"] # 排除to为空的 if not tx["to"]: continue mq_tx["To"] = tx["to"] mq_tx["Time"] = block["timestamp"] mq_tx["Amount"] = tx["value"] # 手续费 gas_price = tx.get("gasPrice", "0x0") gas_used = receipts[i].get("gasUsed", "0x0") if receipts[i] else "0x0" mq_tx["Fee"] = hex(int(gas_price, 16) * int(gas_used, 16)) # 交易状态 mq_tx["Valid"] = self.get_status(receipts[i], is_token_transfer) mq_tx["status"] = self.get_tx_status(receipts[i], is_token_transfer) if 'contract' in tx.keys(): mq_tx["Contract"] = tx["contract"] push_list.append(mq_tx) # 缓存已推送的区块高度和hash self.db.redis.save_cache_block(hex_to_int(block["number"]), block["hash"]) return push_list except ForkError as ex: raise ForkError(ex.height, ex.msg) except Exception as e: traceback.print_exc() G_LOGGER.info(f"获取块出现异常,尝试重新获取。异常原因:{str(e)}")