Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added eth namespace apis Call, EstimateGas #1134

Merged
merged 19 commits into from
Feb 11, 2022

Conversation

aeharvlee
Copy link
Contributor

@aeharvlee aeharvlee commented Jan 27, 2022

Proposed changes

This PR implemented Eth namespace APIs Call and EstimateGas.

Ethereum Source Code links

Review Points

1. State Override feature

2. Differences from Klaytn

  • Ethereum's Call and EstimateGas basically checks if caller (= the from address) has enough balance to proceeds call if user specified gas and gasPrice field.
    • Checks balance of caller is higher than gas * gasPrice.

For this, I moved logic adding balance of caller from GetEVM to out of it because both klay_call and eth_call are now using same API but works differently.

Klaytn add gasFee by default before proceeding call and estimateGas.

3. Differences from Ethereum implementation

  • Removed ExecutionResult struct

    • There is no easy way to classify whether the error returned from TransitionDb in Klaytn is vmError or consensusError or just simple error can be detected before executing EVM (e.g. user specify gas limit which is lower than intrinsicGas).
    • Ethereum uses ExecutionResult because they can specify that errors in their codebase, but this is not adaptable to current Klaytn codebase.
      We can apply this changes maybe later.
  • AccessList is not supported yet

    • AccessList is not supported yet in Klaytn, so it does not process with it even though user passes AccessList as parameter.

How to test this PR

First, please clone my origin repo.

1. Build ken with source codes related with this PR.

  • Run make ken

2. Run ken

  • In my case, I tested with CN 1 - PN 1 - EN 1 in my local.
  • Please note that you should enable eth namepsace to RPC_API and WS_API in kend.conf

Prepare the test

Click to expand

Bytecode

To test State Override feature, I compiled simple storage contract and got bytecode to use.

Storage.bin-runtime: 6080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680632e64cec114604e5780636057361d146076575b600080fd5b348015605957600080fd5b50606060a0565b6040518082815260200191505060405180910390f35b348015608157600080fd5b50609e6004803603810190808035906020019092919050505060a9565b005b60008054905090565b80600081905550505600a165627a7a723058207783dba41884f73679e167576362b7277f88458815141651f48ca38c25b498f80029

I used $ docker run -v /Users/denver.lee/playground/solidity/storage:/sol ethereum/solc:0.4.24 --asm --abi --bin --bin-runtime --metadata --overwrite -o /sol/output /sol/storage.sol to compile below Storage contract code.

pragma solidity ^0.4.24;

/**
 * @title Storage
 * @dev Store & retrieve value in a variable
 */
contract Storage {

    uint256 number;

    /**
     * @dev Store value in variable
     * @param num value to store
     */
    function store(uint256 num) public {
        number = num;
    }

    /**
     * @dev Return value 
     * @return value of 'number'
     */
    function retrieve() public view returns (uint256){
        return number;
    }
}

ABI Set to test function call of KIP-7

> totalSupplyAbi
{ name: 'totalSupply', type: 'function', inputs: [] }
> caver.abi.encodeFunctionCall(totalSupplyAbi, [])
'0x18160ddd' 

> balanceOfAbi
{
  constant: true,
  inputs: [ { internalType: 'address', name: 'account', type: 'address' } ],
  name: 'balanceOf',
  outputs: [ { internalType: 'uint256', name: '', type: 'uint256' } ],
  payable: false,
  stateMutability: 'view',
  type: 'function'
}
> caver.abi.encodeFunctionCall(balanceOfAbi, ["0xca7a99380131e6c76cfa622396347107aeedca2d"])
'0x70a08231000000000000000000000000ca7a99380131e6c76cfa622396347107aeedca2d'

> retrieveAbi
{ name: 'retrieve', type: 'function', inputs: [] }
> caver.abi.encodeFunctionCall(retrieveAbiObj, [])
'0x2e64cec1'

Test Call

Click to expand

I tested this api in the following order.

  1. Deploy KIP-7 contract.
  2. Call KIP-7 contract functions totalSupply and balanceOf to check whether call is working or not.
  3. State Override already deployed KIP-7 contract account by replacing it's code with runtime bytecode of Storage.sol and call retrieve function of Storage.sol.

Here is the my test scenario info below.

  • KIP-7 Contract address: 0xbE3892d33620bE5aca8c75D39e7401871194d290
  • Deployer address: 0xca7a99380131e6c76cfa622396347107aeedca2d

