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

feat: ethrpc: implement call, estimateGas, getTransactionCount #5567

Merged
merged 2 commits into from
Dec 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 34 additions & 9 deletions app/node/rpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"reflect"

"github.com/filecoin-project/go-jsonrpc"
"github.com/filecoin-project/venus/app/submodule/eth"
v0api "github.com/filecoin-project/venus/venus-shared/api/chain/v0"
v1api "github.com/filecoin-project/venus/venus-shared/api/chain/v1"
"github.com/filecoin-project/venus/venus-shared/api/permission"
Expand Down Expand Up @@ -38,17 +39,29 @@ func (builder *RPCBuilder) AddServices(services ...RPCService) error {
return nil
}

func (builder *RPCBuilder) AddService(service RPCService) error {
methodName := "V0API"
var ethSubModuleTyp = reflect.TypeOf(&eth.EthSubModule{}).Elem()

func skipV0API(in interface{}) bool {
inT := reflect.TypeOf(in)
if inT.Kind() == reflect.Pointer {
inT = inT.Elem()
}

return inT.AssignableTo(ethSubModuleTyp)
}

func (builder *RPCBuilder) AddV0API(service RPCService) error {
methodName := "V0API"
serviceV := reflect.ValueOf(service)
apiMethod := serviceV.MethodByName(methodName)
if !apiMethod.IsValid() {
return errors.New("expect API function")
if skipV0API(service) {
return nil
}
return errors.New("expect V0API function")
}

apiImpls := apiMethod.Call([]reflect.Value{})

for _, apiImpl := range apiImpls {
rt := reflect.TypeOf(apiImpl)
rv := reflect.ValueOf(apiImpl)
Expand All @@ -65,15 +78,18 @@ func (builder *RPCBuilder) AddService(service RPCService) error {
}
}

methodName = "API"
serviceV = reflect.ValueOf(service)
apiMethod = serviceV.MethodByName(methodName)
return nil
}

func (builder *RPCBuilder) AddAPI(service RPCService) error {
methodName := "API"
serviceV := reflect.ValueOf(service)
apiMethod := serviceV.MethodByName(methodName)
if !apiMethod.IsValid() {
return errors.New("expect API function")
}

apiImpls = apiMethod.Call([]reflect.Value{})

apiImpls := apiMethod.Call([]reflect.Value{})
for _, apiImpl := range apiImpls {
rt := reflect.TypeOf(apiImpl)
rv := reflect.ValueOf(apiImpl)
Expand All @@ -92,6 +108,15 @@ func (builder *RPCBuilder) AddService(service RPCService) error {
return nil
}

func (builder *RPCBuilder) AddService(service RPCService) error {
err := builder.AddV0API(service)
if err != nil {
return err
}

return builder.AddAPI(service)
}

func (builder *RPCBuilder) Build(version string, limiter *ratelimit.RateLimiter) *jsonrpc.RPCServer {
serverOptions := make([]jsonrpc.ServerOption, 0)
serverOptions = append(serverOptions, jsonrpc.WithProxyBind(jsonrpc.PBMethod))
Expand Down
2 changes: 1 addition & 1 deletion app/submodule/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,6 @@ func TestConfigSet(t *testing.T) {
// bad address
jsonBlobBadAddr := "f4cqnyc0muxjajygqavu645m8ja04vckk2kcorrupt"
err = cfgAPI.Set("walletModule.defaultAddress", jsonBlobBadAddr)
assert.EqualError(t, err, address.ErrUnknownProtocol.Error())
assert.EqualError(t, err, address.ErrInvalidPayload.Error())
})
}
165 changes: 152 additions & 13 deletions app/submodule/eth/eth_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,20 @@ import (
"strconv"

"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/big"
"github.com/filecoin-project/go-state-types/exitcode"
"github.com/filecoin-project/specs-actors/actors/builtin"
"github.com/filecoin-project/venus/pkg/constants"
"github.com/filecoin-project/venus/pkg/fork"
"github.com/filecoin-project/venus/pkg/vm/vmcontext"
v1 "github.com/filecoin-project/venus/venus-shared/api/chain/v1"
"github.com/filecoin-project/venus/venus-shared/types"
)

type ethAPI struct {
em *EthSubModule
chain v1.IChain
mpool v1.IMessagePool
}

func (a *ethAPI) EthBlockNumber(ctx context.Context) (types.EthInt, error) {
Expand Down Expand Up @@ -89,10 +95,41 @@ func (a *ethAPI) EthGetTransactionByHash(ctx context.Context, txHash types.EthHa
}

func (a *ethAPI) EthGetTransactionCount(ctx context.Context, sender types.EthAddress, blkParam string) (types.EthInt, error) {
return types.EthInt(0), nil
addr, err := sender.ToFilecoinAddress()
if err != nil {
return types.EthInt(0), err
}
nonce, err := a.mpool.MpoolGetNonce(ctx, addr)
if err != nil {
return types.EthInt(0), err
}
return types.EthInt(nonce), nil
}

func (a *ethAPI) EthGetTransactionReceipt(ctx context.Context, blkHash types.EthHash) (types.EthTxReceipt, error) {
// todo: 实现 StateReplay 接口
func (a *ethAPI) EthGetTransactionReceipt(ctx context.Context, txHash types.EthHash) (types.EthTxReceipt, error) {
// cid := txHash.ToCid()

// msgLookup, err := a.chain.StateSearchMsg(ctx, types.EmptyTSK, cid, constants.LookbackNoLimit, true)
// if err != nil {
// return types.EthTxReceipt{}, err
// }

// tx, err := a.ethTxFromFilecoinMessageLookup(ctx, msgLookup)
// if err != nil {
// return types.EthTxReceipt{}, err
// }

// replay, err := a.chain.StateReplay(ctx, types.EmptyTSK, cid)
// if err != nil {
// return types.EthTxReceipt{}, err
// }

// receipt, err := types.NewEthTxReceipt(tx, msgLookup, replay)
// if err != nil {
// return types.EthTxReceipt{}, err
// }
// return receipt, nil
return types.EthTxReceipt{}, nil
}

Expand Down Expand Up @@ -145,23 +182,119 @@ func (a *ethAPI) NetListening(ctx context.Context) (bool, error) {
}

func (a *ethAPI) EthProtocolVersion(ctx context.Context) (types.EthInt, error) {
return types.EthInt(0), nil
head, err := a.chain.ChainHead(ctx)
if err != nil {
return types.EthInt(0), err
}

return types.EthInt(a.em.chainModule.Fork.GetNetworkVersion(ctx, head.Height())), nil
}

func (a *ethAPI) EthMaxPriorityFeePerGas(ctx context.Context) (types.EthInt, error) {
return types.EthInt(0), nil
func (a *ethAPI) EthMaxPriorityFeePerGas(ctx context.Context) (types.EthBigInt, error) {
gasPremium, err := a.mpool.GasEstimateGasPremium(ctx, 0, builtin.SystemActorAddr, 10000, types.EmptyTSK)
if err != nil {
return types.EthBigInt(big.Zero()), err
}
return types.EthBigInt(gasPremium), nil
}

func (a *ethAPI) EthGasPrice(ctx context.Context) (types.EthInt, error) {
return types.EthInt(0), nil
func (a *ethAPI) EthGasPrice(ctx context.Context) (types.EthBigInt, error) {
// According to Geth's implementation, eth_gasPrice should return base + tip
// Ref: https://github.com/ethereum/pm/issues/328#issuecomment-853234014

ts, err := a.chain.ChainHead(ctx)
if err != nil {
return types.EthBigInt(big.Zero()), err
}
baseFee := ts.Blocks()[0].ParentBaseFee

premium, err := a.EthMaxPriorityFeePerGas(ctx)
if err != nil {
return types.EthBigInt(big.Zero()), err
}

gasPrice := big.Add(baseFee, big.Int(premium))
return types.EthBigInt(gasPrice), nil
}

func (a *ethAPI) EthSendRawTransaction(ctx context.Context, rawTx types.EthBytes) (types.EthHash, error) {
return types.EthHash{}, nil
}

func (a *ethAPI) applyEvmMsg(ctx context.Context, tx types.EthCall) (*types.InvocResult, error) {
from, err := tx.From.ToFilecoinAddress()
if err != nil {
return nil, err
}
to, err := tx.To.ToFilecoinAddress()
if err != nil {
return nil, fmt.Errorf("cannot get Filecoin address: %w", err)
}
msg := &types.Message{
From: from,
To: to,
Value: big.Int(tx.Value),
Method: abi.MethodNum(2),
Params: tx.Data,
GasLimit: constants.BlockGasLimit,
GasFeeCap: big.Zero(),
GasPremium: big.Zero(),
}
ts, err := a.chain.ChainHead(ctx)
if err != nil {
return nil, fmt.Errorf("failed to got head %v", err)
}

// Try calling until we find a height with no migration.
var res *vmcontext.Ret
for {
res, err = a.em.chainModule.Stmgr.CallWithGas(ctx, msg, []types.ChainMsg{}, ts)
if err != fork.ErrExpensiveFork {
break
}
ts, err = a.chain.ChainGetTipSet(ctx, ts.Parents())
if err != nil {
return nil, fmt.Errorf("getting parent tipset: %w", err)
}
}
if err != nil {
return nil, fmt.Errorf("CallWithGas failed: %w", err)
}
if res.Receipt.ExitCode != exitcode.Ok {
return nil, fmt.Errorf("message execution failed: exit %s, reason: %s", res.Receipt.ExitCode, res.ActorErr)
}
var errStr string
if res.ActorErr != nil {
errStr = res.ActorErr.Error()
}
return &types.InvocResult{
MsgCid: msg.Cid(),
Msg: msg,
MsgRct: &res.Receipt,
ExecutionTrace: res.GasTracker.ExecutionTrace,
Error: errStr,
Duration: res.Duration,
}, nil
}

func (a *ethAPI) EthEstimateGas(ctx context.Context, tx types.EthCall, blkParam string) (types.EthInt, error) {
return types.EthInt(0), nil
invokeResult, err := a.applyEvmMsg(ctx, tx)
if err != nil {
return types.EthInt(0), err
}
ret := invokeResult.MsgRct.GasUsed
return types.EthInt(ret), nil
}

func (a *ethAPI) EthCall(ctx context.Context, tx types.EthCall, blkParam string) (string, error) {
return "", nil
func (a *ethAPI) EthCall(ctx context.Context, tx types.EthCall, blkParam string) (types.EthBytes, error) {
invokeResult, err := a.applyEvmMsg(ctx, tx)
if err != nil {
return nil, err
}
if len(invokeResult.MsgRct.Return) > 0 {
return types.EthBytes(invokeResult.MsgRct.Return), nil
}
return types.EthBytes{}, nil
}

func (a *ethAPI) ethBlockFromFilecoinTipSet(ctx context.Context, ts *types.TipSet, fullTxInfo bool) (types.EthBlock, error) {
Expand Down Expand Up @@ -243,7 +376,7 @@ func (a *ethAPI) ethTxFromFilecoinMessageLookup(ctx context.Context, msgLookup *
return types.EthTx{}, err
}

fromFilIDAddr, err := a.chain.StateLookupID(ctx, msg.From, types.EmptyTSK)
fromFilIDAddr, err := a.chain.StateLookupID(ctx, msg.To, types.EmptyTSK)
if err != nil {
return types.EthTx{}, err
}
Expand All @@ -263,13 +396,19 @@ func (a *ethAPI) ethTxFromFilecoinMessageLookup(ctx context.Context, msgLookup *
return types.EthTx{}, err
}

toAddr := &toEthAddr
_, err = types.CheckContractCreation(msgLookup)
if err == nil {
toAddr = nil
}

tx := types.EthTx{
ChainID: types.EthInt(a.em.networkCfg.Eip155ChainID),
Hash: txHash,
BlockHash: blkHash,
BlockNumber: types.EthInt(msgLookup.Height),
From: fromEthAddr,
To: toEthAddr,
To: toAddr,
Value: types.EthBigInt(msg.Value),
Type: types.EthInt(2),
Gas: types.EthInt(msg.GasLimit),
Expand All @@ -278,7 +417,7 @@ func (a *ethAPI) ethTxFromFilecoinMessageLookup(ctx context.Context, msgLookup *
V: types.EthBigIntZero,
R: types.EthBigIntZero,
S: types.EthBigIntZero,
// TODO: Input:
Input: msg.Params,
}
return tx, nil
}
Expand Down
10 changes: 6 additions & 4 deletions venus-shared/api/chain/v1/eth.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ type IETH interface {
EthGetBlockByNumber(ctx context.Context, blkNum types.EthInt, fullTxInfo bool) (types.EthBlock, error) //perm:read
EthGetTransactionByHash(ctx context.Context, txHash types.EthHash) (types.EthTx, error) //perm:read
EthGetTransactionCount(ctx context.Context, sender types.EthAddress, blkOpt string) (types.EthInt, error) //perm:read
EthGetTransactionReceipt(ctx context.Context, blkHash types.EthHash) (types.EthTxReceipt, error) //perm:read
EthGetTransactionReceipt(ctx context.Context, txHash types.EthHash) (types.EthTxReceipt, error) //perm:read
EthGetTransactionByBlockHashAndIndex(ctx context.Context, blkHash types.EthHash, txIndex types.EthInt) (types.EthTx, error) //perm:read
EthGetTransactionByBlockNumberAndIndex(ctx context.Context, blkNum types.EthInt, txIndex types.EthInt) (types.EthTx, error) //perm:read

Expand All @@ -33,8 +33,10 @@ type IETH interface {
NetVersion(ctx context.Context) (string, error) //perm:read
NetListening(ctx context.Context) (bool, error) //perm:read
EthProtocolVersion(ctx context.Context) (types.EthInt, error) //perm:read
EthGasPrice(ctx context.Context) (types.EthInt, error) //perm:read
EthMaxPriorityFeePerGas(ctx context.Context) (types.EthInt, error) //perm:read
EthGasPrice(ctx context.Context) (types.EthBigInt, error) //perm:read
EthMaxPriorityFeePerGas(ctx context.Context) (types.EthBigInt, error) //perm:read
EthEstimateGas(ctx context.Context, tx types.EthCall, blkParam string) (types.EthInt, error) //perm:read
EthCall(ctx context.Context, tx types.EthCall, blkParam string) (string, error) //perm:read
EthCall(ctx context.Context, tx types.EthCall, blkParam string) (types.EthBytes, error) //perm:read

EthSendRawTransaction(ctx context.Context, rawTx types.EthBytes) (types.EthHash, error) //perm:read
}
Loading