diff --git a/CHANGELOG.md b/CHANGELOG.md index 94ef2aba3..5b13ba3f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,22 @@ # Lotus changelog -# 1.20.0-rc2 / 2023-02-17 +# v1.20.1 / 2023-03-06 -This is the second 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! +This an optional patch releases for node operators/API service providers that run ETH RPC service. -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. +## Bug fixes +- fix: EthAPI: Correctly get parent hash #10389 +- fix: EthAPI: Make newEthBlockFromFilecoinTipSet faster and correct #10380 +- fix: state: short-circuit genesis state computation -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)): +# 1.20.0 / 2023-02-28 + +This is a MANDATORY release of Lotus that delivers the [Hygge network upgrade](https://github.com/filecoin-project/community/discussions/74?sort=top#discussioncomment-4313888), 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! + +The Filecoin mainnet is scheduled to upgrade to nv18 at epoch 2683348, on March 14th at 2023-03-14T15:14:00Z. All node operators, including storage providers, must upgrade to this release before that time. Storage providers must update their daemons, miners, market and worker(s)/boost. +At the upgrade, a short migration will run that converts code actors v9 code CIDs to v10 CIDs, and installs the new Ethereum Address Manager singleton (see below). This is expected to be a lightweight migration that causes no service disruption. + +The Hygge upgrade introduces the following Filecoin Improvement Proposals (FIPs), delivered in FVM3 (see FVM [v3.0.0](https://github.com/filecoin-project/ref-fvm/pull/1683)) and builtin-actors v10 (see actors [v10.0.0](https://github.com/filecoin-project/builtin-actors/releases/tag/v10.0.0)): - [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 @@ -15,14 +25,6 @@ The Hygge upgrade introduces the following Filecoin Improvement Proposals (FIPs) - [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 -## Changes since v1.20.0-rc1 - -- feat: API: Add an endpoint to convert Ethereum-like addresses to Filecoin (f410) addresses (#10286) -- fix: EthAPI: correctly decode EthGetStorageAt output (#10284) -- feat: EthAPI: parse revert data in EthCall (#10295) -- chore: deps: bump go-libipfs to v0.4.1 -- feat: EthAPI: return revert data on failed gas estimation #10298 - ## 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. @@ -33,18 +35,40 @@ The FEVM is principally delivered through the introduction of **the new [EVM act 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 +### v10 Built-in actor bundles -This release candidate sets the calibration-net nv18 Hygge upgrade at epoch 322354, 2023-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`. +Bundles for all networks (mainnet, calibnet, etc.) are included in the lotus source tree (`build/actors/`) and embedded on build, for v10 actors you can find it [here](https://github.com/filecoin-project/lotus/blob/master/build/actors/v10.tar.zst). +Reminder: Lotus verifies that the bundle CIDs are the right ones upon build & upgrade against the values in `build/builtin_actors_gen.go`, according to the network you are building. You may also check the bundle manifest CID matches the bundle gen-ed values by running `lotus state actor-cids --network-version 18`. + +The manifest CID & full list of actor code CIDs for nv18 using [actor v10](https://github.com/filecoin-project/builtin-actors/releases/tag/v10.0.0) is: + + "_manifest": "bafy2bzacecsuyf7mmvrhkx2evng5gnz5canlnz2fdlzu2lvcgptiq2pzuovos" + "account": "bafk2bzaceampw4romta75hyz5p4cqriypmpbgnkxncgxgqn6zptv5lsp2w2bo" + "cron": "bafk2bzacedcbtsifegiu432m5tysjzkxkmoczxscb6hqpmrr6img7xzdbbs2g" + "datacap": "bafk2bzacealj5uk7wixhvk7l5tnredtelralwnceafqq34nb2lbylhtuyo64u" + "eam": "bafk2bzacedrpm5gbleh4xkyo2jvs7p5g6f34soa6dpv7ashcdgy676snsum6g" + "ethaccount": "bafk2bzaceaqoc5zakbhjxn3jljc4lxnthllzunhdor7sxhwgmskvc6drqc3fa" + "evm": "bafk2bzaceahmzdxhqsm7cu2mexusjp6frm7r4kdesvti3etv5evfqboos2j4g" + "init": "bafk2bzaced2f5rhir3hbpqbz5ght7ohv2kgj42g5ykxrypuo2opxsup3ykwl6" + "multisig": "bafk2bzaceduf3hayh63jnl4z2knxv7cnrdenoubni22fxersc4octlwpxpmy4" + "paymentchannel": "bafk2bzaceartlg4mrbwgzcwric6mtvyawpbgx2xclo2vj27nna57nxynf3pgc" + "placeholder": "bafk2bzacedfvut2myeleyq67fljcrw4kkmn5pb5dpyozovj7jpoez5irnc3ro" + "reward": "bafk2bzacebnhtaejfjtzymyfmbdrfmo7vgj3zsof6zlucbmkhrvcuotw5dxpq" + "storagemarket": "bafk2bzaceclejwjtpu2dhw3qbx6ow7b4pmhwa7ocrbbiqwp36sq5yeg6jz2bc" + "storageminer": "bafk2bzaced4h7noksockro7glnssz2jnmo2rpzd7dvnmfs4p24zx3h6gtx47s" + "storagepower": "bafk2bzacec4ay4crzo73ypmh7o3fjendhbqrxake46bprabw67fvwjz5q6ixq" + "system": "bafk2bzacedakk5nofebyup4m7nvx6djksfwhnxzrfuq4oyemhpl4lllaikr64" + "verifiedregistry": "bafk2bzacedfel6edzqpe5oujno7fog4i526go4dtcs6vwrdtbpy2xq6htvcg6" ## 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. +With FEVM on Filecoin, we aim to provide full compatibility with the existing EVM ecosystem and its tooling out of the box. +Consequently, 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. +For full documentation on this new tooling, please see the [Lotus docs website](https://lotus.filecoin.io/lotus/configure/ethereum-rpc/). -**Enable Ethereum JSON RPC API** +**Enabling 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. @@ -56,7 +80,8 @@ Most of the Eth APIs take Eth accounts and tx has as an input, and they start wi [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 `~//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 you’d like to see! +Note: All three features are new, and we welcome user feedback, please create an issue if you have any enhancements that you’d like to see! + # 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. diff --git a/api/api_full.go b/api/api_full.go index e7a2d289e..1f7c38edf 100644 --- a/api/api_full.go +++ b/api/api_full.go @@ -771,6 +771,8 @@ type FullNode interface { 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 + // FilecoinAddressToEthAddress converts an f410 or f0 Filecoin Address to an EthAddress + FilecoinAddressToEthAddress(ctx context.Context, filecoinAddress address.Address) (ethtypes.EthAddress, error) //perm:read // EthBlockNumber returns the height of the latest (heaviest) TipSet EthBlockNumber(ctx context.Context) (ethtypes.EthUint64, error) //perm:read // EthGetBlockTransactionCountByNumber returns the number of messages in the TipSet diff --git a/api/mocks/mock_full.go b/api/mocks/mock_full.go index f49e912a2..370a213a0 100644 --- a/api/mocks/mock_full.go +++ b/api/mocks/mock_full.go @@ -1448,6 +1448,21 @@ func (mr *MockFullNodeMockRecorder) EthUnsubscribe(arg0, arg1 interface{}) *gomo return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EthUnsubscribe", reflect.TypeOf((*MockFullNode)(nil).EthUnsubscribe), arg0, arg1) } +// FilecoinAddressToEthAddress mocks base method. +func (m *MockFullNode) FilecoinAddressToEthAddress(arg0 context.Context, arg1 address.Address) (ethtypes.EthAddress, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "FilecoinAddressToEthAddress", arg0, arg1) + ret0, _ := ret[0].(ethtypes.EthAddress) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// FilecoinAddressToEthAddress indicates an expected call of FilecoinAddressToEthAddress. +func (mr *MockFullNodeMockRecorder) FilecoinAddressToEthAddress(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FilecoinAddressToEthAddress", reflect.TypeOf((*MockFullNode)(nil).FilecoinAddressToEthAddress), arg0, arg1) +} + // GasEstimateFeeCap mocks base method. func (m *MockFullNode) GasEstimateFeeCap(arg0 context.Context, arg1 *types.Message, arg2 int64, arg3 types.TipSetKey) (big.Int, error) { m.ctrl.T.Helper() diff --git a/api/proxy_gen.go b/api/proxy_gen.go index baf433735..ef257e69b 100644 --- a/api/proxy_gen.go +++ b/api/proxy_gen.go @@ -310,6 +310,8 @@ type FullNodeMethods struct { EthUnsubscribe func(p0 context.Context, p1 ethtypes.EthSubscriptionID) (bool, error) `perm:"write"` + FilecoinAddressToEthAddress func(p0 context.Context, p1 address.Address) (ethtypes.EthAddress, error) `perm:"read"` + GasEstimateFeeCap func(p0 context.Context, p1 *types.Message, p2 int64, p3 types.TipSetKey) (types.BigInt, error) `perm:"read"` GasEstimateGasLimit func(p0 context.Context, p1 *types.Message, p2 types.TipSetKey) (int64, error) `perm:"read"` @@ -2372,6 +2374,17 @@ func (s *FullNodeStub) EthUnsubscribe(p0 context.Context, p1 ethtypes.EthSubscri return false, ErrNotSupported } +func (s *FullNodeStruct) FilecoinAddressToEthAddress(p0 context.Context, p1 address.Address) (ethtypes.EthAddress, error) { + if s.Internal.FilecoinAddressToEthAddress == nil { + return *new(ethtypes.EthAddress), ErrNotSupported + } + return s.Internal.FilecoinAddressToEthAddress(p0, p1) +} + +func (s *FullNodeStub) FilecoinAddressToEthAddress(p0 context.Context, p1 address.Address) (ethtypes.EthAddress, error) { + return *new(ethtypes.EthAddress), ErrNotSupported +} + func (s *FullNodeStruct) GasEstimateFeeCap(p0 context.Context, p1 *types.Message, p2 int64, p3 types.TipSetKey) (types.BigInt, error) { if s.Internal.GasEstimateFeeCap == nil { return *new(types.BigInt), ErrNotSupported diff --git a/build/openrpc/worker.json.gz b/build/openrpc/worker.json.gz index c711608f5..ff47f7b11 100644 Binary files a/build/openrpc/worker.json.gz and b/build/openrpc/worker.json.gz differ diff --git a/build/version.go b/build/version.go index 78fb184c2..0b0ee7ad6 100644 --- a/build/version.go +++ b/build/version.go @@ -40,7 +40,7 @@ func BuildTypeString() string { } // BuildVersion is the local build version -const BuildVersion = "1.20.0-rc2" +const BuildVersion = "1.20.1" func UserVersion() string { if os.Getenv("LOTUS_VERSION_IGNORE_COMMIT") == "1" { diff --git a/chain/consensus/filcns/compute_state.go b/chain/consensus/filcns/compute_state.go index 995be156d..010b03fad 100644 --- a/chain/consensus/filcns/compute_state.go +++ b/chain/consensus/filcns/compute_state.go @@ -339,6 +339,14 @@ func (t *TipSetExecutor) ExecuteTipSet(ctx context.Context, } } + if ts.Height() == 0 { + // NB: This is here because the process that executes blocks requires that the + // block miner reference a valid miner in the state tree. Unless we create some + // magical genesis miner, this won't work properly, so we short circuit here + // This avoids the question of 'who gets paid the genesis block reward' + return blks[0].ParentStateRoot, blks[0].ParentMessageReceipts, nil + } + var parentEpoch abi.ChainEpoch pstate := blks[0].ParentStateRoot if blks[0].Height > 0 { diff --git a/chain/stmgr/execute.go b/chain/stmgr/execute.go index 60ee069d0..e67398299 100644 --- a/chain/stmgr/execute.go +++ b/chain/stmgr/execute.go @@ -52,14 +52,6 @@ func (sm *StateManager) TipSetState(ctx context.Context, ts *types.TipSet) (st c sm.stlk.Unlock() - if ts.Height() == 0 { - // NB: This is here because the process that executes blocks requires that the - // block miner reference a valid miner in the state tree. Unless we create some - // magical genesis miner, this won't work properly, so we short circuit here - // This avoids the question of 'who gets paid the genesis block reward' - return ts.Blocks()[0].ParentStateRoot, ts.Blocks()[0].ParentMessageReceipts, nil - } - st, rec, err = sm.tsExec.ExecuteTipSet(ctx, sm, ts, sm.tsExecMonitor, false) if err != nil { return cid.Undef, cid.Undef, err diff --git a/chain/types/ethtypes/eth_types.go b/chain/types/ethtypes/eth_types.go index 006ffffdc..ddda91ea0 100644 --- a/chain/types/ethtypes/eth_types.go +++ b/chain/types/ethtypes/eth_types.go @@ -295,6 +295,19 @@ func EthAddressFromPubKey(pubk []byte) ([]byte, error) { return ethAddr, nil } +func IsEthAddress(addr address.Address) bool { + if addr.Protocol() != address.Delegated { + return false + } + payload := addr.Payload() + namespace, _, err := varint.FromUvarint(payload) + if err != nil { + return false + } + + return namespace == builtintypes.EthereumAddressManagerActorID +} + func EthAddressFromFilecoinAddress(addr address.Address) (EthAddress, error) { switch addr.Protocol() { case address.ID: diff --git a/cli/send.go b/cli/send.go index e18aa544e..cfa2515c0 100644 --- a/cli/send.go +++ b/cli/send.go @@ -1,15 +1,18 @@ package cli import ( + "bytes" "encoding/hex" "fmt" "strings" "github.com/urfave/cli/v2" + cbg "github.com/whyrusleeping/cbor-gen" "golang.org/x/xerrors" "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/abi" + builtintypes "github.com/filecoin-project/go-state-types/builtin" "github.com/filecoin-project/lotus/chain/actors/builtin" "github.com/filecoin-project/lotus/chain/types" @@ -117,15 +120,51 @@ var sendCmd = &cli.Command{ params.From = faddr } - if params.From.Protocol() == address.Delegated { + if cctx.IsSet("params-hex") { + decparams, err := hex.DecodeString(cctx.String("params-hex")) + if err != nil { + return fmt.Errorf("failed to decode hex params: %w", err) + } + params.Params = decparams + } + + if ethtypes.IsEthAddress(params.From) { + // Method numbers don't make sense from eth accounts. + if cctx.IsSet("method") { + return xerrors.Errorf("messages from f410f addresses may not specify a method number") + } + + // Now, figure out the correct method number from the recipient. + if params.To == builtintypes.EthereumAddressManagerActorAddr { + params.Method = builtintypes.MethodsEAM.CreateExternal + } else { + params.Method = builtintypes.MethodsEVM.InvokeContract + } + + if cctx.IsSet("params-json") { + return xerrors.Errorf("may not call with json parameters from an eth account") + } + + // And format the parameters, if present. + if len(params.Params) > 0 { + var buf bytes.Buffer + if err := cbg.WriteByteArray(&buf, params.Params); err != nil { + return xerrors.Errorf("failed to marshal EVM parameters") + } + params.Params = buf.Bytes() + } + + // We can only send to an f410f or f0 address. if !(params.To.Protocol() == address.ID || params.To.Protocol() == address.Delegated) { api := srv.FullNodeAPI() // Resolve id addr if possible. params.To, err = api.StateLookupID(ctx, params.To, types.EmptyTSK) if err != nil { - return xerrors.Errorf("f4 addresses can only send to other f4 or id addresses. could not find id address for %s", params.To.String()) + return xerrors.Errorf("addresses starting with f410f can only send to other addresses starting with f410f, or id addresses. could not find id address for %s", params.To.String()) } } + } else { + params.Method = abi.MethodNum(cctx.Uint64("method")) } if cctx.IsSet("gas-premium") { @@ -149,22 +188,13 @@ var sendCmd = &cli.Command{ params.GasLimit = &limit } - params.Method = abi.MethodNum(cctx.Uint64("method")) - if cctx.IsSet("params-json") { - decparams, err := srv.DecodeTypedParamsFromJSON(ctx, params.To, params.Method, cctx.String("params-json")) - if err != nil { - return fmt.Errorf("failed to decode json params: %w", err) - } - params.Params = decparams - } - if cctx.IsSet("params-hex") { if params.Params != nil { return fmt.Errorf("can only specify one of 'params-json' and 'params-hex'") } - decparams, err := hex.DecodeString(cctx.String("params-hex")) + decparams, err := srv.DecodeTypedParamsFromJSON(ctx, params.To, params.Method, cctx.String("params-json")) if err != nil { - return fmt.Errorf("failed to decode hex params: %w", err) + return fmt.Errorf("failed to decode json params: %w", err) } params.Params = decparams } diff --git a/cli/send_test.go b/cli/send_test.go index ec858774c..2c59a9641 100644 --- a/cli/send_test.go +++ b/cli/send_test.go @@ -7,14 +7,16 @@ import ( "testing" "github.com/golang/mock/gomock" - "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ucli "github.com/urfave/cli/v2" "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/builtin" "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/chain/types/ethtypes" ) func mustAddr(a address.Address, err error) address.Address { @@ -65,7 +67,51 @@ func TestSendCLI(t *testing.T) { mockSrvcs.EXPECT().Close(), ) err := app.Run([]string{"lotus", "send", "t01", "1"}) - assert.NoError(t, err) - assert.EqualValues(t, sigMsg.Cid().String()+"\n", buf.String()) + require.NoError(t, err) + require.EqualValues(t, sigMsg.Cid().String()+"\n", buf.String()) + }) +} + +func TestSendEthereum(t *testing.T) { + oneFil := abi.TokenAmount(types.MustParseFIL("1")) + + t.Run("simple", func(t *testing.T) { + app, mockSrvcs, buf, done := newMockApp(t, sendCmd) + defer done() + + testEthAddr, err := ethtypes.CastEthAddress(make([]byte, 20)) + require.NoError(t, err) + testAddr := mustAddr(testEthAddr.ToFilecoinAddress()) + + params := abi.CborBytes([]byte{1, 2, 3, 4}) + var paramsBuf bytes.Buffer + require.NoError(t, params.MarshalCBOR(¶msBuf)) + + arbtProto := &api.MessagePrototype{ + Message: types.Message{ + From: testAddr, + To: mustAddr(address.NewIDAddress(1)), + Value: oneFil, + Method: builtin.MethodsEVM.InvokeContract, + Params: paramsBuf.Bytes(), + }, + } + sigMsg := fakeSign(&arbtProto.Message) + + gomock.InOrder( + mockSrvcs.EXPECT().MessageForSend(gomock.Any(), SendParams{ + From: testAddr, + To: mustAddr(address.NewIDAddress(1)), + Val: oneFil, + Method: builtin.MethodsEVM.InvokeContract, + Params: paramsBuf.Bytes(), + }).Return(arbtProto, nil), + mockSrvcs.EXPECT().PublishMessage(gomock.Any(), arbtProto, false). + Return(sigMsg, nil, nil), + mockSrvcs.EXPECT().Close(), + ) + err = app.Run([]string{"lotus", "send", "--from-eth-addr", testEthAddr.String(), "--params-hex", "01020304", "f01", "1"}) + require.NoError(t, err) + require.EqualValues(t, sigMsg.Cid().String()+"\n", buf.String()) }) } diff --git a/documentation/en/api-v1-unstable-methods.md b/documentation/en/api-v1-unstable-methods.md index c25ba1158..b951a51f1 100644 --- a/documentation/en/api-v1-unstable-methods.md +++ b/documentation/en/api-v1-unstable-methods.md @@ -101,6 +101,8 @@ * [EthSubscribe](#EthSubscribe) * [EthUninstallFilter](#EthUninstallFilter) * [EthUnsubscribe](#EthUnsubscribe) +* [Filecoin](#Filecoin) + * [FilecoinAddressToEthAddress](#FilecoinAddressToEthAddress) * [Gas](#Gas) * [GasEstimateFeeCap](#GasEstimateFeeCap) * [GasEstimateGasLimit](#GasEstimateGasLimit) @@ -2944,6 +2946,24 @@ Inputs: Response: `true` +## Filecoin + + +### FilecoinAddressToEthAddress +FilecoinAddressToEthAddress converts an f410 or f0 Filecoin Address to an EthAddress + + +Perms: read + +Inputs: +```json +[ + "f01234" +] +``` + +Response: `"0x5cbeecf99d3fdb3f25e309cc264f240bb0664031"` + ## Gas diff --git a/documentation/en/cli-lotus-miner.md b/documentation/en/cli-lotus-miner.md index e345c71c1..82c9ba147 100644 --- a/documentation/en/cli-lotus-miner.md +++ b/documentation/en/cli-lotus-miner.md @@ -7,7 +7,7 @@ USAGE: lotus-miner [global options] command [command options] [arguments...] VERSION: - 1.20.0-rc2 + 1.20.1 COMMANDS: init Initialize a lotus miner repo diff --git a/documentation/en/cli-lotus-worker.md b/documentation/en/cli-lotus-worker.md index eb08e14e5..1673ba528 100644 --- a/documentation/en/cli-lotus-worker.md +++ b/documentation/en/cli-lotus-worker.md @@ -7,7 +7,7 @@ USAGE: lotus-worker [global options] command [command options] [arguments...] VERSION: - 1.20.0-rc2 + 1.20.1 COMMANDS: run Start lotus worker diff --git a/documentation/en/cli-lotus.md b/documentation/en/cli-lotus.md index 5a416c736..7feaf42c7 100644 --- a/documentation/en/cli-lotus.md +++ b/documentation/en/cli-lotus.md @@ -7,7 +7,7 @@ USAGE: lotus [global options] command [command options] [arguments...] VERSION: - 1.20.0-rc2 + 1.20.1 COMMANDS: daemon Start a lotus daemon process diff --git a/extern/filecoin-ffi b/extern/filecoin-ffi index fead3c628..1bc8bf8b4 160000 --- a/extern/filecoin-ffi +++ b/extern/filecoin-ffi @@ -1 +1 @@ -Subproject commit fead3c628794a332ebc2ef0a153885bfab87a901 +Subproject commit 1bc8bf8b482b2ef34cbce17bfc3a7ec68c047461 diff --git a/gateway/node.go b/gateway/node.go index 00a83df96..90a6812b5 100644 --- a/gateway/node.go +++ b/gateway/node.go @@ -89,6 +89,7 @@ type TargetAPI interface { WalletBalance(context.Context, address.Address) (types.BigInt, error) EthAddressToFilecoinAddress(ctx context.Context, ethAddress ethtypes.EthAddress) (address.Address, error) + FilecoinAddressToEthAddress(ctx context.Context, filecoinAddress address.Address) (ethtypes.EthAddress, error) EthBlockNumber(ctx context.Context) (ethtypes.EthUint64, error) EthGetBlockTransactionCountByNumber(ctx context.Context, blkNum ethtypes.EthUint64) (ethtypes.EthUint64, error) EthGetBlockTransactionCountByHash(ctx context.Context, blkHash ethtypes.EthHash) (ethtypes.EthUint64, error) diff --git a/itests/eth_api_test.go b/itests/eth_api_test.go index 63c6aa5a2..c24b32416 100644 --- a/itests/eth_api_test.go +++ b/itests/eth_api_test.go @@ -46,3 +46,66 @@ func TestEthAddressToFilecoinAddress(t *testing.T) { require.Equal(t, filecoinIdArr, apiFilAddr) } + +func TestFilecoinAddressToEthAddress(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() + + secpDelegatedKey, err := key.GenerateKey(types.KTDelegated) + require.NoError(t, err) + + filecoinKeyAddr, err := client.WalletImport(ctx, &secpDelegatedKey.KeyInfo) + require.NoError(t, err) + + ethAddr, err := ethtypes.EthAddressFromFilecoinAddress(filecoinKeyAddr) + require.NoError(t, err) + + apiEthAddr, err := client.FilecoinAddressToEthAddress(ctx, filecoinKeyAddr) + require.NoError(t, err) + + require.Equal(t, ethAddr, apiEthAddr) + + filecoinIdArr := builtin.StorageMarketActorAddr + ethAddr, err = ethtypes.EthAddressFromFilecoinAddress(filecoinIdArr) + require.NoError(t, err) + + apiEthAddr, err = client.FilecoinAddressToEthAddress(ctx, filecoinIdArr) + require.NoError(t, err) + + require.Equal(t, ethAddr, apiEthAddr) + + secpKey, err := key.GenerateKey(types.KTSecp256k1) + require.NoError(t, err) + + filecoinSecpAddr, err := client.WalletImport(ctx, &secpKey.KeyInfo) + require.NoError(t, err) + + _, err = client.FilecoinAddressToEthAddress(ctx, filecoinSecpAddr) + + require.ErrorContains(t, err, ethtypes.ErrInvalidAddress.Error()) +} + +func TestEthGetGenesis(t *testing.T) { + blockTime := 100 * time.Millisecond + client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC()) + ens.InterconnectAll().BeginMining(blockTime) + + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + ethBlk, err := client.EVM().EthGetBlockByNumber(ctx, "0x0", true) + require.NoError(t, err) + + genesis, err := client.ChainGetGenesis(ctx) + require.NoError(t, err) + + genesisCid, err := genesis.Key().Cid() + require.NoError(t, err) + + genesisHash, err := ethtypes.EthHashFromCid(genesisCid) + require.NoError(t, err) + require.Equal(t, ethBlk.Hash, genesisHash) +} diff --git a/itests/eth_balance_test.go b/itests/eth_balance_test.go index 3176aefc8..d5f151227 100644 --- a/itests/eth_balance_test.go +++ b/itests/eth_balance_test.go @@ -2,6 +2,7 @@ package itests import ( "context" + "strconv" "testing" "time" @@ -11,6 +12,7 @@ import ( "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/big" + "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types/ethtypes" "github.com/filecoin-project/lotus/itests/kit" @@ -95,3 +97,39 @@ func TestEthGetBalanceBuiltinActor(t *testing.T) { require.NoError(t, err) require.Equal(t, ethtypes.EthBigInt{Int: big.NewInt(10).Int}, ebal) } + +func TestEthBalanceCorrectLookup(t *testing.T) { + blockTime := 100 * time.Millisecond + client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC()) + ens.InterconnectAll().BeginMining(blockTime) + + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + _, ethAddr, filAddr := client.EVM().NewAccount() + + val := int64(100) + + smsg, err := client.MpoolPushMessage(ctx, &types.Message{ + To: filAddr, + From: client.DefaultKey.Address, + Value: abi.NewTokenAmount(val), + }, nil) + require.NoError(t, err) + + ml, err := client.StateWaitMsg(ctx, smsg.Cid(), 3, api.LookbackNoLimit, false) + require.NoError(t, err) + require.True(t, ml.Receipt.ExitCode.IsSuccess()) + + bal, err := client.EVM().EthGetBalance(ctx, ethAddr, strconv.FormatInt(int64(ml.Height-2), 10)) + require.NoError(t, err) + require.Equal(t, int64(0), bal.Int64()) + + bal, err = client.EVM().EthGetBalance(ctx, ethAddr, strconv.FormatInt(int64(ml.Height-1), 10)) + require.NoError(t, err) + require.Equal(t, val, bal.Int64()) + + bal, err = client.EVM().EthGetBalance(ctx, ethAddr, strconv.FormatInt(int64(ml.Height), 10)) + require.NoError(t, err) + require.Equal(t, val, bal.Int64()) +} diff --git a/node/impl/full/eth.go b/node/impl/full/eth.go index 23ecffd15..3fccabe4c 100644 --- a/node/impl/full/eth.go +++ b/node/impl/full/eth.go @@ -187,6 +187,10 @@ func (a *EthAPI) EthAddressToFilecoinAddress(ctx context.Context, ethAddress eth return ethAddress.ToFilecoinAddress() } +func (a *EthAPI) FilecoinAddressToEthAddress(ctx context.Context, filecoinAddress address.Address) (ethtypes.EthAddress, error) { + return ethtypes.EthAddressFromFilecoinAddress(filecoinAddress) +} + func (a *EthModule) countTipsetMsgs(ctx context.Context, ts *types.TipSet) (int, error) { blkMsgs, err := a.Chain.BlockMsgsForTipset(ctx, ts) if err != nil { @@ -633,7 +637,12 @@ func (a *EthModule) EthGetBalance(ctx context.Context, address ethtypes.EthAddre return ethtypes.EthBigInt{}, xerrors.Errorf("cannot parse block param: %s", blkParam) } - actor, err := a.StateGetActor(ctx, filAddr, ts.Key()) + st, _, err := a.StateManager.TipSetState(ctx, ts) + if err != nil { + return ethtypes.EthBigInt{}, xerrors.Errorf("failed to compute tipset state: %w", err) + } + + actor, err := a.StateManager.LoadActorRaw(ctx, filAddr, st) if xerrors.Is(err, types.ErrActorNotFound) { return ethtypes.EthBigIntZero, nil } else if err != nil { @@ -1768,11 +1777,7 @@ func (e *ethSubscription) stop() { } func newEthBlockFromFilecoinTipSet(ctx context.Context, ts *types.TipSet, fullTxInfo bool, cs *store.ChainStore, sa StateAPI) (ethtypes.EthBlock, error) { - parent, err := cs.LoadTipSet(ctx, ts.Parents()) - if err != nil { - return ethtypes.EthBlock{}, err - } - parentKeyCid, err := parent.Key().Cid() + parentKeyCid, err := ts.Parents().Cid() if err != nil { return ethtypes.EthBlock{}, err } @@ -1781,6 +1786,8 @@ func newEthBlockFromFilecoinTipSet(ctx context.Context, ts *types.TipSet, fullTx return ethtypes.EthBlock{}, err } + bn := ethtypes.EthUint64(ts.Height()) + blkCid, err := ts.Key().Cid() if err != nil { return ethtypes.EthBlock{}, err @@ -1797,20 +1804,35 @@ func newEthBlockFromFilecoinTipSet(ctx context.Context, ts *types.TipSet, fullTx block := ethtypes.NewEthBlock(len(msgs) > 0) - // this seems to be a very expensive way to get gasUsed of the block. may need to find an efficient way to do it gasUsed := int64(0) - for txIdx, msg := range msgs { - msgLookup, err := sa.StateSearchMsg(ctx, types.EmptyTSK, msg.Cid(), api.LookbackNoLimit, false) - if err != nil || msgLookup == nil { - return ethtypes.EthBlock{}, nil - } - gasUsed += msgLookup.Receipt.GasUsed + compOutput, err := sa.StateCompute(ctx, ts.Height(), nil, ts.Key()) + if err != nil { + return ethtypes.EthBlock{}, xerrors.Errorf("failed to compute state: %w", err) + } - tx, err := newEthTxFromMessageLookup(ctx, msgLookup, txIdx, cs, sa) - if err != nil { - return ethtypes.EthBlock{}, nil + for txIdx, msg := range compOutput.Trace { + // skip system messages like reward application and cron + if msg.Msg.From == builtintypes.SystemActorAddr { + continue } + gasUsed += msg.MsgRct.GasUsed + smsgCid, err := getSignedMessage(ctx, cs, msg.MsgCid) + if err != nil { + return ethtypes.EthBlock{}, xerrors.Errorf("failed to get signed msg %s: %w", msg.MsgCid, err) + } + tx, err := newEthTxFromSignedMessage(ctx, smsgCid, sa) + if err != nil { + return ethtypes.EthBlock{}, xerrors.Errorf("failed to convert msg to ethTx: %w", err) + } + + ti := ethtypes.EthUint64(txIdx) + + tx.ChainID = ethtypes.EthUint64(build.Eip155ChainId) + tx.BlockHash = &blkHash + tx.BlockNumber = &bn + tx.TransactionIndex = &ti + if fullTxInfo { block.Transactions = append(block.Transactions, tx) } else { @@ -1819,7 +1841,7 @@ func newEthBlockFromFilecoinTipSet(ctx context.Context, ts *types.TipSet, fullTx } block.Hash = blkHash - block.Number = ethtypes.EthUint64(ts.Height()) + block.Number = bn block.ParentHash = parentBlkHash block.Timestamp = ethtypes.EthUint64(ts.Blocks()[0].Timestamp) block.BaseFeePerGas = ethtypes.EthBigInt{Int: ts.Blocks()[0].ParentBaseFee.Int} @@ -2000,20 +2022,9 @@ func newEthTxFromMessageLookup(ctx context.Context, msgLookup *api.MsgLookup, tx return ethtypes.EthTx{}, err } - smsg, err := cs.GetSignedMessage(ctx, msgLookup.Message) + smsg, err := getSignedMessage(ctx, cs, msgLookup.Message) if err != nil { - // We couldn't find the signed message, it might be a BLS message, so search for a regular message. - msg, err := cs.GetMessage(ctx, msgLookup.Message) - if err != nil { - return ethtypes.EthTx{}, err - } - smsg = &types.SignedMessage{ - Message: *msg, - Signature: crypto.Signature{ - Type: crypto.SigTypeBLS, - Data: nil, - }, - } + return ethtypes.EthTx{}, xerrors.Errorf("failed to get signed msg: %w", err) } tx, err := newEthTxFromSignedMessage(ctx, smsg, sa) @@ -2369,6 +2380,25 @@ func calculateRewardsAndGasUsed(rewardPercentiles []float64, txGasRewards gasRew return rewards, totalGasUsed } +func getSignedMessage(ctx context.Context, cs *store.ChainStore, msgCid cid.Cid) (*types.SignedMessage, error) { + smsg, err := cs.GetSignedMessage(ctx, msgCid) + if err != nil { + // We couldn't find the signed message, it might be a BLS message, so search for a regular message. + msg, err := cs.GetMessage(ctx, msgCid) + if err != nil { + return nil, xerrors.Errorf("failed to find msg %s: %w", msgCid, err) + } + smsg = &types.SignedMessage{ + Message: *msg, + Signature: crypto.Signature{ + Type: crypto.SigTypeBLS, + }, + } + } + + return smsg, nil +} + type gasRewardTuple struct { gas uint64 reward ethtypes.EthBigInt diff --git a/node/impl/full/gas.go b/node/impl/full/gas.go index c0e328bc9..cd3cb0156 100644 --- a/node/impl/full/gas.go +++ b/node/impl/full/gas.go @@ -327,10 +327,15 @@ func gasEstimateGasLimit( transitionalMulti := 1.0 // Overestimate gas around the upgrade - if ts.Height() <= build.UpgradeSkyrHeight && (build.UpgradeSkyrHeight-ts.Height() <= 20) { - transitionalMulti = 2.0 - + if ts.Height() <= build.UpgradeHyggeHeight && (build.UpgradeHyggeHeight-ts.Height() <= 20) { func() { + + // Bare transfers get about 3x more expensive: https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0057.md#product-considerations + if msgIn.Method == builtin.MethodSend { + transitionalMulti = 3.0 + return + } + st, err := smgr.ParentState(ts) if err != nil { return @@ -342,26 +347,27 @@ func gasEstimateGasLimit( if lbuiltin.IsStorageMinerActor(act.Code) { switch msgIn.Method { - case 5: - transitionalMulti = 3.954 + case 3: + transitionalMulti = 1.92 + case 4: + transitionalMulti = 1.72 case 6: - transitionalMulti = 4.095 + transitionalMulti = 1.06 case 7: - // skip, stay at 2.0 - // transitionalMulti = 1.289 - case 11: - transitionalMulti = 17.8758 + transitionalMulti = 1.2 case 16: - transitionalMulti = 2.1704 - case 25: - transitionalMulti = 3.1177 + transitionalMulti = 1.19 + case 18: + transitionalMulti = 1.73 + case 23: + transitionalMulti = 1.73 case 26: - transitionalMulti = 2.3322 + transitionalMulti = 1.15 + case 27: + transitionalMulti = 1.18 default: } } - - // skip storage market, 80th percentie for everything ~1.9, leave it at 2.0 }() } ret = (ret * int64(transitionalMulti*1024)) >> 10 diff --git a/scripts/build-appimage-bundle.sh b/scripts/build-appimage-bundle.sh new file mode 100755 index 000000000..d4ce6de77 --- /dev/null +++ b/scripts/build-appimage-bundle.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash +set -ex + +REQUIRED=( + "ipfs" + "sha512sum" +) +for REQUIRE in "${REQUIRED[@]}" +do + command -v "${REQUIRE}" >/dev/null 2>&1 || echo >&2 "'${REQUIRE}' must be installed" +done + +mkdir bundle +pushd bundle + +export IPFS_PATH=`mktemp -d` +ipfs init +ipfs daemon & +PID="$!" +trap "kill -9 ${PID}" EXIT +sleep 30 + +cp "/tmp/workspace/appimage/Lotus-${CIRCLE_TAG}-x86_64.AppImage" . +sha512sum "Lotus-${CIRCLE_TAG}-x86_64.AppImage" > "Lotus-${CIRCLE_TAG}-x86_64.AppImage.sha512" +ipfs add -q "Lotus-${CIRCLE_TAG}-x86_64.AppImage" > "Lotus-${CIRCLE_TAG}-x86_64.AppImage.cid" +popd