def resolver_receipt(cls, receipt):
     block_height = digit.hex_to_int(receipt['blockNumber'])
     block_hash = receipt['blockHash']
     tx_hash = receipt['transactionHash']
     sender = receipt['from']
     receiver = receipt['to']
     contract = receipt['contractAddress']
     status = digit.hex_to_int(receipt['status'])
     gas_used = digit.hex_to_int(receipt['gasUsed'])
     return TxReceipt(block_height, block_hash, tx_hash, sender, receiver,
                      contract, status, gas_used)
    def resolver_block(cls, block, detail=True):
        """不包含交易"""
        block_height = digit.hex_to_int(block['number'])
        block_hash = block['hash']
        block_time = digit.hex_to_int(block['timestamp'])
        if detail:
            transactions = [
                cls.resolver_transaction(tx) for tx in block['transactions']
            ]
        else:
            transactions = []

        return Block(height=block_height,
                     hash=block_hash,
                     timestamp=block_time,
                     transactions=transactions)
Exemple #3
0
 def get_wallet_balance(self,
                        contract=None,
                        block_height='latest',
                        *,
                        exclude: list = None):
     if exclude is None:
         exclude = set()
     else:
         exclude = set(exclude)
     addresses = self.personal_list_accounts()
     if isinstance(addresses, list):
         addresses = set(addresses)
     else:
         self.logger.warning(
             "personal_listAccounts address list not is list, it's {}".
             format(addresses))
         return 0
     check_addresses = list((exclude ^ addresses) & addresses)
     balance = 0
     offset = 100
     for s in range(0, len(check_addresses), offset):
         batch_address = check_addresses[s:s + offset]
         balances = self.get_balance(batch_address, contract, block_height)
         for _ in balances:
             if _:
                 balance += digit.hex_to_int(_)
             else:
                 self.logger.error("地址获取余额错误: {}".format(_))
     return balance
Exemple #4
0
 def get_block_height(self):
     sync_method = 'eth_syncing'
     number_method = 'eth_blockNumber'
     sync, number = self._diff_post([sync_method, number_method],
                                    [None, None])
     if sync:
         block_height = BlockHeight(digit.hex_to_int(sync['currentBlock']),
                                    digit.hex_to_int(sync['highestBlock']))
     elif number:
         block_height = BlockHeight(digit.hex_to_int(number),
                                    digit.hex_to_int(number))
     else:
         self.logger.error(
             'get_block_height 请求, 两个方式均未获取到正常拿. sync: {} number: {}'.
             format(sync, number))
         raise JsonRpcError(
             code=1,
             message='get_block_height 请求, 两个方式均未获取到正常拿. sync: {} number: {}'
             .format(sync, number))
     return block_height
 def resolver_transaction(cls, tx):
     """
     只支持标准 erc20 与 eth交易, 其他交易暂时不支持
     :param tx:
     :return:
     """
     block_height = digit.hex_to_int(tx['blockNumber'])
     block_hash = tx['blockHash']
     tx_hash = tx['hash']
     sender = cls.get_address(tx['from'])
     receiver = cls.get_address(tx['to'])
     value = digit.hex_to_int(tx['value'])
     gas = digit.hex_to_int(tx['gas'])
     gas_price = digit.hex_to_int(tx['gasPrice'])
     nonce = digit.hex_to_int(tx['nonce'])
     status = TxStatusEnum.UNKNOWN.value
     data = None
     contract = None
     # 去掉开头 0x
     if tx['input'].startswith(cls.TRANSFER_ABI):
         _input = tx['input']
         data = _input
         contract = tx['to']
         abi_length = len(cls.TRANSFER_ABI)
         abi, address, amount = (_input[0:abi_length],
                                 _input[abi_length:abi_length +
                                        cls.ADDRESS_FULL_LENGTH],
                                 _input[abi_length +
                                        cls.ADDRESS_FULL_LENGTH:])
         receiver = cls.get_address(address[cls.ADDRESS_FILL_LENGTH:])
         value = digit.hex_to_int(amount)
     return Tx(block_height, block_hash, tx_hash, sender, receiver, value,
               gas, gas_price, nonce, data, contract, status)
