Beispiel #1
0
 def _check_uncle(self, block):
     """
     检查当前区块的叔块,看是否需要回滚
     """
     # 检查当前传入的区块高度和最新的区块高度是否一致,相差大于100说明当前是处理比较老的区块了则不用检查叔块
     current_height = hex_to_int(block["number"])
     newest_height = self.rpc.eth_block_number()
     if newest_height - current_height > 100:
         return False
     # 没有叔块也不用检查叔块
     uncles = block["uncles"]
     if not uncles:
         return False
     G_LOGGER.info(f"区块{current_height}发现叔块:{','.join(uncles)}")
     # 获取叔块对应最小区块高度的hash
     uncle_height = []
     for uncle in uncles:
         uncle_block = self.rpc.eth_get_block_by_hash(uncle,
                                                      tx_detail=False)
         if uncle_block:
             uncle_height.append(hex_to_int(uncle_block["number"]))
     if uncle_height:
         min_height = min(uncle_height)
         # 查询缓存的区块中是否有uncle hash
         push_block_hash = self.db.redis.get_cache_block(min_height)
         # 已经推送的该高度对应的区块hash如果不在uncles中,说明之前推送的区块就是正确的区块,不用继续处理
         if push_block_hash not in uncles:
             return False
         raise ForkError(min_height, f"{min_height}高度同步过程中出现临时分叉")
Beispiel #2
0
 def eth_block_number(self):
     """
     获取最新高度
     :return:
     """
     method = "eth_blockNumber"
     return hex_to_int(self._single_post(method))
Beispiel #3
0
    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)
Beispiel #4
0
 def parse_multi_data_pos(self, _input, _abi, pos):
     _len = self.multi_abi_len
     return int_to_hex(
         hex_to_int(
             add_0x(_input[len(_abi) + _len * pos:len(_abi) + _len *
                           (pos + 1)])))
Beispiel #5
0
 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)}")
Beispiel #6
0
 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