def block_height():
    rpc = RpcConfig.get_rpc()
    if rpc is None:
        return ResponseObject.error(**out_data_missing)
    try:
        node_height = rpc.get_block_height().to_dict()
    except Exception as e:
        return ResponseObject.error(**rpc_service_error)
    return ResponseObject.success(data=node_height)
def get_secret(project_id, coin_name) -> (bool, object):
    project_secret = runtime.rsa_secret.get(project_id)
    if project_secret is None:
        return False, ResponseObject.error(**project_passphrase_miss)

    coin_secret = project_secret.get(coin_name)
    if coin_secret is None:
        return False, ResponseObject.error(**coin_passphrase_miss)

    secret = coin_secret.get('secret')
    crypto = coin_secret.get('crypto')
    if not all([secret, crypto]):
        return False, ResponseObject.error(**coin_passphrase_miss)
    return True, secret
def set_passphrase(project_id, coin_name, secret):
    project_coin = ProjectCoin.get_pro_coin_by_pid_cname(project_id, coin_name)
    if not project_coin:
        return False, ResponseObject.error(**sign_rsa_not_found)

    is_valid, result, passphrase, rpc = check_passphrase(project_coin, secret)
    if not is_valid:
        return result
    crypto = result

    coin_secret_map = {coin_name: {'secret': secret, 'crypto': crypto}}
    if runtime.rsa_secret.get(project_id):
        runtime.rsa_secret[project_id].update(coin_secret_map)
    else:
        runtime.rsa_secret[project_id] = coin_secret_map
    return ResponseObject.success(data=True)
Example #4
0
def check_address():
    """查询某地址是否归属我方钱包中

    @@@
    #### 签名
    [v] 必须

    #### args

    | args | nullable | type | default | remark |
    |--------|--------|--------|--------|--------|
    | address | false | string |  | 查询地址 |

    #### return
    | args | nullable | type | remark |
    |--------|--------|--------|--------|
    | isSet | false | bool | 是否为我方地址 |
    - ##### json
    > "100.00"
    @@@
    :return:
    """
    _json = get_json(request)
    project_id = request.project_id

    if _json is not None:
        address = _json.get("address")
        if address is not None:
            return make_response(jsonify(is_mine_address(project_id, address)))

    result = ResponseObject.raise_args_error()
    return make_response(jsonify(result))
Example #5
0
def get_balance():
    """获取地址余额
    查询某地址余额

    @@@
    #### 签名
    [v] 必须

    #### args

    | args | nullable | type | default | remark |
    |--------|--------|--------|--------|--------|
    | address | false | string |  | 查询地址 |

    #### return
    | args | nullable | type | remark |
    |--------|--------|--------|--------|
    | balance | false | string | 余额 |
    - ##### json
    > "100.00"
    @@@
    :return:
    """
    _json = get_json(request)

    if _json is not None:
        address = _json.get("address")
        if address is not None:
            return make_response(jsonify(get_balance_by_address(address)))

    result = ResponseObject.raise_args_error()

    return make_response(jsonify(result))
Example #6
0
def send_transaction():
    """发送交易
    向指定账户发送交易

    发送交易之后不一定就会成功.
    业务解决:
            1. 等待用户确认
    技术解决:
            1. 需要使用 getTransactoon 确认
            2. 等待推送

    ******* 这里是重要, 一定要看 *******
    该接口包含的 actionId 字段为校验幂等字段.
    该字段必须唯一, 且如果交易存在的情况下, 不会重复发送交易.
    ******* 这里是重要, 一定要看 *******

    `该接口必须要在先设置密码后才能使用`
    @@@
    #### 签名
    [v] 必须

    #### args
    暂不需要入参

    | args | nullable | type | default | remark |
    |--------|--------|--------|--------|--------|
    | actionId | false | string | | 执行码 |
    | sender | false | string |  | 发送地址 |
    | receiver | false | string | | 接口地址 |
    | amount | false | string | | 发送金额 |
    | coin | true | string | Ethereum | 币种, 默认使用ETH ERC20 的 USDT, 当前该选项更改暂时无效 |
    | contract | true | string | null | 合约地址, 默认使用USDT, 若指定时指使用指定 |

    #### return
    | args | nullable | type | remark |
    |--------|--------|--------|--------|
    | txHash | false | string | 交易流水号 |
    - ##### json
    > "100.00"
    @@@
    """
    _json = get_json(request)
    project_id = request.project_id

    if _json is not None:
        action_id = _json.get("actionId")
        sender = _json.get("address")
        receiver = _json.get("receiver")
        amount = _json.get("amount")
        coin = _json.get("coin", 'Ethereum')
        contract = _json.get("contract")
        if None not in [action_id, sender, receiver, amount]:
            return make_response(
                jsonify(
                    send_transaction_handle(project_id, action_id, sender,
                                            receiver, amount, coin, contract)))

    result = ResponseObject.raise_args_error()
    return make_response(jsonify(result))