def get_balance_by_address(address: str):
    """这里只能查单个地址的余额"""
    rpc = RpcConfig.get_rpc()
    if rpc is None:
        return ResponseObject.error(**out_data_missing)
    coin = Coin.get_erc20_usdt_coin()
    if coin is None:
        return ResponseObject.error(**coin_missing)
    try:
        balance_hex = rpc.get_balance(address, coin.contract)
    except Exception as e:
        return ResponseObject.error(**rpc_service_error)
    if balance_hex:
        balance = safe_math.divided(hex_to_int(balance_hex),
                                    safe_math.e_calc(coin.decimal))
    else:
        return ResponseObject.error(**balance_rpc_error)

    return ResponseObject.success(data=balance.to_eng_string())
Exemple #7
0
 def send_transaction(self,
                      sender: str,
                      receiver: str,
                      value: int,
                      passphrase: str,
                      gas: int = None,
                      gas_price: int = None,
                      fee: int = None,
                      contract: str = None,
                      comment: str = None,
                      **kwargs):
     """目前只支持单交易发送, 暂时没有想到更好的数据结构"""
     method = 'personal_signAndSendTransaction'
     if gas is None:
         gas = 21000
     if gas_price is None:
         gas_price = digit.hex_to_int(self.gas_price())
     params = EthereumResolver.get_transfer_body(sender, receiver, int(gas),
                                                 int(gas_price), value,
                                                 contract)
     payload = self.get_params(params, passphrase)
     return self._single_post(method, payload, ignore_err=False)
 def parse_abi_total(cls, total_abi):
     return digit.hex_to_int(total_abi)
 def parse_abi_decimal(cls, decimal_abi):
     return digit.hex_to_int(decimal_abi)
Exemple #10
0
    def scan(self):
        self.block_info = self.rpc.get_block_height()
        self.newest_height = self.block_info.current_height
        self.highest_height = self.block_info.highest_height
        # 延迟扫 SCAN_DELAY_NUMBER 个块
        need_to_height = self.newest_height - self.SCAN_DELAY_NUMBER
        self.logger.info('起始扫块高度:{} 最新高度:{} 需要同步:{}'.format(
            self.current_scan_height, self.newest_height,
            need_to_height - self.current_scan_height))
        while self.current_scan_height < need_to_height:
            self.logger.info('当前已扫块高度:{} 最新高度:{} 需要同步:{}  节点最高高度:{}'.format(
                self.current_scan_height, self.newest_height,
                need_to_height - self.current_scan_height,
                self.highest_height))

            for height in range(self.current_scan_height, need_to_height,
                                self.SCAN_HEIGHT_NUMBER):
                # 分批处理, 一次处理 SCAN_HEIGHT_NUMBER 或 剩余要处理的块
                block_batch = min(self.SCAN_HEIGHT_NUMBER,
                                  need_to_height - self.current_scan_height)

                blocks = self.rpc.get_block_by_number([
                    digit.int_to_hex(height)
                    for height in range(height, height + block_batch)
                ])
                save_tx_count = 0
                with runtime.app.app_context():
                    # 一次处理一批
                    session = db.session()
                    try:
                        for block in blocks:
                            if block is None:
                                return
                            block_height = digit.hex_to_int(block['number'])
                            block_hash = block['hash']
                            block_timestamp = digit.hex_to_int(
                                block['timestamp'])
                            block_time = datetime.fromtimestamp(
                                block_timestamp)

                            session.begin(subtransactions=True)
                            db_block = Block(height=block_height,
                                             block_hash=block_hash,
                                             block_time=block_time)
                            session.add(db_block)
                            session.commit()

                            for transaction in block.get('transactions', []):
                                tx = EthereumResolver.resolver_transaction(
                                    transaction)
                                if tx.sender in runtime.project_address:
                                    # 提现的暂时不要
                                    continue
                                if tx.receiver in runtime.project_address:
                                    receipt_raw_tx = self.rpc.get_transaction_receipt(
                                        tx.tx_hash)
                                    if tx.contract:
                                        coin = runtime.coins.get(tx.contract)
                                    else:
                                        coin = runtime.coins.get(
                                            self.COIN_NAME)
                                    if coin is None:
                                        continue

                                    if receipt_raw_tx:
                                        receipt_tx = EthereumResolver.resolver_receipt(
                                            receipt_raw_tx)
                                    else:
                                        self.logger.error(
                                            '请求 {} receipt 错误, 重新处理')
                                        raise
                                    tx.status = receipt_tx.status
                                    # session.begin(subtransactions=True)
                                    # db_tx = Transaction(block_id=db_block.id, coin_id=coin['coin_id'],
                                    #                     tx_hash=tx.tx_hash, height=db_block.height,
                                    #                     block_time=block_timestamp,
                                    #                     amount=tx.value, sender=tx.sender, receiver=tx.receiver,
                                    #                     gas=tx.gas, gas_price=tx.gas_price,
                                    #                     is_send=SendEnum.NOT_PUSH.value,
                                    #                     fee=receipt_tx.gas_used * tx.gas_price,
                                    #                     contract=tx.contract, status=receipt_tx.status,
                                    #                     type=TxTypeEnum.DEPOSIT.value)
                                    Transaction.add_transaction_or_update(
                                        block_id=db_block.id,
                                        coin_id=coin['coin_id'],
                                        tx_hash=tx.tx_hash,
                                        height=db_block.height,
                                        block_time=block_timestamp,
                                        amount=tx.value,
                                        sender=tx.sender,
                                        receiver=tx.receiver,
                                        gas=tx.gas,
                                        gas_price=tx.gas_price,
                                        is_send=SendEnum.NOT_PUSH.value,
                                        fee=receipt_tx.gas_used * tx.gas_price,
                                        contract=tx.contract,
                                        status=receipt_tx.status,
                                        type=TxTypeEnum.DEPOSIT.value,
                                        session=session,
                                        commit=False)
                                    save_tx_count += 1
                                    # session.add(db_tx)
                                    # session.commit()
                                    # 添加推送信息

                        session.query(SyncConfig).filter(
                            SyncConfig.id == self.config_id).update({
                                'synced_height':
                                height + block_batch,
                                'highest_height':
                                self.highest_height
                            })
                        self.current_scan_height = height + block_batch
                        session.commit()
                        self.logger.info("本次同步高度为:{} -- {}, 保存交易: {} 笔".format(
                            height, height + block_batch, save_tx_count))

                    except Exception as e:
                        self.logger.error('同步块出现异常, 事务回滚. {}'.format(e))
                        session.rollback()
                        return
        self.logger.info("扫链结束, 本次同步")