Call totalSupply()

➜  corecell curl -H "Content-Type: application/json" --data '{"jsonrpc":"2.0", "method": "eth_call", "params": [{"from": "0xca7a99380131e6c76cfa622396347107aeedca2d", "to": "0xbE3892d33620bE5aca8c75D39e7401871194d290", "data": "0x18160ddd"}, "latest"], "id": 1}' http://localhost:8551

{"jsonrpc":"2.0","id":1,"result":"0x0000000000000000000000000000000000000000000000000de0b6b3a7640000"}

Call balanceOf("0xca7a99380131e6c76cfa622396347107aeedca2d")

➜  corecell curl -H "Content-Type: application/json" --data '{"jsonrpc":"2.0", "method": "eth_call", "params": [{"from": "0xca7a99380131e6c76cfa622396347107aeedca2d", "to": "0xbE3892d33620bE5aca8c75D39e7401871194d290", "data": "0x70a08231000000000000000000000000ca7a99380131e6c76cfa622396347107aeedca2d"}, "latest"], "id": 1}' http://localhost:8551

{"jsonrpc":"2.0","id":1,"result":"0x0000000000000000000000000000000000000000000000000de0b6b3a763ffff"}

State Override with Storage.sol and Call retrieve

➜  corecell curl -H "Content-Type: application/json" --data '{"jsonrpc":"2.0", "method": "eth_call", "params": [{"from": "0xca7a99380131e6c76cfa622396347107aeedca2d", "to": "0xbE3892d33620bE5aca8c75D39e7401871194d290", "data": "0x2e64cec1"}, "latest", {"0xbE3892d33620bE5aca8c75D39e7401871194d290": {"code":"0x6080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680632e64cec114604e5780636057361d146076575b600080fd5b348015605957600080fd5b50606060a0565b6040518082815260200191505060405180910390f35b348015608157600080fd5b50609e6004803603810190808035906020019092919050505060a9565b005b60008054905090565b80600081905550505600a165627a7a723058207783dba41884f73679e167576362b7277f88458815141651f48ca38c25b498f80029"}}], "id": 1}' http://localhost:8551

{"jsonrpc":"2.0","id":1,"result":"0x0000000000000000000000000000000000000000000000000000000000000000"}

Call with gas and gasPrice but balance is not enough
Balance of caller 0xe2df3753d74bebd8c4ccdbd5320b36e5ef8ca08d: 0.

➜  corecell curl -H "Content-Type: application/json" --data '{"jsonrpc":"2.0", "method": "eth_call", "params": [{"from": "0xe2df3753d74bebd8c4ccdbd5320b36e5ef8ca08d", "to": "0xbE3892d33620bE5aca8c75D39e7401871194d290", "data": "0x70a08231000000000000000000000000ca7a99380131e6c76cfa622396347107aeedca2d", "gas": "0x99999", "gasPrice": "0x1"}, "latest"], "id": 1}' http://localhost:8551
{"jsonrpc":"2.0","id":1,"error":{"code":-32000,"message":"err: insufficient balance of the fee payer to pay for gas (supplied gas 629145)"}}

Call with gas and gasPrice but balance is not enough -> Using State Override to cover this
But if we use State Override we can use it. Let's override state with balance 0x99999.

➜  corecell curl -H "Content-Type: application/json" --data '{"jsonrpc":"2.0", "method": "eth_call", "params": [{"from": "0xe2df3753d74bebd8c4ccdbd5320b36e5ef8ca08d", "to": "0xbE3892d33620bE5aca8c75D39e7401871194d290", "data": "0x70a08231000000000000000000000000ca7a99380131e6c76cfa622396347107aeedca2d", "gas": "0x99999", "gasPrice": "0x1"}, "latest", {"0xe2df3753d74bebd8c4ccdbd5320b36e5ef8ca08d": {"balance": "0x99999"}}], "id": 1}' http://localhost:8551

{"jsonrpc":"2.0","id":1,"result":"0x0000000000000000000000000000000000000000000000000de0b6b3a763ffff"}

Test EstimateGas

Click to expand

Estimate Gas with gas and gasPrice. Caller balance is enough to call it.
Balance of caller 0xca7a99380131e6c76cfa622396347107aeedca2d: 9.99999999999999999999999999999999097166e+49