def get_wallet_total_balance():
    """目前该接口逻辑仅针对于 ETH USDT"""
    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 = rpc.get_wallet_balance(contract=coin.contract)
    except Exception as e:
        return ResponseObject.error(**rpc_service_error)
    if balance or balance == 0:
        balance = safe_math.divided(balance, safe_math.e_calc(coin.decimal))
    else:
        return ResponseObject.error(**balance_rpc_error)
    return ResponseObject.success(data=balance.to_eng_string())
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())
def check_passphrase(project_coin, secret) -> (bool, object, str, object):
    private_key = project_coin.ProjectCoin.hot_pk
    if private_key is None:
        return False, ResponseObject.error(**sign_rsa_not_found), None, None
    crypto = RsaCrypto()
    crypto.import_key(private_key)
    try:
        passphrase = crypto.decrypt(secret)
    except Exception as e:
        return False, ResponseObject.error(**sign_rsa_invalid), None, None

    rpc = RpcConfig.get_rpc()
    if rpc is None:
        return False, ResponseObject.error(**out_data_missing), None, None

    is_correct_passphrase = rpc.open_wallet(
        passphrase=passphrase, address=project_coin.ProjectCoin.hot_address)
    if not is_correct_passphrase:
        return False, ResponseObject.error(**passphrase_invalid), None, None
    return True, crypto, passphrase, rpc
Example #10
0
def auth():
    if app.config.get('OPEN_SIGN_AUTH') and any(
        [request.path.startswith(path)
         for path in app.config.get('SIGN_API')]):
        try:
            _auth = Auth(request)
            allow = _auth.check()
        except Exception as e:
            return make_response(
                jsonify(ResponseObject.raise_sign_exception()), 200)
        if isinstance(allow, dict):
            return make_response(jsonify(ResponseObject.error(**allow)), 200)
        else:
            if _auth.api_auth.project_id is None:
                return make_response(
                    jsonify(ResponseObject.error(**sign_key_not_bind_project)),
                    200)
            request.project_id = _auth.api_auth.project_id
    if not app.config.get('OPEN_SIGN_AUTH'):
        # 为调试使用
        request.project_id = 1
def create_address(project_id, coin_name, count):
    is_valid_secret, secret_result = get_secret(project_id, coin_name)
    if not is_valid_secret:
        return secret_result
    secret = secret_result

    project_coin = ProjectCoin.get_pro_coin_by_pid_cname(project_id, coin_name)
    if not project_coin:
        return ResponseObject.error(**sign_rsa_not_found)

    is_valid_pass, result, passphrase, rpc = check_passphrase(
        project_coin, secret)
    if not is_valid_pass:
        return result

    addresses = rpc.new_address(passphrase=passphrase, count=count)
    if not isinstance(addresses, (list, tuple)):
        addresses = [addresses]
    success_count = 0
    addresses_add_list = []
    for address in addresses:
        if address is not None:
            addresses_add_list.append({
                "project_id":
                project_id,
                'address':
                address,
                "coin_id":
                project_coin.ProjectCoin.coin_id,
                "address_type":
                AddressTypeEnum.DEPOSIT.value
            })
            success_count += 1
            runtime.project_address[address] = {
                "project_id": project_id,
                "coin_id": project_coin.ProjectCoin.coin_id,
                "coin_name": coin_name
            }
    Address.add_addresses(addresses_add_list)
    return ResponseObject.success(data=addresses)
Example #12
0
def get_transaction():
    """查询交易详情

    @@@
    #### 签名
    [v] 必须

    #### args

    | args | nullable | type | default | remark |
    |--------|--------|--------|--------|--------|
    | txHash | false | string |  | 交易流水号 |

    #### return
    | args | nullable | type | remark |
    |--------|--------|--------|--------|
    | sender | false | string | 发送地址 |
    | receiver | false | string | 接口地址 |
    | txHash | false | string | 交易流水号 |
    | value | false | string | 交易金额 |
    | blockHeight | false | int | 所在高度 |
    | blockTime | false | int | 时间戳, 秒级 |
    | contract | true | string | 合约地址 |
    | isValid | false | bool | 是否有效 |
    | confirmNumber | false | int | 最新确认数 |


    - ##### json
    > {“sender”: "1392019302193029",
    "receiver": "1392019302193029",
    "txHash": "1392019302193029",
    "value": "100.00",
    "blockHeight": 10000,
    "blockTime": 10000,
    "contract": "1392019302193029",
    "isValid": true,
    "confirmNumber": 100
    }
    @@@
    :return:
    """
    _json = get_json(request)

    if _json is not None:
        tx_hash = _json.get("txHash")
        if tx_hash is not None:
            return make_response(jsonify(get_tx_by_tx_hash(tx_hash)))

    result = ResponseObject.raise_args_error()
    return make_response(jsonify(result))