Exemple #11
0
    def render(self):
        self.logger.info('开始补充手续费进程')
        for pid, p in self.project_addresses.items():
            project_address = p['address']
            for ck, coin in runtime.coins.items():
                if coin['coin_name'] == self.COIN_NAME:
                    self.logger.warning('币种名称为: {}, 不需要补充手续费!'.format(
                        coin['coin_name']))
                    continue
                contract = coin['contract']
                offset, count = 0, self.BALANCE_QUERY_NUMBER
                for s in range(0, len(project_address), count):
                    addresses = project_address[offset:count]
                    balances = self.rpc.get_balance(addresses, contract)
                    balances_sum = sum([
                        digit.hex_to_int(balance) for balance in balances
                        if balance
                    ])
                    if not balances_sum:
                        self.logger.info("本 {} 个地址无额外, 不需要补充手续费".format(
                            len(addresses)))
                        continue
                    for idx, balance in enumerate(balances):
                        balance_int = hex_to_int(balance)
                        if not balance_int:
                            continue
                        balance_eth_int = hex_to_int(
                            self.rpc.get_balance(address=addresses[idx]))

                        if hasattr(config, 'GAS'):
                            gas = config.GAS
                        else:
                            gas = self.rpc.get_smart_fee(contract=contract)
                        if hasattr(config, 'GAS_PRICE'):
                            gas_price = config.GAS_PRICE
                        else:
                            gas_price = self.rpc.gas_price()
                        if gas is None:
                            self.logger.info("未找到合适 gas . {}".format(gas))
                            continue
                        if gas_price is None:
                            self.logger.info(
                                "未找到合适 gas_price . {}".format(gas_price))
                            continue
                        gas, gas_price = hex_to_int(gas), hex_to_int(gas_price)
                        fee = gas * gas_price

                        if balance_eth_int > fee:
                            self.logger.info("地址: {} 手续费足够, 不需要补充手续费".format(
                                addresses[idx]))
                            continue

                        render_amount = int(config.COLLECTION_MIN_ETH * 1e18)

                        tx_hash = self.rpc.send_transaction(
                            sender=config.RENDER_ADDRESS,
                            receiver=addresses[idx],
                            value=render_amount,
                            passphrase=p['passphrase'],
                            gas=gas,
                            gas_price=gas_price,
                            contract=contract)
                        if not tx_hash:
                            self.logger.error("给地址: {} 补充手续费失败".format(
                                addresses[idx]))
                            continue
                        self.logger.info("给地址: {} 补充手续费成功".format(
                            addresses[idx]))
                        with runtime.app.app_context():
                            saved = Transaction.add_transaction(
                                coin_id=coin['coin_id'],
                                tx_hash=tx_hash,
                                block_time=datetime.now().timestamp(),
                                sender=config.RENDER_ADDRESS,
                                receiver=addresses[idx],
                                amount=render_amount,
                                status=TxStatusEnum.UNKNOWN.value,
                                type=TxTypeEnum.RENDER.value,
                                block_id=-1,
                                height=-1,
                                gas=gas,
                                gas_price=gas_price,
                                contract=contract)
        self.logger.info('结束补充手续费进程')
