예제 #1
0
 async def getTransactionsByAddress(self, address, start="0x", limit="0xa"):
     """ "start" should be the "next" in the response for fetching next page.
         "start" can also be "0x" to fetch from the beginning (i.e., latest).
         "start" can be "0x00" to fetch the pending outgoing transactions.
     """
     address = Address.create_from(address)
     if limit > 20:
         limit = 20
     result = await self.master.get_transactions_by_address(address, start, limit)
     if not result:
         return None
     tx_list, next = result
     txs = []
     for tx in tx_list:
         txs.append(
             {
                 "txId": id_encoder(tx.tx_hash, tx.from_address.full_shard_key),
                 "fromAddress": address_encoder(tx.from_address.serialize()),
                 "toAddress": address_encoder(tx.to_address.serialize())
                 if tx.to_address
                 else "0x",
                 "value": quantity_encoder(tx.value),
                 "transferTokenId": quantity_encoder(tx.transfer_token_id),
                 "transferTokenStr": token_id_decode(tx.transfer_token_id),
                 "gasTokenId": quantity_encoder(tx.gas_token_id),
                 "gasTokenStr": token_id_decode(tx.gas_token_id),
                 "blockHeight": quantity_encoder(tx.block_height),
                 "timestamp": quantity_encoder(tx.timestamp),
                 "success": tx.success,
             }
         )
     return {"txList": txs, "next": data_encoder(next)}
예제 #2
0
def balances_encoder(balances: TokenBalanceMap) -> List[Dict]:
    balance_list = []
    for k, v in balances.balance_map.items():
        balance_list.append({
            "tokenId": quantity_encoder(k),
            "tokenStr": token_id_decode(k),
            "balance": quantity_encoder(v),
        })
    return balance_list
예제 #3
0
def balances_encoder(balances: List[TokenBalancePair]) -> List[Dict]:
    balance_list = []
    for pair in balances:
        balance_list.append({
            "tokenId": quantity_encoder(pair.token_id),
            "tokenStr": token_id_decode(pair.token_id),
            "balance": quantity_encoder(pair.balance),
        })
    return balance_list
예제 #4
0
def tx_detail_encoder(tx):
    """Encode a transaction detail object as JSON object. Used for indexing server."""
    return {
        "txId": id_encoder(tx.tx_hash, tx.from_address.full_shard_key),
        "fromAddress": address_encoder(tx.from_address.serialize()),
        "toAddress": address_encoder(tx.to_address.serialize())
        if tx.to_address
        else "0x",
        "value": quantity_encoder(tx.value),
        "transferTokenId": quantity_encoder(tx.transfer_token_id),
        "transferTokenStr": token_id_decode(tx.transfer_token_id),
        "gasTokenId": quantity_encoder(tx.gas_token_id),
        "gasTokenStr": token_id_decode(tx.gas_token_id),
        "blockHeight": quantity_encoder(tx.block_height),
        "timestamp": quantity_encoder(tx.timestamp),
        "success": tx.success,
        "isFromRootChain": tx.is_from_root_chain,
    }
예제 #5
0
def main():
    args = parser.parse_args()

    if args.name is not None:
        print("Token name %s to id: %d (%s)" %
              (args.name, token_id_encode(
                  args.name), hex(token_id_encode(args.name))))

    if args.id is not None:
        print("Token id %d (%s) to id: %s" %
              (args.id, hex(args.id), token_id_decode(args.id)))