Example #13
0
def new_address():
    """生成新地址
    生成新地址, 当前接口是同步响应返回, 所以该方法不建议一次调用超过10
    V2 版本将升级为异步, 需要提供 callback 地址, 届时可生成较大数量地址

    `该接口必须要在先设置密码后才能使用`
    @@@
    #### 签名
    [v] 必须

    #### args

    暂不需要入参

    | args | nullable | type | default | remark |
    |--------|--------|--------|--------|--------|
    | count | false | int | 10 | 生成地址数量 |

    #### return
    | args | nullable | type | remark |
    |--------|--------|--------|--------|
    | addresses | false | array[string] | 地址, ['新地址'...] |
    - ##### json
    > ["abc", "bbc"]
    @@@
    """
    _json = get_json(request)
    project_id = request.project_id

    if _json is not None:
        coin = _json.get("coin", 'Ethereum')
        count = _json.get("count", 10)
        if count is not None and isinstance(count, int):
            return make_response(
                jsonify(create_address(project_id, coin, count)))

    result = ResponseObject.raise_args_error()

    return make_response(jsonify(result))
Example #14
0
def set_wallet_passphrase():
    """设置钱包密码

    该字段是加密传输, 加密算法请见...
    @@@
    #### 签名
    [v] 必须

    #### args

    | args | nullable | type | default | remark |
    |--------|--------|--------|--------|--------|
    | secret | false | string |  | 密钥 |
    | coin | true | string | Ethereum | 币种, 默认使用ETH ERC20 的 USDT, 当前该选项更改暂时无效 |

    #### return
    | args | nullable | type | remark |
    |--------|--------|--------|--------|
    | isSet | false | bool | 是否设置成功 |

    - ##### json
    > true
    @@@
    :return:
    """
    _json = get_json(request)
    project_id = request.project_id

    if _json is not None:
        secret = _json.get("secret")
        coin = _json.get("coin", 'Ethereum')
        if secret is not None or coin is None:
            return make_response(
                jsonify(set_passphrase(project_id, coin, secret)))

    result = ResponseObject.raise_args_error()
    return make_response(jsonify(result))
Example #15
0
def add_order_id():
    """添加订单号
    ********* 这个地方逻辑有问题, 当前无法处理 ********
    外部给预某个订单号, 给外界推送匹配订单号
    @@@
    #### 签名
    [v] 必须

    #### args

    | args | nullable | type | default | remark |
    |--------|--------|--------|--------|--------|
    | orderId | false | string |  | 订单号 |
    | address | false | string | | 订单绑定的地址 |

    #### return
    | args | nullable | type | remark |
    |--------|--------|--------|--------|
    | isSet | false | bool | 是否设置成功 |

    - ##### json
    > true
    @@@
    :return:
    """
    _json = get_json(request)
    project_id = request.project_id

    if _json is not None:
        order_id = _json.get("orderId")
        address = _json.get("address")
        if order_id is not None or address is None:
            return make_response(
                jsonify(set_deposit_order_id(project_id, order_id, address)))

    result = ResponseObject.raise_args_error()
    return make_response(jsonify(result))
