diff --git a/CHANGELOG.md b/CHANGELOG.md index 94ef2aba312..5b13ba3f625 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 + +# 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-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)): +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 - -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`. +### v10 Built-in actor bundles + +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 e7a2d289e01..1f7c38edf80 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 f49e912a25f..370a213a0c1 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 baf43373594..ef257e69b4c 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/full.json.gz b/build/openrpc/full.json.gz index d136b416580..7ed2f717a32 100644 Binary files a/build/openrpc/full.json.gz and b/build/openrpc/full.json.gz differ diff --git a/build/openrpc/gateway.json.gz b/build/openrpc/gateway.json.gz index 4143b649ee0..eeb3f56f76e 100644 Binary files a/build/openrpc/gateway.json.gz and b/build/openrpc/gateway.json.gz differ diff --git a/build/openrpc/miner.json.gz b/build/openrpc/miner.json.gz index 5060356955c..a60c8d04d54 100644 Binary files a/build/openrpc/miner.json.gz and b/build/openrpc/miner.json.gz differ diff --git a/build/openrpc/worker.json.gz b/build/openrpc/worker.json.gz index c711608f563..ff47f7b1115 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 78fb184c285..0b0ee7ad6d6 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 995be156d01..010b03fadcf 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 60ee069d04d..e673982992a 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 006ffffdc0a..ddda91ea0a7 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 e18aa544e13..cfa2515c07b 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 ec858774ce2..2c59a9641f6 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 c25ba11582b..b951a51f118 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 e345c71c17c..82c9ba14756 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 eb08e14e5cf..1673ba5288e 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 5a416c736ce..7feaf42c708 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/gateway/node.go b/gateway/node.go index 00a83df9631..90a6812b536 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 63c6aa5a219..c24b3241615 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 3176aefc854..d5f15122715 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 23ecffd15fb..3fccabe4cea 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,19 +1804,34 @@ 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 + compOutput, err := sa.StateCompute(ctx, ts.Height(), nil, ts.Key()) + if err != nil { + return ethtypes.EthBlock{}, xerrors.Errorf("failed to compute state: %w", err) + } + + for txIdx, msg := range compOutput.Trace { + // skip system messages like reward application and cron + if msg.Msg.From == builtintypes.SystemActorAddr { + continue } - gasUsed += msgLookup.Receipt.GasUsed - tx, err := newEthTxFromMessageLookup(ctx, msgLookup, txIdx, cs, sa) + gasUsed += msg.MsgRct.GasUsed + smsgCid, err := getSignedMessage(ctx, cs, msg.MsgCid) if err != nil { - return ethtypes.EthBlock{}, 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) @@ -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 c0e328bc94e..cd3cb0156b6 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 00000000000..d4ce6de770a --- /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