Merge branch 'release/v1.20.0' into asr/merge-release-into-master

This commit is contained in:
Aayush 2023-02-17 16:03:47 -05:00
commit 984b4c78cb
51 changed files with 608 additions and 154 deletions

View File

@ -674,6 +674,12 @@ workflows:
- build - build
suite: itest-eth_account_abstraction suite: itest-eth_account_abstraction
target: "./itests/eth_account_abstraction_test.go" target: "./itests/eth_account_abstraction_test.go"
- test:
name: test-itest-eth_api
requires:
- build
suite: itest-eth_api
target: "./itests/eth_api_test.go"
- test: - test:
name: test-itest-eth_balance name: test-itest-eth_balance
requires: requires:

View File

@ -1,5 +1,55 @@
# Lotus changelog # Lotus changelog
# 1.20.0-rc1 / 2023-02-14
This is the first release candidate for the upcoming MANDATORY 1.20.0 release of Lotus. This release will deliver the Hygge network upgrade, introducing Filecoin network version 18. The centerpiece of the upgrade is the introduction of the [Filecoin Virtual Machine (FVM)s Milestone 2.1](https://fvm.filecoin.io/), which will allow for EVM-compatible contracts to be deployed on the Filecoin network. This upgrade delivers user-programmablity to the Filecoin network for the first time!
Note that this release candidate does NOT set the epoch at which mainnet will upgrade; that detail will be finalized in the 1.20.0 release.
The Hygge upgrade introduces the following Filecoin Improvement Proposals (FIPs), delivered in FVM3 (see FVM [v3.0.0-rc.1](https://github.com/filecoin-project/ref-fvm/pull/1664)) and builtin-actors v10 (see actors [v1.10.0-rc.1](https://github.com/filecoin-project/builtin-actors/releases/tag/v10.0.0-rc.1)):
- [FIP-0048](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0048.md): f4 Address Class
- [FIP-0049](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0049.md): Actor events
- [FIP-0050](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0050.md): API between user-programmed actors and built-in actors
- [FIP-0054](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0054.md): Filecoin EVM runtime (FEVM)
- [FIP-0055](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0055.md): Supporting Ethereum Accounts, Addresses, and Transactions
- [FIP-0057](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0057.md): Update gas charging schedule and system limits for FEVM
## Filecoin Ethereum Virtual Machine (FEVM)
The Filecoin Ethereum Virtual Machine (FEVM) is built on top of the WASM-based execution environment introduced in the Skyr v16 upgrade. The chief feature introduced is the ability for anyone participating in the Filecoin network to deploy their own EVM-compatible contracts onto the blockchain, and invoke them as appropriate.
## New Built-in Actors
The FEVM is principally delivered through the introduction of **the new [EVM actor](https://github.com/filecoin-project/builtin-actors/tree/master/actors/evm)**. This actor “represents” smart contracts on the Filecoin network, and includes an interpreter that implements all EVM opcodes as their Filecoin equivalents, and translates state I/O operations to be compatible with Filecoins IPLD-based data model. For more on the EVM actors, please see [FIP-0054](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0054.md).
The creation of EVM actors is managed by **the new** [Ethereum Address Manager actor (EAM)](https://github.com/filecoin-project/builtin-actors/tree/master/actors/eam), a singleton that is invoked in order to deploy EVM actors. In order to make usage of the FEVM as seamless as possible for users familiar with the Ethereum ecosystem, this upgrades also introduces **a dedicated actor to serve as “[Ethereum Accounts](https://github.com/filecoin-project/builtin-actors/tree/master/actors/ethaccount)”**. This actor exists to allow for secp keys to be used in the Ethereum addressing scheme. **The last new built-in actor introduced is [the Placeholder actor](https://github.com/filecoin-project/builtin-actors/tree/master/actors/placeholder)**, a thin “shell” of an actor that can transform into either EVM or EthAccount actors. For more on the EAM, EthAccount, and Placeholder actors, please see [FIP-0055](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0055.md).
## Calibration nv18 Hygge Upgrade
This release candidate sets the calibration-net nv18 Hygge upgrade at epoch 322354, 22023-02-21T16:30:00Z. The bundle the network will be using is [v10.0.0 actors](https://github.com/filecoin-project/builtin-actors/releases/tag/v10.0.0-rc.1)
(located at `build/actors/v10.tar.zst`) upon/post migration, manifest CID `bafy2bzaced25ta3j6ygs34roprilbtb3f6mxifyfnm7z7ndquaruxzdq3y7lo`.
## Node Operators
FVM has been running in lotus since v1.16.0 and up, and the new FEVM does not increase any node hardware spec requirement.
With FEVM on Filecoin, we aim to provide full compatibility with the existing EVM ecosystem and its tooling out of box and thus, lotus now provides a full set of [Ethereum-styled APIs](https://github.com/filecoin-project/lotus/blob/release/v1.20.0/node/impl/full/eth.go) for developers and token holders to interact with the Filecoin network as well.
**Enable Ethereum JSON RPC API**
Note that Ethereum APIs are only supported in the lotus v1 API, meaning that any node operator who wants to enable Eth API services must be using the v1 API, instead of the v0 API. To enable Eth RPC, simply set `EnableEthRPC` to `true` in your node config.toml file; or set env var `LOTUS_FEVM_ENABLEETHRPC` to `1` before starting your lotus node.
**Eth tx hash and Filecoin message CID**
Most of the Eth APIs take Eth accounts and tx has as an input, and they start with `0x` , and that is what Ethereum tooling support. However, in Filecoin, we have Filecoin account formats where things start with `f` (`f410` specifically for eth accounts on Filecoin) and the messages are in the format of CIDs. To enable a smooth developer experience, Lotus internally converts between Ethereum address and Filecoin account address as needed. In addition, lotus also keeps a Eth tx hash <> Filecoin message CID map and stores them in a SQLite database as node sees a FEVM messages. The database is initiated and the maps are populated automatically in `~/<lotus_repo>/sqlite/txhash.db` for any node that as Eth RPC enabled. Node operators can configure how many historical mappings they wanna store by configuring `EthTxHashMappingLifetimeDays` .
**Events***
[FIP-0049 introduces actor events](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0049.md) that can be emitted and externally observable during message execution. An `events.db` is created automatically under `~/<lotus_repo>/sqlite` to store these events if the node has Eth RPC enabled. Node operators can configure the events support base on their needs by configuration `Events` configurations.
Note: All three features are new and we welcome user feedbacks, create an issue if you have any enhancements that youd like to see!
# 1.19.0 / 2022-12-07 # 1.19.0 / 2022-12-07
This is an optional feature release of Lotus. This feature release includes the SplitStore beta, the experimental Lotus node cluster feature, as well as numerous enhancments and bugfixes. This is an optional feature release of Lotus. This feature release includes the SplitStore beta, the experimental Lotus node cluster feature, as well as numerous enhancments and bugfixes.

View File

@ -298,7 +298,7 @@ actors-gen: actors-code-gen fiximports
.PHONY: actors-gen .PHONY: actors-gen
bundle-gen: bundle-gen:
$(GOCC) run ./gen/bundle $(RELEASE) $(GOCC) run ./gen/bundle $(VERSION) $(RELEASE) $(RELEASE_OVERRIDES)
$(GOCC) fmt ./build/... $(GOCC) fmt ./build/...
.PHONY: bundle-gen .PHONY: bundle-gen

View File

@ -769,6 +769,8 @@ type FullNode interface {
// //
// EthAccounts will always return [] since we don't expect Lotus to manage private keys // EthAccounts will always return [] since we don't expect Lotus to manage private keys
EthAccounts(ctx context.Context) ([]ethtypes.EthAddress, error) //perm:read EthAccounts(ctx context.Context) ([]ethtypes.EthAddress, error) //perm:read
// EthAddressToFilecoinAddress converts an EthAddress into an f410 Filecoin Address
EthAddressToFilecoinAddress(ctx context.Context, ethAddress ethtypes.EthAddress) (address.Address, error) //perm:read
// EthBlockNumber returns the height of the latest (heaviest) TipSet // EthBlockNumber returns the height of the latest (heaviest) TipSet
EthBlockNumber(ctx context.Context) (ethtypes.EthUint64, error) //perm:read EthBlockNumber(ctx context.Context) (ethtypes.EthUint64, error) //perm:read
// EthGetBlockTransactionCountByNumber returns the number of messages in the TipSet // EthGetBlockTransactionCountByNumber returns the number of messages in the TipSet

View File

@ -953,6 +953,21 @@ func (mr *MockFullNodeMockRecorder) EthAccounts(arg0 interface{}) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EthAccounts", reflect.TypeOf((*MockFullNode)(nil).EthAccounts), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EthAccounts", reflect.TypeOf((*MockFullNode)(nil).EthAccounts), arg0)
} }
// EthAddressToFilecoinAddress mocks base method.
func (m *MockFullNode) EthAddressToFilecoinAddress(arg0 context.Context, arg1 ethtypes.EthAddress) (address.Address, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "EthAddressToFilecoinAddress", arg0, arg1)
ret0, _ := ret[0].(address.Address)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// EthAddressToFilecoinAddress indicates an expected call of EthAddressToFilecoinAddress.
func (mr *MockFullNodeMockRecorder) EthAddressToFilecoinAddress(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EthAddressToFilecoinAddress", reflect.TypeOf((*MockFullNode)(nil).EthAddressToFilecoinAddress), arg0, arg1)
}
// EthBlockNumber mocks base method. // EthBlockNumber mocks base method.
func (m *MockFullNode) EthBlockNumber(arg0 context.Context) (ethtypes.EthUint64, error) { func (m *MockFullNode) EthBlockNumber(arg0 context.Context) (ethtypes.EthUint64, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()

View File

@ -244,6 +244,8 @@ type FullNodeMethods struct {
EthAccounts func(p0 context.Context) ([]ethtypes.EthAddress, error) `perm:"read"` EthAccounts func(p0 context.Context) ([]ethtypes.EthAddress, error) `perm:"read"`
EthAddressToFilecoinAddress func(p0 context.Context, p1 ethtypes.EthAddress) (address.Address, error) `perm:"read"`
EthBlockNumber func(p0 context.Context) (ethtypes.EthUint64, error) `perm:"read"` EthBlockNumber func(p0 context.Context) (ethtypes.EthUint64, error) `perm:"read"`
EthCall func(p0 context.Context, p1 ethtypes.EthCall, p2 string) (ethtypes.EthBytes, error) `perm:"read"` EthCall func(p0 context.Context, p1 ethtypes.EthCall, p2 string) (ethtypes.EthBytes, error) `perm:"read"`
@ -2007,6 +2009,17 @@ func (s *FullNodeStub) EthAccounts(p0 context.Context) ([]ethtypes.EthAddress, e
return *new([]ethtypes.EthAddress), ErrNotSupported return *new([]ethtypes.EthAddress), ErrNotSupported
} }
func (s *FullNodeStruct) EthAddressToFilecoinAddress(p0 context.Context, p1 ethtypes.EthAddress) (address.Address, error) {
if s.Internal.EthAddressToFilecoinAddress == nil {
return *new(address.Address), ErrNotSupported
}
return s.Internal.EthAddressToFilecoinAddress(p0, p1)
}
func (s *FullNodeStub) EthAddressToFilecoinAddress(p0 context.Context, p1 ethtypes.EthAddress) (address.Address, error) {
return *new(address.Address), ErrNotSupported
}
func (s *FullNodeStruct) EthBlockNumber(p0 context.Context) (ethtypes.EthUint64, error) { func (s *FullNodeStruct) EthBlockNumber(p0 context.Context) (ethtypes.EthUint64, error) {
if s.Internal.EthBlockNumber == nil { if s.Internal.EthBlockNumber == nil {
return *new(ethtypes.EthUint64), ErrNotSupported return *new(ethtypes.EthUint64), ErrNotSupported

View File

@ -52,4 +52,4 @@ popd
echo "Generating metadata..." echo "Generating metadata..."
make -C ../../ RELEASE="$RELEASE" bundle-gen make -C ../../ VERSION="$VERSION" RELEASE="$RELEASE" RELEASE_OVERRIDES="${RELEASE_OVERRIDES[*]}" bundle-gen

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -48,6 +48,7 @@ type State interface {
cbor.Marshaler cbor.Marshaler
Nonce() (uint64, error) Nonce() (uint64, error)
IsAlive() (bool, error)
GetState() interface{} GetState() interface{}
GetBytecode() ([]byte, error) GetBytecode() ([]byte, error)

View File

@ -48,6 +48,7 @@ type State interface {
cbor.Marshaler cbor.Marshaler
Nonce() (uint64, error) Nonce() (uint64, error)
IsAlive() (bool, error)
GetState() interface{} GetState() interface{}
GetBytecode() ([]byte, error) GetBytecode() ([]byte, error)

View File

@ -4,6 +4,8 @@ import (
"github.com/ipfs/go-cid" "github.com/ipfs/go-cid"
"github.com/filecoin-project/lotus/chain/actors/adt" "github.com/filecoin-project/lotus/chain/actors/adt"
"github.com/filecoin-project/lotus/chain/actors/adt"
"github.com/filecoin-project/go-state-types/abi"
evm{{.v}} "github.com/filecoin-project/go-state-types/builtin{{.import}}evm" evm{{.v}} "github.com/filecoin-project/go-state-types/builtin{{.import}}evm"
) )
@ -40,6 +42,10 @@ func (s *state{{.v}}) Nonce() (uint64, error) {
return s.State.Nonce, nil return s.State.Nonce, nil
} }
func (s *state{{.v}}) IsAlive() (bool, error) {
return s.State.Tombstone == nil, nil
}
func (s *state{{.v}}) GetState() interface{} { func (s *state{{.v}}) GetState() interface{} {
return &s.State return &s.State
} }

View File

@ -41,6 +41,10 @@ func (s *state10) Nonce() (uint64, error) {
return s.State.Nonce, nil return s.State.Nonce, nil
} }
func (s *state10) IsAlive() (bool, error) {
return s.State.Tombstone == nil, nil
}
func (s *state10) GetState() interface{} { func (s *state10) GetState() interface{} {
return &s.State return &s.State
} }

View File

@ -66,6 +66,18 @@ func EthUint64FromHex(s string) (EthUint64, error) {
return EthUint64(parsedInt), nil return EthUint64(parsedInt), nil
} }
// Parse a uint64 from big-endian encoded bytes.
func EthUint64FromBytes(b []byte) (EthUint64, error) {
if len(b) != 32 {
return 0, xerrors.Errorf("eth int must be 32 bytes long")
}
var zeros [32 - 8]byte
if !bytes.Equal(b[:len(zeros)], zeros[:]) {
return 0, xerrors.Errorf("eth int overflows 64 bits")
}
return EthUint64(binary.BigEndian.Uint64(b[len(zeros):])), nil
}
func (e EthUint64) Hex() string { func (e EthUint64) Hex() string {
if e == 0 { if e == 0 {
return "0x0" return "0x0"
@ -78,11 +90,15 @@ type EthBigInt big.Int
var EthBigIntZero = EthBigInt{Int: big.Zero().Int} var EthBigIntZero = EthBigInt{Int: big.Zero().Int}
func (e EthBigInt) MarshalJSON() ([]byte, error) { func (e EthBigInt) String() string {
if e.Int == nil || e.Int.BitLen() == 0 { if e.Int == nil || e.Int.BitLen() == 0 {
return json.Marshal("0x0") return "0x0"
} }
return json.Marshal(fmt.Sprintf("0x%x", e.Int)) return fmt.Sprintf("0x%x", e.Int)
}
func (e EthBigInt) MarshalJSON() ([]byte, error) {
return json.Marshal(e.String())
} }
func (e *EthBigInt) UnmarshalJSON(b []byte) error { func (e *EthBigInt) UnmarshalJSON(b []byte) error {
@ -106,12 +122,15 @@ func (e *EthBigInt) UnmarshalJSON(b []byte) error {
// EthBytes represent arbitrary bytes. A nil or empty slice serializes to "0x". // EthBytes represent arbitrary bytes. A nil or empty slice serializes to "0x".
type EthBytes []byte type EthBytes []byte
func (e EthBytes) MarshalJSON() ([]byte, error) { func (e EthBytes) String() string {
if len(e) == 0 { if len(e) == 0 {
return json.Marshal("0x") return "0x"
} }
s := hex.EncodeToString(e) return "0x" + hex.EncodeToString(e)
return json.Marshal("0x" + s) }
func (e EthBytes) MarshalJSON() ([]byte, error) {
return json.Marshal(e.String())
} }
func (e *EthBytes) UnmarshalJSON(b []byte) error { func (e *EthBytes) UnmarshalJSON(b []byte) error {

View File

@ -21,6 +21,7 @@ import (
"github.com/filecoin-project/lotus/api/v0api" "github.com/filecoin-project/lotus/api/v0api"
"github.com/filecoin-project/lotus/chain/actors" "github.com/filecoin-project/lotus/chain/actors"
"github.com/filecoin-project/lotus/chain/actors/builtin"
"github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/types/ethtypes" "github.com/filecoin-project/lotus/chain/types/ethtypes"
) )
@ -75,13 +76,18 @@ var EvmGetInfoCmd = &cli.Command{
} }
actor, err := api.StateGetActor(ctx, faddr, types.EmptyTSK) actor, err := api.StateGetActor(ctx, faddr, types.EmptyTSK)
if err != nil {
return err
}
fmt.Println("Filecoin address: ", faddr) fmt.Println("Filecoin address: ", faddr)
fmt.Println("Eth address: ", eaddr) fmt.Println("Eth address: ", eaddr)
fmt.Println("Code cid: ", actor.Code.String()) if err != nil {
fmt.Printf("Actor lookup failed for faddr %s with error: %s\n", faddr, err)
} else {
idAddr, err := api.StateLookupID(ctx, faddr, types.EmptyTSK)
if err == nil {
fmt.Println("ID address: ", idAddr)
fmt.Println("Code cid: ", actor.Code.String())
fmt.Println("Actor Type: ", builtin.ActorNameByCode(actor.Code))
}
}
return nil return nil
}, },

View File

@ -455,9 +455,8 @@ var chainBalanceStateCmd = &cli.Command{
Description: "Produces a csv file of all account balances from a given stateroot", Description: "Produces a csv file of all account balances from a given stateroot",
Flags: []cli.Flag{ Flags: []cli.Flag{
&cli.StringFlag{ &cli.StringFlag{
Name: "repo", Name: "repo",
Value: "~/.lotus", Value: "~/.lotus",
EnvVars: []string{"LOTUS_PATH"},
}, },
&cli.BoolFlag{ &cli.BoolFlag{
Name: "miner-info", Name: "miner-info",
@ -679,9 +678,8 @@ var chainPledgeCmd = &cli.Command{
Description: "Calculate sector pledge numbers", Description: "Calculate sector pledge numbers",
Flags: []cli.Flag{ Flags: []cli.Flag{
&cli.StringFlag{ &cli.StringFlag{
Name: "repo", Name: "repo",
Value: "~/.lotus", Value: "~/.lotus",
EnvVars: []string{"LOTUS_PATH"},
}, },
}, },
ArgsUsage: "[stateroot epoch]", ArgsUsage: "[stateroot epoch]",

View File

@ -41,11 +41,6 @@ var datastoreListCmd = &cli.Command{
Name: "list", Name: "list",
Description: "list datastore keys", Description: "list datastore keys",
Flags: []cli.Flag{ Flags: []cli.Flag{
&cli.StringFlag{
Name: "repo",
Value: "~/.lotus",
EnvVars: []string{"LOTUS_PATH"},
},
&cli.StringFlag{ &cli.StringFlag{
Name: "repo-type", Name: "repo-type",
Usage: "node type (FullNode, StorageMiner, Worker, Wallet)", Usage: "node type (FullNode, StorageMiner, Worker, Wallet)",
@ -115,11 +110,6 @@ var datastoreGetCmd = &cli.Command{
Name: "get", Name: "get",
Description: "list datastore keys", Description: "list datastore keys",
Flags: []cli.Flag{ Flags: []cli.Flag{
&cli.StringFlag{
Name: "repo",
Value: "~/.lotus",
EnvVars: []string{"LOTUS_PATH"},
},
&cli.StringFlag{ &cli.StringFlag{
Name: "repo-type", Name: "repo-type",
Usage: "node type (FullNode, StorageMiner, Worker, Wallet)", Usage: "node type (FullNode, StorageMiner, Worker, Wallet)",
@ -133,7 +123,7 @@ var datastoreGetCmd = &cli.Command{
}, },
ArgsUsage: "[namespace key]", ArgsUsage: "[namespace key]",
Action: func(cctx *cli.Context) error { Action: func(cctx *cli.Context) error {
_ = logging.SetLogLevel("badger", "ERROR") logging.SetLogLevel("badger", "ERROR") // nolint:errcheck
r, err := repo.NewFS(cctx.String("repo")) r, err := repo.NewFS(cctx.String("repo"))
if err != nil { if err != nil {

View File

@ -24,9 +24,8 @@ var dealLabelCmd = &cli.Command{
Usage: "Scrape state to report on how many deals have non UTF-8 labels", Usage: "Scrape state to report on how many deals have non UTF-8 labels",
Flags: []cli.Flag{ Flags: []cli.Flag{
&cli.StringFlag{ &cli.StringFlag{
Name: "repo", Name: "repo",
Value: "~/.lotus", Value: "~/.lotus",
EnvVars: []string{"LOTUS_PATH"},
}, },
}, },
Action: func(cctx *cli.Context) error { Action: func(cctx *cli.Context) error {

View File

@ -33,9 +33,8 @@ var diffMinerStates = &cli.Command{
ArgsUsage: "<stateCidA> <stateCidB>", ArgsUsage: "<stateCidA> <stateCidB>",
Flags: []cli.Flag{ Flags: []cli.Flag{
&cli.StringFlag{ &cli.StringFlag{
Name: "repo", Name: "repo",
Value: "~/.lotus", Value: "~/.lotus",
EnvVars: []string{"LOTUS_PATH"},
}, },
}, },
Action: func(cctx *cli.Context) error { Action: func(cctx *cli.Context) error {

View File

@ -35,9 +35,8 @@ var exportCarCmd = &cli.Command{
Description: "Export a car from repo", Description: "Export a car from repo",
Flags: []cli.Flag{ Flags: []cli.Flag{
&cli.StringFlag{ &cli.StringFlag{
Name: "repo", Name: "repo",
Value: "~/.lotus", Value: "~/.lotus",
EnvVars: []string{"LOTUS_PATH"},
}, },
}, },
ArgsUsage: "[outfile] [root cid]", ArgsUsage: "[outfile] [root cid]",

View File

@ -42,9 +42,8 @@ var exportChainCmd = &cli.Command{
Description: "Export chain from repo (requires node to be offline)", Description: "Export chain from repo (requires node to be offline)",
Flags: []cli.Flag{ Flags: []cli.Flag{
&cli.StringFlag{ &cli.StringFlag{
Name: "repo", Name: "repo",
Value: "~/.lotus", Value: "~/.lotus",
EnvVars: []string{"LOTUS_PATH"},
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "tipset", Name: "tipset",
@ -147,9 +146,8 @@ var exportRawCmd = &cli.Command{
Description: "Export raw blocks from repo (requires node to be offline)", Description: "Export raw blocks from repo (requires node to be offline)",
Flags: []cli.Flag{ Flags: []cli.Flag{
&cli.StringFlag{ &cli.StringFlag{
Name: "repo", Name: "repo",
Value: "~/.lotus", Value: "~/.lotus",
EnvVars: []string{"LOTUS_PATH"},
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "car-size", Name: "car-size",

View File

@ -58,6 +58,12 @@ var fip36PollCmd = &cli.Command{
Name: "fip36poll", Name: "fip36poll",
Usage: "Process the FIP0036 FilPoll result", Usage: "Process the FIP0036 FilPoll result",
ArgsUsage: "[state root, votes]", ArgsUsage: "[state root, votes]",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "repo",
Value: "~/.lotus",
},
},
Subcommands: []*cli.Command{ Subcommands: []*cli.Command{
finalResultCmd, finalResultCmd,
}, },
@ -69,9 +75,8 @@ var finalResultCmd = &cli.Command{
ArgsUsage: "[state root] [height] [votes json]", ArgsUsage: "[state root] [height] [votes json]",
Flags: []cli.Flag{ Flags: []cli.Flag{
&cli.StringFlag{ &cli.StringFlag{
Name: "repo", Name: "repo",
Value: "~/.lotus", Value: "~/.lotus",
EnvVars: []string{"LOTUS_PATH"},
}, },
}, },

View File

@ -43,9 +43,8 @@ var gasTraceCmd = &cli.Command{
ArgsUsage: "[migratedStateRootCid networkVersion messageCid]", ArgsUsage: "[migratedStateRootCid networkVersion messageCid]",
Flags: []cli.Flag{ Flags: []cli.Flag{
&cli.StringFlag{ &cli.StringFlag{
Name: "repo", Name: "repo",
Value: "~/.lotus", Value: "~/.lotus",
EnvVars: []string{"LOTUS_PATH"},
}, },
}, },
Action: func(cctx *cli.Context) error { Action: func(cctx *cli.Context) error {
@ -148,9 +147,8 @@ var replayOfflineCmd = &cli.Command{
ArgsUsage: "[messageCid]", ArgsUsage: "[messageCid]",
Flags: []cli.Flag{ Flags: []cli.Flag{
&cli.StringFlag{ &cli.StringFlag{
Name: "repo", Name: "repo",
Value: "~/.lotus", Value: "~/.lotus",
EnvVars: []string{"LOTUS_PATH"},
}, },
&cli.Int64Flag{ &cli.Int64Flag{
Name: "lookback-limit", Name: "lookback-limit",

View File

@ -19,13 +19,6 @@ import (
var importCarCmd = &cli.Command{ var importCarCmd = &cli.Command{
Name: "import-car", Name: "import-car",
Description: "Import a car file into node chain blockstore", Description: "Import a car file into node chain blockstore",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "repo",
Value: "~/.lotus",
EnvVars: []string{"LOTUS_PATH"},
},
},
Action: func(cctx *cli.Context) error { Action: func(cctx *cli.Context) error {
r, err := repo.NewFS(cctx.String("repo")) r, err := repo.NewFS(cctx.String("repo"))
if err != nil { if err != nil {
@ -103,13 +96,6 @@ var importCarCmd = &cli.Command{
var importObjectCmd = &cli.Command{ var importObjectCmd = &cli.Command{
Name: "import-obj", Name: "import-obj",
Usage: "import a raw ipld object into your datastore", Usage: "import a raw ipld object into your datastore",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "repo",
Value: "~/.lotus",
EnvVars: []string{"LOTUS_PATH"},
},
},
Action: func(cctx *cli.Context) error { Action: func(cctx *cli.Context) error {
r, err := repo.NewFS(cctx.String("repo")) r, err := repo.NewFS(cctx.String("repo"))
if err != nil { if err != nil {

View File

@ -36,9 +36,8 @@ var invariantsCmd = &cli.Command{
ArgsUsage: "[StateRootCid, height]", ArgsUsage: "[StateRootCid, height]",
Flags: []cli.Flag{ Flags: []cli.Flag{
&cli.StringFlag{ &cli.StringFlag{
Name: "repo", Name: "repo",
Value: "~/.lotus", Value: "~/.lotus",
EnvVars: []string{"LOTUS_PATH"},
}, },
}, },
Action: func(cctx *cli.Context) error { Action: func(cctx *cli.Context) error {

View File

@ -146,14 +146,9 @@ var keyinfoImportCmd = &cli.Command{
Examples Examples
env LOTUS_PATH=/var/lib/lotus lotus-shed keyinfo import libp2p-host.keyinfo`, env LOTUS_PATH=/var/lib/lotus lotus-shed keyinfo import libp2p-host.keyinfo`,
Flags: []cli.Flag{
&cli.StringFlag{
Name: "repo",
Value: "~/.lotus",
EnvVars: []string{"LOTUS_PATH"},
},
},
Action: func(cctx *cli.Context) error { Action: func(cctx *cli.Context) error {
flagRepo := cctx.String("repo")
var input io.Reader var input io.Reader
if cctx.NArg() == 0 { if cctx.NArg() == 0 {
input = os.Stdin input = os.Stdin
@ -182,7 +177,7 @@ var keyinfoImportCmd = &cli.Command{
return err return err
} }
fsrepo, err := repo.NewFS(cctx.String("repo")) fsrepo, err := repo.NewFS(flagRepo)
if err != nil { if err != nil {
return err return err
} }

View File

@ -1,6 +1,7 @@
package main package main
import ( import (
"fmt"
"os" "os"
logging "github.com/ipfs/go-log/v2" logging "github.com/ipfs/go-log/v2"
@ -90,6 +91,19 @@ func main() {
Version: build.UserVersion(), Version: build.UserVersion(),
Commands: local, Commands: local,
Flags: []cli.Flag{ Flags: []cli.Flag{
&cli.StringFlag{
Name: "repo",
EnvVars: []string{"LOTUS_PATH"},
Hidden: true,
Value: "~/.lotus", // TODO: Consider XDG_DATA_HOME
},
&cli.StringFlag{
Name: "miner-repo",
Aliases: []string{"storagerepo"},
EnvVars: []string{"LOTUS_MINER_PATH", "LOTUS_STORAGE_PATH"},
Value: "~/.lotusminer", // TODO: Consider XDG_DATA_HOME
Usage: fmt.Sprintf("Specify miner repo path. flag storagerepo and env LOTUS_STORAGE_PATH are DEPRECATION, will REMOVE SOON"),
},
&cli.StringFlag{ &cli.StringFlag{
Name: "log-level", Name: "log-level",
Value: "info", Value: "info",

View File

@ -124,9 +124,8 @@ var marketExportDatastoreCmd = &cli.Command{
Description: "export markets datastore key/values to a file", Description: "export markets datastore key/values to a file",
Flags: []cli.Flag{ Flags: []cli.Flag{
&cli.StringFlag{ &cli.StringFlag{
Name: "repo", Name: "repo",
Value: "~/.lotus", Usage: "path to the repo",
EnvVars: []string{"LOTUS_PATH"},
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "backup-dir", Name: "backup-dir",
@ -242,9 +241,8 @@ var marketImportDatastoreCmd = &cli.Command{
Description: "import markets datastore key/values from a backup file", Description: "import markets datastore key/values from a backup file",
Flags: []cli.Flag{ Flags: []cli.Flag{
&cli.StringFlag{ &cli.StringFlag{
Name: "repo", Name: "repo",
Value: "~/.lotus", Usage: "path to the repo",
EnvVars: []string{"LOTUS_PATH"},
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "backup-path", Name: "backup-path",

View File

@ -55,9 +55,8 @@ var migrationsCmd = &cli.Command{
ArgsUsage: "[new network version, block to look back from]", ArgsUsage: "[new network version, block to look back from]",
Flags: []cli.Flag{ Flags: []cli.Flag{
&cli.StringFlag{ &cli.StringFlag{
Name: "repo", Name: "repo",
Value: "~/.lotus", Value: "~/.lotus",
EnvVars: []string{"LOTUS_PATH"},
}, },
&cli.BoolFlag{ &cli.BoolFlag{
Name: "skip-pre-migration", Name: "skip-pre-migration",

View File

@ -26,12 +26,10 @@ import (
var minerPeeridCmd = &cli.Command{ var minerPeeridCmd = &cli.Command{
Name: "miner-peerid", Name: "miner-peerid",
Usage: "Scrape state to find a miner based on peerid", Usage: "Scrape state to find a miner based on peerid", Flags: []cli.Flag{
Flags: []cli.Flag{
&cli.StringFlag{ &cli.StringFlag{
Name: "repo", Name: "repo",
Value: "~/.lotus", Value: "~/.lotus",
EnvVars: []string{"LOTUS_PATH"},
}, },
}, },
Action: func(cctx *cli.Context) error { Action: func(cctx *cli.Context) error {

View File

@ -28,12 +28,10 @@ import (
var minerTypesCmd = &cli.Command{ var minerTypesCmd = &cli.Command{
Name: "miner-types", Name: "miner-types",
Usage: "Scrape state to report on how many miners of each WindowPoStProofType exist", Usage: "Scrape state to report on how many miners of each WindowPoStProofType exist", Flags: []cli.Flag{
Flags: []cli.Flag{
&cli.StringFlag{ &cli.StringFlag{
Name: "repo", Name: "repo",
Value: "~/.lotus", Value: "~/.lotus",
EnvVars: []string{"LOTUS_PATH"},
}, },
}, },
Action: func(cctx *cli.Context) error { Action: func(cctx *cli.Context) error {

View File

@ -43,9 +43,8 @@ var multisigGetAllCmd = &cli.Command{
ArgsUsage: "[state root]", ArgsUsage: "[state root]",
Flags: []cli.Flag{ Flags: []cli.Flag{
&cli.StringFlag{ &cli.StringFlag{
Name: "repo", Name: "repo",
Value: "~/.lotus", Value: "~/.lotus",
EnvVars: []string{"LOTUS_PATH"},
}, },
}, },
Action: func(cctx *cli.Context) error { Action: func(cctx *cli.Context) error {

View File

@ -17,6 +17,12 @@ import (
var noncefix = &cli.Command{ var noncefix = &cli.Command{
Name: "noncefix", Name: "noncefix",
Flags: []cli.Flag{ Flags: []cli.Flag{
&cli.StringFlag{
Name: "repo",
EnvVars: []string{"LOTUS_PATH"},
Hidden: true,
Value: "~/.lotus", // TODO: Consider XDG_DATA_HOME
},
&cli.Uint64Flag{ &cli.Uint64Flag{
Name: "start", Name: "start",
}, },

View File

@ -86,9 +86,8 @@ var stateTreePruneCmd = &cli.Command{
Description: "Deletes old state root data from local chainstore", Description: "Deletes old state root data from local chainstore",
Flags: []cli.Flag{ Flags: []cli.Flag{
&cli.StringFlag{ &cli.StringFlag{
Name: "repo", Name: "repo",
Value: "~/.lotus", Value: "~/.lotus",
EnvVars: []string{"LOTUS_PATH"},
}, },
&cli.Int64Flag{ &cli.Int64Flag{
Name: "keep-from-lookback", Name: "keep-from-lookback",

View File

@ -39,9 +39,8 @@ var splitstoreRollbackCmd = &cli.Command{
Description: "rollbacks a splitstore installation", Description: "rollbacks a splitstore installation",
Flags: []cli.Flag{ Flags: []cli.Flag{
&cli.StringFlag{ &cli.StringFlag{
Name: "repo", Name: "repo",
Value: "~/.lotus", Value: "~/.lotus",
EnvVars: []string{"LOTUS_PATH"},
}, },
&cli.BoolFlag{ &cli.BoolFlag{
Name: "gc-coldstore", Name: "gc-coldstore",
@ -130,9 +129,8 @@ var splitstoreClearCmd = &cli.Command{
Description: "clears a splitstore installation for restart from snapshot", Description: "clears a splitstore installation for restart from snapshot",
Flags: []cli.Flag{ Flags: []cli.Flag{
&cli.StringFlag{ &cli.StringFlag{
Name: "repo", Name: "repo",
Value: "~/.lotus", Value: "~/.lotus",
EnvVars: []string{"LOTUS_PATH"},
}, },
&cli.BoolFlag{ &cli.BoolFlag{
Name: "keys-only", Name: "keys-only",

View File

@ -33,9 +33,8 @@ var terminationsCmd = &cli.Command{
ArgsUsage: "[block to look back from] [lookback period (epochs)]", ArgsUsage: "[block to look back from] [lookback period (epochs)]",
Flags: []cli.Flag{ Flags: []cli.Flag{
&cli.StringFlag{ &cli.StringFlag{
Name: "repo", Name: "repo",
Value: "~/.lotus", Value: "~/.lotus",
EnvVars: []string{"LOTUS_PATH"},
}, },
}, },
Action: func(cctx *cli.Context) error { Action: func(cctx *cli.Context) error {

View File

@ -68,6 +68,7 @@
* [CreateBackup](#CreateBackup) * [CreateBackup](#CreateBackup)
* [Eth](#Eth) * [Eth](#Eth)
* [EthAccounts](#EthAccounts) * [EthAccounts](#EthAccounts)
* [EthAddressToFilecoinAddress](#EthAddressToFilecoinAddress)
* [EthBlockNumber](#EthBlockNumber) * [EthBlockNumber](#EthBlockNumber)
* [EthCall](#EthCall) * [EthCall](#EthCall)
* [EthChainId](#EthChainId) * [EthChainId](#EthChainId)
@ -2262,6 +2263,21 @@ Response:
] ]
``` ```
### EthAddressToFilecoinAddress
EthAddressToFilecoinAddress converts an EthAddress into an f410 Filecoin Address
Perms: read
Inputs:
```json
[
"0x5cbeecf99d3fdb3f25e309cc264f240bb0664031"
]
```
Response: `"f01234"`
### EthBlockNumber ### EthBlockNumber
EthBlockNumber returns the height of the latest (heaviest) TipSet EthBlockNumber returns the height of the latest (heaviest) TipSet

View File

@ -88,6 +88,7 @@ type TargetAPI interface {
StateVMCirculatingSupplyInternal(context.Context, types.TipSetKey) (api.CirculatingSupply, error) StateVMCirculatingSupplyInternal(context.Context, types.TipSetKey) (api.CirculatingSupply, error)
WalletBalance(context.Context, address.Address) (types.BigInt, error) WalletBalance(context.Context, address.Address) (types.BigInt, error)
EthAddressToFilecoinAddress(ctx context.Context, ethAddress ethtypes.EthAddress) (address.Address, error)
EthBlockNumber(ctx context.Context) (ethtypes.EthUint64, error) EthBlockNumber(ctx context.Context) (ethtypes.EthUint64, error)
EthGetBlockTransactionCountByNumber(ctx context.Context, blkNum ethtypes.EthUint64) (ethtypes.EthUint64, error) EthGetBlockTransactionCountByNumber(ctx context.Context, blkNum ethtypes.EthUint64) (ethtypes.EthUint64, error)
EthGetBlockTransactionCountByHash(ctx context.Context, blkHash ethtypes.EthHash) (ethtypes.EthUint64, error) EthGetBlockTransactionCountByHash(ctx context.Context, blkHash ethtypes.EthHash) (ethtypes.EthUint64, error)

View File

@ -2,6 +2,8 @@ package main
import ( import (
"os" "os"
"strconv"
"strings"
"text/template" "text/template"
"github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/build"
@ -32,19 +34,49 @@ var EmbeddedBuiltinActorsMetadata []*BuiltinActorsMetadata = []*BuiltinActorsMet
} }
`)) `))
func splitOverride(override string) (string, string) {
x := strings.Split(override, "=")
return x[0], x[1]
}
func main() { func main() {
// read metadata from the embedded bundle, includes all info except git tags
metadata, err := build.ReadEmbeddedBuiltinActorsMetadata() metadata, err := build.ReadEmbeddedBuiltinActorsMetadata()
if err != nil { if err != nil {
panic(err) panic(err)
} }
// TODO: Re-enable this when we can set the tag for ONLY the appropriate version // IF args have been provided, extract git tag info from them, otherwise
// https://github.com/filecoin-project/lotus/issues/10185#issuecomment-1422864836 // rely on previously embedded metadata for git tags.
//if len(os.Args) > 1 { if len(os.Args) > 1 {
// for _, m := range metadata { // see ./build/actors/pack.sh
// m.BundleGitTag = os.Args[1] // (optional) expected args are:
// } // $(GOCC) run ./gen/bundle $(VERSION) $(RELEASE) $(RELEASE_OVERRIDES)
//} // overrides are in the format network_name=override
gitTag := os.Args[2]
packedActorsVersion, err := strconv.Atoi(os.Args[1][1:])
if err != nil {
panic(err)
}
overrides := map[string]string{}
for _, override := range os.Args[3:] {
k, v := splitOverride(override)
overrides[k] = v
}
for _, m := range metadata {
if int(m.Version) == packedActorsVersion {
override, ok := overrides[m.Network]
if ok {
m.BundleGitTag = override
} else {
m.BundleGitTag = gitTag
}
} else {
m.BundleGitTag = getOldGitTagFromEmbeddedMetadata(m)
}
}
}
fi, err := os.Create("./build/builtin_actors_gen.go") fi, err := os.Create("./build/builtin_actors_gen.go")
if err != nil { if err != nil {
@ -57,3 +89,14 @@ func main() {
panic(err) panic(err)
} }
} }
func getOldGitTagFromEmbeddedMetadata(m *build.BuiltinActorsMetadata) string {
for _, v := range build.EmbeddedBuiltinActorsMetadata {
// if we agree on the manifestCid for the previously embedded metadata, use the previously set tag
if m.Version == v.Version && m.Network == v.Network && m.ManifestCid == v.ManifestCid {
return m.BundleGitTag
}
}
return ""
}

6
go.mod
View File

@ -97,7 +97,7 @@ require (
github.com/ipfs/go-ipfs-util v0.0.2 github.com/ipfs/go-ipfs-util v0.0.2
github.com/ipfs/go-ipld-cbor v0.0.6 github.com/ipfs/go-ipld-cbor v0.0.6
github.com/ipfs/go-ipld-format v0.4.0 github.com/ipfs/go-ipld-format v0.4.0
github.com/ipfs/go-libipfs v0.4.0 github.com/ipfs/go-libipfs v0.4.1
github.com/ipfs/go-log/v2 v2.5.1 github.com/ipfs/go-log/v2 v2.5.1
github.com/ipfs/go-merkledag v0.8.1 github.com/ipfs/go-merkledag v0.8.1
github.com/ipfs/go-metrics-interface v0.0.1 github.com/ipfs/go-metrics-interface v0.0.1
@ -238,12 +238,12 @@ require (
github.com/ipfs/go-ipfs-config v0.18.0 // indirect github.com/ipfs/go-ipfs-config v0.18.0 // indirect
github.com/ipfs/go-ipfs-delay v0.0.1 // indirect github.com/ipfs/go-ipfs-delay v0.0.1 // indirect
github.com/ipfs/go-ipfs-posinfo v0.0.1 // indirect github.com/ipfs/go-ipfs-posinfo v0.0.1 // indirect
github.com/ipfs/go-ipfs-pq v0.0.2 // indirect github.com/ipfs/go-ipfs-pq v0.0.3 // indirect
github.com/ipfs/go-ipld-legacy v0.1.1 // indirect github.com/ipfs/go-ipld-legacy v0.1.1 // indirect
github.com/ipfs/go-ipns v0.3.0 // indirect github.com/ipfs/go-ipns v0.3.0 // indirect
github.com/ipfs/go-log v1.0.5 // indirect github.com/ipfs/go-log v1.0.5 // indirect
github.com/ipfs/go-path v0.3.0 // indirect github.com/ipfs/go-path v0.3.0 // indirect
github.com/ipfs/go-peertaskqueue v0.8.0 // indirect github.com/ipfs/go-peertaskqueue v0.8.1 // indirect
github.com/ipfs/go-verifcid v0.0.2 // indirect github.com/ipfs/go-verifcid v0.0.2 // indirect
github.com/ipld/go-ipld-adl-hamt v0.0.0-20220616142416-9004dbd839e0 // indirect github.com/ipld/go-ipld-adl-hamt v0.0.0-20220616142416-9004dbd839e0 // indirect
github.com/ipni/storetheindex v0.5.3-0.20221203123030-16745cb63f15 // indirect github.com/ipni/storetheindex v0.5.3-0.20221203123030-16745cb63f15 // indirect

11
go.sum
View File

@ -789,8 +789,9 @@ github.com/ipfs/go-ipfs-http-client v0.4.0/go.mod h1:NXzPUKt/QVCuR74a8angJCGOSLP
github.com/ipfs/go-ipfs-posinfo v0.0.1 h1:Esoxj+1JgSjX0+ylc0hUmJCOv6V2vFoZiETLR6OtpRs= github.com/ipfs/go-ipfs-posinfo v0.0.1 h1:Esoxj+1JgSjX0+ylc0hUmJCOv6V2vFoZiETLR6OtpRs=
github.com/ipfs/go-ipfs-posinfo v0.0.1/go.mod h1:SwyeVP+jCwiDu0C313l/8jg6ZxM0qqtlt2a0vILTc1A= github.com/ipfs/go-ipfs-posinfo v0.0.1/go.mod h1:SwyeVP+jCwiDu0C313l/8jg6ZxM0qqtlt2a0vILTc1A=
github.com/ipfs/go-ipfs-pq v0.0.1/go.mod h1:LWIqQpqfRG3fNc5XsnIhz/wQ2XXGyugQwls7BgUmUfY= github.com/ipfs/go-ipfs-pq v0.0.1/go.mod h1:LWIqQpqfRG3fNc5XsnIhz/wQ2XXGyugQwls7BgUmUfY=
github.com/ipfs/go-ipfs-pq v0.0.2 h1:e1vOOW6MuOwG2lqxcLA+wEn93i/9laCY8sXAw76jFOY=
github.com/ipfs/go-ipfs-pq v0.0.2/go.mod h1:LWIqQpqfRG3fNc5XsnIhz/wQ2XXGyugQwls7BgUmUfY= github.com/ipfs/go-ipfs-pq v0.0.2/go.mod h1:LWIqQpqfRG3fNc5XsnIhz/wQ2XXGyugQwls7BgUmUfY=
github.com/ipfs/go-ipfs-pq v0.0.3 h1:YpoHVJB+jzK15mr/xsWC574tyDLkezVrDNeaalQBsTE=
github.com/ipfs/go-ipfs-pq v0.0.3/go.mod h1:btNw5hsHBpRcSSgZtiNm/SLj5gYIZ18AKtv3kERkRb4=
github.com/ipfs/go-ipfs-routing v0.0.1/go.mod h1:k76lf20iKFxQTjcJokbPM9iBXVXVZhcOwc360N4nuKs= github.com/ipfs/go-ipfs-routing v0.0.1/go.mod h1:k76lf20iKFxQTjcJokbPM9iBXVXVZhcOwc360N4nuKs=
github.com/ipfs/go-ipfs-routing v0.1.0/go.mod h1:hYoUkJLyAUKhF58tysKpids8RNDPO42BVMgK5dNsoqY= github.com/ipfs/go-ipfs-routing v0.1.0/go.mod h1:hYoUkJLyAUKhF58tysKpids8RNDPO42BVMgK5dNsoqY=
github.com/ipfs/go-ipfs-routing v0.2.1/go.mod h1:xiNNiwgjmLqPS1cimvAw6EyB9rkVDbiocA4yY+wRNLM= github.com/ipfs/go-ipfs-routing v0.2.1/go.mod h1:xiNNiwgjmLqPS1cimvAw6EyB9rkVDbiocA4yY+wRNLM=
@ -817,8 +818,8 @@ github.com/ipfs/go-ipld-legacy v0.1.1 h1:BvD8PEuqwBHLTKqlGFTHSwrwFOMkVESEvwIYwR2
github.com/ipfs/go-ipld-legacy v0.1.1/go.mod h1:8AyKFCjgRPsQFf15ZQgDB8Din4DML/fOmKZkkFkrIEg= github.com/ipfs/go-ipld-legacy v0.1.1/go.mod h1:8AyKFCjgRPsQFf15ZQgDB8Din4DML/fOmKZkkFkrIEg=
github.com/ipfs/go-ipns v0.3.0 h1:ai791nTgVo+zTuq2bLvEGmWP1M0A6kGTXUsgv/Yq67A= github.com/ipfs/go-ipns v0.3.0 h1:ai791nTgVo+zTuq2bLvEGmWP1M0A6kGTXUsgv/Yq67A=
github.com/ipfs/go-ipns v0.3.0/go.mod h1:3cLT2rbvgPZGkHJoPO1YMJeh6LtkxopCkKFcio/wE24= github.com/ipfs/go-ipns v0.3.0/go.mod h1:3cLT2rbvgPZGkHJoPO1YMJeh6LtkxopCkKFcio/wE24=
github.com/ipfs/go-libipfs v0.4.0 h1:TkUxJGjtPnSzAgkw7VjS0/DBay3MPjmTBa4dGdUQCDE= github.com/ipfs/go-libipfs v0.4.1 h1:tyu3RRMKFQUyUQt5jyt5SmDnls93H4Tr3HifL50zihg=
github.com/ipfs/go-libipfs v0.4.0/go.mod h1:XsU2cP9jBhDrXoJDe0WxikB8XcVmD3k2MEZvB3dbYu8= github.com/ipfs/go-libipfs v0.4.1/go.mod h1:Ad8ybPqwCkl2cNiNUMvM/iaVc/5bwNpHu8RPZ5te1hw=
github.com/ipfs/go-log v0.0.1/go.mod h1:kL1d2/hzSpI0thNYjiKfjanbVNU+IIGA/WnNESY9leM= github.com/ipfs/go-log v0.0.1/go.mod h1:kL1d2/hzSpI0thNYjiKfjanbVNU+IIGA/WnNESY9leM=
github.com/ipfs/go-log v1.0.0/go.mod h1:JO7RzlMK6rA+CIxFMLOuB6Wf5b81GDiKElL7UPSIKjA= github.com/ipfs/go-log v1.0.0/go.mod h1:JO7RzlMK6rA+CIxFMLOuB6Wf5b81GDiKElL7UPSIKjA=
github.com/ipfs/go-log v1.0.1/go.mod h1:HuWlQttfN6FWNHRhlY5yMk/lW7evQC0HHGOxEwMRR8I= github.com/ipfs/go-log v1.0.1/go.mod h1:HuWlQttfN6FWNHRhlY5yMk/lW7evQC0HHGOxEwMRR8I=
@ -859,8 +860,8 @@ github.com/ipfs/go-peertaskqueue v0.0.4/go.mod h1:03H8fhyeMfKNFWqzYEVyMbcPUeYrqP
github.com/ipfs/go-peertaskqueue v0.1.0/go.mod h1:Jmk3IyCcfl1W3jTW3YpghSwSEC6IJ3Vzz/jUmWw8Z0U= github.com/ipfs/go-peertaskqueue v0.1.0/go.mod h1:Jmk3IyCcfl1W3jTW3YpghSwSEC6IJ3Vzz/jUmWw8Z0U=
github.com/ipfs/go-peertaskqueue v0.7.0/go.mod h1:M/akTIE/z1jGNXMU7kFB4TeSEFvj68ow0Rrb04donIU= github.com/ipfs/go-peertaskqueue v0.7.0/go.mod h1:M/akTIE/z1jGNXMU7kFB4TeSEFvj68ow0Rrb04donIU=
github.com/ipfs/go-peertaskqueue v0.7.1/go.mod h1:M/akTIE/z1jGNXMU7kFB4TeSEFvj68ow0Rrb04donIU= github.com/ipfs/go-peertaskqueue v0.7.1/go.mod h1:M/akTIE/z1jGNXMU7kFB4TeSEFvj68ow0Rrb04donIU=
github.com/ipfs/go-peertaskqueue v0.8.0 h1:JyNO144tfu9bx6Hpo119zvbEL9iQ760FHOiJYsUjqaU= github.com/ipfs/go-peertaskqueue v0.8.1 h1:YhxAs1+wxb5jk7RvS0LHdyiILpNmRIRnZVztekOF0pg=
github.com/ipfs/go-peertaskqueue v0.8.0/go.mod h1:cz8hEnnARq4Du5TGqiWKgMr/BOSQ5XOgMOh1K5YYKKM= github.com/ipfs/go-peertaskqueue v0.8.1/go.mod h1:Oxxd3eaK279FxeydSPPVGHzbwVeHjatZ2GA8XD+KbPU=
github.com/ipfs/go-unixfs v0.2.2-0.20190827150610-868af2e9e5cb/go.mod h1:IwAAgul1UQIcNZzKPYZWOCijryFBeCV79cNubPzol+k= github.com/ipfs/go-unixfs v0.2.2-0.20190827150610-868af2e9e5cb/go.mod h1:IwAAgul1UQIcNZzKPYZWOCijryFBeCV79cNubPzol+k=
github.com/ipfs/go-unixfs v0.2.4/go.mod h1:SUdisfUjNoSDzzhGVxvCL9QO/nKdwXdr+gbMUdqcbYw= github.com/ipfs/go-unixfs v0.2.4/go.mod h1:SUdisfUjNoSDzzhGVxvCL9QO/nKdwXdr+gbMUdqcbYw=
github.com/ipfs/go-unixfs v0.3.1/go.mod h1:h4qfQYzghiIc8ZNFKiLMFWOTzrWIAtzYQ59W/pCFf1o= github.com/ipfs/go-unixfs v0.3.1/go.mod h1:h4qfQYzghiIc8ZNFKiLMFWOTzrWIAtzYQ59W/pCFf1o=

View File

@ -0,0 +1 @@
608060405234801561001057600080fd5b506102de806100206000396000f3fe608060405234801561001057600080fd5b50600436106100575760003560e01c80630abe88b61461005c57806358d4cbce1461006657806359be8c55146100705780638791bd331461007a578063c6dbcf2e14610084575b600080fd5b61006461008e565b005b61006e61009f565b005b6100786100a4565b005b6100826100df565b005b61008c610111565b005b600061009d5761009c61012a565b5b565b600080fd5b6040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100d6906101b6565b60405180910390fd5b6040517f09caebf300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006001905060008082610125919061023e565b505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052600160045260246000fd5b600082825260208201905092915050565b7f6d7920726561736f6e0000000000000000000000000000000000000000000000600082015250565b60006101a0600983610159565b91506101ab8261016a565b602082019050919050565b600060208201905081810360008301526101cf81610193565b9050919050565b6000819050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000610249826101d6565b9150610254836101d6565b925082610264576102636101e0565b5b600160000383147f80000000000000000000000000000000000000000000000000000000000000008314161561029d5761029c61020f565b5b82820590509291505056fea26469706673582212207815355e9e7ced2b8168a953c364e82871c0fe326602bbb9106e6551aea673ed64736f6c63430008120033

View File

@ -0,0 +1,24 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
contract Errors {
error CustomError();
function failRevertEmpty() public {
revert();
}
function failRevertReason() public {
revert("my reason");
}
function failAssert() public {
assert(false);
}
function failDivZero() public {
int a = 1;
int b = 0;
a / b;
}
function failCustom() public {
revert CustomError();
}
}

48
itests/eth_api_test.go Normal file
View File

@ -0,0 +1,48 @@
package itests
import (
"context"
"testing"
"time"
"github.com/stretchr/testify/require"
"github.com/filecoin-project/go-state-types/builtin"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/types/ethtypes"
"github.com/filecoin-project/lotus/chain/wallet/key"
"github.com/filecoin-project/lotus/itests/kit"
)
func TestEthAddressToFilecoinAddress(t *testing.T) {
// Disable EthRPC to confirm that this method does NOT need the EthEnableRPC config set to true
client, _, _ := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC(), kit.DisableEthRPC())
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
defer cancel()
secpKey, err := key.GenerateKey(types.KTDelegated)
require.NoError(t, err)
filecoinKeyAddr, err := client.WalletImport(ctx, &secpKey.KeyInfo)
require.NoError(t, err)
ethAddr, err := ethtypes.EthAddressFromFilecoinAddress(filecoinKeyAddr)
require.NoError(t, err)
apiFilAddr, err := client.EthAddressToFilecoinAddress(ctx, ethAddr)
require.NoError(t, err)
require.Equal(t, filecoinKeyAddr, apiFilAddr)
filecoinIdArr := builtin.StorageMarketActorAddr
ethAddr, err = ethtypes.EthAddressFromFilecoinAddress(filecoinIdArr)
require.NoError(t, err)
apiFilAddr, err = client.EthAddressToFilecoinAddress(ctx, ethAddr)
require.NoError(t, err)
require.Equal(t, filecoinIdArr, apiFilAddr)
}

View File

@ -10,15 +10,16 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/types/ethtypes"
"github.com/filecoin-project/lotus/itests/kit" "github.com/filecoin-project/lotus/itests/kit"
) )
// TestGetCode ensures that GetCode returns the correct results for: // TestGetCodeAndNonce ensures that GetCode and GetTransactionCount return the correct results for:
// 1. Placeholders. // 1. Placeholders.
// 2. Non-existent actors. // 2. Non-existent actors.
// 3. Normal EVM actors. // 3. Normal EVM actors.
// 4. Self-destructed EVM actors. // 4. Self-destructed EVM actors.
func TestGetCode(t *testing.T) { func TestGetCodeAndNonce(t *testing.T) {
kit.QuietMiningLogs() kit.QuietMiningLogs()
blockTime := 100 * time.Millisecond blockTime := 100 * time.Millisecond
@ -28,7 +29,7 @@ func TestGetCode(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), time.Minute) ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
defer cancel() defer cancel()
// Accounts should have empty code. // Accounts should have empty code, empty nonce.
{ {
// A random eth address should have no code. // A random eth address should have no code.
_, ethAddr, filAddr := client.EVM().NewAccount() _, ethAddr, filAddr := client.EVM().NewAccount()
@ -36,6 +37,11 @@ func TestGetCode(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
require.Empty(t, bytecode) require.Empty(t, bytecode)
// Nonce should also be zero
nonce, err := client.EVM().EthGetTransactionCount(ctx, ethAddr, "latest")
require.NoError(t, err)
require.Zero(t, nonce)
// send some funds to the account. // send some funds to the account.
kit.SendFunds(ctx, t, client, filAddr, types.FromFil(10)) kit.SendFunds(ctx, t, client, filAddr, types.FromFil(10))
@ -43,6 +49,11 @@ func TestGetCode(t *testing.T) {
bytecode, err = client.EVM().EthGetCode(ctx, ethAddr, "latest") bytecode, err = client.EVM().EthGetCode(ctx, ethAddr, "latest")
require.NoError(t, err) require.NoError(t, err)
require.Empty(t, bytecode) require.Empty(t, bytecode)
// Nonce should still be zero.
nonce, err = client.EVM().EthGetTransactionCount(ctx, ethAddr, "latest")
require.NoError(t, err)
require.Zero(t, nonce)
} }
// Check contract code. // Check contract code.
@ -61,6 +72,11 @@ func TestGetCode(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
require.NotEmpty(t, bytecode) require.NotEmpty(t, bytecode)
// Nonce should be one.
nonce, err := client.EVM().EthGetTransactionCount(ctx, contractAddr, "latest")
require.NoError(t, err)
require.Equal(t, ethtypes.EthUint64(1), nonce)
// Destroy it. // Destroy it.
_, _, err = client.EVM().InvokeContractByFuncName(ctx, client.DefaultKey.Address, contractFilAddr, "destroy()", nil) _, _, err = client.EVM().InvokeContractByFuncName(ctx, client.DefaultKey.Address, contractFilAddr, "destroy()", nil)
require.NoError(t, err) require.NoError(t, err)
@ -69,6 +85,11 @@ func TestGetCode(t *testing.T) {
bytecode, err = client.EVM().EthGetCode(ctx, contractAddr, "latest") bytecode, err = client.EVM().EthGetCode(ctx, contractAddr, "latest")
require.NoError(t, err) require.NoError(t, err)
require.Empty(t, bytecode) require.Empty(t, bytecode)
// Nonce should go back to zero
nonce, err = client.EVM().EthGetTransactionCount(ctx, contractAddr, "latest")
require.NoError(t, err)
require.Zero(t, nonce)
} }
} }

View File

@ -265,6 +265,20 @@ func TestFEVMDelegateCall(t *testing.T) {
expectedResultActor, err := hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000000") expectedResultActor, err := hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000000")
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, result, expectedResultActor) require.Equal(t, result, expectedResultActor)
// The implementation's storage should not have been updated.
actorAddrEth, err := ethtypes.EthAddressFromFilecoinAddress(actorAddr)
require.NoError(t, err)
value, err := client.EVM().EthGetStorageAt(ctx, actorAddrEth, nil, "latest")
require.NoError(t, err)
require.Equal(t, ethtypes.EthBytes(make([]byte, 32)), value)
// The storage actor's storage _should_ have been updated
storageAddrEth, err := ethtypes.EthAddressFromFilecoinAddress(storageAddr)
require.NoError(t, err)
value, err = client.EVM().EthGetStorageAt(ctx, storageAddrEth, nil, "latest")
require.NoError(t, err)
require.Equal(t, ethtypes.EthBytes(expectedResult), value)
} }
// TestFEVMDelegateCallRevert makes a delegatecall action and then calls revert. // TestFEVMDelegateCallRevert makes a delegatecall action and then calls revert.
@ -868,3 +882,42 @@ func TestFEVMGetBlockDifficulty(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, len(ret), 32) require.Equal(t, len(ret), 32)
} }
func TestFEVMErrorParsing(t *testing.T) {
ctx, cancel, client := kit.SetupFEVMTest(t)
defer cancel()
e := client.EVM()
_, contractAddr := e.DeployContractFromFilename(ctx, "contracts/Errors.hex")
contractAddrEth, err := ethtypes.EthAddressFromFilecoinAddress(contractAddr)
require.NoError(t, err)
customError := ethtypes.EthBytes(kit.CalcFuncSignature("CustomError()")).String()
for sig, expected := range map[string]string{
"failRevertEmpty()": "none",
"failRevertReason()": "Error(my reason)",
"failAssert()": "Assert()",
"failDivZero()": "DivideByZero()",
"failCustom()": customError,
} {
sig := sig
expected := fmt.Sprintf("exit 33, revert reason: %s, vm error", expected)
t.Run(sig, func(t *testing.T) {
entryPoint := kit.CalcFuncSignature(sig)
t.Run("EthCall", func(t *testing.T) {
_, err := e.EthCall(ctx, ethtypes.EthCall{
To: &contractAddrEth,
Data: entryPoint,
}, "latest")
require.ErrorContains(t, err, expected)
})
t.Run("EthEstimateGas", func(t *testing.T) {
_, err := e.EthEstimateGas(ctx, ethtypes.EthCall{
To: &contractAddrEth,
Data: entryPoint,
})
require.ErrorContains(t, err, expected)
})
})
}
}

View File

@ -1794,9 +1794,9 @@
"result": { "result": {
"name": "Filter Identifier", "name": "Filter Identifier",
"schema": { "schema": {
"title": "hex encoded unsigned integer", "title": "32 byte hex value",
"type": "string", "type": "string",
"pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" "pattern": "^0x[0-9a-f]{64}$"
} }
} }
}, },
@ -1807,9 +1807,9 @@
"result": { "result": {
"name": "Filter Identifier", "name": "Filter Identifier",
"schema": { "schema": {
"title": "hex encoded unsigned integer", "title": "32 byte hex value",
"type": "string", "type": "string",
"pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" "pattern": "^0x[0-9a-f]{64}$"
} }
} }
}, },
@ -1820,9 +1820,9 @@
"result": { "result": {
"name": "Filter Identifier", "name": "Filter Identifier",
"schema": { "schema": {
"title": "hex encoded unsigned integer", "title": "32 byte hex value",
"type": "string", "type": "string",
"pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" "pattern": "^0x[0-9a-f]{64}$"
} }
} }
}, },
@ -1833,9 +1833,9 @@
{ {
"name": "Filter Identifier", "name": "Filter Identifier",
"schema": { "schema": {
"title": "hex encoded unsigned integer", "title": "32 byte hex value",
"type": "string", "type": "string",
"pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" "pattern": "^0x[0-9a-f]{64}$"
} }
} }
], ],
@ -1853,9 +1853,9 @@
{ {
"name": "Filter Identifier", "name": "Filter Identifier",
"schema": { "schema": {
"title": "hex encoded unsigned integer", "title": "32 byte hex value",
"type": "string", "type": "string",
"pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" "pattern": "^0x[0-9a-f]{64}$"
} }
} }
], ],
@ -1954,9 +1954,9 @@
{ {
"name": "Filter Identifier", "name": "Filter Identifier",
"schema": { "schema": {
"title": "hex encoded unsigned integer", "title": "32 byte hex value",
"type": "string", "type": "string",
"pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" "pattern": "^0x[0-9a-f]{64}$"
} }
} }
], ],

View File

@ -6,6 +6,7 @@ import (
"github.com/ipfs/go-cid" "github.com/ipfs/go-cid"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-jsonrpc" "github.com/filecoin-project/go-jsonrpc"
"github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/api"
@ -16,6 +17,10 @@ var ErrModuleDisabled = errors.New("module disabled, enable with Fevm.EnableEthR
type EthModuleDummy struct{} type EthModuleDummy struct{}
func (e *EthModuleDummy) EthAddressToFilecoinAddress(ctx context.Context, ethAddress ethtypes.EthAddress) (address.Address, error) {
return address.Undef, ErrModuleDisabled
}
func (e *EthModuleDummy) EthGetMessageCidByTransactionHash(ctx context.Context, txHash *ethtypes.EthHash) (*cid.Cid, error) { func (e *EthModuleDummy) EthGetMessageCidByTransactionHash(ctx context.Context, txHash *ethtypes.EthHash) (*cid.Cid, error) {
return nil, ErrModuleDisabled return nil, ErrModuleDisabled
} }

View File

@ -31,6 +31,7 @@ import (
"github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/actors" "github.com/filecoin-project/lotus/chain/actors"
builtinactors "github.com/filecoin-project/lotus/chain/actors/builtin" builtinactors "github.com/filecoin-project/lotus/chain/actors/builtin"
builtinevm "github.com/filecoin-project/lotus/chain/actors/builtin/evm"
"github.com/filecoin-project/lotus/chain/ethhashlookup" "github.com/filecoin-project/lotus/chain/ethhashlookup"
"github.com/filecoin-project/lotus/chain/events/filter" "github.com/filecoin-project/lotus/chain/events/filter"
"github.com/filecoin-project/lotus/chain/messagepool" "github.com/filecoin-project/lotus/chain/messagepool"
@ -181,6 +182,10 @@ func (a *EthModule) EthAccounts(context.Context) ([]ethtypes.EthAddress, error)
return []ethtypes.EthAddress{}, nil return []ethtypes.EthAddress{}, nil
} }
func (a *EthAPI) EthAddressToFilecoinAddress(ctx context.Context, ethAddress ethtypes.EthAddress) (address.Address, error) {
return ethAddress.ToFilecoinAddress()
}
func (a *EthModule) countTipsetMsgs(ctx context.Context, ts *types.TipSet) (int, error) { func (a *EthModule) countTipsetMsgs(ctx context.Context, ts *types.TipSet) (int, error) {
blkMsgs, err := a.Chain.BlockMsgsForTipset(ctx, ts) blkMsgs, err := a.Chain.BlockMsgsForTipset(ctx, ts)
if err != nil { if err != nil {
@ -363,6 +368,26 @@ func (a *EthModule) EthGetTransactionCount(ctx context.Context, sender ethtypes.
return ethtypes.EthUint64(0), xerrors.Errorf("cannot parse block param: %s", blkParam) return ethtypes.EthUint64(0), xerrors.Errorf("cannot parse block param: %s", blkParam)
} }
// First, handle the case where the "sender" is an EVM actor.
if actor, err := a.StateManager.LoadActor(ctx, addr, ts); err != nil {
if xerrors.Is(err, types.ErrActorNotFound) {
return 0, nil
}
return 0, xerrors.Errorf("failed to lookup contract %s: %w", sender, err)
} else if builtinactors.IsEvmActor(actor.Code) {
evmState, err := builtinevm.Load(a.Chain.ActorStore(ctx), actor)
if err != nil {
return 0, xerrors.Errorf("failed to load evm state: %w", err)
}
if alive, err := evmState.IsAlive(); err != nil {
return 0, err
} else if !alive {
return 0, nil
}
nonce, err := evmState.Nonce()
return ethtypes.EthUint64(nonce), err
}
nonce, err := a.Mpool.GetNonce(ctx, addr, ts.Key()) nonce, err := a.Mpool.GetNonce(ctx, addr, ts.Key())
if err != nil { if err != nil {
return ethtypes.EthUint64(0), nil return ethtypes.EthUint64(0), nil
@ -506,13 +531,18 @@ func (a *EthModule) EthGetCode(ctx context.Context, ethAddr ethtypes.EthAddress,
} }
func (a *EthModule) EthGetStorageAt(ctx context.Context, ethAddr ethtypes.EthAddress, position ethtypes.EthBytes, blkParam string) (ethtypes.EthBytes, error) { func (a *EthModule) EthGetStorageAt(ctx context.Context, ethAddr ethtypes.EthAddress, position ethtypes.EthBytes, blkParam string) (ethtypes.EthBytes, error) {
ts, err := a.parseBlkParam(ctx, blkParam)
if err != nil {
return nil, xerrors.Errorf("cannot parse block param: %s", blkParam)
}
l := len(position) l := len(position)
if l > 32 { if l > 32 {
return nil, fmt.Errorf("supplied storage key is too long") return nil, fmt.Errorf("supplied storage key is too long")
} }
// pad with zero bytes if smaller than 32 bytes // pad with zero bytes if smaller than 32 bytes
position = append(make([]byte, 32-l, 32-l), position...) position = append(make([]byte, 32-l, 32), position...)
to, err := ethAddr.ToFilecoinAddress() to, err := ethAddr.ToFilecoinAddress()
if err != nil { if err != nil {
@ -525,6 +555,18 @@ func (a *EthModule) EthGetStorageAt(ctx context.Context, ethAddr ethtypes.EthAdd
return nil, fmt.Errorf("failed to construct system sender address: %w", err) return nil, fmt.Errorf("failed to construct system sender address: %w", err)
} }
actor, err := a.StateManager.LoadActor(ctx, to, ts)
if err != nil {
if xerrors.Is(err, types.ErrActorNotFound) {
return ethtypes.EthBytes(make([]byte, 32)), nil
}
return nil, xerrors.Errorf("failed to lookup contract %s: %w", ethAddr, err)
}
if !builtinactors.IsEvmActor(actor.Code) {
return ethtypes.EthBytes(make([]byte, 32)), nil
}
params, err := actors.SerializeParams(&evm.GetStorageAtParams{ params, err := actors.SerializeParams(&evm.GetStorageAtParams{
StorageKey: *(*[32]byte)(position), StorageKey: *(*[32]byte)(position),
}) })
@ -543,8 +585,6 @@ func (a *EthModule) EthGetStorageAt(ctx context.Context, ethAddr ethtypes.EthAdd
GasPremium: big.Zero(), GasPremium: big.Zero(),
} }
ts := a.Chain.GetHeaviestTipSet()
// Try calling until we find a height with no migration. // Try calling until we find a height with no migration.
var res *api.InvocResult var res *api.InvocResult
for { for {
@ -563,10 +603,22 @@ func (a *EthModule) EthGetStorageAt(ctx context.Context, ethAddr ethtypes.EthAdd
} }
if res.MsgRct == nil { if res.MsgRct == nil {
return nil, fmt.Errorf("no message receipt") return nil, xerrors.Errorf("no message receipt")
} }
return res.MsgRct.Return, nil if res.MsgRct.ExitCode.IsError() {
return nil, xerrors.Errorf("failed to lookup storage slot: %s", res.Error)
}
var ret abi.CborBytes
if err := ret.UnmarshalCBOR(bytes.NewReader(res.MsgRct.Return)); err != nil {
return nil, xerrors.Errorf("failed to unmarshal storage slot: %w", err)
}
// pad with zero bytes if smaller than 32 bytes
ret = append(make([]byte, 32-len(ret), 32), ret...)
return ethtypes.EthBytes(ret), nil
} }
func (a *EthModule) EthGetBalance(ctx context.Context, address ethtypes.EthAddress, blkParam string) (ethtypes.EthBigInt, error) { func (a *EthModule) EthGetBalance(ctx context.Context, address ethtypes.EthAddress, blkParam string) (ethtypes.EthBigInt, error) {
@ -840,7 +892,8 @@ func (a *EthModule) applyMessage(ctx context.Context, msg *types.Message, tsk ty
return nil, xerrors.Errorf("CallWithGas failed: %w", err) return nil, xerrors.Errorf("CallWithGas failed: %w", err)
} }
if res.MsgRct.ExitCode.IsError() { if res.MsgRct.ExitCode.IsError() {
return nil, xerrors.Errorf("message execution failed: exit %s, msg receipt: %s, reason: %s", res.MsgRct.ExitCode, res.MsgRct.Return, res.Error) reason := parseEthRevert(res.MsgRct.Return)
return nil, xerrors.Errorf("message execution failed: exit %s, revert reason: %s, vm error: %s", res.MsgRct.ExitCode, reason, res.Error)
} }
return res, nil return res, nil
} }
@ -856,14 +909,24 @@ func (a *EthModule) EthEstimateGas(ctx context.Context, tx ethtypes.EthCall) (et
msg.GasLimit = 0 msg.GasLimit = 0
ts := a.Chain.GetHeaviestTipSet() ts := a.Chain.GetHeaviestTipSet()
msg, err = a.GasAPI.GasEstimateMessageGas(ctx, msg, nil, ts.Key()) gassedMsg, err := a.GasAPI.GasEstimateMessageGas(ctx, msg, nil, ts.Key())
if err != nil { if err != nil {
// On failure, GasEstimateMessageGas doesn't actually return the invocation result,
// it just returns an error. That means we can't get the revert reason.
//
// So we re-execute the message with EthCall (well, applyMessage which contains the
// guts of EthCall). This will give us an ethereum specific error with revert
// information.
msg.GasLimit = build.BlockGasLimit
if _, err2 := a.applyMessage(ctx, msg, ts.Key()); err2 != nil {
err = err2
}
return ethtypes.EthUint64(0), xerrors.Errorf("failed to estimate gas: %w", err) return ethtypes.EthUint64(0), xerrors.Errorf("failed to estimate gas: %w", err)
} }
expectedGas, err := ethGasSearch(ctx, a.Chain, a.Stmgr, a.Mpool, msg, ts) expectedGas, err := ethGasSearch(ctx, a.Chain, a.Stmgr, a.Mpool, gassedMsg, ts)
if err != nil { if err != nil {
log.Errorw("expected gas", "err", err) return 0, xerrors.Errorf("gas search failed: %w", err)
} }
return ethtypes.EthUint64(expectedGas), nil return ethtypes.EthUint64(expectedGas), nil
@ -1001,7 +1064,7 @@ func (a *EthModule) EthCall(ctx context.Context, tx ethtypes.EthCall, blkParam s
invokeResult, err := a.applyMessage(ctx, msg, ts.Key()) invokeResult, err := a.applyMessage(ctx, msg, ts.Key())
if err != nil { if err != nil {
return nil, xerrors.Errorf("failed to apply message: %w", err) return nil, err
} }
if msg.To == builtintypes.EthereumAddressManagerActorAddr { if msg.To == builtintypes.EthereumAddressManagerActorAddr {
@ -2125,11 +2188,13 @@ func (m *EthTxHashManager) ProcessSignedMessage(ctx context.Context, msg *types.
ethTx, err := newEthTxFromSignedMessage(ctx, msg, m.StateAPI) ethTx, err := newEthTxFromSignedMessage(ctx, msg, m.StateAPI)
if err != nil { if err != nil {
log.Errorf("error converting filecoin message to eth tx: %s", err) log.Errorf("error converting filecoin message to eth tx: %s", err)
return
} }
err = m.TransactionHashLookup.UpsertHash(ethTx.Hash, msg.Cid()) err = m.TransactionHashLookup.UpsertHash(ethTx.Hash, msg.Cid())
if err != nil { if err != nil {
log.Errorf("error inserting tx mapping to db: %s", err) log.Errorf("error inserting tx mapping to db: %s", err)
return
} }
} }
@ -2180,6 +2245,85 @@ func parseEthTopics(topics ethtypes.EthTopicSpec) (map[string][][]byte, error) {
return keys, nil return keys, nil
} }
const errorFunctionSelector = "\x08\xc3\x79\xa0" // Error(string)
const panicFunctionSelector = "\x4e\x48\x7b\x71" // Panic(uint256)
// Eth ABI (solidity) panic codes.
var panicErrorCodes map[uint64]string = map[uint64]string{
0x00: "Panic()",
0x01: "Assert()",
0x11: "ArithmeticOverflow()",
0x12: "DivideByZero()",
0x21: "InvalidEnumVariant()",
0x22: "InvalidStorageArray()",
0x31: "PopEmptyArray()",
0x32: "ArrayIndexOutOfBounds()",
0x41: "OutOfMemory()",
0x51: "CalledUninitializedFunction()",
}
// Parse an ABI encoded revert reason. This reason should be encoded as if it were the parameters to
// an `Error(string)` function call.
//
// See https://docs.soliditylang.org/en/latest/control-structures.html#panic-via-assert-and-error-via-require
func parseEthRevert(ret []byte) string {
if len(ret) == 0 {
return "none"
}
var cbytes abi.CborBytes
if err := cbytes.UnmarshalCBOR(bytes.NewReader(ret)); err != nil {
return "ERROR: revert reason is not cbor encoded bytes"
}
if len(cbytes) == 0 {
return "none"
}
// If it's not long enough to contain an ABI encoded response, return immediately.
if len(cbytes) < 4+32 {
return ethtypes.EthBytes(cbytes).String()
}
switch string(cbytes[:4]) {
case panicFunctionSelector:
cbytes := cbytes[4 : 4+32]
// Read the and check the code.
code, err := ethtypes.EthUint64FromBytes(cbytes)
if err != nil {
// If it's too big, just return the raw value.
codeInt := big.PositiveFromUnsignedBytes(cbytes)
return fmt.Sprintf("Panic(%s)", ethtypes.EthBigInt(codeInt).String())
}
if s, ok := panicErrorCodes[uint64(code)]; ok {
return s
}
return fmt.Sprintf("Panic(0x%x)", code)
case errorFunctionSelector:
cbytes := cbytes[4:]
cbytesLen := ethtypes.EthUint64(len(cbytes))
// Read the and check the offset.
offset, err := ethtypes.EthUint64FromBytes(cbytes[:32])
if err != nil {
break
}
if cbytesLen < offset {
break
}
// Read and check the length.
if cbytesLen-offset < 32 {
break
}
start := offset + 32
length, err := ethtypes.EthUint64FromBytes(cbytes[offset : offset+32])
if err != nil {
break
}
if cbytesLen-start < length {
break
}
// Slice the error message.
return fmt.Sprintf("Error(%s)", cbytes[start:start+length])
}
return ethtypes.EthBytes(cbytes).String()
}
func calculateRewardsAndGasUsed(rewardPercentiles []float64, txGasRewards gasRewardSorter) ([]ethtypes.EthBigInt, uint64) { func calculateRewardsAndGasUsed(rewardPercentiles []float64, txGasRewards gasRewardSorter) ([]ethtypes.EthBigInt, uint64) {
var totalGasUsed uint64 var totalGasUsed uint64
for _, tx := range txGasRewards { for _, tx := range txGasRewards {