def send_transaction_handle(project_id, action_id, sender, receiver, amount,
                            coin_name, contract):
    """
    该交易暂时只支持约定的 USDT 币种, 其他不支持, coin name 固定为 Ethereum, 其他不受理
    合约暂时也同样不支持自定义, 若合约不是 USDT 合约时, 暂不受理.
    """
    # 获取币种信息
    if contract:
        coin = Coin.get_coin(name=coin_name, contract=contract)
    else:
        # 默认使用 USDT, 但币种依然需要校验
        # coin = Coin.get_coin(name=coin_name, symbol='USDT', is_master=0)
        coin = Coin.get_erc20_usdt_coin()
    if coin is None:
        return ResponseObject.error(**coin_missing)

    if contract is None:
        contract = coin.contract

    # 检查幂等
    tx = ProjectOrder.get_tx_by_action_or_hash(action_id=action_id)
    if tx:
        return ResponseObject.success(data=tx.tx_hash)

    # 获取项目方设置的数据, 当前只有一个项目, 所以暂时不涉及此项, 如果以后我要处理多项目时再根据需求改造.
    project_coin = ProjectCoin.get_pro_coin_by_pid_cname(project_id, coin_name)
    if not project_coin:
        return False, ResponseObject.error(**sign_rsa_not_found)

    # 校验密码
    is_valid_secret, secret_result = get_secret(project_id, coin_name)
    if not is_valid_secret:
        return secret_result
    secret = secret_result

    is_valid, result, passphrase, rpc = check_passphrase(project_coin, secret)
    if not is_valid:
        return result

    # TODO 这里是硬性限制 USDT 的逻辑, 而且是强制 ERC20
    if coin_name != 'Ethereum':
        return ResponseObject.error(**not_support_coin)
    if coin.symbol != 'USDT':
        return ResponseObject.error(**not_support_coin)

    amount = int(safe_math.multi(amount, safe_math.e_calc(coin.decimal)))

    # TODO 因为上面的限制, 所以下面逻辑优化, 可以不判断一些复杂的东西
    if project_coin.ProjectCoin.gas == '0':
        gas = rpc.get_smart_fee(contract=contract)
    else:
        gas = project_coin.ProjectCoin.gas
    if project_coin.ProjectCoin.gas_price == '0':
        gas_price = rpc.gas_price()
    else:
        gas_price = project_coin.ProjectCoin.gas_price

    if gas is None:
        return ResponseObject.error(**fee_args_error)
    if gas_price is None:
        return ResponseObject.error(**fee_args_error)

    try:
        tx_hash = rpc.send_transaction(sender=sender,
                                       receiver=receiver,
                                       value=amount,
                                       passphrase=passphrase,
                                       gas=gas,
                                       gas_price=gas_price,
                                       contract=contract)
    except JsonRpcError as e:
        error = tx_send_error
        error['msg'] = e.message
        return ResponseObject.error(**error)
    if tx_hash:
        ProjectOrder.add(project_id,
                         action_id,
                         coin.id,
                         tx_hash,
                         amount,
                         sender,
                         receiver,
                         gas,
                         gas_price,
                         contract=contract)
        return ResponseObject.success(data=tx_hash)
    return ResponseObject.error(**tx_send_error)
def is_mine_address(project_id, address):
    address = Address.query.filter_by(project_id=project_id,
                                      address=address).first()
    if not address:
        return ResponseObject.success(data=False)
    return ResponseObject.success(data=True)
Example #18
0
def err40x(e):
    return make_response(jsonify(ResponseObject.raise_404_error()), 404)
Example #19
0
def err50x(e):
    return make_response(jsonify(ResponseObject.raise_exception()), e.code)
def get_tx_by_tx_hash(tx_hash: str):
    rpc = RpcConfig.get_rpc()
    if rpc is None:
        return ResponseObject.error(**out_data_missing)
    chain_info = rpc.get_block_height()

    tx = Transaction.get_tx_coin_by_tx_hash(tx_hash=tx_hash)
    if tx:
        return ResponseObject.success(
            data={
                "sender":
                tx.Transaction.sender,
                "receiver":
                tx.Transaction.receiver,
                "txHash":
                tx.Transaction.tx_hash,
                "value":
                safe_math.divided(tx.Transaction.amount,
                                  safe_math.e_calc(
                                      tx.Coin.decimal)).to_eng_string(),
                "blockHeight":
                tx.Transaction.height,
                "blockTime":
                tx.Transaction.block_time,
                "contract":
                tx.Transaction.contract,
                "isValid":
                True if tx.Transaction.status == 1 else False,
                "confirmNumber":
                chain_info.highest_height -
                tx.Transaction.height if tx.Transaction.height > 0 else 0
            })
    else:
        tx_origin, receipt_origin = rpc.get_transaction_by_hash(tx_hash)
        if tx_origin and receipt_origin:
            tx, receipt = EthereumResolver.resolver_transaction(
                tx_origin), EthereumResolver.resolver_receipt(receipt_origin)
            block_info = rpc.get_block_by_number(int_to_hex(tx.block_height),
                                                 False)
            if not block_info:
                return ResponseObject.error(**rpc_block_not_found)
            block = EthereumResolver.resolver_block(block_info, False)
            if tx.contract is not None:
                coin = Coin.get_erc20_usdt_coin()
            else:
                coin = Coin.get_coin(name='Ethereum')
            if coin is None:
                return ResponseObject.error(**coin_missing)
            return ResponseObject.success(
                data={
                    "sender":
                    tx.sender,
                    "receiver":
                    tx.receiver,
                    "txHash":
                    tx.tx_hash,
                    "value":
                    safe_math.divided(tx.value, safe_math.e_calc(
                        coin.decimal)).to_eng_string(),
                    "blockHeight":
                    tx.block_height,
                    "blockTime":
                    block.timestamp,
                    "contract":
                    tx.contract,
                    "isValid":
                    True if receipt.status == 1 else False,
                    "confirmNumber":
                    chain_info.highest_height - tx.block_height
                })
    return ResponseObject.error(**tx_miss)