예제 #6
0
def tx_encoder(block, i):
    """Encode a transaction as JSON object.

    `transaction` is the `i`th transaction in `block`.
    """
    tx = block.tx_list[i]
    evm_tx = tx.tx.to_evm_tx()
    branch = block.header.branch
    return {
        "id": id_encoder(tx.get_hash(), evm_tx.from_full_shard_key),
        "hash": data_encoder(tx.get_hash()),
        "nonce": quantity_encoder(evm_tx.nonce),
        "timestamp": quantity_encoder(block.header.create_time),
        "fullShardId": quantity_encoder(branch.get_full_shard_id()),
        "chainId": quantity_encoder(branch.get_chain_id()),
        "shardId": quantity_encoder(branch.get_shard_id()),
        "blockId": id_encoder(block.header.get_hash(),
                              branch.get_full_shard_id()),
        "blockHeight": quantity_encoder(block.header.height),
        "transactionIndex": quantity_encoder(i),
        "from": data_encoder(evm_tx.sender),
        "to": data_encoder(evm_tx.to),
        "fromFullShardKey": full_shard_key_encoder(evm_tx.from_full_shard_key),
        "toFullShardKey": full_shard_key_encoder(evm_tx.to_full_shard_key),
        "value": quantity_encoder(evm_tx.value),
        "gasPrice": quantity_encoder(evm_tx.gasprice),
        "gas": quantity_encoder(evm_tx.startgas),
        "data": data_encoder(evm_tx.data),
        "networkId": quantity_encoder(evm_tx.network_id),
        "transferTokenId": quantity_encoder(evm_tx.transfer_token_id),
        "gasTokenId": quantity_encoder(evm_tx.gas_token_id),
        "transferTokenStr": token_id_decode(evm_tx.transfer_token_id),
        "gasTokenStr": token_id_decode(evm_tx.gas_token_id),
        "r": quantity_encoder(evm_tx.r),
        "s": quantity_encoder(evm_tx.s),
        "v": quantity_encoder(evm_tx.v),
    }
예제 #7
0
def test_token_id_exceptions():
    with pytest.raises(AssertionError):
        token_id_encode("qkc")
    with pytest.raises(AssertionError):
        token_id_encode(" ")
    with pytest.raises(AssertionError):
        token_id_encode("btc")
    with pytest.raises(AssertionError):
        token_id_encode("ZZZZZZZZZZZZZ")
    with pytest.raises(AssertionError):
        token_id_decode(2 ** 64 - 1)
    with pytest.raises(AssertionError):
        token_id_decode(-1)
    with pytest.raises(AssertionError):
        token_id_decode(ZZZZZZZZZZZZ + 1)
예제 #8
0
def test_token_id_exceptions():
    with pytest.raises(AssertionError):
        token_id_encode("qkc")
    with pytest.raises(AssertionError):
        token_id_encode(" ")
    with pytest.raises(AssertionError):
        token_id_encode("btc")
    with pytest.raises(AssertionError):
        token_id_encode(TOKEN_MAX + "Z")
    with pytest.raises(AssertionError):
        token_id_decode(2 ** 64 - 1)
    with pytest.raises(AssertionError):
        token_id_decode(-1)
    with pytest.raises(AssertionError):
        token_id_decode(TOKEN_ID_MAX + 1)
