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: Add EIP-1898 support needed for The Graph compatibility #6109

Merged
merged 2 commits into from
Aug 22, 2023
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
10 changes: 5 additions & 5 deletions app/submodule/eth/dummy.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ func (e *ethAPIDummy) EthGetTransactionByHashLimited(ctx context.Context, txHash
return nil, ErrModuleDisabled
}

func (e *ethAPIDummy) EthGetTransactionCount(ctx context.Context, sender types.EthAddress, blkOpt string) (types.EthUint64, error) {
func (e *ethAPIDummy) EthGetTransactionCount(ctx context.Context, sender types.EthAddress, blkParam types.EthBlockNumberOrHash) (types.EthUint64, error) {
return 0, ErrModuleDisabled
}

Expand All @@ -85,15 +85,15 @@ func (e *ethAPIDummy) EthGetTransactionByBlockNumberAndIndex(ctx context.Context
return types.EthTx{}, ErrModuleDisabled
}

func (e *ethAPIDummy) EthGetCode(ctx context.Context, address types.EthAddress, blkOpt string) (types.EthBytes, error) {
func (e *ethAPIDummy) EthGetCode(ctx context.Context, address types.EthAddress, blkParam types.EthBlockNumberOrHash) (types.EthBytes, error) {
return nil, ErrModuleDisabled
}

func (e *ethAPIDummy) EthGetStorageAt(ctx context.Context, address types.EthAddress, position types.EthBytes, blkParam string) (types.EthBytes, error) {
func (e *ethAPIDummy) EthGetStorageAt(ctx context.Context, address types.EthAddress, position types.EthBytes, blkParam types.EthBlockNumberOrHash) (types.EthBytes, error) {
return nil, ErrModuleDisabled
}

func (e *ethAPIDummy) EthGetBalance(ctx context.Context, address types.EthAddress, blkParam string) (types.EthBigInt, error) {
func (e *ethAPIDummy) EthGetBalance(ctx context.Context, address types.EthAddress, blkParam types.EthBlockNumberOrHash) (types.EthBigInt, error) {
return types.EthBigIntZero, ErrModuleDisabled
}

Expand Down Expand Up @@ -125,7 +125,7 @@ func (e *ethAPIDummy) EthEstimateGas(ctx context.Context, tx types.EthCall) (typ
return 0, ErrModuleDisabled
}

func (e *ethAPIDummy) EthCall(ctx context.Context, tx types.EthCall, blkParam string) (types.EthBytes, error) {
func (e *ethAPIDummy) EthCall(ctx context.Context, tx types.EthCall, blkParam types.EthBlockNumberOrHash) (types.EthBytes, error) {
return nil, ErrModuleDisabled
}

Expand Down
91 changes: 76 additions & 15 deletions app/submodule/eth/eth_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,67 @@ func (a *ethAPI) EthGetBlockByHash(ctx context.Context, blkHash types.EthHash, f
return newEthBlockFromFilecoinTipSet(ctx, ts, fullTxInfo, a.em.chainModule.MessageStore, a.chain)
}

func (a *ethAPI) getTipsetByEthBlockNumberOrHash(ctx context.Context, blkParam types.EthBlockNumberOrHash) (*types.TipSet, error) {
head, err := a.chain.ChainHead(ctx)
if err != nil {
return nil, err
}

predefined := blkParam.PredefinedBlock
if predefined != nil {
if *predefined == "earliest" {
return nil, fmt.Errorf("block param \"earliest\" is not supported")
} else if *predefined == "pending" {
return head, nil
} else if *predefined == "latest" {
parent, err := a.chain.ChainGetTipSet(ctx, head.Parents())
if err != nil {
return nil, fmt.Errorf("cannot get parent tipset")
}
return parent, nil
} else {
return nil, fmt.Errorf("unknown predefined block %s", *predefined)
}
}

if blkParam.BlockNumber != nil {
height := abi.ChainEpoch(*blkParam.BlockNumber)
if height > head.Height()-1 {
return nil, fmt.Errorf("requested a future epoch (beyond 'latest')")
}
ts, err := a.chain.ChainGetTipSetByHeight(ctx, height, head.Key())
if err != nil {
return nil, fmt.Errorf("cannot get tipset at height: %v", height)
}
return ts, nil
}

if blkParam.BlockHash != nil {
ts, err := a.em.chainModule.ChainReader.GetTipSetByCid(ctx, blkParam.BlockHash.ToCid())
if err != nil {
return nil, fmt.Errorf("cannot get tipset by hash: %v", err)
}

// verify that the tipset is in the canonical chain
if blkParam.RequireCanonical {
// walk up the current chain (our head) until we reach ts.Height()
walkTS, err := a.chain.ChainGetTipSetByHeight(ctx, ts.Height(), head.Key())
if err != nil {
return nil, fmt.Errorf("cannot get tipset at height: %v", ts.Height())
}

// verify that it equals the expected tipset
if !walkTS.Equals(ts) {
return nil, fmt.Errorf("tipset is not canonical")
}
}

return ts, nil
}

return nil, errors.New("invalid block param")
}

func (a *ethAPI) parseBlkParam(ctx context.Context, blkParam string, strict bool) (tipset *types.TipSet, err error) {
if blkParam == "earliest" {
return nil, fmt.Errorf("block param \"earliest\" is not supported")
Expand Down Expand Up @@ -331,14 +392,14 @@ func (a *ethAPI) EthGetTransactionHashByCid(ctx context.Context, cid cid.Cid) (*
return &hash, err
}

func (a *ethAPI) EthGetTransactionCount(ctx context.Context, sender types.EthAddress, blkParam string) (types.EthUint64, error) {
func (a *ethAPI) EthGetTransactionCount(ctx context.Context, sender types.EthAddress, blkParam types.EthBlockNumberOrHash) (types.EthUint64, error) {
addr, err := sender.ToFilecoinAddress()
if err != nil {
return types.EthUint64(0), err
}
ts, err := a.parseBlkParam(ctx, blkParam, false)
ts, err := a.getTipsetByEthBlockNumberOrHash(ctx, blkParam)
if err != nil {
return types.EthUint64(0), fmt.Errorf("cannot parse block param: %s", blkParam)
return types.EthUint64(0), fmt.Errorf("cannot parse block param: %v, %v", blkParam, err)
}

// First, handle the case where the "sender" is an EVM actor.
Expand Down Expand Up @@ -418,15 +479,15 @@ func (a *ethAPI) EthGetTransactionByBlockNumberAndIndex(ctx context.Context, blk
}

// EthGetCode returns string value of the compiled bytecode
func (a *ethAPI) EthGetCode(ctx context.Context, ethAddr types.EthAddress, blkParam string) (types.EthBytes, error) {
func (a *ethAPI) EthGetCode(ctx context.Context, ethAddr types.EthAddress, blkParam types.EthBlockNumberOrHash) (types.EthBytes, error) {
to, err := ethAddr.ToFilecoinAddress()
if err != nil {
return nil, fmt.Errorf("cannot get Filecoin address: %w", err)
}

ts, err := a.parseBlkParam(ctx, blkParam, false)
ts, err := a.getTipsetByEthBlockNumberOrHash(ctx, blkParam)
if err != nil {
return nil, fmt.Errorf("cannot parse block param: %s", blkParam)
return nil, fmt.Errorf("cannot parse block param: %v, %v", blkParam, err)
}

// StateManager.Call will panic if there is no parent
Expand Down Expand Up @@ -502,10 +563,10 @@ func (a *ethAPI) EthGetCode(ctx context.Context, ethAddr types.EthAddress, blkPa
return blk.RawData(), nil
}

func (a *ethAPI) EthGetStorageAt(ctx context.Context, ethAddr types.EthAddress, position types.EthBytes, blkParam string) (types.EthBytes, error) {
ts, err := a.parseBlkParam(ctx, blkParam, false)
func (a *ethAPI) EthGetStorageAt(ctx context.Context, ethAddr types.EthAddress, position types.EthBytes, blkParam types.EthBlockNumberOrHash) (types.EthBytes, error) {
ts, err := a.getTipsetByEthBlockNumberOrHash(ctx, blkParam)
if err != nil {
return nil, fmt.Errorf("cannot parse block param: %s", blkParam)
return nil, fmt.Errorf("cannot parse block param: %v, %v", blkParam, err)
}

l := len(position)
Expand Down Expand Up @@ -593,15 +654,15 @@ func (a *ethAPI) EthGetStorageAt(ctx context.Context, ethAddr types.EthAddress,
return types.EthBytes(ret), nil
}

func (a *ethAPI) EthGetBalance(ctx context.Context, address types.EthAddress, blkParam string) (types.EthBigInt, error) {
func (a *ethAPI) EthGetBalance(ctx context.Context, address types.EthAddress, blkParam types.EthBlockNumberOrHash) (types.EthBigInt, error) {
filAddr, err := address.ToFilecoinAddress()
if err != nil {
return types.EthBigInt{}, err
}

ts, err := a.parseBlkParam(ctx, blkParam, false)
ts, err := a.getTipsetByEthBlockNumberOrHash(ctx, blkParam)
if err != nil {
return types.EthBigInt{}, fmt.Errorf("cannot parse block param: %s", blkParam)
return types.EthBigInt{}, fmt.Errorf("cannot parse block param: %v, %v", blkParam, err)
}

_, view, err := a.em.chainModule.Stmgr.StateView(ctx, ts)
Expand Down Expand Up @@ -1025,14 +1086,14 @@ func ethGasSearch(
return -1, fmt.Errorf("message execution failed: exit %s, reason: %s", res.MsgRct.ExitCode, res.Error)
}

func (a *ethAPI) EthCall(ctx context.Context, tx types.EthCall, blkParam string) (types.EthBytes, error) {
func (a *ethAPI) EthCall(ctx context.Context, tx types.EthCall, blkParam types.EthBlockNumberOrHash) (types.EthBytes, error) {
msg, err := a.ethCallToFilecoinMessage(ctx, tx)
if err != nil {
return nil, fmt.Errorf("failed to convert ethcall to filecoin message: %w", err)
}
ts, err := a.parseBlkParam(ctx, blkParam, false)
ts, err := a.getTipsetByEthBlockNumberOrHash(ctx, blkParam)
if err != nil {
return nil, fmt.Errorf("cannot parse block param: %s", blkParam)
return nil, fmt.Errorf("cannot parse block param: %v, %v", blkParam, err)
}

invokeResult, err := a.applyMessage(ctx, msg, ts.Key())
Expand Down
4 changes: 2 additions & 2 deletions cmd/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ var evmCallSimulateCmd = &cmds.Command{
From: &fromEthAddr,
To: &toEthAddr,
Data: params,
}, "")
}, types.NewEthBlockNumberOrHashFromPredefined("latest"))
if err != nil {
_ = re.Emit(fmt.Sprintln("Eth call fails, return val: ", res))
return err
Expand Down Expand Up @@ -468,7 +468,7 @@ var evmGetBytecode = &cmds.Command{
ctx := requestContext(req)
api := getEnv(env)

code, err := api.EthAPI.EthGetCode(ctx, contractAddr, "latest")
code, err := api.EthAPI.EthGetCode(ctx, contractAddr, types.NewEthBlockNumberOrHashFromPredefined("latest"))
if err != nil {
return err
}
Expand Down
90 changes: 90 additions & 0 deletions venus-shared/actors/types/eth.go
Original file line number Diff line number Diff line change
Expand Up @@ -872,3 +872,93 @@ func (e EthFeeHistoryParams) MarshalJSON() ([]byte, error) {
}
return json.Marshal([]interface{}{e.BlkCount, e.NewestBlkNum})
}

type EthBlockNumberOrHash struct {
// PredefinedBlock can be one of "earliest", "pending" or "latest". We could merge this
// field with BlockNumber if the latter could store negative numbers representing
// each predefined value (e.g. -1 for "earliest", -2 for "pending" and -3 for "latest")
PredefinedBlock *string `json:"-"`

BlockNumber *EthUint64 `json:"blockNumber,omitempty"`
BlockHash *EthHash `json:"blockHash,omitempty"`
RequireCanonical bool `json:"requireCanonical,omitempty"`
}

func NewEthBlockNumberOrHashFromPredefined(predefined string) EthBlockNumberOrHash {
return EthBlockNumberOrHash{
PredefinedBlock: &predefined,
BlockNumber: nil,
BlockHash: nil,
RequireCanonical: false,
}
}

func NewEthBlockNumberOrHashFromNumber(number EthUint64) EthBlockNumberOrHash {
return EthBlockNumberOrHash{
PredefinedBlock: nil,
BlockNumber: &number,
BlockHash: nil,
RequireCanonical: false,
}
}

func NewEthBlockNumberOrHashFromHexString(str string) (EthBlockNumberOrHash, error) {
// check if block param is a number (decimal or hex)
var num EthUint64
err := num.UnmarshalJSON([]byte(str))
if err != nil {
return NewEthBlockNumberOrHashFromNumber(0), err
}

return EthBlockNumberOrHash{
PredefinedBlock: nil,
BlockNumber: &num,
BlockHash: nil,
RequireCanonical: false,
}, nil
}

func (e EthBlockNumberOrHash) MarshalJSON() ([]byte, error) {
if e.PredefinedBlock != nil {
return json.Marshal(*e.PredefinedBlock)
}

type tmpStruct EthBlockNumberOrHash
return json.Marshal(tmpStruct(e))
}

func (e *EthBlockNumberOrHash) UnmarshalJSON(b []byte) error {
// we first try to unmarshal into a EthBlockNumberOrHash struct to check
// if the block param is a block hash or block number (see EIP-1898). We use
// a temporary struct to avoid infinite recursion.
type tmpStruct EthBlockNumberOrHash
var tmp tmpStruct
if err := json.Unmarshal(b, &tmp); err == nil {
if tmp.BlockNumber != nil && tmp.BlockHash != nil {
return errors.New("cannot specify both blockNumber and blockHash")
}

*e = EthBlockNumberOrHash(tmp)
return nil
}

// check if block param is once of the special strings
var str string
err := json.Unmarshal(b, &str)
if err != nil {
return err
}
if str == "earliest" || str == "pending" || str == "latest" {
e.PredefinedBlock = &str
return nil
}

// check if block param is a number (decimal or hex)
var num EthUint64
if err := num.UnmarshalJSON(b); err == nil {
e.BlockNumber = &num
return nil
}

return errors.New("invalid block param")
}
50 changes: 25 additions & 25 deletions venus-shared/api/chain/v1/eth.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,31 +31,31 @@ type IETH interface {
// EthGetBlockTransactionCountByHash returns the number of messages in the TipSet
EthGetBlockTransactionCountByHash(ctx context.Context, blkHash types.EthHash) (types.EthUint64, error) //perm:read

EthGetBlockByHash(ctx context.Context, blkHash types.EthHash, fullTxInfo bool) (types.EthBlock, error) //perm:read
EthGetBlockByNumber(ctx context.Context, blkNum string, fullTxInfo bool) (types.EthBlock, error) //perm:read
EthGetTransactionByHash(ctx context.Context, txHash *types.EthHash) (*types.EthTx, error) //perm:read
EthGetTransactionByHashLimited(ctx context.Context, txHash *types.EthHash, limit abi.ChainEpoch) (*types.EthTx, error) //perm:read
EthGetTransactionHashByCid(ctx context.Context, cid cid.Cid) (*types.EthHash, error) //perm:read
EthGetMessageCidByTransactionHash(ctx context.Context, txHash *types.EthHash) (*cid.Cid, error) //perm:read
EthGetTransactionCount(ctx context.Context, sender types.EthAddress, blkOpt string) (types.EthUint64, error) //perm:read
EthGetTransactionReceipt(ctx context.Context, txHash types.EthHash) (*types.EthTxReceipt, error) //perm:read
EthGetTransactionReceiptLimited(ctx context.Context, txHash types.EthHash, limit abi.ChainEpoch) (*types.EthTxReceipt, error) //perm:read
EthGetTransactionByBlockHashAndIndex(ctx context.Context, blkHash types.EthHash, txIndex types.EthUint64) (types.EthTx, error) //perm:read
EthGetTransactionByBlockNumberAndIndex(ctx context.Context, blkNum types.EthUint64, txIndex types.EthUint64) (types.EthTx, error) //perm:read

EthGetCode(ctx context.Context, address types.EthAddress, blkOpt string) (types.EthBytes, error) //perm:read
EthGetStorageAt(ctx context.Context, address types.EthAddress, position types.EthBytes, blkParam string) (types.EthBytes, error) //perm:read
EthGetBalance(ctx context.Context, address types.EthAddress, blkParam string) (types.EthBigInt, error) //perm:read
EthChainId(ctx context.Context) (types.EthUint64, error) //perm:read
NetVersion(ctx context.Context) (string, error) //perm:read
NetListening(ctx context.Context) (bool, error) //perm:read
EthProtocolVersion(ctx context.Context) (types.EthUint64, error) //perm:read
EthGasPrice(ctx context.Context) (types.EthBigInt, error) //perm:read
EthFeeHistory(ctx context.Context, p jsonrpc.RawParams) (types.EthFeeHistory, error) //perm:read

EthMaxPriorityFeePerGas(ctx context.Context) (types.EthBigInt, error) //perm:read
EthEstimateGas(ctx context.Context, tx types.EthCall) (types.EthUint64, error) //perm:read
EthCall(ctx context.Context, tx types.EthCall, blkParam string) (types.EthBytes, error) //perm:read
EthGetBlockByHash(ctx context.Context, blkHash types.EthHash, fullTxInfo bool) (types.EthBlock, error) //perm:read
EthGetBlockByNumber(ctx context.Context, blkNum string, fullTxInfo bool) (types.EthBlock, error) //perm:read
EthGetTransactionByHash(ctx context.Context, txHash *types.EthHash) (*types.EthTx, error) //perm:read
EthGetTransactionByHashLimited(ctx context.Context, txHash *types.EthHash, limit abi.ChainEpoch) (*types.EthTx, error) //perm:read
EthGetTransactionHashByCid(ctx context.Context, cid cid.Cid) (*types.EthHash, error) //perm:read
EthGetMessageCidByTransactionHash(ctx context.Context, txHash *types.EthHash) (*cid.Cid, error) //perm:read
EthGetTransactionCount(ctx context.Context, sender types.EthAddress, blkParam types.EthBlockNumberOrHash) (types.EthUint64, error) //perm:read
EthGetTransactionReceipt(ctx context.Context, txHash types.EthHash) (*types.EthTxReceipt, error) //perm:read
EthGetTransactionReceiptLimited(ctx context.Context, txHash types.EthHash, limit abi.ChainEpoch) (*types.EthTxReceipt, error) //perm:read
EthGetTransactionByBlockHashAndIndex(ctx context.Context, blkHash types.EthHash, txIndex types.EthUint64) (types.EthTx, error) //perm:read
EthGetTransactionByBlockNumberAndIndex(ctx context.Context, blkNum types.EthUint64, txIndex types.EthUint64) (types.EthTx, error) //perm:read

EthGetCode(ctx context.Context, address types.EthAddress, blkParam types.EthBlockNumberOrHash) (types.EthBytes, error) //perm:read
EthGetStorageAt(ctx context.Context, address types.EthAddress, position types.EthBytes, blkParam types.EthBlockNumberOrHash) (types.EthBytes, error) //perm:read
EthGetBalance(ctx context.Context, address types.EthAddress, blkParam types.EthBlockNumberOrHash) (types.EthBigInt, error) //perm:read
EthChainId(ctx context.Context) (types.EthUint64, error) //perm:read
NetVersion(ctx context.Context) (string, error) //perm:read
NetListening(ctx context.Context) (bool, error) //perm:read
EthProtocolVersion(ctx context.Context) (types.EthUint64, error) //perm:read
EthGasPrice(ctx context.Context) (types.EthBigInt, error) //perm:read
EthFeeHistory(ctx context.Context, p jsonrpc.RawParams) (types.EthFeeHistory, error) //perm:read

EthMaxPriorityFeePerGas(ctx context.Context) (types.EthBigInt, error) //perm:read
EthEstimateGas(ctx context.Context, tx types.EthCall) (types.EthUint64, error) //perm:read
EthCall(ctx context.Context, tx types.EthCall, blkParam types.EthBlockNumberOrHash) (types.EthBytes, error) //perm:read

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

Expand Down
Loading
Loading