➜  corecell curl -H "Content-Type: application/json" --data '{"jsonrpc": "2.0", "method": "eth_estimateGas", "params": [{"from": "0xca7a99380131e6c76cfa622396347107aeedca2d", "to": "0x87ac99835e67168d4f9a40580f8f5c33550ba88b", "gas": "0x100000", "gasPrice": "0x5d21dba00", "value": "0x0", "data": "0x8ada066e"}], "id": 1}' http://localhost:8551

{"jsonrpc":"2.0","id":1,"result":"0x5398"}

Estimate Gas with gas and gasPrice but Caller balance is not enough.
Balance of caller 0xe2df3753d74bebd8c4ccdbd5320b36e5ef8ca08d: 0

➜  corecell curl -H "Content-Type: application/json" --data '{"jsonrpc": "2.0", "method": "eth_estimateGas", "params": [{"from": "0xe2df3753d74bebd8c4ccdbd5320b36e5ef8ca08d", "to": "0x87ac99835e67168d4f9a40580f8f5c33550ba88b", "gas": "0x100000", "gasPrice": "0x5d21dba00", "value": "0x0", "data": "0x8ada066e"}], "id": 1}' http://localhost:8551

{"jsonrpc":"2.0","id":1,"error":{"code":-32000,"message":"insufficient funds for transfer"}}

Types of changes

Please put an x in the boxes related to your change.

  • Bugfix
  • New feature or enhancement
  • Others

Checklist

Put an x in the boxes that apply. You can also fill these out after creating the PR. If you're unsure about any of them, don't hesitate to ask. We're here to help! This is simply a reminder of what we are going to look for before merging your code.

  • I have read the CONTRIBUTING GUIDELINES doc
  • I have signed the CLA
  • Lint and unit tests pass locally with my changes ($ make test)
  • I have added tests that prove my fix is effective or that my feature works
  • I have added necessary documentation (if appropriate)
  • Any dependent changes have been merged and published in downstream modules

Related issues

  • Please leave the issue numbers or links related to this PR here.

Further comments

If this is a relatively large or complex change, kick off the discussion by explaining why you chose the solution you did and what alternatives you considered, etc...

Added SetStorage method to StateDB.
It is needed when overrides option is given.
There is no easy way to classify the error returned from TransitionDb in Klaytn
is vmError or consensusError or just client side error(e.g. user specify gas limit which is lower than intrinsicGas).

Ethereum uses ExecutionResult because they can specify that errors in their codebase, but this is not adaptable to current Klaytn codebase.

The idea which classifies errors looks good, maybe we can apply it later.
Added SetStorage method to StateDB.
It is needed when overrides option is given.
There is no easy way to classify the error returned from TransitionDb in Klaytn
is vmError or consensusError or just client side error(e.g. user specify gas limit which is lower than intrinsicGas).

Ethereum uses ExecutionResult because they can specify that errors in their codebase, but this is not adaptable to current Klaytn codebase.

The idea which classifies errors looks good, maybe we can apply it later.
GetEVM is used by both klay_call and eth_call but both works differently, I moved that logic out of GetEVM.

* Ethereum's Call and EstimateGas both requires caller have enough balance to proceed.
api/api_ethereum.go Outdated Show resolved Hide resolved
api/api_ethereum.go Outdated Show resolved Hide resolved
api/api_ethereum.go Outdated Show resolved Hide resolved
@aeharvlee aeharvlee linked an issue Feb 10, 2022 that may be closed by this pull request
5 tasks
@jimni1222
Copy link
Contributor

Please resolve conflicts

Ethereum can specify vmError and consensus error during state transition in their codebase.
But it is hard to adapt that feature(specifying errors) into Klaytn code base as now.
So I added some additional handling in EthDoEstimateGas to specify whether the error is vmError or not.
api/api_ethereum.go Outdated Show resolved Hide resolved
api/api_ethereum.go Outdated Show resolved Hide resolved
DataError interface is introduced by Ethereum's #22677 PR.
To support Ethereum compatibility, we should support it's error message format too.
Copy link
Member

@aidan-kwon aidan-kwon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's good except for the comment I leaved

@aidan-kwon
Copy link
Member

@jeongkyun-oh @yoomee1313 PTAL

@aeharvlee aeharvlee merged commit ac434b3 into klaytn:dev Feb 11, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
4 participants