예제 #9
0
def validate_transaction(state, tx):
    # (1) The transaction signature is valid;
    if not tx.sender:  # sender is set and validated on Transaction initialization
        raise UnsignedTransaction(tx)

    if tx.version == 2:
        # When tx.version == 2 (EIP155 tx), check
        # 0. EIP155_SIGNER enable
        # 1. tx.v == tx.network_id * 2 + 35 (+ 1)
        # 2. gas_token_id & transfer_token_id should equal to default_token_id (like qkc)
        # 3. tx.from_chain_id == tx.to_chain_id and tx.from_shard_key = 0 & tx.to_shard_key = 0
        # 4. tx.network_id == chain_config.ETH_CHAIN_ID, where chain_config is derived from tx.from_chain_id
        chain_config = state.qkc_config.CHAINS[tx.from_chain_id]
        default_token_id = token_id_encode(chain_config.DEFAULT_CHAIN_TOKEN)
        if (state.qkc_config.ENABLE_EIP155_SIGNER_TIMESTAMP is not None
                and state.timestamp <
                state.qkc_config.ENABLE_EIP155_SIGNER_TIMESTAMP):
            raise InvalidTransaction("EIP155 Signer is not enable yet.")
        if tx.v != 35 + tx.network_id * 2 and tx.v != 36 + tx.network_id * 2:
            raise InvalidTransaction(
                "network_id {} does not match the signature v {}.".format(
                    tx.network_id, tx.v))
        if tx.from_chain_id != tx.to_chain_id:
            raise InvalidTransaction(
                "EIP155 Signer do not support cross shard transaction.")
        if tx.from_shard_key != 0 or tx.to_shard_key != 0:
            raise InvalidTransaction(
                "EIP155 Signer do not support cross shard transaction.")
        if tx.gas_token_id != default_token_id:
            raise InvalidTransaction(
                "EIP155 Signer only support {} as gas token.".format(
                    chain_config.DEFAULT_CHAIN_TOKEN))
        if tx.transfer_token_id != default_token_id:
            raise InvalidTransaction(
                "EIP155 Signer only support {} as transfer token.".format(
                    chain_config.DEFAULT_CHAIN_TOKEN))
        assert (tx.network_id == chain_config.ETH_CHAIN_ID,
                "Invalid network_id.")
        assert (
            tx.eth_chain_id - state.qkc_config.BASE_ETH_CHAIN_ID -
            1 == tx.from_chain_id,
            "Invalid Eth_Chain_Id.",
        )

    # (1a) startgas, gasprice, gas token id, transfer token id must be <= UINT128_MAX
    if (tx.startgas > UINT128_MAX or tx.gasprice > UINT128_MAX
            or tx.gas_token_id > TOKEN_ID_MAX
            or tx.transfer_token_id > TOKEN_ID_MAX):
        raise InvalidTransaction(
            "startgas, gasprice, and token_id must <= UINT128_MAX")

    # (2) the transaction nonce is valid (equivalent to the
    #     sender account's current nonce);
    req_nonce = state.get_nonce(tx.sender)
    if req_nonce != tx.nonce:
        raise InvalidNonce(rp(tx, "nonce", tx.nonce, req_nonce))

    # (3) the gas limit is no smaller than the intrinsic gas,
    # g0, used by the transaction;
    total_gas = tx.intrinsic_gas_used
    if tx.startgas < total_gas:
        raise InsufficientStartGas(rp(tx, "startgas", tx.startgas, total_gas))

    default_chain_token = state.shard_config.default_chain_token
    bal = {
        tx.transfer_token_id: state.get_balance(tx.sender,
                                                tx.transfer_token_id)
    }
    if tx.transfer_token_id != tx.gas_token_id:
        bal[tx.gas_token_id] = state.get_balance(tx.sender, tx.gas_token_id)

    # (4) requires non-zero balance for transfer_token_id and gas_token_id if non-default
    for token_id in [tx.transfer_token_id, tx.gas_token_id]:
        if token_id != default_chain_token and bal[token_id] == 0:
            raise InvalidNativeToken(
                "{}: non-default token {} has zero balance".format(
                    tx.__repr__(), token_id_decode(token_id)))

    # (5) the sender account balance contains at least the cost required in up-front payment
    cost = Counter({tx.transfer_token_id: tx.value}) + Counter(
        {tx.gas_token_id: tx.gasprice * tx.startgas})
    for token_id, b in bal.items():
        if b < cost[token_id]:
            raise InsufficientBalance(
                rp(
                    tx,
                    "token %s balance" % token_id_decode(token_id),
                    b,
                    cost[token_id],
                ))

    # (6) if gas token non-default, need to check system contract for gas conversion
    if tx.gasprice != 0 and tx.gas_token_id != default_chain_token:
        snapshot = state.snapshot()
        _, genesis_token_gas_price = pay_native_token_as_gas(
            state, tx.gas_token_id, tx.startgas, tx.gasprice)
        state.revert(snapshot)
        if genesis_token_gas_price == 0:
            raise InvalidNativeToken(
                "{}: non-default gas token {} not ready for being used to pay gas"
                .format(tx.__repr__(), token_id_decode(tx.gas_token_id)))
        # should be guaranteed by previous check. check added to make sure
        bal_gas_reserve = state.get_balance(
            SystemContract.GENERAL_NATIVE_TOKEN.addr(), state.genesis_token)
        if bal_gas_reserve < genesis_token_gas_price * tx.startgas:
            raise InvalidNativeToken(
                "{}: non-default gas token {} not enough reserve balance for conversion"
                .format(tx.__repr__(), token_id_decode(tx.gas_token_id)))

    # (7) check block gas limit
    if state.gas_used + tx.startgas > state.gas_limit:
        raise BlockGasLimitReached(
            rp(tx, "gaslimit", state.gas_used + tx.startgas, state.gas_limit))

    return True
