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)}
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
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
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, }
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)))
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), }
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)
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)
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
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))
def test_token_id_encode(name, id): assert token_id_encode(name) == id assert name == token_id_decode(id)
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
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