Exemple #12
0
    def collection(self):
        self.logger.info('开始归集进程')
        for pid, p in self.project_addresses.items():
            project_address = p['address']
            for ck, coin in runtime.coins.items():
                contract = coin['contract']
                offset, count = 0, self.BALANCE_QUERY_NUMBER
                for s in range(0, len(project_address), count):
                    addresses = project_address[offset:count]
                    balances = self.rpc.get_balance(addresses, contract)
                    balances_sum = sum([
                        digit.hex_to_int(balance) for balance in balances
                        if balance
                    ])
                    if not balances_sum:
                        self.logger.info("本 {} 个地址不需要归集".format(
                            len(addresses)))
                        continue
                    for idx, balance in enumerate(balances):
                        balance_int = hex_to_int(balance)
                        if not balance_int:
                            continue
                        if hasattr(config, 'GAS'):
                            gas = config.GAS
                        else:
                            gas = self.rpc.get_smart_fee(contract=contract)

                        if hasattr(config, 'GAS_PRICE'):
                            gas_price = config.GAS_PRICE
                        else:
                            gas_price = self.rpc.gas_price()

                        if gas is None:
                            self.logger.info("未找到合适 gas . {}".format(gas))
                            continue
                        if gas_price is None:
                            self.logger.info(
                                "未找到合适 gas_price . {}".format(gas_price))
                            continue

                        gas, gas_price = hex_to_int(gas), hex_to_int(gas_price)
                        fee = gas * gas_price
                        if coin['symbol'] == 'ETH':
                            send_value = max(
                                balance_int -
                                max(int(config.COLLECTION_MIN_ETH * 1e18),
                                    fee), 0)
                        else:
                            send_value = balance_int

                        if send_value <= 0:
                            self.logger.info("地址 {} 需要归集金额低于 0".format(
                                addresses[idx]))
                            continue

                        tx_hash = self.rpc.send_transaction(
                            sender=addresses[idx],
                            receiver=config.COLLECTION_ADDRESS,
                            value=send_value,
                            passphrase=p['passphrase'],
                            gas=gas,
                            gas_price=gas_price,
                            contract=contract)
                        with runtime.app.app_context():
                            saved = Transaction.add_transaction(
                                coin_id=coin['coin_id'],
                                tx_hash=tx_hash,
                                block_time=datetime.now().timestamp(),
                                sender=addresses[idx],
                                receiver=config.COLLECTION_ADDRESS,
                                amount=send_value,
                                status=TxStatusEnum.UNKNOWN.value,
                                type=TxTypeEnum.COLLECTION.value,
                                block_id=-1,
                                height=-1,
                                gas=gas,
                                gas_price=gas_price,
                                contract=contract)

                    offset += count
        self.logger.info("结束归集进程")