예제 #10
0
def test_random_token():
    COUNT = 100000
    random.seed(2)
    for i in range(COUNT):
        id = random.randint(0, ZZZZZZZZZZZZ)
        assert id == token_id_encode(token_id_decode(id))
예제 #11
0
def test_token_id_encode(name, id):
    assert token_id_encode(name) == id
    assert name == token_id_decode(id)
예제 #12
0
def validate_transaction(state, tx):
    # (1) The transaction signature is valid;
    if not tx.sender:  # sender is set and validated on Transaction initialization
        raise UnsignedTransaction(tx)

    # (2) the transaction nonce is valid (equivalent to the
    #     sender account's current nonce);
    req_nonce = state.get_nonce(tx.sender)
    if req_nonce != tx.nonce:
        raise InvalidNonce(rp(tx, "nonce", tx.nonce, req_nonce))

    # (3) the gas limit is no smaller than the intrinsic gas,
    # g0, used by the transaction;
    total_gas = tx.intrinsic_gas_used
    if tx.startgas < total_gas:
        raise InsufficientStartGas(rp(tx, "startgas", tx.startgas, total_gas))

    # (4.0) require transfer_token_id and gas_token_id to be in allowed list
    if tx.transfer_token_id not in state.qkc_config.allowed_transfer_token_ids:
        raise InsufficientBalance(
            "{}: token {} is not in allowed transfer_token list".format(
                tx.__repr__(), token_id_decode(tx.transfer_token_id)))
    if tx.gas_token_id not in state.qkc_config.allowed_gas_token_ids:
        raise InsufficientBalance(
            "{}: token {} is not in allowed gas_token list".format(
                tx.__repr__(), token_id_decode(tx.gas_token_id)))

    # (4) the sender account balance contains at least the
    # cost, v0, required in up-front payment.
    if tx.transfer_token_id == tx.gas_token_id:
        total_cost = tx.value + tx.gasprice * tx.startgas
        if state.get_balance(tx.sender,
                             token_id=tx.transfer_token_id) < total_cost:
            raise InsufficientBalance(
                rp(
                    tx,
                    "token %d balance" % tx.transfer_token_id,
                    state.get_balance(tx.sender,
                                      token_id=tx.transfer_token_id),
                    total_cost,
                ))
    else:
        if state.get_balance(tx.sender,
                             token_id=tx.transfer_token_id) < tx.value:
            raise InsufficientBalance(
                rp(
                    tx,
                    "token %d balance" % tx.transfer_token_id,
                    state.get_balance(tx.sender,
                                      token_id=tx.transfer_token_id),
                    tx.value,
                ))
        if (state.get_balance(tx.sender, token_id=tx.gas_token_id) <
                tx.gasprice * tx.startgas):
            raise InsufficientBalance(
                rp(
                    tx,
                    "token %d balance" % tx.gas_token_id,
                    state.get_balance(tx.sender, token_id=tx.gas_token_id),
                    tx.gasprice * tx.startgas,
                ))

    # check block gas limit
    if state.gas_used + tx.startgas > state.gas_limit:
        raise BlockGasLimitReached(
            rp(tx, "gaslimit", state.gas_used + tx.startgas, state.gas_limit))

    return True
예제 #13
0
def validate_transaction(state, tx):
    # (1) The transaction signature is valid;
    if not tx.sender:  # sender is set and validated on Transaction initialization
        raise UnsignedTransaction(tx)

    # (1a) startgas, gasprice, gas token id, transfer token id must be <= UINT128_MAX
    if (tx.startgas > UINT128_MAX or tx.gasprice > UINT128_MAX
            or tx.gas_token_id > TOKEN_ID_MAX
            or tx.transfer_token_id > TOKEN_ID_MAX):
        raise InvalidTransaction(
            "startgas, gasprice, and token_id must <= UINT128_MAX")

    # (2) the transaction nonce is valid (equivalent to the
    #     sender account's current nonce);
    req_nonce = state.get_nonce(tx.sender)
    if req_nonce != tx.nonce:
        raise InvalidNonce(rp(tx, "nonce", tx.nonce, req_nonce))

    # (3) the gas limit is no smaller than the intrinsic gas,
    # g0, used by the transaction;
    total_gas = tx.intrinsic_gas_used
    if tx.startgas < total_gas:
        raise InsufficientStartGas(rp(tx, "startgas", tx.startgas, total_gas))

    default_chain_token = state.shard_config.default_chain_token
    bal = {
        tx.transfer_token_id: state.get_balance(tx.sender,
                                                tx.transfer_token_id)
    }
    if tx.transfer_token_id != tx.gas_token_id:
        bal[tx.gas_token_id] = state.get_balance(tx.sender, tx.gas_token_id)

    # (4) requires non-zero balance for transfer_token_id and gas_token_id if non-default
    for token_id in [tx.transfer_token_id, tx.gas_token_id]:
        if token_id != default_chain_token and bal[token_id] == 0:
            raise InvalidNativeToken(
                "{}: non-default token {} has zero balance".format(
                    tx.__repr__(), token_id_decode(token_id)))

    # (5) the sender account balance contains at least the cost required in up-front payment
    cost = Counter({tx.transfer_token_id: tx.value}) + Counter(
        {tx.gas_token_id: tx.gasprice * tx.startgas})
    for token_id, b in bal.items():
        if b < cost[token_id]:
            raise InsufficientBalance(
                rp(
                    tx,
                    "token %s balance" % token_id_decode(token_id),
                    b,
                    cost[token_id],
                ))

    # (6) if gas token non-default, need to check system contract for gas conversion
    if tx.gasprice != 0 and tx.gas_token_id != default_chain_token:
        snapshot = state.snapshot()
        _, genesis_token_gas_price = pay_native_token_as_gas(
            state, tx.gas_token_id, tx.startgas, tx.gasprice)
        state.revert(snapshot)
        if genesis_token_gas_price == 0:
            raise InvalidNativeToken(
                "{}: non-default gas token {} not ready for being used to pay gas"
                .format(tx.__repr__(), token_id_decode(tx.gas_token_id)))
        # should be guaranteed by previous check. check added to make sure
        bal_gas_reserve = state.get_balance(
            SystemContract.GENERAL_NATIVE_TOKEN.addr(), state.genesis_token)
        if bal_gas_reserve < genesis_token_gas_price * tx.startgas:
            raise InvalidNativeToken(
                "{}: non-default gas token {} not enough reserve balance for conversion"
                .format(tx.__repr__(), token_id_decode(tx.gas_token_id)))

    # (7) check block gas limit
    if state.gas_used + tx.startgas > state.gas_limit:
        raise BlockGasLimitReached(
            rp(tx, "gaslimit", state.gas_used + tx.startgas, state.gas_limit))

    return True