From 63189cd81deea4f7ea93aa9a47377fae45703cdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 26 Apr 2021 16:48:20 +0200 Subject: [PATCH 001/160] Generate wrappers for new actor versions --- Makefile | 10 +- api/test/ccupgrade.go | 2 +- api/test/deadlines.go | 2 +- api/test/mining.go | 2 +- api/test/test.go | 2 +- api/test/window_post.go | 8 +- chain/actors/agen/main.go | 218 +++++++++ chain/actors/builtin/account/account.go | 12 + .../actors/builtin/account/actor.go.template | 41 ++ .../actors/builtin/account/state.go.template | 30 ++ chain/actors/builtin/builtin.go | 168 +++++-- chain/actors/builtin/builtin.go.template | 144 ++++++ chain/actors/builtin/cron/actor.go.template | 10 + chain/actors/builtin/init/actor.go.template | 60 +++ chain/actors/builtin/init/init.go | 12 + chain/actors/builtin/init/state.go.template | 89 ++++ chain/actors/builtin/init/v3.go | 3 +- chain/actors/builtin/init/v4.go | 3 +- chain/actors/builtin/market/actor.go.template | 142 ++++++ chain/actors/builtin/market/market.go | 17 +- chain/actors/builtin/market/state.go.template | 209 ++++++++ chain/actors/builtin/market/v0.go | 3 +- chain/actors/builtin/market/v2.go | 32 +- chain/actors/builtin/market/v3.go | 56 +-- chain/actors/builtin/market/v4.go | 48 +- chain/actors/builtin/miner/actor.go.template | 270 ++++++++++ chain/actors/builtin/miner/miner.go | 22 +- chain/actors/builtin/miner/state.go.template | 460 ++++++++++++++++++ chain/actors/builtin/miner/v0.go | 7 + chain/actors/builtin/miner/v2.go | 6 + chain/actors/builtin/miner/v3.go | 29 +- chain/actors/builtin/miner/v4.go | 29 +- .../actors/builtin/multisig/actor.go.template | 118 +++++ chain/actors/builtin/multisig/message.go | 79 --- .../builtin/multisig/message.go.template | 146 ++++++ .../multisig/{state.go => multisig.go} | 86 +++- .../actors/builtin/multisig/state.go.template | 97 ++++ .../builtin/multisig/{state0.go => v0.go} | 6 +- .../builtin/multisig/{state2.go => v2.go} | 3 +- .../builtin/multisig/{state3.go => v3.go} | 5 +- .../builtin/multisig/{state4.go => v4.go} | 7 +- chain/actors/builtin/paych/actor.go.template | 109 +++++ chain/actors/builtin/paych/message.go | 36 -- .../actors/builtin/paych/message.go.template | 74 +++ .../builtin/paych/{state.go => paych.go} | 46 +- chain/actors/builtin/paych/state.go.template | 104 ++++ .../actors/builtin/paych/{state0.go => v0.go} | 0 .../actors/builtin/paych/{state2.go => v2.go} | 0 .../actors/builtin/paych/{state3.go => v3.go} | 0 .../actors/builtin/paych/{state4.go => v4.go} | 0 chain/actors/builtin/power/actor.go.template | 78 +++ chain/actors/builtin/power/power.go | 14 +- chain/actors/builtin/power/state.go.template | 151 ++++++ chain/actors/builtin/power/v0.go | 11 +- chain/actors/builtin/power/v2.go | 6 +- chain/actors/builtin/power/v3.go | 5 +- chain/actors/builtin/power/v4.go | 5 +- chain/actors/builtin/reward/actor.go.template | 60 +++ chain/actors/builtin/reward/reward.go | 15 +- chain/actors/builtin/reward/state.go.template | 103 ++++ chain/actors/builtin/reward/v0.go | 8 +- chain/actors/builtin/reward/v2.go | 2 + chain/actors/builtin/reward/v3.go | 2 + chain/actors/builtin/reward/v4.go | 2 + .../actors/builtin/verifreg/actor.go.template | 51 ++ .../actors/builtin/verifreg/state.go.template | 58 +++ chain/actors/builtin/verifreg/verifreg.go | 14 + chain/actors/policy/policy.go | 68 ++- chain/actors/policy/policy.go.template | 233 +++++++++ cmd/lotus-storage-miner/actor_test.go | 2 +- .../misc/actors_version_checklist.md | 17 + 71 files changed, 3666 insertions(+), 301 deletions(-) create mode 100644 chain/actors/agen/main.go create mode 100644 chain/actors/builtin/account/actor.go.template create mode 100644 chain/actors/builtin/account/state.go.template create mode 100644 chain/actors/builtin/builtin.go.template create mode 100644 chain/actors/builtin/cron/actor.go.template create mode 100644 chain/actors/builtin/init/actor.go.template create mode 100644 chain/actors/builtin/init/state.go.template create mode 100644 chain/actors/builtin/market/actor.go.template create mode 100644 chain/actors/builtin/market/state.go.template create mode 100644 chain/actors/builtin/miner/actor.go.template create mode 100644 chain/actors/builtin/miner/state.go.template create mode 100644 chain/actors/builtin/multisig/actor.go.template delete mode 100644 chain/actors/builtin/multisig/message.go create mode 100644 chain/actors/builtin/multisig/message.go.template rename chain/actors/builtin/multisig/{state.go => multisig.go} (51%) create mode 100644 chain/actors/builtin/multisig/state.go.template rename chain/actors/builtin/multisig/{state0.go => v0.go} (95%) rename chain/actors/builtin/multisig/{state2.go => v2.go} (99%) rename chain/actors/builtin/multisig/{state3.go => v3.go} (96%) rename chain/actors/builtin/multisig/{state4.go => v4.go} (93%) create mode 100644 chain/actors/builtin/paych/actor.go.template delete mode 100644 chain/actors/builtin/paych/message.go create mode 100644 chain/actors/builtin/paych/message.go.template rename chain/actors/builtin/paych/{state.go => paych.go} (80%) create mode 100644 chain/actors/builtin/paych/state.go.template rename chain/actors/builtin/paych/{state0.go => v0.go} (100%) rename chain/actors/builtin/paych/{state2.go => v2.go} (100%) rename chain/actors/builtin/paych/{state3.go => v3.go} (100%) rename chain/actors/builtin/paych/{state4.go => v4.go} (100%) create mode 100644 chain/actors/builtin/power/actor.go.template create mode 100644 chain/actors/builtin/power/state.go.template create mode 100644 chain/actors/builtin/reward/actor.go.template create mode 100644 chain/actors/builtin/reward/state.go.template create mode 100644 chain/actors/builtin/verifreg/actor.go.template create mode 100644 chain/actors/builtin/verifreg/state.go.template create mode 100644 chain/actors/policy/policy.go.template create mode 100644 documentation/misc/actors_version_checklist.md diff --git a/Makefile b/Makefile index 16269e133..93b647942 100644 --- a/Makefile +++ b/Makefile @@ -325,6 +325,10 @@ type-gen: method-gen: (cd ./lotuspond/front/src/chain && go run ./methodgen.go) +actors-gen: + go run ./chain/actors/agen + go fmt ./... + api-gen: go run ./gen/api > api/apistruct/struct.go goimports -w api/apistruct @@ -333,9 +337,9 @@ api-gen: docsgen: docsgen-md docsgen-openrpc -docsgen-md-bin: +docsgen-md-bin: actors-gen go build $(GOFLAGS) -o docgen-md ./api/docgen/cmd -docsgen-openrpc-bin: +docsgen-openrpc-bin: actors-gen go build $(GOFLAGS) -o docgen-openrpc ./api/docgen-openrpc/cmd docsgen-md: docsgen-md-full docsgen-md-storage docsgen-md-worker @@ -358,7 +362,7 @@ docsgen-openrpc-worker: docsgen-openrpc-bin .PHONY: docsgen docsgen-md-bin docsgen-openrpc-bin -gen: type-gen method-gen docsgen api-gen +gen: actors-gen type-gen method-gen docsgen api-gen .PHONY: gen print-%: diff --git a/api/test/ccupgrade.go b/api/test/ccupgrade.go index 8c2bdc9b4..283c8f610 100644 --- a/api/test/ccupgrade.go +++ b/api/test/ccupgrade.go @@ -31,7 +31,7 @@ func TestCCUpgrade(t *testing.T, b APIBuilder, blocktime time.Duration) { func testCCUpgrade(t *testing.T, b APIBuilder, blocktime time.Duration, upgradeHeight abi.ChainEpoch) { ctx := context.Background() - n, sn := b(t, []FullNodeOpts{FullNodeWithActorsV4At(upgradeHeight)}, OneMiner) + n, sn := b(t, []FullNodeOpts{FullNodeWithLatestActorsAt(upgradeHeight)}, OneMiner) client := n[0].FullNode.(*impl.FullNodeAPI) miner := sn[0] diff --git a/api/test/deadlines.go b/api/test/deadlines.go index c896e7e00..8060c18f3 100644 --- a/api/test/deadlines.go +++ b/api/test/deadlines.go @@ -61,7 +61,7 @@ func TestDeadlineToggling(t *testing.T, b APIBuilder, blocktime time.Duration) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - n, sn := b(t, []FullNodeOpts{FullNodeWithActorsV4At(upgradeH)}, OneMiner) + n, sn := b(t, []FullNodeOpts{FullNodeWithLatestActorsAt(upgradeH)}, OneMiner) client := n[0].FullNode.(*impl.FullNodeAPI) minerA := sn[0] diff --git a/api/test/mining.go b/api/test/mining.go index 8e300a9c9..4a4f1e1a4 100644 --- a/api/test/mining.go +++ b/api/test/mining.go @@ -206,7 +206,7 @@ func TestDealMining(t *testing.T, b APIBuilder, blocktime time.Duration, carExpo func (ts *testSuite) testNonGenesisMiner(t *testing.T) { ctx := context.Background() n, sn := ts.makeNodes(t, []FullNodeOpts{ - FullNodeWithActorsV4At(-1), + FullNodeWithLatestActorsAt(-1), }, []StorageMiner{ {Full: 0, Preseal: PresealGenesis}, }) diff --git a/api/test/test.go b/api/test/test.go index 613a4f1dc..eeddc23a0 100644 --- a/api/test/test.go +++ b/api/test/test.go @@ -120,7 +120,7 @@ var OneMiner = []StorageMiner{{Full: 0, Preseal: PresealGenesis}} var OneFull = DefaultFullOpts(1) var TwoFull = DefaultFullOpts(2) -var FullNodeWithActorsV4At = func(upgradeHeight abi.ChainEpoch) FullNodeOpts { +var FullNodeWithLatestActorsAt = func(upgradeHeight abi.ChainEpoch) FullNodeOpts { if upgradeHeight == -1 { upgradeHeight = 3 } diff --git a/api/test/window_post.go b/api/test/window_post.go index 4992741f4..c987fa1f9 100644 --- a/api/test/window_post.go +++ b/api/test/window_post.go @@ -223,7 +223,7 @@ func testWindowPostUpgrade(t *testing.T, b APIBuilder, blocktime time.Duration, ctx, cancel := context.WithCancel(context.Background()) defer cancel() - n, sn := b(t, []FullNodeOpts{FullNodeWithActorsV4At(upgradeHeight)}, OneMiner) + n, sn := b(t, []FullNodeOpts{FullNodeWithLatestActorsAt(upgradeHeight)}, OneMiner) client := n[0].FullNode.(*impl.FullNodeAPI) miner := sn[0] @@ -442,7 +442,7 @@ func TestTerminate(t *testing.T, b APIBuilder, blocktime time.Duration) { nSectors := uint64(2) - n, sn := b(t, []FullNodeOpts{FullNodeWithActorsV4At(-1)}, []StorageMiner{{Full: 0, Preseal: int(nSectors)}}) + n, sn := b(t, []FullNodeOpts{FullNodeWithLatestActorsAt(-1)}, []StorageMiner{{Full: 0, Preseal: int(nSectors)}}) client := n[0].FullNode.(*impl.FullNodeAPI) miner := sn[0] @@ -617,7 +617,7 @@ func TestWindowPostDispute(t *testing.T, b APIBuilder, blocktime time.Duration) /// // Then we're going to manually submit bad proofs. n, sn := b(t, []FullNodeOpts{ - FullNodeWithActorsV4At(-1), + FullNodeWithLatestActorsAt(-1), }, []StorageMiner{ {Full: 0, Preseal: PresealGenesis}, {Full: 0}, @@ -900,7 +900,7 @@ func TestWindowPostDisputeFails(t *testing.T, b APIBuilder, blocktime time.Durat ctx, cancel := context.WithCancel(context.Background()) defer cancel() - n, sn := b(t, []FullNodeOpts{FullNodeWithActorsV4At(-1)}, OneMiner) + n, sn := b(t, []FullNodeOpts{FullNodeWithLatestActorsAt(-1)}, OneMiner) client := n[0].FullNode.(*impl.FullNodeAPI) miner := sn[0] diff --git a/chain/actors/agen/main.go b/chain/actors/agen/main.go new file mode 100644 index 000000000..7269d9ae5 --- /dev/null +++ b/chain/actors/agen/main.go @@ -0,0 +1,218 @@ +package main + +import ( + "bytes" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "text/template" + + "golang.org/x/xerrors" +) + +var latestVersion = 4 + +var versions = []int{0, 2, 3, latestVersion} + +var versionImports = map[int]string{ + 0: "/", + 2: "/v2/", + 3: "/v3/", + latestVersion: "/v4/", +} + +var actors = map[string][]int{ + "account": versions, + "cron": versions, + "init": versions, + "market": versions, + "miner": versions, + "multisig": versions, + "paych": versions, + "power": versions, + "reward": versions, + "verifreg": versions, +} + +func main() { + if err := generateAdapters(); err != nil { + fmt.Println(err) + return + } + + if err := generatePolicy("chain/actors/policy/policy.go"); err != nil { + fmt.Println(err) + return + } + + if err := generateBuiltin("chain/actors/builtin/builtin.go"); err != nil { + fmt.Println(err) + return + } +} + +func generateAdapters() error { + for act, versions := range actors { + actDir := filepath.Join("chain/actors/builtin", act) + + if err := generateState(actDir); err != nil { + return err + } + + if err := generateMessages(actDir); err != nil { + return err + } + + { + af, err := ioutil.ReadFile(filepath.Join(actDir, "actor.go.template")) + if err != nil { + return xerrors.Errorf("loading actor template: %w", err) + } + + tpl := template.Must(template.New("").Funcs(template.FuncMap{ + "import": func(v int) string { return versionImports[v] }, + }).Parse(string(af))) + + var b bytes.Buffer + + err = tpl.Execute(&b, map[string]interface{}{ + "versions": versions, + "latestVersion": latestVersion, + }) + if err != nil { + return err + } + + if err := ioutil.WriteFile(filepath.Join(actDir, fmt.Sprintf("%s.go", act)), b.Bytes(), 0666); err != nil { + return err + } + } + } + + return nil +} + +func generateState(actDir string) error { + af, err := ioutil.ReadFile(filepath.Join(actDir, "state.go.template")) + if err != nil { + if os.IsNotExist(err) { + return nil // skip + } + + return xerrors.Errorf("loading state adapter template: %w", err) + } + + for _, version := range versions { + tpl := template.Must(template.New("").Funcs(template.FuncMap{}).Parse(string(af))) + + var b bytes.Buffer + + err := tpl.Execute(&b, map[string]interface{}{ + "v": version, + "import": versionImports[version], + }) + if err != nil { + return err + } + + if err := ioutil.WriteFile(filepath.Join(actDir, fmt.Sprintf("v%d.go", version)), b.Bytes(), 0666); err != nil { + return err + } + } + + return nil +} + +func generateMessages(actDir string) error { + af, err := ioutil.ReadFile(filepath.Join(actDir, "message.go.template")) + if err != nil { + if os.IsNotExist(err) { + return nil // skip + } + + return xerrors.Errorf("loading message adapter template: %w", err) + } + + for _, version := range versions { + tpl := template.Must(template.New("").Funcs(template.FuncMap{}).Parse(string(af))) + + var b bytes.Buffer + + err := tpl.Execute(&b, map[string]interface{}{ + "v": version, + "import": versionImports[version], + }) + if err != nil { + return err + } + + if err := ioutil.WriteFile(filepath.Join(actDir, fmt.Sprintf("message%d.go", version)), b.Bytes(), 0666); err != nil { + return err + } + } + + return nil +} + +func generatePolicy(policyPath string) error { + + pf, err := ioutil.ReadFile(policyPath + ".template") + if err != nil { + if os.IsNotExist(err) { + return nil // skip + } + + return xerrors.Errorf("loading policy template file: %w", err) + } + + tpl := template.Must(template.New("").Funcs(template.FuncMap{ + "import": func(v int) string { return versionImports[v] }, + }).Parse(string(pf))) + var b bytes.Buffer + + err = tpl.Execute(&b, map[string]interface{}{ + "versions": versions, + "latestVersion": latestVersion, + }) + if err != nil { + return err + } + + if err := ioutil.WriteFile(policyPath, b.Bytes(), 0666); err != nil { + return err + } + + return nil +} + +func generateBuiltin(builtinPath string) error { + + bf, err := ioutil.ReadFile(builtinPath + ".template") + if err != nil { + if os.IsNotExist(err) { + return nil // skip + } + + return xerrors.Errorf("loading builtin template file: %w", err) + } + + tpl := template.Must(template.New("").Funcs(template.FuncMap{ + "import": func(v int) string { return versionImports[v] }, + }).Parse(string(bf))) + var b bytes.Buffer + + err = tpl.Execute(&b, map[string]interface{}{ + "versions": versions, + "latestVersion": latestVersion, + }) + if err != nil { + return err + } + + if err := ioutil.WriteFile(builtinPath, b.Bytes(), 0666); err != nil { + return err + } + + return nil +} diff --git a/chain/actors/builtin/account/account.go b/chain/actors/builtin/account/account.go index 4d94b245a..8242e300d 100644 --- a/chain/actors/builtin/account/account.go +++ b/chain/actors/builtin/account/account.go @@ -12,21 +12,28 @@ import ( "github.com/filecoin-project/lotus/chain/types" builtin0 "github.com/filecoin-project/specs-actors/actors/builtin" + builtin2 "github.com/filecoin-project/specs-actors/v2/actors/builtin" + builtin3 "github.com/filecoin-project/specs-actors/v3/actors/builtin" + builtin4 "github.com/filecoin-project/specs-actors/v4/actors/builtin" ) func init() { + builtin.RegisterActorState(builtin0.AccountActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load0(store, root) }) + builtin.RegisterActorState(builtin2.AccountActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load2(store, root) }) + builtin.RegisterActorState(builtin3.AccountActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load3(store, root) }) + builtin.RegisterActorState(builtin4.AccountActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load4(store, root) }) @@ -36,14 +43,19 @@ var Methods = builtin4.MethodsAccount func Load(store adt.Store, act *types.Actor) (State, error) { switch act.Code { + case builtin0.AccountActorCodeID: return load0(store, act.Head) + case builtin2.AccountActorCodeID: return load2(store, act.Head) + case builtin3.AccountActorCodeID: return load3(store, act.Head) + case builtin4.AccountActorCodeID: return load4(store, act.Head) + } return nil, xerrors.Errorf("unknown actor code %s", act.Code) } diff --git a/chain/actors/builtin/account/actor.go.template b/chain/actors/builtin/account/actor.go.template new file mode 100644 index 000000000..f75af3dfb --- /dev/null +++ b/chain/actors/builtin/account/actor.go.template @@ -0,0 +1,41 @@ +package account + +import ( + "golang.org/x/xerrors" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/cbor" + "github.com/ipfs/go-cid" + + "github.com/filecoin-project/lotus/chain/actors/adt" + "github.com/filecoin-project/lotus/chain/actors/builtin" + "github.com/filecoin-project/lotus/chain/types" +{{range .versions}} + builtin{{.}} "github.com/filecoin-project/specs-actors{{import .}}actors/builtin" +{{end}} +) + +func init() { +{{range .versions}} + builtin.RegisterActorState(builtin{{.}}.AccountActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { + return load{{.}}(store, root) + }) +{{end}}} + +var Methods = builtin4.MethodsAccount + +func Load(store adt.Store, act *types.Actor) (State, error) { + switch act.Code { +{{range .versions}} + case builtin{{.}}.AccountActorCodeID: + return load{{.}}(store, act.Head) +{{end}} + } + return nil, xerrors.Errorf("unknown actor code %s", act.Code) +} + +type State interface { + cbor.Marshaler + + PubkeyAddress() (address.Address, error) +} diff --git a/chain/actors/builtin/account/state.go.template b/chain/actors/builtin/account/state.go.template new file mode 100644 index 000000000..65d874c80 --- /dev/null +++ b/chain/actors/builtin/account/state.go.template @@ -0,0 +1,30 @@ +package account + +import ( + "github.com/filecoin-project/go-address" + "github.com/ipfs/go-cid" + + "github.com/filecoin-project/lotus/chain/actors/adt" + + account{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/builtin/account" +) + +var _ State = (*state{{.v}})(nil) + +func load{{.v}}(store adt.Store, root cid.Cid) (State, error) { + out := state{{.v}}{store: store} + err := store.Get(store.Context(), root, &out) + if err != nil { + return nil, err + } + return &out, nil +} + +type state{{.v}} struct { + account{{.v}}.State + store adt.Store +} + +func (s *state{{.v}}) PubkeyAddress() (address.Address, error) { + return s.Address, nil +} diff --git a/chain/actors/builtin/builtin.go b/chain/actors/builtin/builtin.go index 4ff524797..5e34c015a 100644 --- a/chain/actors/builtin/builtin.go +++ b/chain/actors/builtin/builtin.go @@ -6,9 +6,16 @@ import ( "golang.org/x/xerrors" builtin0 "github.com/filecoin-project/specs-actors/actors/builtin" + smoothing0 "github.com/filecoin-project/specs-actors/actors/util/smoothing" + builtin2 "github.com/filecoin-project/specs-actors/v2/actors/builtin" + smoothing2 "github.com/filecoin-project/specs-actors/v2/actors/util/smoothing" + builtin3 "github.com/filecoin-project/specs-actors/v3/actors/builtin" + smoothing3 "github.com/filecoin-project/specs-actors/v3/actors/util/smoothing" + builtin4 "github.com/filecoin-project/specs-actors/v4/actors/builtin" + smoothing4 "github.com/filecoin-project/specs-actors/v4/actors/util/smoothing" "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/cbor" @@ -16,30 +23,25 @@ import ( "github.com/filecoin-project/lotus/chain/actors/adt" "github.com/filecoin-project/lotus/chain/types" - smoothing0 "github.com/filecoin-project/specs-actors/actors/util/smoothing" - smoothing2 "github.com/filecoin-project/specs-actors/v2/actors/util/smoothing" - smoothing3 "github.com/filecoin-project/specs-actors/v3/actors/util/smoothing" - smoothing4 "github.com/filecoin-project/specs-actors/v4/actors/util/smoothing" - - miner0 "github.com/filecoin-project/specs-actors/actors/builtin/miner" - proof0 "github.com/filecoin-project/specs-actors/actors/runtime/proof" + miner4 "github.com/filecoin-project/specs-actors/v4/actors/builtin/miner" + proof4 "github.com/filecoin-project/specs-actors/v4/actors/runtime/proof" ) -var SystemActorAddr = builtin0.SystemActorAddr -var BurntFundsActorAddr = builtin0.BurntFundsActorAddr -var CronActorAddr = builtin0.CronActorAddr +var SystemActorAddr = builtin4.SystemActorAddr +var BurntFundsActorAddr = builtin4.BurntFundsActorAddr +var CronActorAddr = builtin4.CronActorAddr var SaftAddress = makeAddress("t0122") var ReserveAddress = makeAddress("t090") var RootVerifierAddress = makeAddress("t080") var ( - ExpectedLeadersPerEpoch = builtin0.ExpectedLeadersPerEpoch + ExpectedLeadersPerEpoch = builtin4.ExpectedLeadersPerEpoch ) const ( - EpochDurationSeconds = builtin0.EpochDurationSeconds - EpochsInDay = builtin0.EpochsInDay - SecondsInDay = builtin0.SecondsInDay + EpochDurationSeconds = builtin4.EpochDurationSeconds + EpochsInDay = builtin4.EpochsInDay + SecondsInDay = builtin4.SecondsInDay ) const ( @@ -47,31 +49,38 @@ const ( MethodConstructor = builtin4.MethodConstructor ) -// These are all just type aliases across actor versions 0, 2, & 3. In the future, that might change +// These are all just type aliases across actor versions. In the future, that might change // and we might need to do something fancier. -type SectorInfo = proof0.SectorInfo -type PoStProof = proof0.PoStProof +type SectorInfo = proof4.SectorInfo +type PoStProof = proof4.PoStProof type FilterEstimate = smoothing0.FilterEstimate -func FromV0FilterEstimate(v0 smoothing0.FilterEstimate) FilterEstimate { - return (FilterEstimate)(v0) //nolint:unconvert +func QAPowerForWeight(size abi.SectorSize, duration abi.ChainEpoch, dealWeight, verifiedWeight abi.DealWeight) abi.StoragePower { + return miner4.QAPowerForWeight(size, duration, dealWeight, verifiedWeight) } -// Doesn't change between actors v0, v2, and v3. -func QAPowerForWeight(size abi.SectorSize, duration abi.ChainEpoch, dealWeight, verifiedWeight abi.DealWeight) abi.StoragePower { - return miner0.QAPowerForWeight(size, duration, dealWeight, verifiedWeight) +func FromV0FilterEstimate(v0 smoothing0.FilterEstimate) FilterEstimate { + + return (FilterEstimate)(v0) //nolint:unconvert + } func FromV2FilterEstimate(v2 smoothing2.FilterEstimate) FilterEstimate { + return (FilterEstimate)(v2) + } func FromV3FilterEstimate(v3 smoothing3.FilterEstimate) FilterEstimate { + return (FilterEstimate)(v3) + } func FromV4FilterEstimate(v4 smoothing4.FilterEstimate) FilterEstimate { + return (FilterEstimate)(v4) + } type ActorStateLoader func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) @@ -92,52 +101,127 @@ func Load(store adt.Store, act *types.Actor) (cbor.Marshaler, error) { func ActorNameByCode(c cid.Cid) string { switch { + case builtin0.IsBuiltinActor(c): return builtin0.ActorNameByCode(c) + case builtin2.IsBuiltinActor(c): return builtin2.ActorNameByCode(c) + case builtin3.IsBuiltinActor(c): return builtin3.ActorNameByCode(c) + case builtin4.IsBuiltinActor(c): return builtin4.ActorNameByCode(c) + default: return "" } } func IsBuiltinActor(c cid.Cid) bool { - return builtin0.IsBuiltinActor(c) || - builtin2.IsBuiltinActor(c) || - builtin3.IsBuiltinActor(c) || - builtin4.IsBuiltinActor(c) + + if builtin0.IsBuiltinActor(c) { + return true + } + + if builtin2.IsBuiltinActor(c) { + return true + } + + if builtin3.IsBuiltinActor(c) { + return true + } + + if builtin4.IsBuiltinActor(c) { + return true + } + + return false } func IsAccountActor(c cid.Cid) bool { - return c == builtin0.AccountActorCodeID || - c == builtin2.AccountActorCodeID || - c == builtin3.AccountActorCodeID || - c == builtin4.AccountActorCodeID + + if c == builtin0.AccountActorCodeID { + return true + } + + if c == builtin2.AccountActorCodeID { + return true + } + + if c == builtin3.AccountActorCodeID { + return true + } + + if c == builtin4.AccountActorCodeID { + return true + } + + return false } func IsStorageMinerActor(c cid.Cid) bool { - return c == builtin0.StorageMinerActorCodeID || - c == builtin2.StorageMinerActorCodeID || - c == builtin3.StorageMinerActorCodeID || - c == builtin4.StorageMinerActorCodeID + + if c == builtin0.StorageMinerActorCodeID { + return true + } + + if c == builtin2.StorageMinerActorCodeID { + return true + } + + if c == builtin3.StorageMinerActorCodeID { + return true + } + + if c == builtin4.StorageMinerActorCodeID { + return true + } + + return false } func IsMultisigActor(c cid.Cid) bool { - return c == builtin0.MultisigActorCodeID || - c == builtin2.MultisigActorCodeID || - c == builtin3.MultisigActorCodeID || - c == builtin4.MultisigActorCodeID + + if c == builtin0.MultisigActorCodeID { + return true + } + + if c == builtin2.MultisigActorCodeID { + return true + } + + if c == builtin3.MultisigActorCodeID { + return true + } + + if c == builtin4.MultisigActorCodeID { + return true + } + + return false } func IsPaymentChannelActor(c cid.Cid) bool { - return c == builtin0.PaymentChannelActorCodeID || - c == builtin2.PaymentChannelActorCodeID || - c == builtin3.PaymentChannelActorCodeID || - c == builtin4.PaymentChannelActorCodeID + + if c == builtin0.PaymentChannelActorCodeID { + return true + } + + if c == builtin2.PaymentChannelActorCodeID { + return true + } + + if c == builtin3.PaymentChannelActorCodeID { + return true + } + + if c == builtin4.PaymentChannelActorCodeID { + return true + } + + return false } func makeAddress(addr string) address.Address { diff --git a/chain/actors/builtin/builtin.go.template b/chain/actors/builtin/builtin.go.template new file mode 100644 index 000000000..9b89b13f5 --- /dev/null +++ b/chain/actors/builtin/builtin.go.template @@ -0,0 +1,144 @@ +package builtin + +import ( + "github.com/filecoin-project/go-address" + "github.com/ipfs/go-cid" + "golang.org/x/xerrors" + + {{range .versions}} + builtin{{.}} "github.com/filecoin-project/specs-actors{{import .}}actors/builtin" + smoothing{{.}} "github.com/filecoin-project/specs-actors{{import .}}actors/util/smoothing" + {{end}} + + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/cbor" + + "github.com/filecoin-project/lotus/chain/actors/adt" + "github.com/filecoin-project/lotus/chain/types" + + miner{{.latestVersion}} "github.com/filecoin-project/specs-actors{{import .latestVersion}}actors/builtin/miner" + proof{{.latestVersion}} "github.com/filecoin-project/specs-actors{{import .latestVersion}}actors/runtime/proof" +) + +var SystemActorAddr = builtin{{.latestVersion}}.SystemActorAddr +var BurntFundsActorAddr = builtin{{.latestVersion}}.BurntFundsActorAddr +var CronActorAddr = builtin{{.latestVersion}}.CronActorAddr +var SaftAddress = makeAddress("t0122") +var ReserveAddress = makeAddress("t090") +var RootVerifierAddress = makeAddress("t080") + +var ( + ExpectedLeadersPerEpoch = builtin{{.latestVersion}}.ExpectedLeadersPerEpoch +) + +const ( + EpochDurationSeconds = builtin{{.latestVersion}}.EpochDurationSeconds + EpochsInDay = builtin{{.latestVersion}}.EpochsInDay + SecondsInDay = builtin{{.latestVersion}}.SecondsInDay +) + +const ( + MethodSend = builtin{{.latestVersion}}.MethodSend + MethodConstructor = builtin{{.latestVersion}}.MethodConstructor +) + +// These are all just type aliases across actor versions. In the future, that might change +// and we might need to do something fancier. +type SectorInfo = proof{{.latestVersion}}.SectorInfo +type PoStProof = proof{{.latestVersion}}.PoStProof +type FilterEstimate = smoothing0.FilterEstimate + +func QAPowerForWeight(size abi.SectorSize, duration abi.ChainEpoch, dealWeight, verifiedWeight abi.DealWeight) abi.StoragePower { + return miner{{.latestVersion}}.QAPowerForWeight(size, duration, dealWeight, verifiedWeight) +} + +{{range .versions}} + func FromV{{.}}FilterEstimate(v{{.}} smoothing{{.}}.FilterEstimate) FilterEstimate { + {{if (eq . 0)}} + return (FilterEstimate)(v{{.}}) //nolint:unconvert + {{else}} + return (FilterEstimate)(v{{.}}) + {{end}} + } +{{end}} + +type ActorStateLoader func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) + +var ActorStateLoaders = make(map[cid.Cid]ActorStateLoader) + +func RegisterActorState(code cid.Cid, loader ActorStateLoader) { + ActorStateLoaders[code] = loader +} + +func Load(store adt.Store, act *types.Actor) (cbor.Marshaler, error) { + loader, found := ActorStateLoaders[act.Code] + if !found { + return nil, xerrors.Errorf("unknown actor code %s", act.Code) + } + return loader(store, act.Head) +} + +func ActorNameByCode(c cid.Cid) string { + switch { + {{range .versions}} + case builtin{{.}}.IsBuiltinActor(c): + return builtin{{.}}.ActorNameByCode(c) + {{end}} + default: + return "" + } +} + +func IsBuiltinActor(c cid.Cid) bool { + {{range .versions}} + if builtin{{.}}.IsBuiltinActor(c) { + return true + } + {{end}} + return false +} + +func IsAccountActor(c cid.Cid) bool { + {{range .versions}} + if c == builtin{{.}}.AccountActorCodeID { + return true + } + {{end}} + return false +} + +func IsStorageMinerActor(c cid.Cid) bool { + {{range .versions}} + if c == builtin{{.}}.StorageMinerActorCodeID { + return true + } + {{end}} + return false +} + +func IsMultisigActor(c cid.Cid) bool { + {{range .versions}} + if c == builtin{{.}}.MultisigActorCodeID { + return true + } + {{end}} + return false +} + +func IsPaymentChannelActor(c cid.Cid) bool { + {{range .versions}} + if c == builtin{{.}}.PaymentChannelActorCodeID { + return true + } + {{end}} + return false +} + +func makeAddress(addr string) address.Address { + ret, err := address.NewFromString(addr) + if err != nil { + panic(err) + } + + return ret +} diff --git a/chain/actors/builtin/cron/actor.go.template b/chain/actors/builtin/cron/actor.go.template new file mode 100644 index 000000000..6b7449617 --- /dev/null +++ b/chain/actors/builtin/cron/actor.go.template @@ -0,0 +1,10 @@ +package cron + +import ( + builtin{{.latestVersion}} "github.com/filecoin-project/specs-actors{{import .latestVersion}}actors/builtin" +) + +var ( + Address = builtin{{.latestVersion}}.CronActorAddr + Methods = builtin{{.latestVersion}}.MethodsCron +) diff --git a/chain/actors/builtin/init/actor.go.template b/chain/actors/builtin/init/actor.go.template new file mode 100644 index 000000000..5b700cec8 --- /dev/null +++ b/chain/actors/builtin/init/actor.go.template @@ -0,0 +1,60 @@ +package init + +import ( + "golang.org/x/xerrors" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/cbor" + "github.com/ipfs/go-cid" + + "github.com/filecoin-project/lotus/chain/actors/adt" + "github.com/filecoin-project/lotus/chain/actors/builtin" + "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/node/modules/dtypes" +{{range .versions}} + builtin{{.}} "github.com/filecoin-project/specs-actors{{import .}}actors/builtin" +{{end}} +) + +func init() { +{{range .versions}} + builtin.RegisterActorState(builtin{{.}}.InitActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { + return load{{.}}(store, root) + }) +{{end}}} + +var ( + Address = builtin{{.latestVersion}}.InitActorAddr + Methods = builtin{{.latestVersion}}.MethodsInit +) + +func Load(store adt.Store, act *types.Actor) (State, error) { + switch act.Code { +{{range .versions}} + case builtin{{.}}.InitActorCodeID: + return load{{.}}(store, act.Head) +{{end}} + } + return nil, xerrors.Errorf("unknown actor code %s", act.Code) +} + +type State interface { + cbor.Marshaler + + ResolveAddress(address address.Address) (address.Address, bool, error) + MapAddressToNewID(address address.Address) (address.Address, error) + NetworkName() (dtypes.NetworkName, error) + + ForEachActor(func(id abi.ActorID, address address.Address) error) error + + // Remove exists to support tooling that manipulates state for testing. + // It should not be used in production code, as init actor entries are + // immutable. + Remove(addrs ...address.Address) error + + // Sets the network's name. This should only be used on upgrade/fork. + SetNetworkName(name string) error + + addressMap() (adt.Map, error) +} diff --git a/chain/actors/builtin/init/init.go b/chain/actors/builtin/init/init.go index 697148641..730d21fd8 100644 --- a/chain/actors/builtin/init/init.go +++ b/chain/actors/builtin/init/init.go @@ -14,21 +14,28 @@ import ( "github.com/filecoin-project/lotus/node/modules/dtypes" builtin0 "github.com/filecoin-project/specs-actors/actors/builtin" + builtin2 "github.com/filecoin-project/specs-actors/v2/actors/builtin" + builtin3 "github.com/filecoin-project/specs-actors/v3/actors/builtin" + builtin4 "github.com/filecoin-project/specs-actors/v4/actors/builtin" ) func init() { + builtin.RegisterActorState(builtin0.InitActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load0(store, root) }) + builtin.RegisterActorState(builtin2.InitActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load2(store, root) }) + builtin.RegisterActorState(builtin3.InitActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load3(store, root) }) + builtin.RegisterActorState(builtin4.InitActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load4(store, root) }) @@ -41,14 +48,19 @@ var ( func Load(store adt.Store, act *types.Actor) (State, error) { switch act.Code { + case builtin0.InitActorCodeID: return load0(store, act.Head) + case builtin2.InitActorCodeID: return load2(store, act.Head) + case builtin3.InitActorCodeID: return load3(store, act.Head) + case builtin4.InitActorCodeID: return load4(store, act.Head) + } return nil, xerrors.Errorf("unknown actor code %s", act.Code) } diff --git a/chain/actors/builtin/init/state.go.template b/chain/actors/builtin/init/state.go.template new file mode 100644 index 000000000..95f052bda --- /dev/null +++ b/chain/actors/builtin/init/state.go.template @@ -0,0 +1,89 @@ +package init + +import ( + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + "github.com/ipfs/go-cid" + cbg "github.com/whyrusleeping/cbor-gen" + "golang.org/x/xerrors" + + "github.com/filecoin-project/lotus/chain/actors/adt" + "github.com/filecoin-project/lotus/node/modules/dtypes" + +{{if (ge .v 3)}} + builtin{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/builtin" +{{end}} + + init{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/builtin/init" + adt{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/util/adt" +) + +var _ State = (*state{{.v}})(nil) + +func load{{.v}}(store adt.Store, root cid.Cid) (State, error) { + out := state{{.v}}{store: store} + err := store.Get(store.Context(), root, &out) + if err != nil { + return nil, err + } + return &out, nil +} + +type state{{.v}} struct { + init{{.v}}.State + store adt.Store +} + +func (s *state{{.v}}) ResolveAddress(address address.Address) (address.Address, bool, error) { + return s.State.ResolveAddress(s.store, address) +} + +func (s *state{{.v}}) MapAddressToNewID(address address.Address) (address.Address, error) { + return s.State.MapAddressToNewID(s.store, address) +} + +func (s *state{{.v}}) ForEachActor(cb func(id abi.ActorID, address address.Address) error) error { + addrs, err := adt{{.v}}.AsMap(s.store, s.State.AddressMap{{if (ge .v 3)}}, builtin{{.v}}.DefaultHamtBitwidth{{end}}) + if err != nil { + return err + } + var actorID cbg.CborInt + return addrs.ForEach(&actorID, func(key string) error { + addr, err := address.NewFromBytes([]byte(key)) + if err != nil { + return err + } + return cb(abi.ActorID(actorID), addr) + }) +} + +func (s *state{{.v}}) NetworkName() (dtypes.NetworkName, error) { + return dtypes.NetworkName(s.State.NetworkName), nil +} + +func (s *state{{.v}}) SetNetworkName(name string) error { + s.State.NetworkName = name + return nil +} + +func (s *state{{.v}}) Remove(addrs ...address.Address) (err error) { + m, err := adt{{.v}}.AsMap(s.store, s.State.AddressMap{{if (ge .v 3)}}, builtin{{.v}}.DefaultHamtBitwidth{{end}}) + if err != nil { + return err + } + for _, addr := range addrs { + if err = m.Delete(abi.AddrKey(addr)); err != nil { + return xerrors.Errorf("failed to delete entry for address: %s; err: %w", addr, err) + } + } + amr, err := m.Root() + if err != nil { + return xerrors.Errorf("failed to get address map root: %w", err) + } + s.State.AddressMap = amr + return nil +} + +func (s *state{{.v}}) addressMap() (adt.Map, error) { + return adt{{.v}}.AsMap(s.store, s.AddressMap{{if (ge .v 3)}}, builtin{{.v}}.DefaultHamtBitwidth{{end}}) +} diff --git a/chain/actors/builtin/init/v3.go b/chain/actors/builtin/init/v3.go index e586b3b11..eaa54dfd4 100644 --- a/chain/actors/builtin/init/v3.go +++ b/chain/actors/builtin/init/v3.go @@ -3,7 +3,6 @@ package init import ( "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/abi" - builtin3 "github.com/filecoin-project/specs-actors/v3/actors/builtin" "github.com/ipfs/go-cid" cbg "github.com/whyrusleeping/cbor-gen" "golang.org/x/xerrors" @@ -11,6 +10,8 @@ import ( "github.com/filecoin-project/lotus/chain/actors/adt" "github.com/filecoin-project/lotus/node/modules/dtypes" + builtin3 "github.com/filecoin-project/specs-actors/v3/actors/builtin" + init3 "github.com/filecoin-project/specs-actors/v3/actors/builtin/init" adt3 "github.com/filecoin-project/specs-actors/v3/actors/util/adt" ) diff --git a/chain/actors/builtin/init/v4.go b/chain/actors/builtin/init/v4.go index a2be603a3..38749eed5 100644 --- a/chain/actors/builtin/init/v4.go +++ b/chain/actors/builtin/init/v4.go @@ -3,7 +3,6 @@ package init import ( "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/abi" - builtin4 "github.com/filecoin-project/specs-actors/v4/actors/builtin" "github.com/ipfs/go-cid" cbg "github.com/whyrusleeping/cbor-gen" "golang.org/x/xerrors" @@ -11,6 +10,8 @@ import ( "github.com/filecoin-project/lotus/chain/actors/adt" "github.com/filecoin-project/lotus/node/modules/dtypes" + builtin4 "github.com/filecoin-project/specs-actors/v4/actors/builtin" + init4 "github.com/filecoin-project/specs-actors/v4/actors/builtin/init" adt4 "github.com/filecoin-project/specs-actors/v4/actors/util/adt" ) diff --git a/chain/actors/builtin/market/actor.go.template b/chain/actors/builtin/market/actor.go.template new file mode 100644 index 000000000..eaa06b867 --- /dev/null +++ b/chain/actors/builtin/market/actor.go.template @@ -0,0 +1,142 @@ +package market + +import ( + "golang.org/x/xerrors" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/cbor" + "github.com/ipfs/go-cid" + cbg "github.com/whyrusleeping/cbor-gen" + + market0 "github.com/filecoin-project/specs-actors/actors/builtin/market" +{{range .versions}} + builtin{{.}} "github.com/filecoin-project/specs-actors{{import .}}actors/builtin" +{{end}} + + "github.com/filecoin-project/lotus/chain/actors/adt" + "github.com/filecoin-project/lotus/chain/actors/builtin" + "github.com/filecoin-project/lotus/chain/types" +) + +func init() { +{{range .versions}} + builtin.RegisterActorState(builtin{{.}}.StorageMarketActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { + return load{{.}}(store, root) + }) +{{end}}} + +var ( + Address = builtin{{.latestVersion}}.StorageMarketActorAddr + Methods = builtin{{.latestVersion}}.MethodsMarket +) + +func Load(store adt.Store, act *types.Actor) (State, error) { + switch act.Code { +{{range .versions}} + case builtin{{.}}.StorageMarketActorCodeID: + return load{{.}}(store, act.Head) +{{end}} + } + return nil, xerrors.Errorf("unknown actor code %s", act.Code) +} + +type State interface { + cbor.Marshaler + BalancesChanged(State) (bool, error) + EscrowTable() (BalanceTable, error) + LockedTable() (BalanceTable, error) + TotalLocked() (abi.TokenAmount, error) + StatesChanged(State) (bool, error) + States() (DealStates, error) + ProposalsChanged(State) (bool, error) + Proposals() (DealProposals, error) + VerifyDealsForActivation( + minerAddr address.Address, deals []abi.DealID, currEpoch, sectorExpiry abi.ChainEpoch, + ) (weight, verifiedWeight abi.DealWeight, err error) + NextID() (abi.DealID, error) +} + +type BalanceTable interface { + ForEach(cb func(address.Address, abi.TokenAmount) error) error + Get(key address.Address) (abi.TokenAmount, error) +} + +type DealStates interface { + ForEach(cb func(id abi.DealID, ds DealState) error) error + Get(id abi.DealID) (*DealState, bool, error) + + array() adt.Array + decode(*cbg.Deferred) (*DealState, error) +} + +type DealProposals interface { + ForEach(cb func(id abi.DealID, dp DealProposal) error) error + Get(id abi.DealID) (*DealProposal, bool, error) + + array() adt.Array + decode(*cbg.Deferred) (*DealProposal, error) +} + +type PublishStorageDealsParams = market0.PublishStorageDealsParams +type PublishStorageDealsReturn = market0.PublishStorageDealsReturn +type VerifyDealsForActivationParams = market0.VerifyDealsForActivationParams +type WithdrawBalanceParams = market0.WithdrawBalanceParams + +type ClientDealProposal = market0.ClientDealProposal + +type DealState struct { + SectorStartEpoch abi.ChainEpoch // -1 if not yet included in proven sector + LastUpdatedEpoch abi.ChainEpoch // -1 if deal state never updated + SlashEpoch abi.ChainEpoch // -1 if deal never slashed +} + +type DealProposal struct { + PieceCID cid.Cid + PieceSize abi.PaddedPieceSize + VerifiedDeal bool + Client address.Address + Provider address.Address + Label string + StartEpoch abi.ChainEpoch + EndEpoch abi.ChainEpoch + StoragePricePerEpoch abi.TokenAmount + ProviderCollateral abi.TokenAmount + ClientCollateral abi.TokenAmount +} + +type DealStateChanges struct { + Added []DealIDState + Modified []DealStateChange + Removed []DealIDState +} + +type DealIDState struct { + ID abi.DealID + Deal DealState +} + +// DealStateChange is a change in deal state from -> to +type DealStateChange struct { + ID abi.DealID + From *DealState + To *DealState +} + +type DealProposalChanges struct { + Added []ProposalIDState + Removed []ProposalIDState +} + +type ProposalIDState struct { + ID abi.DealID + Proposal DealProposal +} + +func EmptyDealState() *DealState { + return &DealState{ + SectorStartEpoch: -1, + SlashEpoch: -1, + LastUpdatedEpoch: -1, + } +} \ No newline at end of file diff --git a/chain/actors/builtin/market/market.go b/chain/actors/builtin/market/market.go index 738151503..63e8c42d3 100644 --- a/chain/actors/builtin/market/market.go +++ b/chain/actors/builtin/market/market.go @@ -9,10 +9,14 @@ import ( "github.com/ipfs/go-cid" cbg "github.com/whyrusleeping/cbor-gen" - builtin0 "github.com/filecoin-project/specs-actors/actors/builtin" market0 "github.com/filecoin-project/specs-actors/actors/builtin/market" + + builtin0 "github.com/filecoin-project/specs-actors/actors/builtin" + builtin2 "github.com/filecoin-project/specs-actors/v2/actors/builtin" + builtin3 "github.com/filecoin-project/specs-actors/v3/actors/builtin" + builtin4 "github.com/filecoin-project/specs-actors/v4/actors/builtin" "github.com/filecoin-project/lotus/chain/actors/adt" @@ -21,15 +25,19 @@ import ( ) func init() { + builtin.RegisterActorState(builtin0.StorageMarketActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load0(store, root) }) + builtin.RegisterActorState(builtin2.StorageMarketActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load2(store, root) }) + builtin.RegisterActorState(builtin3.StorageMarketActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load3(store, root) }) + builtin.RegisterActorState(builtin4.StorageMarketActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load4(store, root) }) @@ -40,16 +48,21 @@ var ( Methods = builtin4.MethodsMarket ) -func Load(store adt.Store, act *types.Actor) (st State, err error) { +func Load(store adt.Store, act *types.Actor) (State, error) { switch act.Code { + case builtin0.StorageMarketActorCodeID: return load0(store, act.Head) + case builtin2.StorageMarketActorCodeID: return load2(store, act.Head) + case builtin3.StorageMarketActorCodeID: return load3(store, act.Head) + case builtin4.StorageMarketActorCodeID: return load4(store, act.Head) + } return nil, xerrors.Errorf("unknown actor code %s", act.Code) } diff --git a/chain/actors/builtin/market/state.go.template b/chain/actors/builtin/market/state.go.template new file mode 100644 index 000000000..a55743ce5 --- /dev/null +++ b/chain/actors/builtin/market/state.go.template @@ -0,0 +1,209 @@ +package market + +import ( + "bytes" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + "github.com/ipfs/go-cid" + cbg "github.com/whyrusleeping/cbor-gen" + + "github.com/filecoin-project/lotus/chain/actors/adt" + "github.com/filecoin-project/lotus/chain/types" + + market{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/builtin/market" + adt{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/util/adt" +) + +var _ State = (*state{{.v}})(nil) + +func load{{.v}}(store adt.Store, root cid.Cid) (State, error) { + out := state{{.v}}{store: store} + err := store.Get(store.Context(), root, &out) + if err != nil { + return nil, err + } + return &out, nil +} + +type state{{.v}} struct { + market{{.v}}.State + store adt.Store +} + +func (s *state{{.v}}) TotalLocked() (abi.TokenAmount, error) { + fml := types.BigAdd(s.TotalClientLockedCollateral, s.TotalProviderLockedCollateral) + fml = types.BigAdd(fml, s.TotalClientStorageFee) + return fml, nil +} + +func (s *state{{.v}}) BalancesChanged(otherState State) (bool, error) { + otherState{{.v}}, ok := otherState.(*state{{.v}}) + if !ok { + // there's no way to compare different versions of the state, so let's + // just say that means the state of balances has changed + return true, nil + } + return !s.State.EscrowTable.Equals(otherState{{.v}}.State.EscrowTable) || !s.State.LockedTable.Equals(otherState{{.v}}.State.LockedTable), nil +} + +func (s *state{{.v}}) StatesChanged(otherState State) (bool, error) { + otherState{{.v}}, ok := otherState.(*state{{.v}}) + if !ok { + // there's no way to compare different versions of the state, so let's + // just say that means the state of balances has changed + return true, nil + } + return !s.State.States.Equals(otherState{{.v}}.State.States), nil +} + +func (s *state{{.v}}) States() (DealStates, error) { + stateArray, err := adt{{.v}}.AsArray(s.store, s.State.States{{if (ge .v 3)}}, market{{.v}}.StatesAmtBitwidth{{end}}) + if err != nil { + return nil, err + } + return &dealStates{{.v}}{stateArray}, nil +} + +func (s *state{{.v}}) ProposalsChanged(otherState State) (bool, error) { + otherState{{.v}}, ok := otherState.(*state{{.v}}) + if !ok { + // there's no way to compare different versions of the state, so let's + // just say that means the state of balances has changed + return true, nil + } + return !s.State.Proposals.Equals(otherState{{.v}}.State.Proposals), nil +} + +func (s *state{{.v}}) Proposals() (DealProposals, error) { + proposalArray, err := adt{{.v}}.AsArray(s.store, s.State.Proposals{{if (ge .v 3)}}, market{{.v}}.ProposalsAmtBitwidth{{end}}) + if err != nil { + return nil, err + } + return &dealProposals{{.v}}{proposalArray}, nil +} + +func (s *state{{.v}}) EscrowTable() (BalanceTable, error) { + bt, err := adt{{.v}}.AsBalanceTable(s.store, s.State.EscrowTable) + if err != nil { + return nil, err + } + return &balanceTable{{.v}}{bt}, nil +} + +func (s *state{{.v}}) LockedTable() (BalanceTable, error) { + bt, err := adt{{.v}}.AsBalanceTable(s.store, s.State.LockedTable) + if err != nil { + return nil, err + } + return &balanceTable{{.v}}{bt}, nil +} + +func (s *state{{.v}}) VerifyDealsForActivation( + minerAddr address.Address, deals []abi.DealID, currEpoch, sectorExpiry abi.ChainEpoch, +) (weight, verifiedWeight abi.DealWeight, err error) { + w, vw{{if (ge .v 2)}}, _{{end}}, err := market{{.v}}.ValidateDealsForActivation(&s.State, s.store, deals, minerAddr, sectorExpiry, currEpoch) + return w, vw, err +} + +func (s *state{{.v}}) NextID() (abi.DealID, error) { + return s.State.NextID, nil +} + +type balanceTable{{.v}} struct { + *adt{{.v}}.BalanceTable +} + +func (bt *balanceTable{{.v}}) ForEach(cb func(address.Address, abi.TokenAmount) error) error { + asMap := (*adt{{.v}}.Map)(bt.BalanceTable) + var ta abi.TokenAmount + return asMap.ForEach(&ta, func(key string) error { + a, err := address.NewFromBytes([]byte(key)) + if err != nil { + return err + } + return cb(a, ta) + }) +} + +type dealStates{{.v}} struct { + adt.Array +} + +func (s *dealStates{{.v}}) Get(dealID abi.DealID) (*DealState, bool, error) { + var deal{{.v}} market{{.v}}.DealState + found, err := s.Array.Get(uint64(dealID), &deal{{.v}}) + if err != nil { + return nil, false, err + } + if !found { + return nil, false, nil + } + deal := fromV{{.v}}DealState(deal{{.v}}) + return &deal, true, nil +} + +func (s *dealStates{{.v}}) ForEach(cb func(dealID abi.DealID, ds DealState) error) error { + var ds{{.v}} market{{.v}}.DealState + return s.Array.ForEach(&ds{{.v}}, func(idx int64) error { + return cb(abi.DealID(idx), fromV{{.v}}DealState(ds{{.v}})) + }) +} + +func (s *dealStates{{.v}}) decode(val *cbg.Deferred) (*DealState, error) { + var ds{{.v}} market{{.v}}.DealState + if err := ds{{.v}}.UnmarshalCBOR(bytes.NewReader(val.Raw)); err != nil { + return nil, err + } + ds := fromV{{.v}}DealState(ds{{.v}}) + return &ds, nil +} + +func (s *dealStates{{.v}}) array() adt.Array { + return s.Array +} + +func fromV{{.v}}DealState(v{{.v}} market{{.v}}.DealState) DealState { + return (DealState)(v{{.v}}) +} + +type dealProposals{{.v}} struct { + adt.Array +} + +func (s *dealProposals{{.v}}) Get(dealID abi.DealID) (*DealProposal, bool, error) { + var proposal{{.v}} market{{.v}}.DealProposal + found, err := s.Array.Get(uint64(dealID), &proposal{{.v}}) + if err != nil { + return nil, false, err + } + if !found { + return nil, false, nil + } + proposal := fromV{{.v}}DealProposal(proposal{{.v}}) + return &proposal, true, nil +} + +func (s *dealProposals{{.v}}) ForEach(cb func(dealID abi.DealID, dp DealProposal) error) error { + var dp{{.v}} market{{.v}}.DealProposal + return s.Array.ForEach(&dp{{.v}}, func(idx int64) error { + return cb(abi.DealID(idx), fromV{{.v}}DealProposal(dp{{.v}})) + }) +} + +func (s *dealProposals{{.v}}) decode(val *cbg.Deferred) (*DealProposal, error) { + var dp{{.v}} market{{.v}}.DealProposal + if err := dp{{.v}}.UnmarshalCBOR(bytes.NewReader(val.Raw)); err != nil { + return nil, err + } + dp := fromV{{.v}}DealProposal(dp{{.v}}) + return &dp, nil +} + +func (s *dealProposals{{.v}}) array() adt.Array { + return s.Array +} + +func fromV{{.v}}DealProposal(v{{.v}} market{{.v}}.DealProposal) DealProposal { + return (DealProposal)(v{{.v}}) +} diff --git a/chain/actors/builtin/market/v0.go b/chain/actors/builtin/market/v0.go index f3b885995..175c0a2ea 100644 --- a/chain/actors/builtin/market/v0.go +++ b/chain/actors/builtin/market/v0.go @@ -102,7 +102,8 @@ func (s *state0) LockedTable() (BalanceTable, error) { func (s *state0) VerifyDealsForActivation( minerAddr address.Address, deals []abi.DealID, currEpoch, sectorExpiry abi.ChainEpoch, ) (weight, verifiedWeight abi.DealWeight, err error) { - return market0.ValidateDealsForActivation(&s.State, s.store, deals, minerAddr, sectorExpiry, currEpoch) + w, vw, err := market0.ValidateDealsForActivation(&s.State, s.store, deals, minerAddr, sectorExpiry, currEpoch) + return w, vw, err } func (s *state0) NextID() (abi.DealID, error) { diff --git a/chain/actors/builtin/market/v2.go b/chain/actors/builtin/market/v2.go index 1ce051c38..dafae8feb 100644 --- a/chain/actors/builtin/market/v2.go +++ b/chain/actors/builtin/market/v2.go @@ -144,18 +144,18 @@ func (s *dealStates2) Get(dealID abi.DealID) (*DealState, bool, error) { } func (s *dealStates2) ForEach(cb func(dealID abi.DealID, ds DealState) error) error { - var ds1 market2.DealState - return s.Array.ForEach(&ds1, func(idx int64) error { - return cb(abi.DealID(idx), fromV2DealState(ds1)) + var ds2 market2.DealState + return s.Array.ForEach(&ds2, func(idx int64) error { + return cb(abi.DealID(idx), fromV2DealState(ds2)) }) } func (s *dealStates2) decode(val *cbg.Deferred) (*DealState, error) { - var ds1 market2.DealState - if err := ds1.UnmarshalCBOR(bytes.NewReader(val.Raw)); err != nil { + var ds2 market2.DealState + if err := ds2.UnmarshalCBOR(bytes.NewReader(val.Raw)); err != nil { return nil, err } - ds := fromV2DealState(ds1) + ds := fromV2DealState(ds2) return &ds, nil } @@ -163,8 +163,8 @@ func (s *dealStates2) array() adt.Array { return s.Array } -func fromV2DealState(v1 market2.DealState) DealState { - return (DealState)(v1) +func fromV2DealState(v2 market2.DealState) DealState { + return (DealState)(v2) } type dealProposals2 struct { @@ -185,18 +185,18 @@ func (s *dealProposals2) Get(dealID abi.DealID) (*DealProposal, bool, error) { } func (s *dealProposals2) ForEach(cb func(dealID abi.DealID, dp DealProposal) error) error { - var dp1 market2.DealProposal - return s.Array.ForEach(&dp1, func(idx int64) error { - return cb(abi.DealID(idx), fromV2DealProposal(dp1)) + var dp2 market2.DealProposal + return s.Array.ForEach(&dp2, func(idx int64) error { + return cb(abi.DealID(idx), fromV2DealProposal(dp2)) }) } func (s *dealProposals2) decode(val *cbg.Deferred) (*DealProposal, error) { - var dp1 market2.DealProposal - if err := dp1.UnmarshalCBOR(bytes.NewReader(val.Raw)); err != nil { + var dp2 market2.DealProposal + if err := dp2.UnmarshalCBOR(bytes.NewReader(val.Raw)); err != nil { return nil, err } - dp := fromV2DealProposal(dp1) + dp := fromV2DealProposal(dp2) return &dp, nil } @@ -204,6 +204,6 @@ func (s *dealProposals2) array() adt.Array { return s.Array } -func fromV2DealProposal(v1 market2.DealProposal) DealProposal { - return (DealProposal)(v1) +func fromV2DealProposal(v2 market2.DealProposal) DealProposal { + return (DealProposal)(v2) } diff --git a/chain/actors/builtin/market/v3.go b/chain/actors/builtin/market/v3.go index 15251985b..dec8d6e25 100644 --- a/chain/actors/builtin/market/v3.go +++ b/chain/actors/builtin/market/v3.go @@ -38,23 +38,23 @@ func (s *state3) TotalLocked() (abi.TokenAmount, error) { } func (s *state3) BalancesChanged(otherState State) (bool, error) { - otherState2, ok := otherState.(*state3) + otherState3, ok := otherState.(*state3) if !ok { // there's no way to compare different versions of the state, so let's // just say that means the state of balances has changed return true, nil } - return !s.State.EscrowTable.Equals(otherState2.State.EscrowTable) || !s.State.LockedTable.Equals(otherState2.State.LockedTable), nil + return !s.State.EscrowTable.Equals(otherState3.State.EscrowTable) || !s.State.LockedTable.Equals(otherState3.State.LockedTable), nil } func (s *state3) StatesChanged(otherState State) (bool, error) { - otherState2, ok := otherState.(*state3) + otherState3, ok := otherState.(*state3) if !ok { // there's no way to compare different versions of the state, so let's // just say that means the state of balances has changed return true, nil } - return !s.State.States.Equals(otherState2.State.States), nil + return !s.State.States.Equals(otherState3.State.States), nil } func (s *state3) States() (DealStates, error) { @@ -66,13 +66,13 @@ func (s *state3) States() (DealStates, error) { } func (s *state3) ProposalsChanged(otherState State) (bool, error) { - otherState2, ok := otherState.(*state3) + otherState3, ok := otherState.(*state3) if !ok { // there's no way to compare different versions of the state, so let's // just say that means the state of balances has changed return true, nil } - return !s.State.Proposals.Equals(otherState2.State.Proposals), nil + return !s.State.Proposals.Equals(otherState3.State.Proposals), nil } func (s *state3) Proposals() (DealProposals, error) { @@ -131,31 +131,31 @@ type dealStates3 struct { } func (s *dealStates3) Get(dealID abi.DealID) (*DealState, bool, error) { - var deal2 market3.DealState - found, err := s.Array.Get(uint64(dealID), &deal2) + var deal3 market3.DealState + found, err := s.Array.Get(uint64(dealID), &deal3) if err != nil { return nil, false, err } if !found { return nil, false, nil } - deal := fromV3DealState(deal2) + deal := fromV3DealState(deal3) return &deal, true, nil } func (s *dealStates3) ForEach(cb func(dealID abi.DealID, ds DealState) error) error { - var ds1 market3.DealState - return s.Array.ForEach(&ds1, func(idx int64) error { - return cb(abi.DealID(idx), fromV3DealState(ds1)) + var ds3 market3.DealState + return s.Array.ForEach(&ds3, func(idx int64) error { + return cb(abi.DealID(idx), fromV3DealState(ds3)) }) } func (s *dealStates3) decode(val *cbg.Deferred) (*DealState, error) { - var ds1 market3.DealState - if err := ds1.UnmarshalCBOR(bytes.NewReader(val.Raw)); err != nil { + var ds3 market3.DealState + if err := ds3.UnmarshalCBOR(bytes.NewReader(val.Raw)); err != nil { return nil, err } - ds := fromV3DealState(ds1) + ds := fromV3DealState(ds3) return &ds, nil } @@ -163,8 +163,8 @@ func (s *dealStates3) array() adt.Array { return s.Array } -func fromV3DealState(v1 market3.DealState) DealState { - return (DealState)(v1) +func fromV3DealState(v3 market3.DealState) DealState { + return (DealState)(v3) } type dealProposals3 struct { @@ -172,31 +172,31 @@ type dealProposals3 struct { } func (s *dealProposals3) Get(dealID abi.DealID) (*DealProposal, bool, error) { - var proposal2 market3.DealProposal - found, err := s.Array.Get(uint64(dealID), &proposal2) + var proposal3 market3.DealProposal + found, err := s.Array.Get(uint64(dealID), &proposal3) if err != nil { return nil, false, err } if !found { return nil, false, nil } - proposal := fromV3DealProposal(proposal2) + proposal := fromV3DealProposal(proposal3) return &proposal, true, nil } func (s *dealProposals3) ForEach(cb func(dealID abi.DealID, dp DealProposal) error) error { - var dp1 market3.DealProposal - return s.Array.ForEach(&dp1, func(idx int64) error { - return cb(abi.DealID(idx), fromV3DealProposal(dp1)) + var dp3 market3.DealProposal + return s.Array.ForEach(&dp3, func(idx int64) error { + return cb(abi.DealID(idx), fromV3DealProposal(dp3)) }) } func (s *dealProposals3) decode(val *cbg.Deferred) (*DealProposal, error) { - var dp1 market3.DealProposal - if err := dp1.UnmarshalCBOR(bytes.NewReader(val.Raw)); err != nil { + var dp3 market3.DealProposal + if err := dp3.UnmarshalCBOR(bytes.NewReader(val.Raw)); err != nil { return nil, err } - dp := fromV3DealProposal(dp1) + dp := fromV3DealProposal(dp3) return &dp, nil } @@ -204,6 +204,6 @@ func (s *dealProposals3) array() adt.Array { return s.Array } -func fromV3DealProposal(v1 market3.DealProposal) DealProposal { - return (DealProposal)(v1) +func fromV3DealProposal(v3 market3.DealProposal) DealProposal { + return (DealProposal)(v3) } diff --git a/chain/actors/builtin/market/v4.go b/chain/actors/builtin/market/v4.go index dede98d1a..22514395c 100644 --- a/chain/actors/builtin/market/v4.go +++ b/chain/actors/builtin/market/v4.go @@ -38,23 +38,23 @@ func (s *state4) TotalLocked() (abi.TokenAmount, error) { } func (s *state4) BalancesChanged(otherState State) (bool, error) { - otherState2, ok := otherState.(*state4) + otherState4, ok := otherState.(*state4) if !ok { // there's no way to compare different versions of the state, so let's // just say that means the state of balances has changed return true, nil } - return !s.State.EscrowTable.Equals(otherState2.State.EscrowTable) || !s.State.LockedTable.Equals(otherState2.State.LockedTable), nil + return !s.State.EscrowTable.Equals(otherState4.State.EscrowTable) || !s.State.LockedTable.Equals(otherState4.State.LockedTable), nil } func (s *state4) StatesChanged(otherState State) (bool, error) { - otherState2, ok := otherState.(*state4) + otherState4, ok := otherState.(*state4) if !ok { // there's no way to compare different versions of the state, so let's // just say that means the state of balances has changed return true, nil } - return !s.State.States.Equals(otherState2.State.States), nil + return !s.State.States.Equals(otherState4.State.States), nil } func (s *state4) States() (DealStates, error) { @@ -66,13 +66,13 @@ func (s *state4) States() (DealStates, error) { } func (s *state4) ProposalsChanged(otherState State) (bool, error) { - otherState2, ok := otherState.(*state4) + otherState4, ok := otherState.(*state4) if !ok { // there's no way to compare different versions of the state, so let's // just say that means the state of balances has changed return true, nil } - return !s.State.Proposals.Equals(otherState2.State.Proposals), nil + return !s.State.Proposals.Equals(otherState4.State.Proposals), nil } func (s *state4) Proposals() (DealProposals, error) { @@ -131,31 +131,31 @@ type dealStates4 struct { } func (s *dealStates4) Get(dealID abi.DealID) (*DealState, bool, error) { - var deal2 market4.DealState - found, err := s.Array.Get(uint64(dealID), &deal2) + var deal4 market4.DealState + found, err := s.Array.Get(uint64(dealID), &deal4) if err != nil { return nil, false, err } if !found { return nil, false, nil } - deal := fromV4DealState(deal2) + deal := fromV4DealState(deal4) return &deal, true, nil } func (s *dealStates4) ForEach(cb func(dealID abi.DealID, ds DealState) error) error { - var ds1 market4.DealState - return s.Array.ForEach(&ds1, func(idx int64) error { - return cb(abi.DealID(idx), fromV4DealState(ds1)) + var ds4 market4.DealState + return s.Array.ForEach(&ds4, func(idx int64) error { + return cb(abi.DealID(idx), fromV4DealState(ds4)) }) } func (s *dealStates4) decode(val *cbg.Deferred) (*DealState, error) { - var ds1 market4.DealState - if err := ds1.UnmarshalCBOR(bytes.NewReader(val.Raw)); err != nil { + var ds4 market4.DealState + if err := ds4.UnmarshalCBOR(bytes.NewReader(val.Raw)); err != nil { return nil, err } - ds := fromV4DealState(ds1) + ds := fromV4DealState(ds4) return &ds, nil } @@ -172,31 +172,31 @@ type dealProposals4 struct { } func (s *dealProposals4) Get(dealID abi.DealID) (*DealProposal, bool, error) { - var proposal2 market4.DealProposal - found, err := s.Array.Get(uint64(dealID), &proposal2) + var proposal4 market4.DealProposal + found, err := s.Array.Get(uint64(dealID), &proposal4) if err != nil { return nil, false, err } if !found { return nil, false, nil } - proposal := fromV4DealProposal(proposal2) + proposal := fromV4DealProposal(proposal4) return &proposal, true, nil } func (s *dealProposals4) ForEach(cb func(dealID abi.DealID, dp DealProposal) error) error { - var dp1 market4.DealProposal - return s.Array.ForEach(&dp1, func(idx int64) error { - return cb(abi.DealID(idx), fromV4DealProposal(dp1)) + var dp4 market4.DealProposal + return s.Array.ForEach(&dp4, func(idx int64) error { + return cb(abi.DealID(idx), fromV4DealProposal(dp4)) }) } func (s *dealProposals4) decode(val *cbg.Deferred) (*DealProposal, error) { - var dp1 market4.DealProposal - if err := dp1.UnmarshalCBOR(bytes.NewReader(val.Raw)); err != nil { + var dp4 market4.DealProposal + if err := dp4.UnmarshalCBOR(bytes.NewReader(val.Raw)); err != nil { return nil, err } - dp := fromV4DealProposal(dp1) + dp := fromV4DealProposal(dp4) return &dp, nil } diff --git a/chain/actors/builtin/miner/actor.go.template b/chain/actors/builtin/miner/actor.go.template new file mode 100644 index 000000000..4b3d8db5e --- /dev/null +++ b/chain/actors/builtin/miner/actor.go.template @@ -0,0 +1,270 @@ +package miner + +import ( + "github.com/filecoin-project/go-state-types/big" + "github.com/filecoin-project/go-state-types/network" + "github.com/ipfs/go-cid" + "github.com/libp2p/go-libp2p-core/peer" + cbg "github.com/whyrusleeping/cbor-gen" + "golang.org/x/xerrors" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-bitfield" + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/cbor" + "github.com/filecoin-project/go-state-types/dline" + + "github.com/filecoin-project/lotus/chain/actors/adt" + "github.com/filecoin-project/lotus/chain/actors/builtin" + "github.com/filecoin-project/lotus/chain/types" + + miner0 "github.com/filecoin-project/specs-actors/actors/builtin/miner" + miner2 "github.com/filecoin-project/specs-actors/v2/actors/builtin/miner" + miner3 "github.com/filecoin-project/specs-actors/v3/actors/builtin/miner" +{{range .versions}} + builtin{{.}} "github.com/filecoin-project/specs-actors{{import .}}actors/builtin" +{{end}} +) + +func init() { +{{range .versions}} + builtin.RegisterActorState(builtin{{.}}.StorageMinerActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { + return load{{.}}(store, root) + }) +{{end}} +} + +var Methods = builtin{{.latestVersion}}.MethodsMiner + +// Unchanged between v0, v2, v3, and v4 actors +var WPoStProvingPeriod = miner0.WPoStProvingPeriod +var WPoStPeriodDeadlines = miner0.WPoStPeriodDeadlines +var WPoStChallengeWindow = miner0.WPoStChallengeWindow +var WPoStChallengeLookback = miner0.WPoStChallengeLookback +var FaultDeclarationCutoff = miner0.FaultDeclarationCutoff + +const MinSectorExpiration = miner0.MinSectorExpiration + +// Not used / checked in v0 +// TODO: Abstract over network versions +var DeclarationsMax = miner2.DeclarationsMax +var AddressedSectorsMax = miner2.AddressedSectorsMax + +func Load(store adt.Store, act *types.Actor) (State, error) { + switch act.Code { +{{range .versions}} + case builtin{{.}}.StorageMinerActorCodeID: + return load{{.}}(store, act.Head) +{{end}} +} + return nil, xerrors.Errorf("unknown actor code %s", act.Code) +} + +type State interface { + cbor.Marshaler + + // Total available balance to spend. + AvailableBalance(abi.TokenAmount) (abi.TokenAmount, error) + // Funds that will vest by the given epoch. + VestedFunds(abi.ChainEpoch) (abi.TokenAmount, error) + // Funds locked for various reasons. + LockedFunds() (LockedFunds, error) + FeeDebt() (abi.TokenAmount, error) + + GetSector(abi.SectorNumber) (*SectorOnChainInfo, error) + FindSector(abi.SectorNumber) (*SectorLocation, error) + GetSectorExpiration(abi.SectorNumber) (*SectorExpiration, error) + GetPrecommittedSector(abi.SectorNumber) (*SectorPreCommitOnChainInfo, error) + LoadSectors(sectorNos *bitfield.BitField) ([]*SectorOnChainInfo, error) + NumLiveSectors() (uint64, error) + IsAllocated(abi.SectorNumber) (bool, error) + + LoadDeadline(idx uint64) (Deadline, error) + ForEachDeadline(cb func(idx uint64, dl Deadline) error) error + NumDeadlines() (uint64, error) + DeadlinesChanged(State) (bool, error) + + Info() (MinerInfo, error) + MinerInfoChanged(State) (bool, error) + + DeadlineInfo(epoch abi.ChainEpoch) (*dline.Info, error) + DeadlineCronActive() (bool, error) + + // Diff helpers. Used by Diff* functions internally. + sectors() (adt.Array, error) + decodeSectorOnChainInfo(*cbg.Deferred) (SectorOnChainInfo, error) + precommits() (adt.Map, error) + decodeSectorPreCommitOnChainInfo(*cbg.Deferred) (SectorPreCommitOnChainInfo, error) +} + +type Deadline interface { + LoadPartition(idx uint64) (Partition, error) + ForEachPartition(cb func(idx uint64, part Partition) error) error + PartitionsPoSted() (bitfield.BitField, error) + + PartitionsChanged(Deadline) (bool, error) + DisputableProofCount() (uint64, error) +} + +type Partition interface { + AllSectors() (bitfield.BitField, error) + FaultySectors() (bitfield.BitField, error) + RecoveringSectors() (bitfield.BitField, error) + LiveSectors() (bitfield.BitField, error) + ActiveSectors() (bitfield.BitField, error) +} + +type SectorOnChainInfo struct { + SectorNumber abi.SectorNumber + SealProof abi.RegisteredSealProof + SealedCID cid.Cid + DealIDs []abi.DealID + Activation abi.ChainEpoch + Expiration abi.ChainEpoch + DealWeight abi.DealWeight + VerifiedDealWeight abi.DealWeight + InitialPledge abi.TokenAmount + ExpectedDayReward abi.TokenAmount + ExpectedStoragePledge abi.TokenAmount +} + +type SectorPreCommitInfo = miner0.SectorPreCommitInfo + +type SectorPreCommitOnChainInfo struct { + Info SectorPreCommitInfo + PreCommitDeposit abi.TokenAmount + PreCommitEpoch abi.ChainEpoch + DealWeight abi.DealWeight + VerifiedDealWeight abi.DealWeight +} + +type PoStPartition = miner0.PoStPartition +type RecoveryDeclaration = miner0.RecoveryDeclaration +type FaultDeclaration = miner0.FaultDeclaration + +// Params +type DeclareFaultsParams = miner0.DeclareFaultsParams +type DeclareFaultsRecoveredParams = miner0.DeclareFaultsRecoveredParams +type SubmitWindowedPoStParams = miner0.SubmitWindowedPoStParams +type ProveCommitSectorParams = miner0.ProveCommitSectorParams +type DisputeWindowedPoStParams = miner3.DisputeWindowedPoStParams + +func PreferredSealProofTypeFromWindowPoStType(nver network.Version, proof abi.RegisteredPoStProof) (abi.RegisteredSealProof, error) { + // We added support for the new proofs in network version 7, and removed support for the old + // ones in network version 8. + if nver < network.Version7 { + switch proof { + case abi.RegisteredPoStProof_StackedDrgWindow2KiBV1: + return abi.RegisteredSealProof_StackedDrg2KiBV1, nil + case abi.RegisteredPoStProof_StackedDrgWindow8MiBV1: + return abi.RegisteredSealProof_StackedDrg8MiBV1, nil + case abi.RegisteredPoStProof_StackedDrgWindow512MiBV1: + return abi.RegisteredSealProof_StackedDrg512MiBV1, nil + case abi.RegisteredPoStProof_StackedDrgWindow32GiBV1: + return abi.RegisteredSealProof_StackedDrg32GiBV1, nil + case abi.RegisteredPoStProof_StackedDrgWindow64GiBV1: + return abi.RegisteredSealProof_StackedDrg64GiBV1, nil + default: + return -1, xerrors.Errorf("unrecognized window post type: %d", proof) + } + } + + switch proof { + case abi.RegisteredPoStProof_StackedDrgWindow2KiBV1: + return abi.RegisteredSealProof_StackedDrg2KiBV1_1, nil + case abi.RegisteredPoStProof_StackedDrgWindow8MiBV1: + return abi.RegisteredSealProof_StackedDrg8MiBV1_1, nil + case abi.RegisteredPoStProof_StackedDrgWindow512MiBV1: + return abi.RegisteredSealProof_StackedDrg512MiBV1_1, nil + case abi.RegisteredPoStProof_StackedDrgWindow32GiBV1: + return abi.RegisteredSealProof_StackedDrg32GiBV1_1, nil + case abi.RegisteredPoStProof_StackedDrgWindow64GiBV1: + return abi.RegisteredSealProof_StackedDrg64GiBV1_1, nil + default: + return -1, xerrors.Errorf("unrecognized window post type: %d", proof) + } +} + +func WinningPoStProofTypeFromWindowPoStProofType(nver network.Version, proof abi.RegisteredPoStProof) (abi.RegisteredPoStProof, error) { + switch proof { + case abi.RegisteredPoStProof_StackedDrgWindow2KiBV1: + return abi.RegisteredPoStProof_StackedDrgWinning2KiBV1, nil + case abi.RegisteredPoStProof_StackedDrgWindow8MiBV1: + return abi.RegisteredPoStProof_StackedDrgWinning8MiBV1, nil + case abi.RegisteredPoStProof_StackedDrgWindow512MiBV1: + return abi.RegisteredPoStProof_StackedDrgWinning512MiBV1, nil + case abi.RegisteredPoStProof_StackedDrgWindow32GiBV1: + return abi.RegisteredPoStProof_StackedDrgWinning32GiBV1, nil + case abi.RegisteredPoStProof_StackedDrgWindow64GiBV1: + return abi.RegisteredPoStProof_StackedDrgWinning64GiBV1, nil + default: + return -1, xerrors.Errorf("unknown proof type %d", proof) + } +} + +type MinerInfo struct { + Owner address.Address // Must be an ID-address. + Worker address.Address // Must be an ID-address. + NewWorker address.Address // Must be an ID-address. + ControlAddresses []address.Address // Must be an ID-addresses. + WorkerChangeEpoch abi.ChainEpoch + PeerId *peer.ID + Multiaddrs []abi.Multiaddrs + WindowPoStProofType abi.RegisteredPoStProof + SectorSize abi.SectorSize + WindowPoStPartitionSectors uint64 + ConsensusFaultElapsed abi.ChainEpoch +} + +func (mi MinerInfo) IsController(addr address.Address) bool { + if addr == mi.Owner || addr == mi.Worker { + return true + } + + for _, ca := range mi.ControlAddresses { + if addr == ca { + return true + } + } + + return false +} + +type SectorExpiration struct { + OnTime abi.ChainEpoch + + // non-zero if sector is faulty, epoch at which it will be permanently + // removed if it doesn't recover + Early abi.ChainEpoch +} + +type SectorLocation struct { + Deadline uint64 + Partition uint64 +} + +type SectorChanges struct { + Added []SectorOnChainInfo + Extended []SectorExtensions + Removed []SectorOnChainInfo +} + +type SectorExtensions struct { + From SectorOnChainInfo + To SectorOnChainInfo +} + +type PreCommitChanges struct { + Added []SectorPreCommitOnChainInfo + Removed []SectorPreCommitOnChainInfo +} + +type LockedFunds struct { + VestingFunds abi.TokenAmount + InitialPledgeRequirement abi.TokenAmount + PreCommitDeposits abi.TokenAmount +} + +func (lf LockedFunds) TotalLockedFunds() abi.TokenAmount { + return big.Add(lf.VestingFunds, big.Add(lf.InitialPledgeRequirement, lf.PreCommitDeposits)) +} diff --git a/chain/actors/builtin/miner/miner.go b/chain/actors/builtin/miner/miner.go index 8fffcc8d6..a426e063b 100644 --- a/chain/actors/builtin/miner/miner.go +++ b/chain/actors/builtin/miner/miner.go @@ -18,28 +18,37 @@ import ( "github.com/filecoin-project/lotus/chain/actors/builtin" "github.com/filecoin-project/lotus/chain/types" - builtin0 "github.com/filecoin-project/specs-actors/actors/builtin" miner0 "github.com/filecoin-project/specs-actors/actors/builtin/miner" - builtin2 "github.com/filecoin-project/specs-actors/v2/actors/builtin" miner2 "github.com/filecoin-project/specs-actors/v2/actors/builtin/miner" - builtin3 "github.com/filecoin-project/specs-actors/v3/actors/builtin" miner3 "github.com/filecoin-project/specs-actors/v3/actors/builtin/miner" + + builtin0 "github.com/filecoin-project/specs-actors/actors/builtin" + + builtin2 "github.com/filecoin-project/specs-actors/v2/actors/builtin" + + builtin3 "github.com/filecoin-project/specs-actors/v3/actors/builtin" + builtin4 "github.com/filecoin-project/specs-actors/v4/actors/builtin" ) func init() { + builtin.RegisterActorState(builtin0.StorageMinerActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load0(store, root) }) + builtin.RegisterActorState(builtin2.StorageMinerActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load2(store, root) }) + builtin.RegisterActorState(builtin3.StorageMinerActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load3(store, root) }) + builtin.RegisterActorState(builtin4.StorageMinerActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load4(store, root) }) + } var Methods = builtin4.MethodsMiner @@ -58,16 +67,21 @@ const MinSectorExpiration = miner0.MinSectorExpiration var DeclarationsMax = miner2.DeclarationsMax var AddressedSectorsMax = miner2.AddressedSectorsMax -func Load(store adt.Store, act *types.Actor) (st State, err error) { +func Load(store adt.Store, act *types.Actor) (State, error) { switch act.Code { + case builtin0.StorageMinerActorCodeID: return load0(store, act.Head) + case builtin2.StorageMinerActorCodeID: return load2(store, act.Head) + case builtin3.StorageMinerActorCodeID: return load3(store, act.Head) + case builtin4.StorageMinerActorCodeID: return load4(store, act.Head) + } return nil, xerrors.Errorf("unknown actor code %s", act.Code) } diff --git a/chain/actors/builtin/miner/state.go.template b/chain/actors/builtin/miner/state.go.template new file mode 100644 index 000000000..0769eea10 --- /dev/null +++ b/chain/actors/builtin/miner/state.go.template @@ -0,0 +1,460 @@ +package miner + +import ( + "bytes" + "errors" +{{if (le .v 1)}} + "github.com/filecoin-project/go-state-types/big" +{{end}} + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-bitfield" + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/dline" + "github.com/ipfs/go-cid" + "github.com/libp2p/go-libp2p-core/peer" + cbg "github.com/whyrusleeping/cbor-gen" + "golang.org/x/xerrors" + + "github.com/filecoin-project/lotus/chain/actors/adt" + +{{if (ge .v 3)}} + builtin{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/builtin" +{{end}} + miner{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/builtin/miner" + adt{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/util/adt" +) + +var _ State = (*state{{.v}})(nil) + +func load{{.v}}(store adt.Store, root cid.Cid) (State, error) { + out := state{{.v}}{store: store} + err := store.Get(store.Context(), root, &out) + if err != nil { + return nil, err + } + return &out, nil +} + +type state{{.v}} struct { + miner{{.v}}.State + store adt.Store +} + +type deadline{{.v}} struct { + miner{{.v}}.Deadline + store adt.Store +} + +type partition{{.v}} struct { + miner{{.v}}.Partition + store adt.Store +} + +func (s *state{{.v}}) AvailableBalance(bal abi.TokenAmount) (available abi.TokenAmount, err error) { + defer func() { + if r := recover(); r != nil { + err = xerrors.Errorf("failed to get available balance: %w", r) + available = abi.NewTokenAmount(0) + } + }() + // this panics if the miner doesnt have enough funds to cover their locked pledge + available{{if (ge .v 2)}}, err{{end}} = s.GetAvailableBalance(bal) + return available, err +} + +func (s *state{{.v}}) VestedFunds(epoch abi.ChainEpoch) (abi.TokenAmount, error) { + return s.CheckVestedFunds(s.store, epoch) +} + +func (s *state{{.v}}) LockedFunds() (LockedFunds, error) { + return LockedFunds{ + VestingFunds: s.State.LockedFunds, + InitialPledgeRequirement: s.State.InitialPledge{{if (le .v 1)}}Requirement{{end}}, + PreCommitDeposits: s.State.PreCommitDeposits, + }, nil +} + +func (s *state{{.v}}) FeeDebt() (abi.TokenAmount, error) { + return {{if (ge .v 2)}}s.State.FeeDebt{{else}}big.Zero(){{end}}, nil +} + +func (s *state{{.v}}) InitialPledge() (abi.TokenAmount, error) { + return s.State.InitialPledge{{if (le .v 1)}}Requirement{{end}}, nil +} + +func (s *state{{.v}}) PreCommitDeposits() (abi.TokenAmount, error) { + return s.State.PreCommitDeposits, nil +} + +func (s *state{{.v}}) GetSector(num abi.SectorNumber) (*SectorOnChainInfo, error) { + info, ok, err := s.State.GetSector(s.store, num) + if !ok || err != nil { + return nil, err + } + + ret := fromV{{.v}}SectorOnChainInfo(*info) + return &ret, nil +} + +func (s *state{{.v}}) FindSector(num abi.SectorNumber) (*SectorLocation, error) { + dlIdx, partIdx, err := s.State.FindSector(s.store, num) + if err != nil { + return nil, err + } + return &SectorLocation{ + Deadline: dlIdx, + Partition: partIdx, + }, nil +} + +func (s *state{{.v}}) NumLiveSectors() (uint64, error) { + dls, err := s.State.LoadDeadlines(s.store) + if err != nil { + return 0, err + } + var total uint64 + if err := dls.ForEach(s.store, func(dlIdx uint64, dl *miner{{.v}}.Deadline) error { + total += dl.LiveSectors + return nil + }); err != nil { + return 0, err + } + return total, nil +} + +// GetSectorExpiration returns the effective expiration of the given sector. +// +// If the sector does not expire early, the Early expiration field is 0. +func (s *state{{.v}}) GetSectorExpiration(num abi.SectorNumber) (*SectorExpiration, error) { + dls, err := s.State.LoadDeadlines(s.store) + if err != nil { + return nil, err + } + // NOTE: this can be optimized significantly. + // 1. If the sector is non-faulty, it will either expire on-time (can be + // learned from the sector info), or in the next quantized expiration + // epoch (i.e., the first element in the partition's expiration queue. + // 2. If it's faulty, it will expire early within the first 14 entries + // of the expiration queue. + stopErr := errors.New("stop") + out := SectorExpiration{} + err = dls.ForEach(s.store, func(dlIdx uint64, dl *miner{{.v}}.Deadline) error { + partitions, err := dl.PartitionsArray(s.store) + if err != nil { + return err + } + quant := s.State.QuantSpecForDeadline(dlIdx) + var part miner{{.v}}.Partition + return partitions.ForEach(&part, func(partIdx int64) error { + if found, err := part.Sectors.IsSet(uint64(num)); err != nil { + return err + } else if !found { + return nil + } + if found, err := part.Terminated.IsSet(uint64(num)); err != nil { + return err + } else if found { + // already terminated + return stopErr + } + + q, err := miner{{.v}}.LoadExpirationQueue(s.store, part.ExpirationsEpochs, quant{{if (ge .v 3)}}, miner{{.v}}.PartitionExpirationAmtBitwidth{{end}}) + if err != nil { + return err + } + var exp miner{{.v}}.ExpirationSet + return q.ForEach(&exp, func(epoch int64) error { + if early, err := exp.EarlySectors.IsSet(uint64(num)); err != nil { + return err + } else if early { + out.Early = abi.ChainEpoch(epoch) + return nil + } + if onTime, err := exp.OnTimeSectors.IsSet(uint64(num)); err != nil { + return err + } else if onTime { + out.OnTime = abi.ChainEpoch(epoch) + return stopErr + } + return nil + }) + }) + }) + if err == stopErr { + err = nil + } + if err != nil { + return nil, err + } + if out.Early == 0 && out.OnTime == 0 { + return nil, xerrors.Errorf("failed to find sector %d", num) + } + return &out, nil +} + +func (s *state{{.v}}) GetPrecommittedSector(num abi.SectorNumber) (*SectorPreCommitOnChainInfo, error) { + info, ok, err := s.State.GetPrecommittedSector(s.store, num) + if !ok || err != nil { + return nil, err + } + + ret := fromV{{.v}}SectorPreCommitOnChainInfo(*info) + + return &ret, nil +} + +func (s *state{{.v}}) LoadSectors(snos *bitfield.BitField) ([]*SectorOnChainInfo, error) { + sectors, err := miner{{.v}}.LoadSectors(s.store, s.State.Sectors) + if err != nil { + return nil, err + } + + // If no sector numbers are specified, load all. + if snos == nil { + infos := make([]*SectorOnChainInfo, 0, sectors.Length()) + var info{{.v}} miner{{.v}}.SectorOnChainInfo + if err := sectors.ForEach(&info{{.v}}, func(_ int64) error { + info := fromV{{.v}}SectorOnChainInfo(info{{.v}}) + infos = append(infos, &info) + return nil + }); err != nil { + return nil, err + } + return infos, nil + } + + // Otherwise, load selected. + infos{{.v}}, err := sectors.Load(*snos) + if err != nil { + return nil, err + } + infos := make([]*SectorOnChainInfo, len(infos{{.v}})) + for i, info{{.v}} := range infos{{.v}} { + info := fromV{{.v}}SectorOnChainInfo(*info{{.v}}) + infos[i] = &info + } + return infos, nil +} + +func (s *state{{.v}}) IsAllocated(num abi.SectorNumber) (bool, error) { + var allocatedSectors bitfield.BitField + if err := s.store.Get(s.store.Context(), s.State.AllocatedSectors, &allocatedSectors); err != nil { + return false, err + } + + return allocatedSectors.IsSet(uint64(num)) +} + +func (s *state{{.v}}) LoadDeadline(idx uint64) (Deadline, error) { + dls, err := s.State.LoadDeadlines(s.store) + if err != nil { + return nil, err + } + dl, err := dls.LoadDeadline(s.store, idx) + if err != nil { + return nil, err + } + return &deadline{{.v}}{*dl, s.store}, nil +} + +func (s *state{{.v}}) ForEachDeadline(cb func(uint64, Deadline) error) error { + dls, err := s.State.LoadDeadlines(s.store) + if err != nil { + return err + } + return dls.ForEach(s.store, func(i uint64, dl *miner{{.v}}.Deadline) error { + return cb(i, &deadline{{.v}}{*dl, s.store}) + }) +} + +func (s *state{{.v}}) NumDeadlines() (uint64, error) { + return miner{{.v}}.WPoStPeriodDeadlines, nil +} + +func (s *state{{.v}}) DeadlinesChanged(other State) (bool, error) { + other{{.v}}, ok := other.(*state{{.v}}) + if !ok { + // treat an upgrade as a change, always + return true, nil + } + + return !s.State.Deadlines.Equals(other{{.v}}.Deadlines), nil +} + +func (s *state{{.v}}) MinerInfoChanged(other State) (bool, error) { + other0, ok := other.(*state{{.v}}) + if !ok { + // treat an upgrade as a change, always + return true, nil + } + return !s.State.Info.Equals(other0.State.Info), nil +} + +func (s *state{{.v}}) Info() (MinerInfo, error) { + info, err := s.State.GetInfo(s.store) + if err != nil { + return MinerInfo{}, err + } + + var pid *peer.ID + if peerID, err := peer.IDFromBytes(info.PeerId); err == nil { + pid = &peerID + } +{{if (le .v 2)}} + wpp, err := info.SealProofType.RegisteredWindowPoStProof() + if err != nil { + return MinerInfo{}, err + } +{{end}} + mi := MinerInfo{ + Owner: info.Owner, + Worker: info.Worker, + ControlAddresses: info.ControlAddresses, + + NewWorker: address.Undef, + WorkerChangeEpoch: -1, + + PeerId: pid, + Multiaddrs: info.Multiaddrs, + WindowPoStProofType: {{if (ge .v 3)}}info.WindowPoStProofType{{else}}wpp{{end}}, + SectorSize: info.SectorSize, + WindowPoStPartitionSectors: info.WindowPoStPartitionSectors, + ConsensusFaultElapsed: {{if (ge .v 2)}}info.ConsensusFaultElapsed{{else}}-1{{end}}, + } + + if info.PendingWorkerKey != nil { + mi.NewWorker = info.PendingWorkerKey.NewWorker + mi.WorkerChangeEpoch = info.PendingWorkerKey.EffectiveAt + } + + return mi, nil +} + +func (s *state{{.v}}) DeadlineInfo(epoch abi.ChainEpoch) (*dline.Info, error) { + return s.State.{{if (ge .v 4)}}Recorded{{end}}DeadlineInfo(epoch), nil +} + +func (s *state{{.v}}) DeadlineCronActive() (bool, error) { + return {{if (ge .v 4)}}s.State.DeadlineCronActive{{else}}true{{end}}, nil{{if (lt .v 4)}} // always active in this version{{end}} +} + +func (s *state{{.v}}) sectors() (adt.Array, error) { + return adt{{.v}}.AsArray(s.store, s.Sectors{{if (ge .v 3)}}, miner{{.v}}.SectorsAmtBitwidth{{end}}) +} + +func (s *state{{.v}}) decodeSectorOnChainInfo(val *cbg.Deferred) (SectorOnChainInfo, error) { + var si miner{{.v}}.SectorOnChainInfo + err := si.UnmarshalCBOR(bytes.NewReader(val.Raw)) + if err != nil { + return SectorOnChainInfo{}, err + } + + return fromV{{.v}}SectorOnChainInfo(si), nil +} + +func (s *state{{.v}}) precommits() (adt.Map, error) { + return adt{{.v}}.AsMap(s.store, s.PreCommittedSectors{{if (ge .v 3)}}, builtin{{.v}}.DefaultHamtBitwidth{{end}}) +} + +func (s *state{{.v}}) decodeSectorPreCommitOnChainInfo(val *cbg.Deferred) (SectorPreCommitOnChainInfo, error) { + var sp miner{{.v}}.SectorPreCommitOnChainInfo + err := sp.UnmarshalCBOR(bytes.NewReader(val.Raw)) + if err != nil { + return SectorPreCommitOnChainInfo{}, err + } + + return fromV{{.v}}SectorPreCommitOnChainInfo(sp), nil +} + +func (d *deadline{{.v}}) LoadPartition(idx uint64) (Partition, error) { + p, err := d.Deadline.LoadPartition(d.store, idx) + if err != nil { + return nil, err + } + return &partition{{.v}}{*p, d.store}, nil +} + +func (d *deadline{{.v}}) ForEachPartition(cb func(uint64, Partition) error) error { + ps, err := d.Deadline.PartitionsArray(d.store) + if err != nil { + return err + } + var part miner{{.v}}.Partition + return ps.ForEach(&part, func(i int64) error { + return cb(uint64(i), &partition{{.v}}{part, d.store}) + }) +} + +func (d *deadline{{.v}}) PartitionsChanged(other Deadline) (bool, error) { + other{{.v}}, ok := other.(*deadline{{.v}}) + if !ok { + // treat an upgrade as a change, always + return true, nil + } + + return !d.Deadline.Partitions.Equals(other{{.v}}.Deadline.Partitions), nil +} + +func (d *deadline{{.v}}) PartitionsPoSted() (bitfield.BitField, error) { + return d.Deadline.{{if (ge .v 3)}}PartitionsPoSted{{else}}PostSubmissions{{end}}, nil +} + +func (d *deadline{{.v}}) DisputableProofCount() (uint64, error) { +{{if (ge .v 3)}} + ops, err := d.OptimisticProofsSnapshotArray(d.store) + if err != nil { + return 0, err + } + + return ops.Length(), nil +{{else}} + // field doesn't exist until v3 + return 0, nil +{{end}} +} + +func (p *partition{{.v}}) AllSectors() (bitfield.BitField, error) { + return p.Partition.Sectors, nil +} + +func (p *partition{{.v}}) FaultySectors() (bitfield.BitField, error) { + return p.Partition.Faults, nil +} + +func (p *partition{{.v}}) RecoveringSectors() (bitfield.BitField, error) { + return p.Partition.Recoveries, nil +} + +func fromV{{.v}}SectorOnChainInfo(v{{.v}} miner{{.v}}.SectorOnChainInfo) SectorOnChainInfo { +{{if (ge .v 2)}} + return SectorOnChainInfo{ + SectorNumber: v{{.v}}.SectorNumber, + SealProof: v{{.v}}.SealProof, + SealedCID: v{{.v}}.SealedCID, + DealIDs: v{{.v}}.DealIDs, + Activation: v{{.v}}.Activation, + Expiration: v{{.v}}.Expiration, + DealWeight: v{{.v}}.DealWeight, + VerifiedDealWeight: v{{.v}}.VerifiedDealWeight, + InitialPledge: v{{.v}}.InitialPledge, + ExpectedDayReward: v{{.v}}.ExpectedDayReward, + ExpectedStoragePledge: v{{.v}}.ExpectedStoragePledge, + } +{{else}} + return (SectorOnChainInfo)(v0) +{{end}} +} + +func fromV{{.v}}SectorPreCommitOnChainInfo(v{{.v}} miner{{.v}}.SectorPreCommitOnChainInfo) SectorPreCommitOnChainInfo { +{{if (ge .v 2)}} + return SectorPreCommitOnChainInfo{ + Info: (SectorPreCommitInfo)(v{{.v}}.Info), + PreCommitDeposit: v{{.v}}.PreCommitDeposit, + PreCommitEpoch: v{{.v}}.PreCommitEpoch, + DealWeight: v{{.v}}.DealWeight, + VerifiedDealWeight: v{{.v}}.VerifiedDealWeight, + } +{{else}} + return (SectorPreCommitOnChainInfo)(v0) +{{end}} +} diff --git a/chain/actors/builtin/miner/v0.go b/chain/actors/builtin/miner/v0.go index 4f02e313f..2dc8ae23e 100644 --- a/chain/actors/builtin/miner/v0.go +++ b/chain/actors/builtin/miner/v0.go @@ -196,6 +196,7 @@ func (s *state0) GetPrecommittedSector(num abi.SectorNumber) (*SectorPreCommitOn } ret := fromV0SectorPreCommitOnChainInfo(*info) + return &ret, nil } @@ -396,8 +397,10 @@ func (d *deadline0) PartitionsPoSted() (bitfield.BitField, error) { } func (d *deadline0) DisputableProofCount() (uint64, error) { + // field doesn't exist until v3 return 0, nil + } func (p *partition0) AllSectors() (bitfield.BitField, error) { @@ -413,9 +416,13 @@ func (p *partition0) RecoveringSectors() (bitfield.BitField, error) { } func fromV0SectorOnChainInfo(v0 miner0.SectorOnChainInfo) SectorOnChainInfo { + return (SectorOnChainInfo)(v0) + } func fromV0SectorPreCommitOnChainInfo(v0 miner0.SectorPreCommitOnChainInfo) SectorPreCommitOnChainInfo { + return (SectorPreCommitOnChainInfo)(v0) + } diff --git a/chain/actors/builtin/miner/v2.go b/chain/actors/builtin/miner/v2.go index 1720b619f..7564dd8b8 100644 --- a/chain/actors/builtin/miner/v2.go +++ b/chain/actors/builtin/miner/v2.go @@ -395,8 +395,10 @@ func (d *deadline2) PartitionsPoSted() (bitfield.BitField, error) { } func (d *deadline2) DisputableProofCount() (uint64, error) { + // field doesn't exist until v3 return 0, nil + } func (p *partition2) AllSectors() (bitfield.BitField, error) { @@ -412,6 +414,7 @@ func (p *partition2) RecoveringSectors() (bitfield.BitField, error) { } func fromV2SectorOnChainInfo(v2 miner2.SectorOnChainInfo) SectorOnChainInfo { + return SectorOnChainInfo{ SectorNumber: v2.SectorNumber, SealProof: v2.SealProof, @@ -425,9 +428,11 @@ func fromV2SectorOnChainInfo(v2 miner2.SectorOnChainInfo) SectorOnChainInfo { ExpectedDayReward: v2.ExpectedDayReward, ExpectedStoragePledge: v2.ExpectedStoragePledge, } + } func fromV2SectorPreCommitOnChainInfo(v2 miner2.SectorPreCommitOnChainInfo) SectorPreCommitOnChainInfo { + return SectorPreCommitOnChainInfo{ Info: (SectorPreCommitInfo)(v2.Info), PreCommitDeposit: v2.PreCommitDeposit, @@ -435,4 +440,5 @@ func fromV2SectorPreCommitOnChainInfo(v2 miner2.SectorPreCommitOnChainInfo) Sect DealWeight: v2.DealWeight, VerifiedDealWeight: v2.VerifiedDealWeight, } + } diff --git a/chain/actors/builtin/miner/v3.go b/chain/actors/builtin/miner/v3.go index 5e058ed1f..72a080f73 100644 --- a/chain/actors/builtin/miner/v3.go +++ b/chain/actors/builtin/miner/v3.go @@ -16,6 +16,7 @@ import ( "github.com/filecoin-project/lotus/chain/actors/adt" builtin3 "github.com/filecoin-project/specs-actors/v3/actors/builtin" + miner3 "github.com/filecoin-project/specs-actors/v3/actors/builtin/miner" adt3 "github.com/filecoin-project/specs-actors/v3/actors/util/adt" ) @@ -208,9 +209,9 @@ func (s *state3) LoadSectors(snos *bitfield.BitField) ([]*SectorOnChainInfo, err // If no sector numbers are specified, load all. if snos == nil { infos := make([]*SectorOnChainInfo, 0, sectors.Length()) - var info2 miner3.SectorOnChainInfo - if err := sectors.ForEach(&info2, func(_ int64) error { - info := fromV3SectorOnChainInfo(info2) + var info3 miner3.SectorOnChainInfo + if err := sectors.ForEach(&info3, func(_ int64) error { + info := fromV3SectorOnChainInfo(info3) infos = append(infos, &info) return nil }); err != nil { @@ -220,13 +221,13 @@ func (s *state3) LoadSectors(snos *bitfield.BitField) ([]*SectorOnChainInfo, err } // Otherwise, load selected. - infos2, err := sectors.Load(*snos) + infos3, err := sectors.Load(*snos) if err != nil { return nil, err } - infos := make([]*SectorOnChainInfo, len(infos2)) - for i, info2 := range infos2 { - info := fromV3SectorOnChainInfo(*info2) + infos := make([]*SectorOnChainInfo, len(infos3)) + for i, info3 := range infos3 { + info := fromV3SectorOnChainInfo(*info3) infos[i] = &info } return infos, nil @@ -268,13 +269,13 @@ func (s *state3) NumDeadlines() (uint64, error) { } func (s *state3) DeadlinesChanged(other State) (bool, error) { - other2, ok := other.(*state3) + other3, ok := other.(*state3) if !ok { // treat an upgrade as a change, always return true, nil } - return !s.State.Deadlines.Equals(other2.Deadlines), nil + return !s.State.Deadlines.Equals(other3.Deadlines), nil } func (s *state3) MinerInfoChanged(other State) (bool, error) { @@ -377,13 +378,13 @@ func (d *deadline3) ForEachPartition(cb func(uint64, Partition) error) error { } func (d *deadline3) PartitionsChanged(other Deadline) (bool, error) { - other2, ok := other.(*deadline3) + other3, ok := other.(*deadline3) if !ok { // treat an upgrade as a change, always return true, nil } - return !d.Deadline.Partitions.Equals(other2.Deadline.Partitions), nil + return !d.Deadline.Partitions.Equals(other3.Deadline.Partitions), nil } func (d *deadline3) PartitionsPoSted() (bitfield.BitField, error) { @@ -391,12 +392,14 @@ func (d *deadline3) PartitionsPoSted() (bitfield.BitField, error) { } func (d *deadline3) DisputableProofCount() (uint64, error) { + ops, err := d.OptimisticProofsSnapshotArray(d.store) if err != nil { return 0, err } return ops.Length(), nil + } func (p *partition3) AllSectors() (bitfield.BitField, error) { @@ -412,6 +415,7 @@ func (p *partition3) RecoveringSectors() (bitfield.BitField, error) { } func fromV3SectorOnChainInfo(v3 miner3.SectorOnChainInfo) SectorOnChainInfo { + return SectorOnChainInfo{ SectorNumber: v3.SectorNumber, SealProof: v3.SealProof, @@ -425,9 +429,11 @@ func fromV3SectorOnChainInfo(v3 miner3.SectorOnChainInfo) SectorOnChainInfo { ExpectedDayReward: v3.ExpectedDayReward, ExpectedStoragePledge: v3.ExpectedStoragePledge, } + } func fromV3SectorPreCommitOnChainInfo(v3 miner3.SectorPreCommitOnChainInfo) SectorPreCommitOnChainInfo { + return SectorPreCommitOnChainInfo{ Info: (SectorPreCommitInfo)(v3.Info), PreCommitDeposit: v3.PreCommitDeposit, @@ -435,4 +441,5 @@ func fromV3SectorPreCommitOnChainInfo(v3 miner3.SectorPreCommitOnChainInfo) Sect DealWeight: v3.DealWeight, VerifiedDealWeight: v3.VerifiedDealWeight, } + } diff --git a/chain/actors/builtin/miner/v4.go b/chain/actors/builtin/miner/v4.go index b354dbc33..698bdf2f5 100644 --- a/chain/actors/builtin/miner/v4.go +++ b/chain/actors/builtin/miner/v4.go @@ -16,6 +16,7 @@ import ( "github.com/filecoin-project/lotus/chain/actors/adt" builtin4 "github.com/filecoin-project/specs-actors/v4/actors/builtin" + miner4 "github.com/filecoin-project/specs-actors/v4/actors/builtin/miner" adt4 "github.com/filecoin-project/specs-actors/v4/actors/util/adt" ) @@ -208,9 +209,9 @@ func (s *state4) LoadSectors(snos *bitfield.BitField) ([]*SectorOnChainInfo, err // If no sector numbers are specified, load all. if snos == nil { infos := make([]*SectorOnChainInfo, 0, sectors.Length()) - var info2 miner4.SectorOnChainInfo - if err := sectors.ForEach(&info2, func(_ int64) error { - info := fromV4SectorOnChainInfo(info2) + var info4 miner4.SectorOnChainInfo + if err := sectors.ForEach(&info4, func(_ int64) error { + info := fromV4SectorOnChainInfo(info4) infos = append(infos, &info) return nil }); err != nil { @@ -220,13 +221,13 @@ func (s *state4) LoadSectors(snos *bitfield.BitField) ([]*SectorOnChainInfo, err } // Otherwise, load selected. - infos2, err := sectors.Load(*snos) + infos4, err := sectors.Load(*snos) if err != nil { return nil, err } - infos := make([]*SectorOnChainInfo, len(infos2)) - for i, info2 := range infos2 { - info := fromV4SectorOnChainInfo(*info2) + infos := make([]*SectorOnChainInfo, len(infos4)) + for i, info4 := range infos4 { + info := fromV4SectorOnChainInfo(*info4) infos[i] = &info } return infos, nil @@ -268,13 +269,13 @@ func (s *state4) NumDeadlines() (uint64, error) { } func (s *state4) DeadlinesChanged(other State) (bool, error) { - other2, ok := other.(*state4) + other4, ok := other.(*state4) if !ok { // treat an upgrade as a change, always return true, nil } - return !s.State.Deadlines.Equals(other2.Deadlines), nil + return !s.State.Deadlines.Equals(other4.Deadlines), nil } func (s *state4) MinerInfoChanged(other State) (bool, error) { @@ -377,13 +378,13 @@ func (d *deadline4) ForEachPartition(cb func(uint64, Partition) error) error { } func (d *deadline4) PartitionsChanged(other Deadline) (bool, error) { - other2, ok := other.(*deadline4) + other4, ok := other.(*deadline4) if !ok { // treat an upgrade as a change, always return true, nil } - return !d.Deadline.Partitions.Equals(other2.Deadline.Partitions), nil + return !d.Deadline.Partitions.Equals(other4.Deadline.Partitions), nil } func (d *deadline4) PartitionsPoSted() (bitfield.BitField, error) { @@ -391,12 +392,14 @@ func (d *deadline4) PartitionsPoSted() (bitfield.BitField, error) { } func (d *deadline4) DisputableProofCount() (uint64, error) { + ops, err := d.OptimisticProofsSnapshotArray(d.store) if err != nil { return 0, err } return ops.Length(), nil + } func (p *partition4) AllSectors() (bitfield.BitField, error) { @@ -412,6 +415,7 @@ func (p *partition4) RecoveringSectors() (bitfield.BitField, error) { } func fromV4SectorOnChainInfo(v4 miner4.SectorOnChainInfo) SectorOnChainInfo { + return SectorOnChainInfo{ SectorNumber: v4.SectorNumber, SealProof: v4.SealProof, @@ -425,9 +429,11 @@ func fromV4SectorOnChainInfo(v4 miner4.SectorOnChainInfo) SectorOnChainInfo { ExpectedDayReward: v4.ExpectedDayReward, ExpectedStoragePledge: v4.ExpectedStoragePledge, } + } func fromV4SectorPreCommitOnChainInfo(v4 miner4.SectorPreCommitOnChainInfo) SectorPreCommitOnChainInfo { + return SectorPreCommitOnChainInfo{ Info: (SectorPreCommitInfo)(v4.Info), PreCommitDeposit: v4.PreCommitDeposit, @@ -435,4 +441,5 @@ func fromV4SectorPreCommitOnChainInfo(v4 miner4.SectorPreCommitOnChainInfo) Sect DealWeight: v4.DealWeight, VerifiedDealWeight: v4.VerifiedDealWeight, } + } diff --git a/chain/actors/builtin/multisig/actor.go.template b/chain/actors/builtin/multisig/actor.go.template new file mode 100644 index 000000000..304c0610c --- /dev/null +++ b/chain/actors/builtin/multisig/actor.go.template @@ -0,0 +1,118 @@ +package multisig + +import ( + "fmt" + + "github.com/minio/blake2b-simd" + cbg "github.com/whyrusleeping/cbor-gen" + "golang.org/x/xerrors" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/cbor" + "github.com/ipfs/go-cid" + + msig0 "github.com/filecoin-project/specs-actors/actors/builtin/multisig" + msig4 "github.com/filecoin-project/specs-actors/v4/actors/builtin/multisig" +{{range .versions}} + builtin{{.}} "github.com/filecoin-project/specs-actors{{import .}}actors/builtin" +{{end}} + + "github.com/filecoin-project/lotus/chain/actors" + "github.com/filecoin-project/lotus/chain/actors/adt" + "github.com/filecoin-project/lotus/chain/actors/builtin" + "github.com/filecoin-project/lotus/chain/types" +) + +func init() { +{{range .versions}} + builtin.RegisterActorState(builtin{{.}}.MultisigActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { + return load{{.}}(store, root) + }) +{{end}}} + +func Load(store adt.Store, act *types.Actor) (State, error) { + switch act.Code { +{{range .versions}} + case builtin{{.}}.MultisigActorCodeID: + return load{{.}}(store, act.Head) +{{end}} + } + return nil, xerrors.Errorf("unknown actor code %s", act.Code) +} + +type State interface { + cbor.Marshaler + + LockedBalance(epoch abi.ChainEpoch) (abi.TokenAmount, error) + StartEpoch() (abi.ChainEpoch, error) + UnlockDuration() (abi.ChainEpoch, error) + InitialBalance() (abi.TokenAmount, error) + Threshold() (uint64, error) + Signers() ([]address.Address, error) + + ForEachPendingTxn(func(id int64, txn Transaction) error) error + PendingTxnChanged(State) (bool, error) + + transactions() (adt.Map, error) + decodeTransaction(val *cbg.Deferred) (Transaction, error) +} + +type Transaction = msig0.Transaction + +var Methods = builtin{{.latestVersion}}.MethodsMultisig + +func Message(version actors.Version, from address.Address) MessageBuilder { + switch version { +{{range .versions}} + case actors.Version{{.}}: + return message{{.}}{{"{"}}{{if (ge . 2)}}message0{from}{{else}}from{{end}}} +{{end}} default: + panic(fmt.Sprintf("unsupported actors version: %d", version)) + } +} + +type MessageBuilder interface { + // Create a new multisig with the specified parameters. + Create(signers []address.Address, threshold uint64, + vestingStart, vestingDuration abi.ChainEpoch, + initialAmount abi.TokenAmount) (*types.Message, error) + + // Propose a transaction to the given multisig. + Propose(msig, target address.Address, amt abi.TokenAmount, + method abi.MethodNum, params []byte) (*types.Message, error) + + // Approve a multisig transaction. The "hash" is optional. + Approve(msig address.Address, txID uint64, hash *ProposalHashData) (*types.Message, error) + + // Cancel a multisig transaction. The "hash" is optional. + Cancel(msig address.Address, txID uint64, hash *ProposalHashData) (*types.Message, error) +} + +// this type is the same between v0 and v2 +type ProposalHashData = msig{{.latestVersion}}.ProposalHashData +type ProposeReturn = msig{{.latestVersion}}.ProposeReturn +type ProposeParams = msig{{.latestVersion}}.ProposeParams + +func txnParams(id uint64, data *ProposalHashData) ([]byte, error) { + params := msig{{.latestVersion}}.TxnIDParams{ID: msig4.TxnID(id)} + if data != nil { + if data.Requester.Protocol() != address.ID { + return nil, xerrors.Errorf("proposer address must be an ID address, was %s", data.Requester) + } + if data.Value.Sign() == -1 { + return nil, xerrors.Errorf("proposal value must be non-negative, was %s", data.Value) + } + if data.To == address.Undef { + return nil, xerrors.Errorf("proposed destination address must be set") + } + pser, err := data.Serialize() + if err != nil { + return nil, err + } + hash := blake2b.Sum256(pser) + params.ProposalHash = hash[:] + } + + return actors.SerializeParams(¶ms) +} diff --git a/chain/actors/builtin/multisig/message.go b/chain/actors/builtin/multisig/message.go deleted file mode 100644 index 096049002..000000000 --- a/chain/actors/builtin/multisig/message.go +++ /dev/null @@ -1,79 +0,0 @@ -package multisig - -import ( - "fmt" - - "github.com/minio/blake2b-simd" - "golang.org/x/xerrors" - - "github.com/filecoin-project/go-address" - "github.com/filecoin-project/go-state-types/abi" - - builtin4 "github.com/filecoin-project/specs-actors/v4/actors/builtin" - multisig4 "github.com/filecoin-project/specs-actors/v4/actors/builtin/multisig" - - "github.com/filecoin-project/lotus/chain/actors" - "github.com/filecoin-project/lotus/chain/types" -) - -var Methods = builtin4.MethodsMultisig - -func Message(version actors.Version, from address.Address) MessageBuilder { - switch version { - case actors.Version0: - return message0{from} - case actors.Version2: - return message2{message0{from}} - case actors.Version3: - return message3{message0{from}} - case actors.Version4: - return message4{message0{from}} - default: - panic(fmt.Sprintf("unsupported actors version: %d", version)) - } -} - -type MessageBuilder interface { - // Create a new multisig with the specified parameters. - Create(signers []address.Address, threshold uint64, - vestingStart, vestingDuration abi.ChainEpoch, - initialAmount abi.TokenAmount) (*types.Message, error) - - // Propose a transaction to the given multisig. - Propose(msig, target address.Address, amt abi.TokenAmount, - method abi.MethodNum, params []byte) (*types.Message, error) - - // Approve a multisig transaction. The "hash" is optional. - Approve(msig address.Address, txID uint64, hash *ProposalHashData) (*types.Message, error) - - // Cancel a multisig transaction. The "hash" is optional. - Cancel(msig address.Address, txID uint64, hash *ProposalHashData) (*types.Message, error) -} - -// this type is the same between v0 and v2 -type ProposalHashData = multisig4.ProposalHashData -type ProposeReturn = multisig4.ProposeReturn -type ProposeParams = multisig4.ProposeParams - -func txnParams(id uint64, data *ProposalHashData) ([]byte, error) { - params := multisig4.TxnIDParams{ID: multisig4.TxnID(id)} - if data != nil { - if data.Requester.Protocol() != address.ID { - return nil, xerrors.Errorf("proposer address must be an ID address, was %s", data.Requester) - } - if data.Value.Sign() == -1 { - return nil, xerrors.Errorf("proposal value must be non-negative, was %s", data.Value) - } - if data.To == address.Undef { - return nil, xerrors.Errorf("proposed destination address must be set") - } - pser, err := data.Serialize() - if err != nil { - return nil, err - } - hash := blake2b.Sum256(pser) - params.ProposalHash = hash[:] - } - - return actors.SerializeParams(¶ms) -} diff --git a/chain/actors/builtin/multisig/message.go.template b/chain/actors/builtin/multisig/message.go.template new file mode 100644 index 000000000..6bff8983a --- /dev/null +++ b/chain/actors/builtin/multisig/message.go.template @@ -0,0 +1,146 @@ +package multisig + +import ( + "golang.org/x/xerrors" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + + builtin{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/builtin" + init{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/builtin/init" + multisig{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/builtin/multisig" + + "github.com/filecoin-project/lotus/chain/actors" + init_ "github.com/filecoin-project/lotus/chain/actors/builtin/init" + "github.com/filecoin-project/lotus/chain/types" +) + +type message{{.v}} struct{ {{if (ge .v 2)}}message0{{else}}from address.Address{{end}} } + +func (m message{{.v}}) Create( + signers []address.Address, threshold uint64, + unlockStart, unlockDuration abi.ChainEpoch, + initialAmount abi.TokenAmount, +) (*types.Message, error) { + + lenAddrs := uint64(len(signers)) + + if lenAddrs < threshold { + return nil, xerrors.Errorf("cannot require signing of more addresses than provided for multisig") + } + + if threshold == 0 { + threshold = lenAddrs + } + + if m.from == address.Undef { + return nil, xerrors.Errorf("must provide source address") + } +{{if (le .v 1)}} + if unlockStart != 0 { + return nil, xerrors.Errorf("actors v0 does not support a non-zero vesting start time") + } +{{end}} + // Set up constructor parameters for multisig + msigParams := &multisig{{.v}}.ConstructorParams{ + Signers: signers, + NumApprovalsThreshold: threshold, + UnlockDuration: unlockDuration,{{if (ge .v 2)}} + StartEpoch: unlockStart,{{end}} + } + + enc, actErr := actors.SerializeParams(msigParams) + if actErr != nil { + return nil, actErr + } + + // new actors are created by invoking 'exec' on the init actor with the constructor params + execParams := &init{{.v}}.ExecParams{ + CodeCID: builtin{{.v}}.MultisigActorCodeID, + ConstructorParams: enc, + } + + enc, actErr = actors.SerializeParams(execParams) + if actErr != nil { + return nil, actErr + } + + return &types.Message{ + To: init_.Address, + From: m.from, + Method: builtin{{.v}}.MethodsInit.Exec, + Params: enc, + Value: initialAmount, + }, nil +} + +{{if (le .v 1)}} + +func (m message0) Propose(msig, to address.Address, amt abi.TokenAmount, + method abi.MethodNum, params []byte) (*types.Message, error) { + + if msig == address.Undef { + return nil, xerrors.Errorf("must provide a multisig address for proposal") + } + + if to == address.Undef { + return nil, xerrors.Errorf("must provide a target address for proposal") + } + + if amt.Sign() == -1 { + return nil, xerrors.Errorf("must provide a non-negative amount for proposed send") + } + + if m.from == address.Undef { + return nil, xerrors.Errorf("must provide source address") + } + + enc, actErr := actors.SerializeParams(&multisig0.ProposeParams{ + To: to, + Value: amt, + Method: method, + Params: params, + }) + if actErr != nil { + return nil, xerrors.Errorf("failed to serialize parameters: %w", actErr) + } + + return &types.Message{ + To: msig, + From: m.from, + Value: abi.NewTokenAmount(0), + Method: builtin0.MethodsMultisig.Propose, + Params: enc, + }, nil +} + +func (m message0) Approve(msig address.Address, txID uint64, hashData *ProposalHashData) (*types.Message, error) { + enc, err := txnParams(txID, hashData) + if err != nil { + return nil, err + } + + return &types.Message{ + To: msig, + From: m.from, + Value: types.NewInt(0), + Method: builtin0.MethodsMultisig.Approve, + Params: enc, + }, nil +} + +func (m message0) Cancel(msig address.Address, txID uint64, hashData *ProposalHashData) (*types.Message, error) { + enc, err := txnParams(txID, hashData) + if err != nil { + return nil, err + } + + return &types.Message{ + To: msig, + From: m.from, + Value: types.NewInt(0), + Method: builtin0.MethodsMultisig.Cancel, + Params: enc, + }, nil +} +{{end}} diff --git a/chain/actors/builtin/multisig/state.go b/chain/actors/builtin/multisig/multisig.go similarity index 51% rename from chain/actors/builtin/multisig/state.go rename to chain/actors/builtin/multisig/multisig.go index 0ce10d290..79b1a57d7 100644 --- a/chain/actors/builtin/multisig/state.go +++ b/chain/actors/builtin/multisig/multisig.go @@ -1,6 +1,9 @@ package multisig import ( + "fmt" + + "github.com/minio/blake2b-simd" cbg "github.com/whyrusleeping/cbor-gen" "golang.org/x/xerrors" @@ -9,27 +12,37 @@ import ( "github.com/filecoin-project/go-state-types/cbor" "github.com/ipfs/go-cid" - builtin0 "github.com/filecoin-project/specs-actors/actors/builtin" msig0 "github.com/filecoin-project/specs-actors/actors/builtin/multisig" + msig4 "github.com/filecoin-project/specs-actors/v4/actors/builtin/multisig" + + builtin0 "github.com/filecoin-project/specs-actors/actors/builtin" + builtin2 "github.com/filecoin-project/specs-actors/v2/actors/builtin" + builtin3 "github.com/filecoin-project/specs-actors/v3/actors/builtin" + builtin4 "github.com/filecoin-project/specs-actors/v4/actors/builtin" + "github.com/filecoin-project/lotus/chain/actors" "github.com/filecoin-project/lotus/chain/actors/adt" "github.com/filecoin-project/lotus/chain/actors/builtin" "github.com/filecoin-project/lotus/chain/types" ) func init() { + builtin.RegisterActorState(builtin0.MultisigActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load0(store, root) }) + builtin.RegisterActorState(builtin2.MultisigActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load2(store, root) }) + builtin.RegisterActorState(builtin3.MultisigActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load3(store, root) }) + builtin.RegisterActorState(builtin4.MultisigActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load4(store, root) }) @@ -37,14 +50,19 @@ func init() { func Load(store adt.Store, act *types.Actor) (State, error) { switch act.Code { + case builtin0.MultisigActorCodeID: return load0(store, act.Head) + case builtin2.MultisigActorCodeID: return load2(store, act.Head) + case builtin3.MultisigActorCodeID: return load3(store, act.Head) + case builtin4.MultisigActorCodeID: return load4(store, act.Head) + } return nil, xerrors.Errorf("unknown actor code %s", act.Code) } @@ -67,3 +85,69 @@ type State interface { } type Transaction = msig0.Transaction + +var Methods = builtin4.MethodsMultisig + +func Message(version actors.Version, from address.Address) MessageBuilder { + switch version { + + case actors.Version0: + return message0{from} + + case actors.Version2: + return message2{message0{from}} + + case actors.Version3: + return message3{message0{from}} + + case actors.Version4: + return message4{message0{from}} + default: + panic(fmt.Sprintf("unsupported actors version: %d", version)) + } +} + +type MessageBuilder interface { + // Create a new multisig with the specified parameters. + Create(signers []address.Address, threshold uint64, + vestingStart, vestingDuration abi.ChainEpoch, + initialAmount abi.TokenAmount) (*types.Message, error) + + // Propose a transaction to the given multisig. + Propose(msig, target address.Address, amt abi.TokenAmount, + method abi.MethodNum, params []byte) (*types.Message, error) + + // Approve a multisig transaction. The "hash" is optional. + Approve(msig address.Address, txID uint64, hash *ProposalHashData) (*types.Message, error) + + // Cancel a multisig transaction. The "hash" is optional. + Cancel(msig address.Address, txID uint64, hash *ProposalHashData) (*types.Message, error) +} + +// this type is the same between v0 and v2 +type ProposalHashData = msig4.ProposalHashData +type ProposeReturn = msig4.ProposeReturn +type ProposeParams = msig4.ProposeParams + +func txnParams(id uint64, data *ProposalHashData) ([]byte, error) { + params := msig4.TxnIDParams{ID: msig4.TxnID(id)} + if data != nil { + if data.Requester.Protocol() != address.ID { + return nil, xerrors.Errorf("proposer address must be an ID address, was %s", data.Requester) + } + if data.Value.Sign() == -1 { + return nil, xerrors.Errorf("proposal value must be non-negative, was %s", data.Value) + } + if data.To == address.Undef { + return nil, xerrors.Errorf("proposed destination address must be set") + } + pser, err := data.Serialize() + if err != nil { + return nil, err + } + hash := blake2b.Sum256(pser) + params.ProposalHash = hash[:] + } + + return actors.SerializeParams(¶ms) +} diff --git a/chain/actors/builtin/multisig/state.go.template b/chain/actors/builtin/multisig/state.go.template new file mode 100644 index 000000000..2316aadba --- /dev/null +++ b/chain/actors/builtin/multisig/state.go.template @@ -0,0 +1,97 @@ +package multisig + +import ( + "bytes" + "encoding/binary" + + adt{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/util/adt" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + "github.com/ipfs/go-cid" + cbg "github.com/whyrusleeping/cbor-gen" + "golang.org/x/xerrors" + + "github.com/filecoin-project/lotus/chain/actors/adt" + +{{if (ge .v 3)}} + builtin{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/builtin" +{{end}} + msig{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/builtin/multisig" +) + +var _ State = (*state{{.v}})(nil) + +func load{{.v}}(store adt.Store, root cid.Cid) (State, error) { + out := state{{.v}}{store: store} + err := store.Get(store.Context(), root, &out) + if err != nil { + return nil, err + } + return &out, nil +} + +type state{{.v}} struct { + msig{{.v}}.State + store adt.Store +} + +func (s *state{{.v}}) LockedBalance(currEpoch abi.ChainEpoch) (abi.TokenAmount, error) { + return s.State.AmountLocked(currEpoch - s.State.StartEpoch), nil +} + +func (s *state{{.v}}) StartEpoch() (abi.ChainEpoch, error) { + return s.State.StartEpoch, nil +} + +func (s *state{{.v}}) UnlockDuration() (abi.ChainEpoch, error) { + return s.State.UnlockDuration, nil +} + +func (s *state{{.v}}) InitialBalance() (abi.TokenAmount, error) { + return s.State.InitialBalance, nil +} + +func (s *state{{.v}}) Threshold() (uint64, error) { + return s.State.NumApprovalsThreshold, nil +} + +func (s *state{{.v}}) Signers() ([]address.Address, error) { + return s.State.Signers, nil +} + +func (s *state{{.v}}) ForEachPendingTxn(cb func(id int64, txn Transaction) error) error { + arr, err := adt{{.v}}.AsMap(s.store, s.State.PendingTxns{{if (ge .v 3)}}, builtin{{.v}}.DefaultHamtBitwidth{{end}}) + if err != nil { + return err + } + var out msig{{.v}}.Transaction + return arr.ForEach(&out, func(key string) error { + txid, n := binary.Varint([]byte(key)) + if n <= 0 { + return xerrors.Errorf("invalid pending transaction key: %v", key) + } + return cb(txid, (Transaction)(out)) //nolint:unconvert + }) +} + +func (s *state{{.v}}) PendingTxnChanged(other State) (bool, error) { + other{{.v}}, ok := other.(*state{{.v}}) + if !ok { + // treat an upgrade as a change, always + return true, nil + } + return !s.State.PendingTxns.Equals(other{{.v}}.PendingTxns), nil +} + +func (s *state{{.v}}) transactions() (adt.Map, error) { + return adt{{.v}}.AsMap(s.store, s.PendingTxns{{if (ge .v 3)}}, builtin{{.v}}.DefaultHamtBitwidth{{end}}) +} + +func (s *state{{.v}}) decodeTransaction(val *cbg.Deferred) (Transaction, error) { + var tx msig{{.v}}.Transaction + if err := tx.UnmarshalCBOR(bytes.NewReader(val.Raw)); err != nil { + return Transaction{}, err + } + return tx, nil +} diff --git a/chain/actors/builtin/multisig/state0.go b/chain/actors/builtin/multisig/v0.go similarity index 95% rename from chain/actors/builtin/multisig/state0.go rename to chain/actors/builtin/multisig/v0.go index 27dd5c413..20c1557b0 100644 --- a/chain/actors/builtin/multisig/state0.go +++ b/chain/actors/builtin/multisig/v0.go @@ -4,6 +4,8 @@ import ( "bytes" "encoding/binary" + adt0 "github.com/filecoin-project/specs-actors/actors/util/adt" + "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/abi" "github.com/ipfs/go-cid" @@ -13,8 +15,6 @@ import ( "github.com/filecoin-project/lotus/chain/actors/adt" msig0 "github.com/filecoin-project/specs-actors/actors/builtin/multisig" - multisig0 "github.com/filecoin-project/specs-actors/actors/builtin/multisig" - adt0 "github.com/filecoin-project/specs-actors/actors/util/adt" ) var _ State = (*state0)(nil) @@ -86,7 +86,7 @@ func (s *state0) transactions() (adt.Map, error) { } func (s *state0) decodeTransaction(val *cbg.Deferred) (Transaction, error) { - var tx multisig0.Transaction + var tx msig0.Transaction if err := tx.UnmarshalCBOR(bytes.NewReader(val.Raw)); err != nil { return Transaction{}, err } diff --git a/chain/actors/builtin/multisig/state2.go b/chain/actors/builtin/multisig/v2.go similarity index 99% rename from chain/actors/builtin/multisig/state2.go rename to chain/actors/builtin/multisig/v2.go index d637abb91..ef317f903 100644 --- a/chain/actors/builtin/multisig/state2.go +++ b/chain/actors/builtin/multisig/v2.go @@ -4,6 +4,8 @@ import ( "bytes" "encoding/binary" + adt2 "github.com/filecoin-project/specs-actors/v2/actors/util/adt" + "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/abi" "github.com/ipfs/go-cid" @@ -13,7 +15,6 @@ import ( "github.com/filecoin-project/lotus/chain/actors/adt" msig2 "github.com/filecoin-project/specs-actors/v2/actors/builtin/multisig" - adt2 "github.com/filecoin-project/specs-actors/v2/actors/util/adt" ) var _ State = (*state2)(nil) diff --git a/chain/actors/builtin/multisig/state3.go b/chain/actors/builtin/multisig/v3.go similarity index 96% rename from chain/actors/builtin/multisig/state3.go rename to chain/actors/builtin/multisig/v3.go index a2eb1d909..8834e4553 100644 --- a/chain/actors/builtin/multisig/state3.go +++ b/chain/actors/builtin/multisig/v3.go @@ -15,6 +15,7 @@ import ( "github.com/filecoin-project/lotus/chain/actors/adt" builtin3 "github.com/filecoin-project/specs-actors/v3/actors/builtin" + msig3 "github.com/filecoin-project/specs-actors/v3/actors/builtin/multisig" ) @@ -74,12 +75,12 @@ func (s *state3) ForEachPendingTxn(cb func(id int64, txn Transaction) error) err } func (s *state3) PendingTxnChanged(other State) (bool, error) { - other2, ok := other.(*state3) + other3, ok := other.(*state3) if !ok { // treat an upgrade as a change, always return true, nil } - return !s.State.PendingTxns.Equals(other2.PendingTxns), nil + return !s.State.PendingTxns.Equals(other3.PendingTxns), nil } func (s *state3) transactions() (adt.Map, error) { diff --git a/chain/actors/builtin/multisig/state4.go b/chain/actors/builtin/multisig/v4.go similarity index 93% rename from chain/actors/builtin/multisig/state4.go rename to chain/actors/builtin/multisig/v4.go index 3475ad361..9f9dc7573 100644 --- a/chain/actors/builtin/multisig/state4.go +++ b/chain/actors/builtin/multisig/v4.go @@ -15,6 +15,7 @@ import ( "github.com/filecoin-project/lotus/chain/actors/adt" builtin4 "github.com/filecoin-project/specs-actors/v4/actors/builtin" + msig4 "github.com/filecoin-project/specs-actors/v4/actors/builtin/multisig" ) @@ -69,17 +70,17 @@ func (s *state4) ForEachPendingTxn(cb func(id int64, txn Transaction) error) err if n <= 0 { return xerrors.Errorf("invalid pending transaction key: %v", key) } - return cb(txid, (Transaction)(out)) + return cb(txid, (Transaction)(out)) //nolint:unconvert }) } func (s *state4) PendingTxnChanged(other State) (bool, error) { - other2, ok := other.(*state4) + other4, ok := other.(*state4) if !ok { // treat an upgrade as a change, always return true, nil } - return !s.State.PendingTxns.Equals(other2.PendingTxns), nil + return !s.State.PendingTxns.Equals(other4.PendingTxns), nil } func (s *state4) transactions() (adt.Map, error) { diff --git a/chain/actors/builtin/paych/actor.go.template b/chain/actors/builtin/paych/actor.go.template new file mode 100644 index 000000000..3f68a5cfa --- /dev/null +++ b/chain/actors/builtin/paych/actor.go.template @@ -0,0 +1,109 @@ +package paych + +import ( + "encoding/base64" + "fmt" + + "golang.org/x/xerrors" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + big "github.com/filecoin-project/go-state-types/big" + "github.com/filecoin-project/go-state-types/cbor" + "github.com/ipfs/go-cid" + ipldcbor "github.com/ipfs/go-ipld-cbor" + + paych0 "github.com/filecoin-project/specs-actors/actors/builtin/paych" +{{range .versions}} + builtin{{.}} "github.com/filecoin-project/specs-actors{{import .}}actors/builtin" +{{end}} + + "github.com/filecoin-project/lotus/chain/actors" + "github.com/filecoin-project/lotus/chain/actors/adt" + "github.com/filecoin-project/lotus/chain/actors/builtin" + "github.com/filecoin-project/lotus/chain/types" +) + +func init() { +{{range .versions}} + builtin.RegisterActorState(builtin{{.}}.PaymentChannelActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { + return load{{.}}(store, root) + }) +{{end}}} + +// Load returns an abstract copy of payment channel state, irregardless of actor version +func Load(store adt.Store, act *types.Actor) (State, error) { + switch act.Code { +{{range .versions}} + case builtin{{.}}.PaymentChannelActorCodeID: + return load{{.}}(store, act.Head) +{{end}} + } + return nil, xerrors.Errorf("unknown actor code %s", act.Code) +} + +// State is an abstract version of payment channel state that works across +// versions +type State interface { + cbor.Marshaler + // Channel owner, who has funded the actor + From() (address.Address, error) + // Recipient of payouts from channel + To() (address.Address, error) + + // Height at which the channel can be `Collected` + SettlingAt() (abi.ChainEpoch, error) + + // Amount successfully redeemed through the payment channel, paid out on `Collect()` + ToSend() (abi.TokenAmount, error) + + // Get total number of lanes + LaneCount() (uint64, error) + + // Iterate lane states + ForEachLaneState(cb func(idx uint64, dl LaneState) error) error +} + +// LaneState is an abstract copy of the state of a single lane +type LaneState interface { + Redeemed() (big.Int, error) + Nonce() (uint64, error) +} + +type SignedVoucher = paych0.SignedVoucher +type ModVerifyParams = paych0.ModVerifyParams + +// DecodeSignedVoucher decodes base64 encoded signed voucher. +func DecodeSignedVoucher(s string) (*SignedVoucher, error) { + data, err := base64.RawURLEncoding.DecodeString(s) + if err != nil { + return nil, err + } + + var sv SignedVoucher + if err := ipldcbor.DecodeInto(data, &sv); err != nil { + return nil, err + } + + return &sv, nil +} + +var Methods = builtin{{.latestVersion}}.MethodsPaych + +func Message(version actors.Version, from address.Address) MessageBuilder { + switch version { +{{range .versions}} + case actors.Version{{.}}: + return message{{.}}{from} +{{end}} + default: + panic(fmt.Sprintf("unsupported actors version: %d", version)) + } +} + +type MessageBuilder interface { + Create(to address.Address, initialAmount abi.TokenAmount) (*types.Message, error) + Update(paych address.Address, voucher *SignedVoucher, secret []byte) (*types.Message, error) + Settle(paych address.Address) (*types.Message, error) + Collect(paych address.Address) (*types.Message, error) +} diff --git a/chain/actors/builtin/paych/message.go b/chain/actors/builtin/paych/message.go deleted file mode 100644 index 6669cd227..000000000 --- a/chain/actors/builtin/paych/message.go +++ /dev/null @@ -1,36 +0,0 @@ -package paych - -import ( - "fmt" - - "github.com/filecoin-project/go-address" - "github.com/filecoin-project/go-state-types/abi" - "github.com/filecoin-project/lotus/chain/actors" - "github.com/filecoin-project/lotus/chain/types" - - builtin4 "github.com/filecoin-project/specs-actors/v4/actors/builtin" -) - -var Methods = builtin4.MethodsPaych - -func Message(version actors.Version, from address.Address) MessageBuilder { - switch version { - case actors.Version0: - return message0{from} - case actors.Version2: - return message2{from} - case actors.Version3: - return message3{from} - case actors.Version4: - return message4{from} - default: - panic(fmt.Sprintf("unsupported actors version: %d", version)) - } -} - -type MessageBuilder interface { - Create(to address.Address, initialAmount abi.TokenAmount) (*types.Message, error) - Update(paych address.Address, voucher *SignedVoucher, secret []byte) (*types.Message, error) - Settle(paych address.Address) (*types.Message, error) - Collect(paych address.Address) (*types.Message, error) -} diff --git a/chain/actors/builtin/paych/message.go.template b/chain/actors/builtin/paych/message.go.template new file mode 100644 index 000000000..4a5ea2331 --- /dev/null +++ b/chain/actors/builtin/paych/message.go.template @@ -0,0 +1,74 @@ +package paych + +import ( + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + + builtin{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/builtin" + init{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/builtin/init" + paych{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/builtin/paych" + + "github.com/filecoin-project/lotus/chain/actors" + init_ "github.com/filecoin-project/lotus/chain/actors/builtin/init" + "github.com/filecoin-project/lotus/chain/types" +) + +type message{{.v}} struct{ from address.Address } + +func (m message{{.v}}) Create(to address.Address, initialAmount abi.TokenAmount) (*types.Message, error) { + params, aerr := actors.SerializeParams(&paych{{.v}}.ConstructorParams{From: m.from, To: to}) + if aerr != nil { + return nil, aerr + } + enc, aerr := actors.SerializeParams(&init{{.v}}.ExecParams{ + CodeCID: builtin{{.v}}.PaymentChannelActorCodeID, + ConstructorParams: params, + }) + if aerr != nil { + return nil, aerr + } + + return &types.Message{ + To: init_.Address, + From: m.from, + Value: initialAmount, + Method: builtin{{.v}}.MethodsInit.Exec, + Params: enc, + }, nil +} + +func (m message{{.v}}) Update(paych address.Address, sv *SignedVoucher, secret []byte) (*types.Message, error) { + params, aerr := actors.SerializeParams(&paych{{.v}}.UpdateChannelStateParams{ + Sv: *sv, + Secret: secret, + }) + if aerr != nil { + return nil, aerr + } + + return &types.Message{ + To: paych, + From: m.from, + Value: abi.NewTokenAmount(0), + Method: builtin{{.v}}.MethodsPaych.UpdateChannelState, + Params: params, + }, nil +} + +func (m message{{.v}}) Settle(paych address.Address) (*types.Message, error) { + return &types.Message{ + To: paych, + From: m.from, + Value: abi.NewTokenAmount(0), + Method: builtin{{.v}}.MethodsPaych.Settle, + }, nil +} + +func (m message{{.v}}) Collect(paych address.Address) (*types.Message, error) { + return &types.Message{ + To: paych, + From: m.from, + Value: abi.NewTokenAmount(0), + Method: builtin{{.v}}.MethodsPaych.Collect, + }, nil +} diff --git a/chain/actors/builtin/paych/state.go b/chain/actors/builtin/paych/paych.go similarity index 80% rename from chain/actors/builtin/paych/state.go rename to chain/actors/builtin/paych/paych.go index f28dc2f83..30e4408d8 100644 --- a/chain/actors/builtin/paych/state.go +++ b/chain/actors/builtin/paych/paych.go @@ -2,6 +2,7 @@ package paych import ( "encoding/base64" + "fmt" "golang.org/x/xerrors" @@ -12,27 +13,36 @@ import ( "github.com/ipfs/go-cid" ipldcbor "github.com/ipfs/go-ipld-cbor" - builtin0 "github.com/filecoin-project/specs-actors/actors/builtin" paych0 "github.com/filecoin-project/specs-actors/actors/builtin/paych" + + builtin0 "github.com/filecoin-project/specs-actors/actors/builtin" + builtin2 "github.com/filecoin-project/specs-actors/v2/actors/builtin" + builtin3 "github.com/filecoin-project/specs-actors/v3/actors/builtin" + builtin4 "github.com/filecoin-project/specs-actors/v4/actors/builtin" + "github.com/filecoin-project/lotus/chain/actors" "github.com/filecoin-project/lotus/chain/actors/adt" "github.com/filecoin-project/lotus/chain/actors/builtin" "github.com/filecoin-project/lotus/chain/types" ) func init() { + builtin.RegisterActorState(builtin0.PaymentChannelActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load0(store, root) }) + builtin.RegisterActorState(builtin2.PaymentChannelActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load2(store, root) }) + builtin.RegisterActorState(builtin3.PaymentChannelActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load3(store, root) }) + builtin.RegisterActorState(builtin4.PaymentChannelActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load4(store, root) }) @@ -41,14 +51,19 @@ func init() { // Load returns an abstract copy of payment channel state, irregardless of actor version func Load(store adt.Store, act *types.Actor) (State, error) { switch act.Code { + case builtin0.PaymentChannelActorCodeID: return load0(store, act.Head) + case builtin2.PaymentChannelActorCodeID: return load2(store, act.Head) + case builtin3.PaymentChannelActorCodeID: return load3(store, act.Head) + case builtin4.PaymentChannelActorCodeID: return load4(store, act.Head) + } return nil, xerrors.Errorf("unknown actor code %s", act.Code) } @@ -98,3 +113,32 @@ func DecodeSignedVoucher(s string) (*SignedVoucher, error) { return &sv, nil } + +var Methods = builtin4.MethodsPaych + +func Message(version actors.Version, from address.Address) MessageBuilder { + switch version { + + case actors.Version0: + return message0{from} + + case actors.Version2: + return message2{from} + + case actors.Version3: + return message3{from} + + case actors.Version4: + return message4{from} + + default: + panic(fmt.Sprintf("unsupported actors version: %d", version)) + } +} + +type MessageBuilder interface { + Create(to address.Address, initialAmount abi.TokenAmount) (*types.Message, error) + Update(paych address.Address, voucher *SignedVoucher, secret []byte) (*types.Message, error) + Settle(paych address.Address) (*types.Message, error) + Collect(paych address.Address) (*types.Message, error) +} diff --git a/chain/actors/builtin/paych/state.go.template b/chain/actors/builtin/paych/state.go.template new file mode 100644 index 000000000..b4b575a3e --- /dev/null +++ b/chain/actors/builtin/paych/state.go.template @@ -0,0 +1,104 @@ +package paych + +import ( + "github.com/ipfs/go-cid" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/big" + + "github.com/filecoin-project/lotus/chain/actors/adt" + + paych{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/builtin/paych" + adt{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/util/adt" +) + +var _ State = (*state{{.v}})(nil) + +func load{{.v}}(store adt.Store, root cid.Cid) (State, error) { + out := state{{.v}}{store: store} + err := store.Get(store.Context(), root, &out) + if err != nil { + return nil, err + } + return &out, nil +} + +type state{{.v}} struct { + paych{{.v}}.State + store adt.Store + lsAmt *adt{{.v}}.Array +} + +// Channel owner, who has funded the actor +func (s *state{{.v}}) From() (address.Address, error) { + return s.State.From, nil +} + +// Recipient of payouts from channel +func (s *state{{.v}}) To() (address.Address, error) { + return s.State.To, nil +} + +// Height at which the channel can be `Collected` +func (s *state{{.v}}) SettlingAt() (abi.ChainEpoch, error) { + return s.State.SettlingAt, nil +} + +// Amount successfully redeemed through the payment channel, paid out on `Collect()` +func (s *state{{.v}}) ToSend() (abi.TokenAmount, error) { + return s.State.ToSend, nil +} + +func (s *state{{.v}}) getOrLoadLsAmt() (*adt{{.v}}.Array, error) { + if s.lsAmt != nil { + return s.lsAmt, nil + } + + // Get the lane state from the chain + lsamt, err := adt{{.v}}.AsArray(s.store, s.State.LaneStates{{if (ge .v 3)}}, paych{{.v}}.LaneStatesAmtBitwidth{{end}}) + if err != nil { + return nil, err + } + + s.lsAmt = lsamt + return lsamt, nil +} + +// Get total number of lanes +func (s *state{{.v}}) LaneCount() (uint64, error) { + lsamt, err := s.getOrLoadLsAmt() + if err != nil { + return 0, err + } + return lsamt.Length(), nil +} + +// Iterate lane states +func (s *state{{.v}}) ForEachLaneState(cb func(idx uint64, dl LaneState) error) error { + // Get the lane state from the chain + lsamt, err := s.getOrLoadLsAmt() + if err != nil { + return err + } + + // Note: we use a map instead of an array to store laneStates because the + // client sets the lane ID (the index) and potentially they could use a + // very large index. + var ls paych{{.v}}.LaneState + return lsamt.ForEach(&ls, func(i int64) error { + return cb(uint64(i), &laneState{{.v}}{ls}) + }) +} + +type laneState{{.v}} struct { + paych{{.v}}.LaneState +} + +func (ls *laneState{{.v}}) Redeemed() (big.Int, error) { + return ls.LaneState.Redeemed, nil +} + +func (ls *laneState{{.v}}) Nonce() (uint64, error) { + return ls.LaneState.Nonce, nil +} diff --git a/chain/actors/builtin/paych/state0.go b/chain/actors/builtin/paych/v0.go similarity index 100% rename from chain/actors/builtin/paych/state0.go rename to chain/actors/builtin/paych/v0.go diff --git a/chain/actors/builtin/paych/state2.go b/chain/actors/builtin/paych/v2.go similarity index 100% rename from chain/actors/builtin/paych/state2.go rename to chain/actors/builtin/paych/v2.go diff --git a/chain/actors/builtin/paych/state3.go b/chain/actors/builtin/paych/v3.go similarity index 100% rename from chain/actors/builtin/paych/state3.go rename to chain/actors/builtin/paych/v3.go diff --git a/chain/actors/builtin/paych/state4.go b/chain/actors/builtin/paych/v4.go similarity index 100% rename from chain/actors/builtin/paych/state4.go rename to chain/actors/builtin/paych/v4.go diff --git a/chain/actors/builtin/power/actor.go.template b/chain/actors/builtin/power/actor.go.template new file mode 100644 index 000000000..82f791e58 --- /dev/null +++ b/chain/actors/builtin/power/actor.go.template @@ -0,0 +1,78 @@ +package power + +import ( + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/big" + "github.com/ipfs/go-cid" + cbg "github.com/whyrusleeping/cbor-gen" + "golang.org/x/xerrors" + + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/cbor" + + "github.com/filecoin-project/lotus/chain/actors/adt" + "github.com/filecoin-project/lotus/chain/actors/builtin" + "github.com/filecoin-project/lotus/chain/types" +{{range .versions}} + builtin{{.}} "github.com/filecoin-project/specs-actors{{import .}}actors/builtin" +{{end}} +) + +func init() { +{{range .versions}} + builtin.RegisterActorState(builtin{{.}}.StoragePowerActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { + return load{{.}}(store, root) + }) +{{end}}} + +var ( + Address = builtin{{.latestVersion}}.StoragePowerActorAddr + Methods = builtin{{.latestVersion}}.MethodsPower +) + +func Load(store adt.Store, act *types.Actor) (State, error) { + switch act.Code { +{{range .versions}} + case builtin{{.}}.StoragePowerActorCodeID: + return load{{.}}(store, act.Head) +{{end}} + } + return nil, xerrors.Errorf("unknown actor code %s", act.Code) +} + +type State interface { + cbor.Marshaler + + TotalLocked() (abi.TokenAmount, error) + TotalPower() (Claim, error) + TotalCommitted() (Claim, error) + TotalPowerSmoothed() (builtin.FilterEstimate, error) + + // MinerCounts returns the number of miners. Participating is the number + // with power above the minimum miner threshold. + MinerCounts() (participating, total uint64, err error) + MinerPower(address.Address) (Claim, bool, error) + MinerNominalPowerMeetsConsensusMinimum(address.Address) (bool, error) + ListAllMiners() ([]address.Address, error) + ForEachClaim(func(miner address.Address, claim Claim) error) error + ClaimsChanged(State) (bool, error) + + // Diff helpers. Used by Diff* functions internally. + claims() (adt.Map, error) + decodeClaim(*cbg.Deferred) (Claim, error) +} + +type Claim struct { + // Sum of raw byte power for a miner's sectors. + RawBytePower abi.StoragePower + + // Sum of quality adjusted power for a miner's sectors. + QualityAdjPower abi.StoragePower +} + +func AddClaims(a Claim, b Claim) Claim { + return Claim{ + RawBytePower: big.Add(a.RawBytePower, b.RawBytePower), + QualityAdjPower: big.Add(a.QualityAdjPower, b.QualityAdjPower), + } +} diff --git a/chain/actors/builtin/power/power.go b/chain/actors/builtin/power/power.go index 7e15275a5..bf530a21a 100644 --- a/chain/actors/builtin/power/power.go +++ b/chain/actors/builtin/power/power.go @@ -15,21 +15,28 @@ import ( "github.com/filecoin-project/lotus/chain/types" builtin0 "github.com/filecoin-project/specs-actors/actors/builtin" + builtin2 "github.com/filecoin-project/specs-actors/v2/actors/builtin" + builtin3 "github.com/filecoin-project/specs-actors/v3/actors/builtin" + builtin4 "github.com/filecoin-project/specs-actors/v4/actors/builtin" ) func init() { + builtin.RegisterActorState(builtin0.StoragePowerActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load0(store, root) }) + builtin.RegisterActorState(builtin2.StoragePowerActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load2(store, root) }) + builtin.RegisterActorState(builtin3.StoragePowerActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load3(store, root) }) + builtin.RegisterActorState(builtin4.StoragePowerActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load4(store, root) }) @@ -40,16 +47,21 @@ var ( Methods = builtin4.MethodsPower ) -func Load(store adt.Store, act *types.Actor) (st State, err error) { +func Load(store adt.Store, act *types.Actor) (State, error) { switch act.Code { + case builtin0.StoragePowerActorCodeID: return load0(store, act.Head) + case builtin2.StoragePowerActorCodeID: return load2(store, act.Head) + case builtin3.StoragePowerActorCodeID: return load3(store, act.Head) + case builtin4.StoragePowerActorCodeID: return load4(store, act.Head) + } return nil, xerrors.Errorf("unknown actor code %s", act.Code) } diff --git a/chain/actors/builtin/power/state.go.template b/chain/actors/builtin/power/state.go.template new file mode 100644 index 000000000..4cb904a1d --- /dev/null +++ b/chain/actors/builtin/power/state.go.template @@ -0,0 +1,151 @@ +package power + +import ( + "bytes" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + "github.com/ipfs/go-cid" + cbg "github.com/whyrusleeping/cbor-gen" + + "github.com/filecoin-project/lotus/chain/actors/adt" + "github.com/filecoin-project/lotus/chain/actors/builtin" + +{{if (ge .v 3)}} + builtin{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/builtin" +{{end}} + power{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/builtin/power" + adt{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/util/adt" +) + +var _ State = (*state{{.v}})(nil) + +func load{{.v}}(store adt.Store, root cid.Cid) (State, error) { + out := state{{.v}}{store: store} + err := store.Get(store.Context(), root, &out) + if err != nil { + return nil, err + } + return &out, nil +} + +type state{{.v}} struct { + power{{.v}}.State + store adt.Store +} + +func (s *state{{.v}}) TotalLocked() (abi.TokenAmount, error) { + return s.TotalPledgeCollateral, nil +} + +func (s *state{{.v}}) TotalPower() (Claim, error) { + return Claim{ + RawBytePower: s.TotalRawBytePower, + QualityAdjPower: s.TotalQualityAdjPower, + }, nil +} + +// Committed power to the network. Includes miners below the minimum threshold. +func (s *state{{.v}}) TotalCommitted() (Claim, error) { + return Claim{ + RawBytePower: s.TotalBytesCommitted, + QualityAdjPower: s.TotalQABytesCommitted, + }, nil +} + +func (s *state{{.v}}) MinerPower(addr address.Address) (Claim, bool, error) { + claims, err := s.claims() + if err != nil { + return Claim{}, false, err + } + var claim power{{.v}}.Claim + ok, err := claims.Get(abi.AddrKey(addr), &claim) + if err != nil { + return Claim{}, false, err + } + return Claim{ + RawBytePower: claim.RawBytePower, + QualityAdjPower: claim.QualityAdjPower, + }, ok, nil +} + +func (s *state{{.v}}) MinerNominalPowerMeetsConsensusMinimum(a address.Address) (bool, error) { + return s.State.MinerNominalPowerMeetsConsensusMinimum(s.store, a) +} + +func (s *state{{.v}}) TotalPowerSmoothed() (builtin.FilterEstimate, error) { + return builtin.FromV{{.v}}FilterEstimate({{if (le .v 1)}}*{{end}}s.State.ThisEpochQAPowerSmoothed), nil +} + +func (s *state{{.v}}) MinerCounts() (uint64, uint64, error) { + return uint64(s.State.MinerAboveMinPowerCount), uint64(s.State.MinerCount), nil +} + +func (s *state{{.v}}) ListAllMiners() ([]address.Address, error) { + claims, err := s.claims() + if err != nil { + return nil, err + } + + var miners []address.Address + err = claims.ForEach(nil, func(k string) error { + a, err := address.NewFromBytes([]byte(k)) + if err != nil { + return err + } + miners = append(miners, a) + return nil + }) + if err != nil { + return nil, err + } + + return miners, nil +} + +func (s *state{{.v}}) ForEachClaim(cb func(miner address.Address, claim Claim) error) error { + claims, err := s.claims() + if err != nil { + return err + } + + var claim power{{.v}}.Claim + return claims.ForEach(&claim, func(k string) error { + a, err := address.NewFromBytes([]byte(k)) + if err != nil { + return err + } + return cb(a, Claim{ + RawBytePower: claim.RawBytePower, + QualityAdjPower: claim.QualityAdjPower, + }) + }) +} + +func (s *state{{.v}}) ClaimsChanged(other State) (bool, error) { + other{{.v}}, ok := other.(*state{{.v}}) + if !ok { + // treat an upgrade as a change, always + return true, nil + } + return !s.State.Claims.Equals(other{{.v}}.State.Claims), nil +} + +func (s *state{{.v}}) claims() (adt.Map, error) { + return adt{{.v}}.AsMap(s.store, s.Claims{{if (ge .v 3)}}, builtin{{.v}}.DefaultHamtBitwidth{{end}}) +} + +func (s *state{{.v}}) decodeClaim(val *cbg.Deferred) (Claim, error) { + var ci power{{.v}}.Claim + if err := ci.UnmarshalCBOR(bytes.NewReader(val.Raw)); err != nil { + return Claim{}, err + } + return fromV{{.v}}Claim(ci), nil +} + +func fromV{{.v}}Claim(v{{.v}} power{{.v}}.Claim) Claim { + return Claim{ + RawBytePower: v{{.v}}.RawBytePower, + QualityAdjPower: v{{.v}}.QualityAdjPower, + } +} diff --git a/chain/actors/builtin/power/v0.go b/chain/actors/builtin/power/v0.go index 7636b612b..91fad8c57 100644 --- a/chain/actors/builtin/power/v0.go +++ b/chain/actors/builtin/power/v0.go @@ -51,7 +51,7 @@ func (s *state0) TotalCommitted() (Claim, error) { } func (s *state0) MinerPower(addr address.Address) (Claim, bool, error) { - claims, err := adt0.AsMap(s.store, s.Claims) + claims, err := s.claims() if err != nil { return Claim{}, false, err } @@ -79,7 +79,7 @@ func (s *state0) MinerCounts() (uint64, uint64, error) { } func (s *state0) ListAllMiners() ([]address.Address, error) { - claims, err := adt0.AsMap(s.store, s.Claims) + claims, err := s.claims() if err != nil { return nil, err } @@ -101,7 +101,7 @@ func (s *state0) ListAllMiners() ([]address.Address, error) { } func (s *state0) ForEachClaim(cb func(miner address.Address, claim Claim) error) error { - claims, err := adt0.AsMap(s.store, s.Claims) + claims, err := s.claims() if err != nil { return err } @@ -141,5 +141,8 @@ func (s *state0) decodeClaim(val *cbg.Deferred) (Claim, error) { } func fromV0Claim(v0 power0.Claim) Claim { - return (Claim)(v0) + return Claim{ + RawBytePower: v0.RawBytePower, + QualityAdjPower: v0.QualityAdjPower, + } } diff --git a/chain/actors/builtin/power/v2.go b/chain/actors/builtin/power/v2.go index 012dc2a4f..313160a78 100644 --- a/chain/actors/builtin/power/v2.go +++ b/chain/actors/builtin/power/v2.go @@ -51,7 +51,7 @@ func (s *state2) TotalCommitted() (Claim, error) { } func (s *state2) MinerPower(addr address.Address) (Claim, bool, error) { - claims, err := adt2.AsMap(s.store, s.Claims) + claims, err := s.claims() if err != nil { return Claim{}, false, err } @@ -79,7 +79,7 @@ func (s *state2) MinerCounts() (uint64, uint64, error) { } func (s *state2) ListAllMiners() ([]address.Address, error) { - claims, err := adt2.AsMap(s.store, s.Claims) + claims, err := s.claims() if err != nil { return nil, err } @@ -101,7 +101,7 @@ func (s *state2) ListAllMiners() ([]address.Address, error) { } func (s *state2) ForEachClaim(cb func(miner address.Address, claim Claim) error) error { - claims, err := adt2.AsMap(s.store, s.Claims) + claims, err := s.claims() if err != nil { return err } diff --git a/chain/actors/builtin/power/v3.go b/chain/actors/builtin/power/v3.go index fd161dda5..2ef1e2808 100644 --- a/chain/actors/builtin/power/v3.go +++ b/chain/actors/builtin/power/v3.go @@ -12,6 +12,7 @@ import ( "github.com/filecoin-project/lotus/chain/actors/builtin" builtin3 "github.com/filecoin-project/specs-actors/v3/actors/builtin" + power3 "github.com/filecoin-project/specs-actors/v3/actors/builtin/power" adt3 "github.com/filecoin-project/specs-actors/v3/actors/util/adt" ) @@ -121,12 +122,12 @@ func (s *state3) ForEachClaim(cb func(miner address.Address, claim Claim) error) } func (s *state3) ClaimsChanged(other State) (bool, error) { - other2, ok := other.(*state3) + other3, ok := other.(*state3) if !ok { // treat an upgrade as a change, always return true, nil } - return !s.State.Claims.Equals(other2.State.Claims), nil + return !s.State.Claims.Equals(other3.State.Claims), nil } func (s *state3) claims() (adt.Map, error) { diff --git a/chain/actors/builtin/power/v4.go b/chain/actors/builtin/power/v4.go index bae5d044e..686550456 100644 --- a/chain/actors/builtin/power/v4.go +++ b/chain/actors/builtin/power/v4.go @@ -12,6 +12,7 @@ import ( "github.com/filecoin-project/lotus/chain/actors/builtin" builtin4 "github.com/filecoin-project/specs-actors/v4/actors/builtin" + power4 "github.com/filecoin-project/specs-actors/v4/actors/builtin/power" adt4 "github.com/filecoin-project/specs-actors/v4/actors/util/adt" ) @@ -121,12 +122,12 @@ func (s *state4) ForEachClaim(cb func(miner address.Address, claim Claim) error) } func (s *state4) ClaimsChanged(other State) (bool, error) { - other2, ok := other.(*state4) + other4, ok := other.(*state4) if !ok { // treat an upgrade as a change, always return true, nil } - return !s.State.Claims.Equals(other2.State.Claims), nil + return !s.State.Claims.Equals(other4.State.Claims), nil } func (s *state4) claims() (adt.Map, error) { diff --git a/chain/actors/builtin/reward/actor.go.template b/chain/actors/builtin/reward/actor.go.template new file mode 100644 index 000000000..81437d26f --- /dev/null +++ b/chain/actors/builtin/reward/actor.go.template @@ -0,0 +1,60 @@ +package reward + +import ( + "github.com/filecoin-project/go-state-types/abi" + reward0 "github.com/filecoin-project/specs-actors/actors/builtin/reward" + "github.com/ipfs/go-cid" + "golang.org/x/xerrors" + + "github.com/filecoin-project/go-state-types/cbor" +{{range .versions}} + builtin{{.}} "github.com/filecoin-project/specs-actors{{import .}}actors/builtin" +{{end}} + + "github.com/filecoin-project/lotus/chain/actors/adt" + "github.com/filecoin-project/lotus/chain/actors/builtin" + "github.com/filecoin-project/lotus/chain/types" +) + +func init() { +{{range .versions}} + builtin.RegisterActorState(builtin{{.}}.RewardActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { + return load{{.}}(store, root) + }) +{{end}}} + +var ( + Address = builtin{{.latestVersion}}.RewardActorAddr + Methods = builtin{{.latestVersion}}.MethodsReward +) + +func Load(store adt.Store, act *types.Actor) (State, error) { + switch act.Code { +{{range .versions}} + case builtin{{.}}.RewardActorCodeID: + return load{{.}}(store, act.Head) +{{end}} + } + return nil, xerrors.Errorf("unknown actor code %s", act.Code) +} + +type State interface { + cbor.Marshaler + + ThisEpochBaselinePower() (abi.StoragePower, error) + ThisEpochReward() (abi.StoragePower, error) + ThisEpochRewardSmoothed() (builtin.FilterEstimate, error) + + EffectiveBaselinePower() (abi.StoragePower, error) + EffectiveNetworkTime() (abi.ChainEpoch, error) + + TotalStoragePowerReward() (abi.TokenAmount, error) + + CumsumBaseline() (abi.StoragePower, error) + CumsumRealized() (abi.StoragePower, error) + + InitialPledgeForPower(abi.StoragePower, abi.TokenAmount, *builtin.FilterEstimate, abi.TokenAmount) (abi.TokenAmount, error) + PreCommitDepositForPower(builtin.FilterEstimate, abi.StoragePower) (abi.TokenAmount, error) +} + +type AwardBlockRewardParams = reward0.AwardBlockRewardParams diff --git a/chain/actors/builtin/reward/reward.go b/chain/actors/builtin/reward/reward.go index cfcd68dd5..1037cf741 100644 --- a/chain/actors/builtin/reward/reward.go +++ b/chain/actors/builtin/reward/reward.go @@ -7,9 +7,13 @@ import ( "golang.org/x/xerrors" "github.com/filecoin-project/go-state-types/cbor" + builtin0 "github.com/filecoin-project/specs-actors/actors/builtin" + builtin2 "github.com/filecoin-project/specs-actors/v2/actors/builtin" + builtin3 "github.com/filecoin-project/specs-actors/v3/actors/builtin" + builtin4 "github.com/filecoin-project/specs-actors/v4/actors/builtin" "github.com/filecoin-project/lotus/chain/actors/adt" @@ -18,15 +22,19 @@ import ( ) func init() { + builtin.RegisterActorState(builtin0.RewardActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load0(store, root) }) + builtin.RegisterActorState(builtin2.RewardActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load2(store, root) }) + builtin.RegisterActorState(builtin3.RewardActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load3(store, root) }) + builtin.RegisterActorState(builtin4.RewardActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load4(store, root) }) @@ -37,16 +45,21 @@ var ( Methods = builtin4.MethodsReward ) -func Load(store adt.Store, act *types.Actor) (st State, err error) { +func Load(store adt.Store, act *types.Actor) (State, error) { switch act.Code { + case builtin0.RewardActorCodeID: return load0(store, act.Head) + case builtin2.RewardActorCodeID: return load2(store, act.Head) + case builtin3.RewardActorCodeID: return load3(store, act.Head) + case builtin4.RewardActorCodeID: return load4(store, act.Head) + } return nil, xerrors.Errorf("unknown actor code %s", act.Code) } diff --git a/chain/actors/builtin/reward/state.go.template b/chain/actors/builtin/reward/state.go.template new file mode 100644 index 000000000..1758d1413 --- /dev/null +++ b/chain/actors/builtin/reward/state.go.template @@ -0,0 +1,103 @@ +package reward + +import ( + "github.com/filecoin-project/go-state-types/abi" + "github.com/ipfs/go-cid" + + "github.com/filecoin-project/lotus/chain/actors/adt" + "github.com/filecoin-project/lotus/chain/actors/builtin" + + miner{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/builtin/miner" + reward{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/builtin/reward" + smoothing{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/util/smoothing" +) + +var _ State = (*state{{.v}})(nil) + +func load{{.v}}(store adt.Store, root cid.Cid) (State, error) { + out := state{{.v}}{store: store} + err := store.Get(store.Context(), root, &out) + if err != nil { + return nil, err + } + return &out, nil +} + +type state{{.v}} struct { + reward{{.v}}.State + store adt.Store +} + +func (s *state{{.v}}) ThisEpochReward() (abi.TokenAmount, error) { + return s.State.ThisEpochReward, nil +} + +func (s *state{{.v}}) ThisEpochRewardSmoothed() (builtin.FilterEstimate, error) { +{{if (ge .v 2)}} + return builtin.FilterEstimate{ + PositionEstimate: s.State.ThisEpochRewardSmoothed.PositionEstimate, + VelocityEstimate: s.State.ThisEpochRewardSmoothed.VelocityEstimate, + }, nil +{{else}} + return builtin.FromV0FilterEstimate(*s.State.ThisEpochRewardSmoothed), nil +{{end}} +} + +func (s *state{{.v}}) ThisEpochBaselinePower() (abi.StoragePower, error) { + return s.State.ThisEpochBaselinePower, nil +} + +func (s *state{{.v}}) TotalStoragePowerReward() (abi.TokenAmount, error) { + return s.State.{{if (ge .v 2)}}TotalStoragePowerReward{{else}}TotalMined{{end}}, nil +} + +func (s *state{{.v}}) EffectiveBaselinePower() (abi.StoragePower, error) { + return s.State.EffectiveBaselinePower, nil +} + +func (s *state{{.v}}) EffectiveNetworkTime() (abi.ChainEpoch, error) { + return s.State.EffectiveNetworkTime, nil +} + +func (s *state{{.v}}) CumsumBaseline() (reward{{.v}}.Spacetime, error) { + return s.State.CumsumBaseline, nil +} + +func (s *state{{.v}}) CumsumRealized() (reward{{.v}}.Spacetime, error) { + return s.State.CumsumRealized, nil +} +{{if (ge .v 2)}} +func (s *state{{.v}}) InitialPledgeForPower(qaPower abi.StoragePower, networkTotalPledge abi.TokenAmount, networkQAPower *builtin.FilterEstimate, circSupply abi.TokenAmount) (abi.TokenAmount, error) { + return miner{{.v}}.InitialPledgeForPower( + qaPower, + s.State.ThisEpochBaselinePower, + s.State.ThisEpochRewardSmoothed, + smoothing{{.v}}.FilterEstimate{ + PositionEstimate: networkQAPower.PositionEstimate, + VelocityEstimate: networkQAPower.VelocityEstimate, + }, + circSupply, + ), nil +} +{{else}} +func (s *state0) InitialPledgeForPower(sectorWeight abi.StoragePower, networkTotalPledge abi.TokenAmount, networkQAPower *builtin.FilterEstimate, circSupply abi.TokenAmount) (abi.TokenAmount, error) { + return miner0.InitialPledgeForPower( + sectorWeight, + s.State.ThisEpochBaselinePower, + networkTotalPledge, + s.State.ThisEpochRewardSmoothed, + &smoothing0.FilterEstimate{ + PositionEstimate: networkQAPower.PositionEstimate, + VelocityEstimate: networkQAPower.VelocityEstimate, + }, + circSupply), nil +} +{{end}} +func (s *state{{.v}}) PreCommitDepositForPower(networkQAPower builtin.FilterEstimate, sectorWeight abi.StoragePower) (abi.TokenAmount, error) { + return miner{{.v}}.PreCommitDepositForPower(s.State.ThisEpochRewardSmoothed, + {{if (le .v 0)}}&{{end}}smoothing{{.v}}.FilterEstimate{ + PositionEstimate: networkQAPower.PositionEstimate, + VelocityEstimate: networkQAPower.VelocityEstimate, + }, + sectorWeight), nil +} diff --git a/chain/actors/builtin/reward/v0.go b/chain/actors/builtin/reward/v0.go index 6a6e6d12e..fe053cc16 100644 --- a/chain/actors/builtin/reward/v0.go +++ b/chain/actors/builtin/reward/v0.go @@ -28,12 +28,14 @@ type state0 struct { store adt.Store } -func (s *state0) ThisEpochReward() (abi.StoragePower, error) { +func (s *state0) ThisEpochReward() (abi.TokenAmount, error) { return s.State.ThisEpochReward, nil } func (s *state0) ThisEpochRewardSmoothed() (builtin.FilterEstimate, error) { + return builtin.FromV0FilterEstimate(*s.State.ThisEpochRewardSmoothed), nil + } func (s *state0) ThisEpochBaselinePower() (abi.StoragePower, error) { @@ -52,11 +54,11 @@ func (s *state0) EffectiveNetworkTime() (abi.ChainEpoch, error) { return s.State.EffectiveNetworkTime, nil } -func (s *state0) CumsumBaseline() (abi.StoragePower, error) { +func (s *state0) CumsumBaseline() (reward0.Spacetime, error) { return s.State.CumsumBaseline, nil } -func (s *state0) CumsumRealized() (abi.StoragePower, error) { +func (s *state0) CumsumRealized() (reward0.Spacetime, error) { return s.State.CumsumRealized, nil } diff --git a/chain/actors/builtin/reward/v2.go b/chain/actors/builtin/reward/v2.go index c9a591532..90621e467 100644 --- a/chain/actors/builtin/reward/v2.go +++ b/chain/actors/builtin/reward/v2.go @@ -33,10 +33,12 @@ func (s *state2) ThisEpochReward() (abi.TokenAmount, error) { } func (s *state2) ThisEpochRewardSmoothed() (builtin.FilterEstimate, error) { + return builtin.FilterEstimate{ PositionEstimate: s.State.ThisEpochRewardSmoothed.PositionEstimate, VelocityEstimate: s.State.ThisEpochRewardSmoothed.VelocityEstimate, }, nil + } func (s *state2) ThisEpochBaselinePower() (abi.StoragePower, error) { diff --git a/chain/actors/builtin/reward/v3.go b/chain/actors/builtin/reward/v3.go index 18bd58f8e..926cc085b 100644 --- a/chain/actors/builtin/reward/v3.go +++ b/chain/actors/builtin/reward/v3.go @@ -33,10 +33,12 @@ func (s *state3) ThisEpochReward() (abi.TokenAmount, error) { } func (s *state3) ThisEpochRewardSmoothed() (builtin.FilterEstimate, error) { + return builtin.FilterEstimate{ PositionEstimate: s.State.ThisEpochRewardSmoothed.PositionEstimate, VelocityEstimate: s.State.ThisEpochRewardSmoothed.VelocityEstimate, }, nil + } func (s *state3) ThisEpochBaselinePower() (abi.StoragePower, error) { diff --git a/chain/actors/builtin/reward/v4.go b/chain/actors/builtin/reward/v4.go index 7320d1701..f034b0018 100644 --- a/chain/actors/builtin/reward/v4.go +++ b/chain/actors/builtin/reward/v4.go @@ -33,10 +33,12 @@ func (s *state4) ThisEpochReward() (abi.TokenAmount, error) { } func (s *state4) ThisEpochRewardSmoothed() (builtin.FilterEstimate, error) { + return builtin.FilterEstimate{ PositionEstimate: s.State.ThisEpochRewardSmoothed.PositionEstimate, VelocityEstimate: s.State.ThisEpochRewardSmoothed.VelocityEstimate, }, nil + } func (s *state4) ThisEpochBaselinePower() (abi.StoragePower, error) { diff --git a/chain/actors/builtin/verifreg/actor.go.template b/chain/actors/builtin/verifreg/actor.go.template new file mode 100644 index 000000000..22e809ccf --- /dev/null +++ b/chain/actors/builtin/verifreg/actor.go.template @@ -0,0 +1,51 @@ +package verifreg + +import ( + "github.com/ipfs/go-cid" + "golang.org/x/xerrors" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + + "github.com/filecoin-project/go-state-types/cbor" +{{range .versions}} + builtin{{.}} "github.com/filecoin-project/specs-actors{{import .}}actors/builtin" +{{end}} + + "github.com/filecoin-project/lotus/chain/actors/adt" + "github.com/filecoin-project/lotus/chain/actors/builtin" + "github.com/filecoin-project/lotus/chain/types" +) + +func init() { +{{range .versions}} + builtin.RegisterActorState(builtin{{.}}.VerifiedRegistryActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { + return load{{.}}(store, root) + }) +{{end}} +} + +var ( + Address = builtin{{.latestVersion}}.VerifiedRegistryActorAddr + Methods = builtin{{.latestVersion}}.MethodsVerifiedRegistry +) + +func Load(store adt.Store, act *types.Actor) (State, error) { + switch act.Code { +{{range .versions}} + case builtin{{.}}.VerifiedRegistryActorCodeID: + return load{{.}}(store, act.Head) +{{end}} + } + return nil, xerrors.Errorf("unknown actor code %s", act.Code) +} + +type State interface { + cbor.Marshaler + + RootKey() (address.Address, error) + VerifiedClientDataCap(address.Address) (bool, abi.StoragePower, error) + VerifierDataCap(address.Address) (bool, abi.StoragePower, error) + ForEachVerifier(func(addr address.Address, dcap abi.StoragePower) error) error + ForEachClient(func(addr address.Address, dcap abi.StoragePower) error) error +} diff --git a/chain/actors/builtin/verifreg/state.go.template b/chain/actors/builtin/verifreg/state.go.template new file mode 100644 index 000000000..244d20932 --- /dev/null +++ b/chain/actors/builtin/verifreg/state.go.template @@ -0,0 +1,58 @@ +package verifreg + +import ( + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + "github.com/ipfs/go-cid" + + "github.com/filecoin-project/lotus/chain/actors" + "github.com/filecoin-project/lotus/chain/actors/adt" + +{{if (ge .v 3)}} builtin{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/builtin" +{{end}} verifreg{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/builtin/verifreg" + adt{{.v}} "github.com/filecoin-project/specs-actors{{.import}}actors/util/adt" +) + +var _ State = (*state{{.v}})(nil) + +func load{{.v}}(store adt.Store, root cid.Cid) (State, error) { + out := state{{.v}}{store: store} + err := store.Get(store.Context(), root, &out) + if err != nil { + return nil, err + } + return &out, nil +} + +type state{{.v}} struct { + verifreg{{.v}}.State + store adt.Store +} + +func (s *state{{.v}}) RootKey() (address.Address, error) { + return s.State.RootKey, nil +} + +func (s *state{{.v}}) VerifiedClientDataCap(addr address.Address) (bool, abi.StoragePower, error) { + return getDataCap(s.store, actors.Version{{.v}}, s.verifiedClients, addr) +} + +func (s *state{{.v}}) VerifierDataCap(addr address.Address) (bool, abi.StoragePower, error) { + return getDataCap(s.store, actors.Version{{.v}}, s.verifiers, addr) +} + +func (s *state{{.v}}) ForEachVerifier(cb func(addr address.Address, dcap abi.StoragePower) error) error { + return forEachCap(s.store, actors.Version{{.v}}, s.verifiers, cb) +} + +func (s *state{{.v}}) ForEachClient(cb func(addr address.Address, dcap abi.StoragePower) error) error { + return forEachCap(s.store, actors.Version{{.v}}, s.verifiedClients, cb) +} + +func (s *state{{.v}}) verifiedClients() (adt.Map, error) { + return adt{{.v}}.AsMap(s.store, s.VerifiedClients{{if (ge .v 3)}}, builtin{{.v}}.DefaultHamtBitwidth{{end}}) +} + +func (s *state{{.v}}) verifiers() (adt.Map, error) { + return adt{{.v}}.AsMap(s.store, s.Verifiers{{if (ge .v 3)}}, builtin{{.v}}.DefaultHamtBitwidth{{end}}) +} diff --git a/chain/actors/builtin/verifreg/verifreg.go b/chain/actors/builtin/verifreg/verifreg.go index 83ba0c6c5..32f50a4ae 100644 --- a/chain/actors/builtin/verifreg/verifreg.go +++ b/chain/actors/builtin/verifreg/verifreg.go @@ -8,9 +8,13 @@ import ( "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/cbor" + builtin0 "github.com/filecoin-project/specs-actors/actors/builtin" + builtin2 "github.com/filecoin-project/specs-actors/v2/actors/builtin" + builtin3 "github.com/filecoin-project/specs-actors/v3/actors/builtin" + builtin4 "github.com/filecoin-project/specs-actors/v4/actors/builtin" "github.com/filecoin-project/lotus/chain/actors/adt" @@ -19,18 +23,23 @@ import ( ) func init() { + builtin.RegisterActorState(builtin0.VerifiedRegistryActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load0(store, root) }) + builtin.RegisterActorState(builtin2.VerifiedRegistryActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load2(store, root) }) + builtin.RegisterActorState(builtin3.VerifiedRegistryActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load3(store, root) }) + builtin.RegisterActorState(builtin4.VerifiedRegistryActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load4(store, root) }) + } var ( @@ -40,14 +49,19 @@ var ( func Load(store adt.Store, act *types.Actor) (State, error) { switch act.Code { + case builtin0.VerifiedRegistryActorCodeID: return load0(store, act.Head) + case builtin2.VerifiedRegistryActorCodeID: return load2(store, act.Head) + case builtin3.VerifiedRegistryActorCodeID: return load3(store, act.Head) + case builtin4.VerifiedRegistryActorCodeID: return load4(store, act.Head) + } return nil, xerrors.Errorf("unknown actor code %s", act.Code) } diff --git a/chain/actors/policy/policy.go b/chain/actors/policy/policy.go index 07f489b11..164f19a76 100644 --- a/chain/actors/policy/policy.go +++ b/chain/actors/policy/policy.go @@ -25,8 +25,9 @@ import ( builtin4 "github.com/filecoin-project/specs-actors/v4/actors/builtin" market4 "github.com/filecoin-project/specs-actors/v4/actors/builtin/market" miner4 "github.com/filecoin-project/specs-actors/v4/actors/builtin/miner" - paych4 "github.com/filecoin-project/specs-actors/v4/actors/builtin/paych" verifreg4 "github.com/filecoin-project/specs-actors/v4/actors/builtin/verifreg" + + paych4 "github.com/filecoin-project/specs-actors/v4/actors/builtin/paych" ) const ( @@ -39,7 +40,9 @@ const ( // SetSupportedProofTypes sets supported proof types, across all actor versions. // This should only be used for testing. func SetSupportedProofTypes(types ...abi.RegisteredSealProof) { + miner0.SupportedProofTypes = make(map[abi.RegisteredSealProof]struct{}, len(types)) + miner2.PreCommitSealProofTypesV0 = make(map[abi.RegisteredSealProof]struct{}, len(types)) miner2.PreCommitSealProofTypesV7 = make(map[abi.RegisteredSealProof]struct{}, len(types)*2) miner2.PreCommitSealProofTypesV8 = make(map[abi.RegisteredSealProof]struct{}, len(types)) @@ -63,6 +66,7 @@ func AddSupportedProofTypes(types ...abi.RegisteredSealProof) { panic("must specify v1 proof types only") } // Set for all miner versions. + miner0.SupportedProofTypes[t] = struct{}{} miner2.PreCommitSealProofTypesV0[t] = struct{}{} @@ -79,6 +83,7 @@ func AddSupportedProofTypes(types ...abi.RegisteredSealProof) { miner4.PreCommitSealProofTypesV7[t] = struct{}{} miner4.PreCommitSealProofTypesV7[t+abi.RegisteredSealProof_StackedDrg2KiBV1_1] = struct{}{} miner4.PreCommitSealProofTypesV8[t+abi.RegisteredSealProof_StackedDrg2KiBV1_1] = struct{}{} + } } @@ -86,22 +91,29 @@ func AddSupportedProofTypes(types ...abi.RegisteredSealProof) { // actors versions. Use for testing. func SetPreCommitChallengeDelay(delay abi.ChainEpoch) { // Set for all miner versions. + miner0.PreCommitChallengeDelay = delay + miner2.PreCommitChallengeDelay = delay + miner3.PreCommitChallengeDelay = delay + miner4.PreCommitChallengeDelay = delay + } // TODO: this function shouldn't really exist. Instead, the API should expose the precommit delay. func GetPreCommitChallengeDelay() abi.ChainEpoch { - return miner0.PreCommitChallengeDelay + return miner4.PreCommitChallengeDelay } // SetConsensusMinerMinPower sets the minimum power of an individual miner must // meet for leader election, across all actor versions. This should only be used // for testing. func SetConsensusMinerMinPower(p abi.StoragePower) { + power0.ConsensusMinerMinPower = p + for _, policy := range builtin2.SealProofPolicies { policy.ConsensusMinerMinPower = p } @@ -113,27 +125,42 @@ func SetConsensusMinerMinPower(p abi.StoragePower) { for _, policy := range builtin4.PoStProofPolicies { policy.ConsensusMinerMinPower = p } + } // SetMinVerifiedDealSize sets the minimum size of a verified deal. This should // only be used for testing. func SetMinVerifiedDealSize(size abi.StoragePower) { + verifreg0.MinVerifiedDealSize = size + verifreg2.MinVerifiedDealSize = size + verifreg3.MinVerifiedDealSize = size + verifreg4.MinVerifiedDealSize = size + } func GetMaxProveCommitDuration(ver actors.Version, t abi.RegisteredSealProof) abi.ChainEpoch { switch ver { + case actors.Version0: + return miner0.MaxSealDuration[t] + case actors.Version2: + return miner2.MaxProveCommitDuration[t] + case actors.Version3: + return miner3.MaxProveCommitDuration[t] + case actors.Version4: + return miner4.MaxProveCommitDuration[t] + default: panic("unsupported actors version") } @@ -145,26 +172,36 @@ func DealProviderCollateralBounds( circulatingFil abi.TokenAmount, nwVer network.Version, ) (min, max abi.TokenAmount) { switch actors.VersionForNetwork(nwVer) { + case actors.Version0: + return market0.DealProviderCollateralBounds(size, verified, rawBytePower, qaPower, baselinePower, circulatingFil, nwVer) + case actors.Version2: + return market2.DealProviderCollateralBounds(size, verified, rawBytePower, qaPower, baselinePower, circulatingFil) + case actors.Version3: + return market3.DealProviderCollateralBounds(size, verified, rawBytePower, qaPower, baselinePower, circulatingFil) + case actors.Version4: + return market4.DealProviderCollateralBounds(size, verified, rawBytePower, qaPower, baselinePower, circulatingFil) + default: panic("unsupported actors version") } } func DealDurationBounds(pieceSize abi.PaddedPieceSize) (min, max abi.ChainEpoch) { - return market2.DealDurationBounds(pieceSize) + return market4.DealDurationBounds(pieceSize) } // Sets the challenge window and scales the proving period to match (such that // there are always 48 challenge windows in a proving period). func SetWPoStChallengeWindow(period abi.ChainEpoch) { + miner0.WPoStChallengeWindow = period miner0.WPoStProvingPeriod = period * abi.ChainEpoch(miner0.WPoStPeriodDeadlines) @@ -173,13 +210,18 @@ func SetWPoStChallengeWindow(period abi.ChainEpoch) { miner3.WPoStChallengeWindow = period miner3.WPoStProvingPeriod = period * abi.ChainEpoch(miner3.WPoStPeriodDeadlines) + // by default, this is 2x finality which is 30 periods. // scale it if we're scaling the challenge period. miner3.WPoStDisputeWindow = period * 30 miner4.WPoStChallengeWindow = period miner4.WPoStProvingPeriod = period * abi.ChainEpoch(miner4.WPoStPeriodDeadlines) - miner4.WPoStDisputeWindow = period * 30 // see the miner3 comment + + // by default, this is 2x finality which is 30 periods. + // scale it if we're scaling the challenge period. + miner4.WPoStDisputeWindow = period * 30 + } func GetWinningPoStSectorSetLookback(nwVer network.Version) abi.ChainEpoch { @@ -192,12 +234,12 @@ func GetWinningPoStSectorSetLookback(nwVer network.Version) abi.ChainEpoch { } func GetMaxSectorExpirationExtension() abi.ChainEpoch { - return miner0.MaxSectorExpirationExtension + return miner4.MaxSectorExpirationExtension } // TODO: we'll probably need to abstract over this better in the future. func GetMaxPoStPartitions(p abi.RegisteredPoStProof) (int, error) { - sectorsPerPart, err := builtin3.PoStProofWindowPoStPartitionSectors(p) + sectorsPerPart, err := builtin4.PoStProofWindowPoStPartitionSectors(p) if err != nil { return 0, err } @@ -233,14 +275,19 @@ func GetSectorMaxLifetime(proof abi.RegisteredSealProof, nwVer network.Version) func GetAddressedSectorsMax(nwVer network.Version) int { switch actors.VersionForNetwork(nwVer) { + case actors.Version0: return miner0.AddressedSectorsMax + case actors.Version2: return miner2.AddressedSectorsMax + case actors.Version3: return miner3.AddressedSectorsMax + case actors.Version4: return miner4.AddressedSectorsMax + default: panic("unsupported network version") } @@ -248,15 +295,24 @@ func GetAddressedSectorsMax(nwVer network.Version) int { func GetDeclarationsMax(nwVer network.Version) int { switch actors.VersionForNetwork(nwVer) { + case actors.Version0: + // TODO: Should we instead panic here since the concept doesn't exist yet? return miner0.AddressedPartitionsMax + case actors.Version2: + return miner2.DeclarationsMax + case actors.Version3: + return miner3.DeclarationsMax + case actors.Version4: + return miner4.DeclarationsMax + default: panic("unsupported network version") } diff --git a/chain/actors/policy/policy.go.template b/chain/actors/policy/policy.go.template new file mode 100644 index 000000000..d395d7132 --- /dev/null +++ b/chain/actors/policy/policy.go.template @@ -0,0 +1,233 @@ +package policy + +import ( + "sort" + + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/network" + "github.com/filecoin-project/lotus/chain/actors" + + {{range .versions}} + {{if (ge . 2)}} builtin{{.}} "github.com/filecoin-project/specs-actors{{import .}}actors/builtin" {{end}} + market{{.}} "github.com/filecoin-project/specs-actors{{import .}}actors/builtin/market" + miner{{.}} "github.com/filecoin-project/specs-actors{{import .}}actors/builtin/miner" + verifreg{{.}} "github.com/filecoin-project/specs-actors{{import .}}actors/builtin/verifreg" + {{if (eq . 0)}} power{{.}} "github.com/filecoin-project/specs-actors{{import .}}actors/builtin/power" {{end}} + {{end}} + + paych{{.latestVersion}} "github.com/filecoin-project/specs-actors{{import .latestVersion}}actors/builtin/paych" +) + +const ( + ChainFinality = miner{{.latestVersion}}.ChainFinality + SealRandomnessLookback = ChainFinality + PaychSettleDelay = paych{{.latestVersion}}.SettleDelay + MaxPreCommitRandomnessLookback = builtin{{.latestVersion}}.EpochsInDay + SealRandomnessLookback +) + +// SetSupportedProofTypes sets supported proof types, across all actor versions. +// This should only be used for testing. +func SetSupportedProofTypes(types ...abi.RegisteredSealProof) { + {{range .versions}} + {{if (eq . 0)}} + miner{{.}}.SupportedProofTypes = make(map[abi.RegisteredSealProof]struct{}, len(types)) + {{else}} + miner{{.}}.PreCommitSealProofTypesV0 = make(map[abi.RegisteredSealProof]struct{}, len(types)) + miner{{.}}.PreCommitSealProofTypesV7 = make(map[abi.RegisteredSealProof]struct{}, len(types)*2) + miner{{.}}.PreCommitSealProofTypesV8 = make(map[abi.RegisteredSealProof]struct{}, len(types)) + {{end}} + {{end}} + + AddSupportedProofTypes(types...) +} + +// AddSupportedProofTypes sets supported proof types, across all actor versions. +// This should only be used for testing. +func AddSupportedProofTypes(types ...abi.RegisteredSealProof) { + for _, t := range types { + if t >= abi.RegisteredSealProof_StackedDrg2KiBV1_1 { + panic("must specify v1 proof types only") + } + // Set for all miner versions. + + {{range .versions}} + {{if (eq . 0)}} + miner{{.}}.SupportedProofTypes[t] = struct{}{} + {{else}} + miner{{.}}.PreCommitSealProofTypesV0[t] = struct{}{} + miner{{.}}.PreCommitSealProofTypesV7[t] = struct{}{} + miner{{.}}.PreCommitSealProofTypesV7[t+abi.RegisteredSealProof_StackedDrg2KiBV1_1] = struct{}{} + miner{{.}}.PreCommitSealProofTypesV8[t+abi.RegisteredSealProof_StackedDrg2KiBV1_1] = struct{}{} + {{end}} + {{end}} + } +} + +// SetPreCommitChallengeDelay sets the pre-commit challenge delay across all +// actors versions. Use for testing. +func SetPreCommitChallengeDelay(delay abi.ChainEpoch) { + // Set for all miner versions. + {{range .versions}} + miner{{.}}.PreCommitChallengeDelay = delay + {{end}} +} + +// TODO: this function shouldn't really exist. Instead, the API should expose the precommit delay. +func GetPreCommitChallengeDelay() abi.ChainEpoch { + return miner{{.latestVersion}}.PreCommitChallengeDelay +} + +// SetConsensusMinerMinPower sets the minimum power of an individual miner must +// meet for leader election, across all actor versions. This should only be used +// for testing. +func SetConsensusMinerMinPower(p abi.StoragePower) { + {{range .versions}} + {{if (eq . 0)}} + power{{.}}.ConsensusMinerMinPower = p + {{else if (eq . 2)}} + for _, policy := range builtin{{.}}.SealProofPolicies { + policy.ConsensusMinerMinPower = p + } + {{else}} + for _, policy := range builtin{{.}}.PoStProofPolicies { + policy.ConsensusMinerMinPower = p + } + {{end}} + {{end}} +} + +// SetMinVerifiedDealSize sets the minimum size of a verified deal. This should +// only be used for testing. +func SetMinVerifiedDealSize(size abi.StoragePower) { + {{range .versions}} + verifreg{{.}}.MinVerifiedDealSize = size + {{end}} +} + +func GetMaxProveCommitDuration(ver actors.Version, t abi.RegisteredSealProof) abi.ChainEpoch { + switch ver { + {{range .versions}} + case actors.Version{{.}}: + {{if (eq . 0)}} + return miner{{.}}.MaxSealDuration[t] + {{else}} + return miner{{.}}.MaxProveCommitDuration[t] + {{end}} + {{end}} + default: + panic("unsupported actors version") + } +} + +func DealProviderCollateralBounds( + size abi.PaddedPieceSize, verified bool, + rawBytePower, qaPower, baselinePower abi.StoragePower, + circulatingFil abi.TokenAmount, nwVer network.Version, +) (min, max abi.TokenAmount) { + switch actors.VersionForNetwork(nwVer) { + {{range .versions}} + case actors.Version{{.}}: + {{if (eq . 0)}} + return market{{.}}.DealProviderCollateralBounds(size, verified, rawBytePower, qaPower, baselinePower, circulatingFil, nwVer) + {{else}} + return market{{.}}.DealProviderCollateralBounds(size, verified, rawBytePower, qaPower, baselinePower, circulatingFil) + {{end}} + {{end}} + default: + panic("unsupported actors version") + } +} + +func DealDurationBounds(pieceSize abi.PaddedPieceSize) (min, max abi.ChainEpoch) { + return market{{.latestVersion}}.DealDurationBounds(pieceSize) +} + +// Sets the challenge window and scales the proving period to match (such that +// there are always 48 challenge windows in a proving period). +func SetWPoStChallengeWindow(period abi.ChainEpoch) { + {{range .versions}} + miner{{.}}.WPoStChallengeWindow = period + miner{{.}}.WPoStProvingPeriod = period * abi.ChainEpoch(miner{{.}}.WPoStPeriodDeadlines) + {{if (ge . 3)}} + // by default, this is 2x finality which is 30 periods. + // scale it if we're scaling the challenge period. + miner{{.}}.WPoStDisputeWindow = period * 30 + {{end}} + {{end}} +} + +func GetWinningPoStSectorSetLookback(nwVer network.Version) abi.ChainEpoch { + if nwVer <= network.Version3 { + return 10 + } + + // NOTE: if this ever changes, adjust it in a (*Miner).mineOne() logline as well + return ChainFinality +} + +func GetMaxSectorExpirationExtension() abi.ChainEpoch { + return miner{{.latestVersion}}.MaxSectorExpirationExtension +} + +// TODO: we'll probably need to abstract over this better in the future. +func GetMaxPoStPartitions(p abi.RegisteredPoStProof) (int, error) { + sectorsPerPart, err := builtin{{.latestVersion}}.PoStProofWindowPoStPartitionSectors(p) + if err != nil { + return 0, err + } + return int(miner{{.latestVersion}}.AddressedSectorsMax / sectorsPerPart), nil +} + +func GetDefaultSectorSize() abi.SectorSize { + // supported sector sizes are the same across versions. + szs := make([]abi.SectorSize, 0, len(miner{{.latestVersion}}.PreCommitSealProofTypesV8)) + for spt := range miner{{.latestVersion}}.PreCommitSealProofTypesV8 { + ss, err := spt.SectorSize() + if err != nil { + panic(err) + } + + szs = append(szs, ss) + } + + sort.Slice(szs, func(i, j int) bool { + return szs[i] < szs[j] + }) + + return szs[0] +} + +func GetSectorMaxLifetime(proof abi.RegisteredSealProof, nwVer network.Version) abi.ChainEpoch { + if nwVer <= network.Version10 { + return builtin{{.latestVersion}}.SealProofPoliciesV0[proof].SectorMaxLifetime + } + + return builtin{{.latestVersion}}.SealProofPoliciesV11[proof].SectorMaxLifetime +} + +func GetAddressedSectorsMax(nwVer network.Version) int { + switch actors.VersionForNetwork(nwVer) { + {{range .versions}} + case actors.Version{{.}}: + return miner{{.}}.AddressedSectorsMax + {{end}} + default: + panic("unsupported network version") + } +} + +func GetDeclarationsMax(nwVer network.Version) int { + switch actors.VersionForNetwork(nwVer) { + {{range .versions}} + case actors.Version{{.}}: + {{if (eq . 0)}} + // TODO: Should we instead panic here since the concept doesn't exist yet? + return miner{{.}}.AddressedPartitionsMax + {{else}} + return miner{{.}}.DeclarationsMax + {{end}} + {{end}} + default: + panic("unsupported network version") + } +} diff --git a/cmd/lotus-storage-miner/actor_test.go b/cmd/lotus-storage-miner/actor_test.go index bbe9362d0..5bc82d842 100644 --- a/cmd/lotus-storage-miner/actor_test.go +++ b/cmd/lotus-storage-miner/actor_test.go @@ -51,7 +51,7 @@ func TestWorkerKeyChange(t *testing.T) { blocktime := 1 * time.Millisecond - n, sn := builder.MockSbBuilder(t, []test.FullNodeOpts{test.FullNodeWithActorsV4At(-1), test.FullNodeWithActorsV4At(-1)}, test.OneMiner) + n, sn := builder.MockSbBuilder(t, []test.FullNodeOpts{test.FullNodeWithLatestActorsAt(-1), test.FullNodeWithLatestActorsAt(-1)}, test.OneMiner) client1 := n[0] client2 := n[1] diff --git a/documentation/misc/actors_version_checklist.md b/documentation/misc/actors_version_checklist.md new file mode 100644 index 000000000..6c06441ab --- /dev/null +++ b/documentation/misc/actors_version_checklist.md @@ -0,0 +1,17 @@ +### Actor version integration checklist + +- [ ] Import new actors +- [ ] Define upgrade heights in `build/params_` +- [ ] Generate adapters + - [ ] Add the new version in `chain/actors/agen/main.go` +- [ ] Update `chain/actors/version.go` +- [ ] Register in `chain/vm/invoker.go` +- [ ] Register in `chain/vm/mkactor.go` +- [ ] Update `chain/types/state.go` +- [ ] Update `chain/state/statetree.go` +- [ ] Update `chain/stmgr/forks.go` + - [ ] Schedule + - [ ] Migration +- [ ] Update upgrade schedule in `api/test/test.go` +- [ ] Update `NewestNetworkVersion` in `build/params_shared_vals.go` +- [ ] Register in init in `chain/stmgr/utils.go` From b5da2655dc3520a3808af4bb5ecfa5c5e8fd0844 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Thu, 6 May 2021 01:44:11 -0400 Subject: [PATCH 002/160] Introduce v5 actors --- api/test/test.go | 9 +- build/openrpc/full.json.gz | Bin 22804 -> 22804 bytes build/openrpc/miner.json.gz | Bin 7828 -> 7828 bytes build/openrpc/worker.json.gz | Bin 2573 -> 2573 bytes build/params_2k.go | 15 +- build/params_butterfly.go | 9 +- build/params_calibnet.go | 8 +- build/params_mainnet.go | 17 +- build/params_nerpanet.go | 7 +- build/params_shared_vals.go | 2 +- build/params_testground.go | 29 +- chain/actors/agen/main.go | 7 +- chain/actors/builtin/account/account.go | 9 + chain/actors/builtin/account/v5.go | 30 ++ chain/actors/builtin/builtin.go | 60 ++- chain/actors/builtin/cron/cron.go | 6 +- chain/actors/builtin/init/init.go | 13 +- chain/actors/builtin/init/v5.go | 87 ++++ chain/actors/builtin/market/market.go | 13 +- chain/actors/builtin/market/v5.go | 209 ++++++++ chain/actors/builtin/miner/miner.go | 11 +- chain/actors/builtin/miner/v5.go | 445 ++++++++++++++++++ .../actors/builtin/multisig/actor.go.template | 4 +- chain/actors/builtin/multisig/message5.go | 71 +++ chain/actors/builtin/multisig/multisig.go | 24 +- chain/actors/builtin/multisig/v5.go | 96 ++++ chain/actors/builtin/paych/message5.go | 74 +++ chain/actors/builtin/paych/paych.go | 14 +- chain/actors/builtin/paych/v5.go | 104 ++++ chain/actors/builtin/power/power.go | 13 +- chain/actors/builtin/power/v5.go | 150 ++++++ chain/actors/builtin/reward/reward.go | 13 +- chain/actors/builtin/reward/v5.go | 88 ++++ chain/actors/builtin/verifreg/v5.go | 58 +++ chain/actors/builtin/verifreg/verifreg.go | 13 +- chain/actors/policy/policy.go | 70 ++- chain/actors/version.go | 3 + chain/gen/genesis/util.go | 2 +- chain/messagepool/selection.go | 2 +- chain/stmgr/forks.go | 115 ++++- chain/stmgr/stmgr.go | 6 +- chain/stmgr/utils.go | 3 + chain/vm/invoker.go | 2 + chain/vm/mkactor.go | 3 + cmd/tvx/codenames.go | 2 +- cmd/tvx/codenames_test.go | 2 +- documentation/en/api-methods.md | 2 +- go.mod | 3 +- go.sum | 4 + lotuspond/front/src/chain/methods.json | 104 ++++ testplans/lotus-soup/init.go | 2 +- 51 files changed, 1922 insertions(+), 111 deletions(-) create mode 100644 chain/actors/builtin/account/v5.go create mode 100644 chain/actors/builtin/init/v5.go create mode 100644 chain/actors/builtin/market/v5.go create mode 100644 chain/actors/builtin/miner/v5.go create mode 100644 chain/actors/builtin/multisig/message5.go create mode 100644 chain/actors/builtin/multisig/v5.go create mode 100644 chain/actors/builtin/paych/message5.go create mode 100644 chain/actors/builtin/paych/v5.go create mode 100644 chain/actors/builtin/power/v5.go create mode 100644 chain/actors/builtin/reward/v5.go create mode 100644 chain/actors/builtin/verifreg/v5.go diff --git a/api/test/test.go b/api/test/test.go index eeddc23a0..44530191b 100644 --- a/api/test/test.go +++ b/api/test/test.go @@ -122,7 +122,8 @@ var TwoFull = DefaultFullOpts(2) var FullNodeWithLatestActorsAt = func(upgradeHeight abi.ChainEpoch) FullNodeOpts { if upgradeHeight == -1 { - upgradeHeight = 3 + // Attention: Update this when introducing new actor versions or your tests will be sad + upgradeHeight = 4 } return FullNodeOpts{ @@ -138,8 +139,12 @@ var FullNodeWithLatestActorsAt = func(upgradeHeight abi.ChainEpoch) FullNodeOpts Migration: stmgr.UpgradeActorsV3, }, { Network: network.Version12, - Height: upgradeHeight, + Height: 3, Migration: stmgr.UpgradeActorsV4, + }, { + Network: network.Version13, + Height: upgradeHeight, + Migration: stmgr.UpgradeActorsV5, }}) }, } diff --git a/build/openrpc/full.json.gz b/build/openrpc/full.json.gz index 6cc950d730897d8c52ba5121c06489f43d23b890..65c5eddd3a304996f9bd099df40cf3a6af605706 100644 GIT binary patch delta 30 mcmbQTiE+v%#tB`FejB^fTp2HK{^Tkp%<(;nv0bT#g#iGWF2jlOJoy>9^P6rNUu5Mst003h#2y*}c delta 21 dcmbPYJH>WF2V?cdPG&idwZA;iu5Mst003Y;2!j9s diff --git a/build/openrpc/worker.json.gz b/build/openrpc/worker.json.gz index cd1e2683ac9a9c34cb00bda9242b14892b7336f3..798a6376cacd1d613567d6c963b3e734655e62e7 100644 GIT binary patch delta 21 ccmeAb=@psK#Mr*ExrvkGZ$)7NV>JT<09gtLb^rhX delta 21 ccmeAb=@psK#Mr&Dxrvh_^6BjtjMWSb09)}0B>(^b diff --git a/build/params_2k.go b/build/params_2k.go index 12d497c4b..d93b26468 100644 --- a/build/params_2k.go +++ b/build/params_2k.go @@ -24,7 +24,7 @@ var UpgradeIgnitionHeight = abi.ChainEpoch(-2) var UpgradeRefuelHeight = abi.ChainEpoch(-3) var UpgradeTapeHeight = abi.ChainEpoch(-4) -var UpgradeActorsV2Height = abi.ChainEpoch(10) +var UpgradeAssemblyHeight = abi.ChainEpoch(10) var UpgradeLiftoffHeight = abi.ChainEpoch(-5) var UpgradeKumquatHeight = abi.ChainEpoch(15) @@ -33,11 +33,13 @@ var UpgradePersianHeight = abi.ChainEpoch(25) var UpgradeOrangeHeight = abi.ChainEpoch(27) var UpgradeClausHeight = abi.ChainEpoch(30) -var UpgradeActorsV3Height = abi.ChainEpoch(35) +var UpgradeTrustHeight = abi.ChainEpoch(35) var UpgradeNorwegianHeight = abi.ChainEpoch(40) -var UpgradeActorsV4Height = abi.ChainEpoch(45) +var UpgradeTurboHeight = abi.ChainEpoch(45) + +var UpgradeHyperdriveHeight = abi.ChainEpoch(50) var DrandSchedule = map[abi.ChainEpoch]DrandEnum{ 0: DrandMainnet, @@ -68,16 +70,17 @@ func init() { UpgradeIgnitionHeight = getUpgradeHeight("LOTUS_IGNITION_HEIGHT", UpgradeIgnitionHeight) UpgradeRefuelHeight = getUpgradeHeight("LOTUS_REFUEL_HEIGHT", UpgradeRefuelHeight) UpgradeTapeHeight = getUpgradeHeight("LOTUS_TAPE_HEIGHT", UpgradeTapeHeight) - UpgradeActorsV2Height = getUpgradeHeight("LOTUS_ACTORSV2_HEIGHT", UpgradeActorsV2Height) + UpgradeAssemblyHeight = getUpgradeHeight("LOTUS_ACTORSV2_HEIGHT", UpgradeAssemblyHeight) UpgradeLiftoffHeight = getUpgradeHeight("LOTUS_LIFTOFF_HEIGHT", UpgradeLiftoffHeight) UpgradeKumquatHeight = getUpgradeHeight("LOTUS_KUMQUAT_HEIGHT", UpgradeKumquatHeight) UpgradeCalicoHeight = getUpgradeHeight("LOTUS_CALICO_HEIGHT", UpgradeCalicoHeight) UpgradePersianHeight = getUpgradeHeight("LOTUS_PERSIAN_HEIGHT", UpgradePersianHeight) UpgradeOrangeHeight = getUpgradeHeight("LOTUS_ORANGE_HEIGHT", UpgradeOrangeHeight) UpgradeClausHeight = getUpgradeHeight("LOTUS_CLAUS_HEIGHT", UpgradeClausHeight) - UpgradeActorsV3Height = getUpgradeHeight("LOTUS_ACTORSV3_HEIGHT", UpgradeActorsV3Height) + UpgradeTrustHeight = getUpgradeHeight("LOTUS_ACTORSV3_HEIGHT", UpgradeTrustHeight) UpgradeNorwegianHeight = getUpgradeHeight("LOTUS_NORWEGIAN_HEIGHT", UpgradeNorwegianHeight) - UpgradeActorsV4Height = getUpgradeHeight("LOTUS_ACTORSV4_HEIGHT", UpgradeActorsV4Height) + UpgradeTurboHeight = getUpgradeHeight("LOTUS_ACTORSV4_HEIGHT", UpgradeTurboHeight) + UpgradeHyperdriveHeight = getUpgradeHeight("LOTUS_HYPERDRIVE_HEIGHT", UpgradeHyperdriveHeight) BuildType |= Build2k } diff --git a/build/params_butterfly.go b/build/params_butterfly.go index 6daeca502..258f6ab0f 100644 --- a/build/params_butterfly.go +++ b/build/params_butterfly.go @@ -23,7 +23,7 @@ const UpgradeSmokeHeight = -2 const UpgradeIgnitionHeight = -3 const UpgradeRefuelHeight = -4 -var UpgradeActorsV2Height = abi.ChainEpoch(30) +var UpgradeAssemblyHeight = abi.ChainEpoch(30) const UpgradeTapeHeight = 60 const UpgradeLiftoffHeight = -5 @@ -32,9 +32,10 @@ const UpgradeCalicoHeight = 120 const UpgradePersianHeight = 150 const UpgradeClausHeight = 180 const UpgradeOrangeHeight = 210 -const UpgradeActorsV3Height = 240 -const UpgradeNorwegianHeight = UpgradeActorsV3Height + (builtin2.EpochsInHour * 12) -const UpgradeActorsV4Height = 8922 +const UpgradeTrustHeight = 240 +const UpgradeNorwegianHeight = UpgradeTrustHeight + (builtin2.EpochsInHour * 12) +const UpgradeTurboHeight = 8922 +const UpgradeHyperdriveHeight = 9999999 func init() { policy.SetConsensusMinerMinPower(abi.NewStoragePower(2 << 30)) diff --git a/build/params_calibnet.go b/build/params_calibnet.go index 997bb395b..4685ec30c 100644 --- a/build/params_calibnet.go +++ b/build/params_calibnet.go @@ -25,7 +25,7 @@ const UpgradeSmokeHeight = -2 const UpgradeIgnitionHeight = -3 const UpgradeRefuelHeight = -4 -var UpgradeActorsV2Height = abi.ChainEpoch(30) +var UpgradeAssemblyHeight = abi.ChainEpoch(30) const UpgradeTapeHeight = 60 @@ -40,10 +40,12 @@ const UpgradeClausHeight = 250 const UpgradeOrangeHeight = 300 -const UpgradeActorsV3Height = 600 +const UpgradeTrustHeight = 600 const UpgradeNorwegianHeight = 114000 -const UpgradeActorsV4Height = 193789 +const UpgradeTurboHeight = 193789 + +const UpgradeHyperdriveHeight = 9999999 func init() { policy.SetConsensusMinerMinPower(abi.NewStoragePower(32 << 30)) diff --git a/build/params_mainnet.go b/build/params_mainnet.go index ea4ae7d75..5c3171a27 100644 --- a/build/params_mainnet.go +++ b/build/params_mainnet.go @@ -34,7 +34,7 @@ const UpgradeSmokeHeight = 51000 const UpgradeIgnitionHeight = 94000 const UpgradeRefuelHeight = 130800 -const UpgradeActorsV2Height = 138720 +const UpgradeAssemblyHeight = 138720 const UpgradeTapeHeight = 140760 @@ -54,13 +54,16 @@ const UpgradeOrangeHeight = 336458 const UpgradeClausHeight = 343200 // 2021-03-04T00:00:30Z -var UpgradeActorsV3Height = abi.ChainEpoch(550321) +const UpgradeTrustHeight = 550321 // 2021-04-12T22:00:00Z const UpgradeNorwegianHeight = 665280 // 2021-04-29T06:00:00Z -var UpgradeActorsV4Height = abi.ChainEpoch(712320) +const UpgradeTurboHeight = 712320 + +// ??? +var UpgradeHyperdriveHeight = abi.ChainEpoch(9999999) func init() { policy.SetConsensusMinerMinPower(abi.NewStoragePower(10 << 40)) @@ -69,12 +72,8 @@ func init() { SetAddressNetwork(address.Mainnet) } - if os.Getenv("LOTUS_DISABLE_V3_ACTOR_MIGRATION") == "1" { - UpgradeActorsV3Height = math.MaxInt64 - } - - if os.Getenv("LOTUS_DISABLE_V4_ACTOR_MIGRATION") == "1" { - UpgradeActorsV4Height = math.MaxInt64 + if os.Getenv("LOTUS_DISABLE_HYPERDRIVE") == "1" { + UpgradeHyperdriveHeight = math.MaxInt64 } Devnet = false diff --git a/build/params_nerpanet.go b/build/params_nerpanet.go index fb6cfc47a..069016080 100644 --- a/build/params_nerpanet.go +++ b/build/params_nerpanet.go @@ -27,7 +27,7 @@ const UpgradeRefuelHeight = -3 const UpgradeLiftoffHeight = -5 -const UpgradeActorsV2Height = 30 // critical: the network can bootstrap from v1 only +const UpgradeAssemblyHeight = 30 // critical: the network can bootstrap from v1 only const UpgradeTapeHeight = 60 const UpgradeKumquatHeight = 90 @@ -39,9 +39,10 @@ const UpgradeClausHeight = 250 const UpgradeOrangeHeight = 300 -const UpgradeActorsV3Height = 600 +const UpgradeTrustHeight = 600 const UpgradeNorwegianHeight = 999999 -const UpgradeActorsV4Height = 99999999 +const UpgradeTurboHeight = 99999999 +const UpgradeHyperdriveHeight = 999999999 func init() { // Minimum block production power is set to 4 TiB diff --git a/build/params_shared_vals.go b/build/params_shared_vals.go index 92bbc5db9..e4240ccce 100644 --- a/build/params_shared_vals.go +++ b/build/params_shared_vals.go @@ -25,7 +25,7 @@ const UnixfsLinksPerLevel = 1024 // Consensus / Network const AllowableClockDriftSecs = uint64(1) -const NewestNetworkVersion = network.Version11 +const NewestNetworkVersion = network.Version13 const ActorUpgradeNetworkVersion = network.Version4 // Epochs diff --git a/build/params_testground.go b/build/params_testground.go index 7da3c2272..252d23e75 100644 --- a/build/params_testground.go +++ b/build/params_testground.go @@ -82,20 +82,21 @@ var ( UpgradeBreezeHeight abi.ChainEpoch = -1 BreezeGasTampingDuration abi.ChainEpoch = 0 - UpgradeSmokeHeight abi.ChainEpoch = -1 - UpgradeIgnitionHeight abi.ChainEpoch = -2 - UpgradeRefuelHeight abi.ChainEpoch = -3 - UpgradeTapeHeight abi.ChainEpoch = -4 - UpgradeActorsV2Height abi.ChainEpoch = 10 - UpgradeLiftoffHeight abi.ChainEpoch = -5 - UpgradeKumquatHeight abi.ChainEpoch = -6 - UpgradeCalicoHeight abi.ChainEpoch = -7 - UpgradePersianHeight abi.ChainEpoch = -8 - UpgradeOrangeHeight abi.ChainEpoch = -9 - UpgradeClausHeight abi.ChainEpoch = -10 - UpgradeActorsV3Height abi.ChainEpoch = -11 - UpgradeNorwegianHeight abi.ChainEpoch = -12 - UpgradeActorsV4Height abi.ChainEpoch = -13 + UpgradeSmokeHeight abi.ChainEpoch = -1 + UpgradeIgnitionHeight abi.ChainEpoch = -2 + UpgradeRefuelHeight abi.ChainEpoch = -3 + UpgradeTapeHeight abi.ChainEpoch = -4 + UpgradeAssemblyHeight abi.ChainEpoch = 10 + UpgradeLiftoffHeight abi.ChainEpoch = -5 + UpgradeKumquatHeight abi.ChainEpoch = -6 + UpgradeCalicoHeight abi.ChainEpoch = -7 + UpgradePersianHeight abi.ChainEpoch = -8 + UpgradeOrangeHeight abi.ChainEpoch = -9 + UpgradeClausHeight abi.ChainEpoch = -10 + UpgradeTrustHeight abi.ChainEpoch = -11 + UpgradeNorwegianHeight abi.ChainEpoch = -12 + UpgradeTurboHeight abi.ChainEpoch = -13 + UpgradeHyperdriveHeight abi.ChainEpoch = -13 DrandSchedule = map[abi.ChainEpoch]DrandEnum{ 0: DrandMainnet, diff --git a/chain/actors/agen/main.go b/chain/actors/agen/main.go index 7269d9ae5..d34b43ca8 100644 --- a/chain/actors/agen/main.go +++ b/chain/actors/agen/main.go @@ -11,15 +11,16 @@ import ( "golang.org/x/xerrors" ) -var latestVersion = 4 +var latestVersion = 5 -var versions = []int{0, 2, 3, latestVersion} +var versions = []int{0, 2, 3, 4, latestVersion} var versionImports = map[int]string{ 0: "/", 2: "/v2/", 3: "/v3/", - latestVersion: "/v4/", + 4: "/v4/", + latestVersion: "/v5/", } var actors = map[string][]int{ diff --git a/chain/actors/builtin/account/account.go b/chain/actors/builtin/account/account.go index 8242e300d..7ac8f62d0 100644 --- a/chain/actors/builtin/account/account.go +++ b/chain/actors/builtin/account/account.go @@ -18,6 +18,8 @@ import ( builtin3 "github.com/filecoin-project/specs-actors/v3/actors/builtin" builtin4 "github.com/filecoin-project/specs-actors/v4/actors/builtin" + + builtin5 "github.com/filecoin-project/specs-actors/v5/actors/builtin" ) func init() { @@ -37,6 +39,10 @@ func init() { builtin.RegisterActorState(builtin4.AccountActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load4(store, root) }) + + builtin.RegisterActorState(builtin5.AccountActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { + return load5(store, root) + }) } var Methods = builtin4.MethodsAccount @@ -56,6 +62,9 @@ func Load(store adt.Store, act *types.Actor) (State, error) { case builtin4.AccountActorCodeID: return load4(store, act.Head) + case builtin5.AccountActorCodeID: + return load5(store, act.Head) + } return nil, xerrors.Errorf("unknown actor code %s", act.Code) } diff --git a/chain/actors/builtin/account/v5.go b/chain/actors/builtin/account/v5.go new file mode 100644 index 000000000..e2df5904d --- /dev/null +++ b/chain/actors/builtin/account/v5.go @@ -0,0 +1,30 @@ +package account + +import ( + "github.com/filecoin-project/go-address" + "github.com/ipfs/go-cid" + + "github.com/filecoin-project/lotus/chain/actors/adt" + + account5 "github.com/filecoin-project/specs-actors/v5/actors/builtin/account" +) + +var _ State = (*state5)(nil) + +func load5(store adt.Store, root cid.Cid) (State, error) { + out := state5{store: store} + err := store.Get(store.Context(), root, &out) + if err != nil { + return nil, err + } + return &out, nil +} + +type state5 struct { + account5.State + store adt.Store +} + +func (s *state5) PubkeyAddress() (address.Address, error) { + return s.Address, nil +} diff --git a/chain/actors/builtin/builtin.go b/chain/actors/builtin/builtin.go index 5e34c015a..74d622819 100644 --- a/chain/actors/builtin/builtin.go +++ b/chain/actors/builtin/builtin.go @@ -17,46 +17,49 @@ import ( builtin4 "github.com/filecoin-project/specs-actors/v4/actors/builtin" smoothing4 "github.com/filecoin-project/specs-actors/v4/actors/util/smoothing" + builtin5 "github.com/filecoin-project/specs-actors/v5/actors/builtin" + smoothing5 "github.com/filecoin-project/specs-actors/v5/actors/util/smoothing" + "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/cbor" "github.com/filecoin-project/lotus/chain/actors/adt" "github.com/filecoin-project/lotus/chain/types" - miner4 "github.com/filecoin-project/specs-actors/v4/actors/builtin/miner" - proof4 "github.com/filecoin-project/specs-actors/v4/actors/runtime/proof" + miner5 "github.com/filecoin-project/specs-actors/v5/actors/builtin/miner" + proof5 "github.com/filecoin-project/specs-actors/v5/actors/runtime/proof" ) -var SystemActorAddr = builtin4.SystemActorAddr -var BurntFundsActorAddr = builtin4.BurntFundsActorAddr -var CronActorAddr = builtin4.CronActorAddr +var SystemActorAddr = builtin5.SystemActorAddr +var BurntFundsActorAddr = builtin5.BurntFundsActorAddr +var CronActorAddr = builtin5.CronActorAddr var SaftAddress = makeAddress("t0122") var ReserveAddress = makeAddress("t090") var RootVerifierAddress = makeAddress("t080") var ( - ExpectedLeadersPerEpoch = builtin4.ExpectedLeadersPerEpoch + ExpectedLeadersPerEpoch = builtin5.ExpectedLeadersPerEpoch ) const ( - EpochDurationSeconds = builtin4.EpochDurationSeconds - EpochsInDay = builtin4.EpochsInDay - SecondsInDay = builtin4.SecondsInDay + EpochDurationSeconds = builtin5.EpochDurationSeconds + EpochsInDay = builtin5.EpochsInDay + SecondsInDay = builtin5.SecondsInDay ) const ( - MethodSend = builtin4.MethodSend - MethodConstructor = builtin4.MethodConstructor + MethodSend = builtin5.MethodSend + MethodConstructor = builtin5.MethodConstructor ) // These are all just type aliases across actor versions. In the future, that might change // and we might need to do something fancier. -type SectorInfo = proof4.SectorInfo -type PoStProof = proof4.PoStProof +type SectorInfo = proof5.SectorInfo +type PoStProof = proof5.PoStProof type FilterEstimate = smoothing0.FilterEstimate func QAPowerForWeight(size abi.SectorSize, duration abi.ChainEpoch, dealWeight, verifiedWeight abi.DealWeight) abi.StoragePower { - return miner4.QAPowerForWeight(size, duration, dealWeight, verifiedWeight) + return miner5.QAPowerForWeight(size, duration, dealWeight, verifiedWeight) } func FromV0FilterEstimate(v0 smoothing0.FilterEstimate) FilterEstimate { @@ -83,6 +86,12 @@ func FromV4FilterEstimate(v4 smoothing4.FilterEstimate) FilterEstimate { } +func FromV5FilterEstimate(v5 smoothing5.FilterEstimate) FilterEstimate { + + return (FilterEstimate)(v5) + +} + type ActorStateLoader func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) var ActorStateLoaders = make(map[cid.Cid]ActorStateLoader) @@ -114,6 +123,9 @@ func ActorNameByCode(c cid.Cid) string { case builtin4.IsBuiltinActor(c): return builtin4.ActorNameByCode(c) + case builtin5.IsBuiltinActor(c): + return builtin5.ActorNameByCode(c) + default: return "" } @@ -137,6 +149,10 @@ func IsBuiltinActor(c cid.Cid) bool { return true } + if builtin5.IsBuiltinActor(c) { + return true + } + return false } @@ -158,6 +174,10 @@ func IsAccountActor(c cid.Cid) bool { return true } + if c == builtin5.AccountActorCodeID { + return true + } + return false } @@ -179,6 +199,10 @@ func IsStorageMinerActor(c cid.Cid) bool { return true } + if c == builtin5.StorageMinerActorCodeID { + return true + } + return false } @@ -200,6 +224,10 @@ func IsMultisigActor(c cid.Cid) bool { return true } + if c == builtin5.MultisigActorCodeID { + return true + } + return false } @@ -221,6 +249,10 @@ func IsPaymentChannelActor(c cid.Cid) bool { return true } + if c == builtin5.PaymentChannelActorCodeID { + return true + } + return false } diff --git a/chain/actors/builtin/cron/cron.go b/chain/actors/builtin/cron/cron.go index 52a9fab07..a601f2b1e 100644 --- a/chain/actors/builtin/cron/cron.go +++ b/chain/actors/builtin/cron/cron.go @@ -1,10 +1,10 @@ package cron import ( - builtin4 "github.com/filecoin-project/specs-actors/v4/actors/builtin" + builtin5 "github.com/filecoin-project/specs-actors/v5/actors/builtin" ) var ( - Address = builtin4.CronActorAddr - Methods = builtin4.MethodsCron + Address = builtin5.CronActorAddr + Methods = builtin5.MethodsCron ) diff --git a/chain/actors/builtin/init/init.go b/chain/actors/builtin/init/init.go index 730d21fd8..605f2d103 100644 --- a/chain/actors/builtin/init/init.go +++ b/chain/actors/builtin/init/init.go @@ -20,6 +20,8 @@ import ( builtin3 "github.com/filecoin-project/specs-actors/v3/actors/builtin" builtin4 "github.com/filecoin-project/specs-actors/v4/actors/builtin" + + builtin5 "github.com/filecoin-project/specs-actors/v5/actors/builtin" ) func init() { @@ -39,11 +41,15 @@ func init() { builtin.RegisterActorState(builtin4.InitActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load4(store, root) }) + + builtin.RegisterActorState(builtin5.InitActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { + return load5(store, root) + }) } var ( - Address = builtin4.InitActorAddr - Methods = builtin4.MethodsInit + Address = builtin5.InitActorAddr + Methods = builtin5.MethodsInit ) func Load(store adt.Store, act *types.Actor) (State, error) { @@ -61,6 +67,9 @@ func Load(store adt.Store, act *types.Actor) (State, error) { case builtin4.InitActorCodeID: return load4(store, act.Head) + case builtin5.InitActorCodeID: + return load5(store, act.Head) + } return nil, xerrors.Errorf("unknown actor code %s", act.Code) } diff --git a/chain/actors/builtin/init/v5.go b/chain/actors/builtin/init/v5.go new file mode 100644 index 000000000..01c20fc23 --- /dev/null +++ b/chain/actors/builtin/init/v5.go @@ -0,0 +1,87 @@ +package init + +import ( + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + "github.com/ipfs/go-cid" + cbg "github.com/whyrusleeping/cbor-gen" + "golang.org/x/xerrors" + + "github.com/filecoin-project/lotus/chain/actors/adt" + "github.com/filecoin-project/lotus/node/modules/dtypes" + + builtin5 "github.com/filecoin-project/specs-actors/v5/actors/builtin" + + init5 "github.com/filecoin-project/specs-actors/v5/actors/builtin/init" + adt5 "github.com/filecoin-project/specs-actors/v5/actors/util/adt" +) + +var _ State = (*state5)(nil) + +func load5(store adt.Store, root cid.Cid) (State, error) { + out := state5{store: store} + err := store.Get(store.Context(), root, &out) + if err != nil { + return nil, err + } + return &out, nil +} + +type state5 struct { + init5.State + store adt.Store +} + +func (s *state5) ResolveAddress(address address.Address) (address.Address, bool, error) { + return s.State.ResolveAddress(s.store, address) +} + +func (s *state5) MapAddressToNewID(address address.Address) (address.Address, error) { + return s.State.MapAddressToNewID(s.store, address) +} + +func (s *state5) ForEachActor(cb func(id abi.ActorID, address address.Address) error) error { + addrs, err := adt5.AsMap(s.store, s.State.AddressMap, builtin5.DefaultHamtBitwidth) + if err != nil { + return err + } + var actorID cbg.CborInt + return addrs.ForEach(&actorID, func(key string) error { + addr, err := address.NewFromBytes([]byte(key)) + if err != nil { + return err + } + return cb(abi.ActorID(actorID), addr) + }) +} + +func (s *state5) NetworkName() (dtypes.NetworkName, error) { + return dtypes.NetworkName(s.State.NetworkName), nil +} + +func (s *state5) SetNetworkName(name string) error { + s.State.NetworkName = name + return nil +} + +func (s *state5) Remove(addrs ...address.Address) (err error) { + m, err := adt5.AsMap(s.store, s.State.AddressMap, builtin5.DefaultHamtBitwidth) + if err != nil { + return err + } + for _, addr := range addrs { + if err = m.Delete(abi.AddrKey(addr)); err != nil { + return xerrors.Errorf("failed to delete entry for address: %s; err: %w", addr, err) + } + } + amr, err := m.Root() + if err != nil { + return xerrors.Errorf("failed to get address map root: %w", err) + } + s.State.AddressMap = amr + return nil +} + +func (s *state5) addressMap() (adt.Map, error) { + return adt5.AsMap(s.store, s.AddressMap, builtin5.DefaultHamtBitwidth) +} diff --git a/chain/actors/builtin/market/market.go b/chain/actors/builtin/market/market.go index 63e8c42d3..8f5964ecc 100644 --- a/chain/actors/builtin/market/market.go +++ b/chain/actors/builtin/market/market.go @@ -19,6 +19,8 @@ import ( builtin4 "github.com/filecoin-project/specs-actors/v4/actors/builtin" + builtin5 "github.com/filecoin-project/specs-actors/v5/actors/builtin" + "github.com/filecoin-project/lotus/chain/actors/adt" "github.com/filecoin-project/lotus/chain/actors/builtin" "github.com/filecoin-project/lotus/chain/types" @@ -41,11 +43,15 @@ func init() { builtin.RegisterActorState(builtin4.StorageMarketActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load4(store, root) }) + + builtin.RegisterActorState(builtin5.StorageMarketActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { + return load5(store, root) + }) } var ( - Address = builtin4.StorageMarketActorAddr - Methods = builtin4.MethodsMarket + Address = builtin5.StorageMarketActorAddr + Methods = builtin5.MethodsMarket ) func Load(store adt.Store, act *types.Actor) (State, error) { @@ -63,6 +69,9 @@ func Load(store adt.Store, act *types.Actor) (State, error) { case builtin4.StorageMarketActorCodeID: return load4(store, act.Head) + case builtin5.StorageMarketActorCodeID: + return load5(store, act.Head) + } return nil, xerrors.Errorf("unknown actor code %s", act.Code) } diff --git a/chain/actors/builtin/market/v5.go b/chain/actors/builtin/market/v5.go new file mode 100644 index 000000000..9acd3d57e --- /dev/null +++ b/chain/actors/builtin/market/v5.go @@ -0,0 +1,209 @@ +package market + +import ( + "bytes" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + "github.com/ipfs/go-cid" + cbg "github.com/whyrusleeping/cbor-gen" + + "github.com/filecoin-project/lotus/chain/actors/adt" + "github.com/filecoin-project/lotus/chain/types" + + market5 "github.com/filecoin-project/specs-actors/v5/actors/builtin/market" + adt5 "github.com/filecoin-project/specs-actors/v5/actors/util/adt" +) + +var _ State = (*state5)(nil) + +func load5(store adt.Store, root cid.Cid) (State, error) { + out := state5{store: store} + err := store.Get(store.Context(), root, &out) + if err != nil { + return nil, err + } + return &out, nil +} + +type state5 struct { + market5.State + store adt.Store +} + +func (s *state5) TotalLocked() (abi.TokenAmount, error) { + fml := types.BigAdd(s.TotalClientLockedCollateral, s.TotalProviderLockedCollateral) + fml = types.BigAdd(fml, s.TotalClientStorageFee) + return fml, nil +} + +func (s *state5) BalancesChanged(otherState State) (bool, error) { + otherState5, ok := otherState.(*state5) + if !ok { + // there's no way to compare different versions of the state, so let's + // just say that means the state of balances has changed + return true, nil + } + return !s.State.EscrowTable.Equals(otherState5.State.EscrowTable) || !s.State.LockedTable.Equals(otherState5.State.LockedTable), nil +} + +func (s *state5) StatesChanged(otherState State) (bool, error) { + otherState5, ok := otherState.(*state5) + if !ok { + // there's no way to compare different versions of the state, so let's + // just say that means the state of balances has changed + return true, nil + } + return !s.State.States.Equals(otherState5.State.States), nil +} + +func (s *state5) States() (DealStates, error) { + stateArray, err := adt5.AsArray(s.store, s.State.States, market5.StatesAmtBitwidth) + if err != nil { + return nil, err + } + return &dealStates5{stateArray}, nil +} + +func (s *state5) ProposalsChanged(otherState State) (bool, error) { + otherState5, ok := otherState.(*state5) + if !ok { + // there's no way to compare different versions of the state, so let's + // just say that means the state of balances has changed + return true, nil + } + return !s.State.Proposals.Equals(otherState5.State.Proposals), nil +} + +func (s *state5) Proposals() (DealProposals, error) { + proposalArray, err := adt5.AsArray(s.store, s.State.Proposals, market5.ProposalsAmtBitwidth) + if err != nil { + return nil, err + } + return &dealProposals5{proposalArray}, nil +} + +func (s *state5) EscrowTable() (BalanceTable, error) { + bt, err := adt5.AsBalanceTable(s.store, s.State.EscrowTable) + if err != nil { + return nil, err + } + return &balanceTable5{bt}, nil +} + +func (s *state5) LockedTable() (BalanceTable, error) { + bt, err := adt5.AsBalanceTable(s.store, s.State.LockedTable) + if err != nil { + return nil, err + } + return &balanceTable5{bt}, nil +} + +func (s *state5) VerifyDealsForActivation( + minerAddr address.Address, deals []abi.DealID, currEpoch, sectorExpiry abi.ChainEpoch, +) (weight, verifiedWeight abi.DealWeight, err error) { + w, vw, _, err := market5.ValidateDealsForActivation(&s.State, s.store, deals, minerAddr, sectorExpiry, currEpoch) + return w, vw, err +} + +func (s *state5) NextID() (abi.DealID, error) { + return s.State.NextID, nil +} + +type balanceTable5 struct { + *adt5.BalanceTable +} + +func (bt *balanceTable5) ForEach(cb func(address.Address, abi.TokenAmount) error) error { + asMap := (*adt5.Map)(bt.BalanceTable) + var ta abi.TokenAmount + return asMap.ForEach(&ta, func(key string) error { + a, err := address.NewFromBytes([]byte(key)) + if err != nil { + return err + } + return cb(a, ta) + }) +} + +type dealStates5 struct { + adt.Array +} + +func (s *dealStates5) Get(dealID abi.DealID) (*DealState, bool, error) { + var deal5 market5.DealState + found, err := s.Array.Get(uint64(dealID), &deal5) + if err != nil { + return nil, false, err + } + if !found { + return nil, false, nil + } + deal := fromV5DealState(deal5) + return &deal, true, nil +} + +func (s *dealStates5) ForEach(cb func(dealID abi.DealID, ds DealState) error) error { + var ds5 market5.DealState + return s.Array.ForEach(&ds5, func(idx int64) error { + return cb(abi.DealID(idx), fromV5DealState(ds5)) + }) +} + +func (s *dealStates5) decode(val *cbg.Deferred) (*DealState, error) { + var ds5 market5.DealState + if err := ds5.UnmarshalCBOR(bytes.NewReader(val.Raw)); err != nil { + return nil, err + } + ds := fromV5DealState(ds5) + return &ds, nil +} + +func (s *dealStates5) array() adt.Array { + return s.Array +} + +func fromV5DealState(v5 market5.DealState) DealState { + return (DealState)(v5) +} + +type dealProposals5 struct { + adt.Array +} + +func (s *dealProposals5) Get(dealID abi.DealID) (*DealProposal, bool, error) { + var proposal5 market5.DealProposal + found, err := s.Array.Get(uint64(dealID), &proposal5) + if err != nil { + return nil, false, err + } + if !found { + return nil, false, nil + } + proposal := fromV5DealProposal(proposal5) + return &proposal, true, nil +} + +func (s *dealProposals5) ForEach(cb func(dealID abi.DealID, dp DealProposal) error) error { + var dp5 market5.DealProposal + return s.Array.ForEach(&dp5, func(idx int64) error { + return cb(abi.DealID(idx), fromV5DealProposal(dp5)) + }) +} + +func (s *dealProposals5) decode(val *cbg.Deferred) (*DealProposal, error) { + var dp5 market5.DealProposal + if err := dp5.UnmarshalCBOR(bytes.NewReader(val.Raw)); err != nil { + return nil, err + } + dp := fromV5DealProposal(dp5) + return &dp, nil +} + +func (s *dealProposals5) array() adt.Array { + return s.Array +} + +func fromV5DealProposal(v5 market5.DealProposal) DealProposal { + return (DealProposal)(v5) +} diff --git a/chain/actors/builtin/miner/miner.go b/chain/actors/builtin/miner/miner.go index a426e063b..195e8f177 100644 --- a/chain/actors/builtin/miner/miner.go +++ b/chain/actors/builtin/miner/miner.go @@ -29,6 +29,8 @@ import ( builtin3 "github.com/filecoin-project/specs-actors/v3/actors/builtin" builtin4 "github.com/filecoin-project/specs-actors/v4/actors/builtin" + + builtin5 "github.com/filecoin-project/specs-actors/v5/actors/builtin" ) func init() { @@ -49,9 +51,13 @@ func init() { return load4(store, root) }) + builtin.RegisterActorState(builtin5.StorageMinerActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { + return load5(store, root) + }) + } -var Methods = builtin4.MethodsMiner +var Methods = builtin5.MethodsMiner // Unchanged between v0, v2, v3, and v4 actors var WPoStProvingPeriod = miner0.WPoStProvingPeriod @@ -82,6 +88,9 @@ func Load(store adt.Store, act *types.Actor) (State, error) { case builtin4.StorageMinerActorCodeID: return load4(store, act.Head) + case builtin5.StorageMinerActorCodeID: + return load5(store, act.Head) + } return nil, xerrors.Errorf("unknown actor code %s", act.Code) } diff --git a/chain/actors/builtin/miner/v5.go b/chain/actors/builtin/miner/v5.go new file mode 100644 index 000000000..eea00b6e9 --- /dev/null +++ b/chain/actors/builtin/miner/v5.go @@ -0,0 +1,445 @@ +package miner + +import ( + "bytes" + "errors" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-bitfield" + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/dline" + "github.com/ipfs/go-cid" + "github.com/libp2p/go-libp2p-core/peer" + cbg "github.com/whyrusleeping/cbor-gen" + "golang.org/x/xerrors" + + "github.com/filecoin-project/lotus/chain/actors/adt" + + builtin5 "github.com/filecoin-project/specs-actors/v5/actors/builtin" + + miner5 "github.com/filecoin-project/specs-actors/v5/actors/builtin/miner" + adt5 "github.com/filecoin-project/specs-actors/v5/actors/util/adt" +) + +var _ State = (*state5)(nil) + +func load5(store adt.Store, root cid.Cid) (State, error) { + out := state5{store: store} + err := store.Get(store.Context(), root, &out) + if err != nil { + return nil, err + } + return &out, nil +} + +type state5 struct { + miner5.State + store adt.Store +} + +type deadline5 struct { + miner5.Deadline + store adt.Store +} + +type partition5 struct { + miner5.Partition + store adt.Store +} + +func (s *state5) AvailableBalance(bal abi.TokenAmount) (available abi.TokenAmount, err error) { + defer func() { + if r := recover(); r != nil { + err = xerrors.Errorf("failed to get available balance: %w", r) + available = abi.NewTokenAmount(0) + } + }() + // this panics if the miner doesnt have enough funds to cover their locked pledge + available, err = s.GetAvailableBalance(bal) + return available, err +} + +func (s *state5) VestedFunds(epoch abi.ChainEpoch) (abi.TokenAmount, error) { + return s.CheckVestedFunds(s.store, epoch) +} + +func (s *state5) LockedFunds() (LockedFunds, error) { + return LockedFunds{ + VestingFunds: s.State.LockedFunds, + InitialPledgeRequirement: s.State.InitialPledge, + PreCommitDeposits: s.State.PreCommitDeposits, + }, nil +} + +func (s *state5) FeeDebt() (abi.TokenAmount, error) { + return s.State.FeeDebt, nil +} + +func (s *state5) InitialPledge() (abi.TokenAmount, error) { + return s.State.InitialPledge, nil +} + +func (s *state5) PreCommitDeposits() (abi.TokenAmount, error) { + return s.State.PreCommitDeposits, nil +} + +func (s *state5) GetSector(num abi.SectorNumber) (*SectorOnChainInfo, error) { + info, ok, err := s.State.GetSector(s.store, num) + if !ok || err != nil { + return nil, err + } + + ret := fromV5SectorOnChainInfo(*info) + return &ret, nil +} + +func (s *state5) FindSector(num abi.SectorNumber) (*SectorLocation, error) { + dlIdx, partIdx, err := s.State.FindSector(s.store, num) + if err != nil { + return nil, err + } + return &SectorLocation{ + Deadline: dlIdx, + Partition: partIdx, + }, nil +} + +func (s *state5) NumLiveSectors() (uint64, error) { + dls, err := s.State.LoadDeadlines(s.store) + if err != nil { + return 0, err + } + var total uint64 + if err := dls.ForEach(s.store, func(dlIdx uint64, dl *miner5.Deadline) error { + total += dl.LiveSectors + return nil + }); err != nil { + return 0, err + } + return total, nil +} + +// GetSectorExpiration returns the effective expiration of the given sector. +// +// If the sector does not expire early, the Early expiration field is 0. +func (s *state5) GetSectorExpiration(num abi.SectorNumber) (*SectorExpiration, error) { + dls, err := s.State.LoadDeadlines(s.store) + if err != nil { + return nil, err + } + // NOTE: this can be optimized significantly. + // 1. If the sector is non-faulty, it will either expire on-time (can be + // learned from the sector info), or in the next quantized expiration + // epoch (i.e., the first element in the partition's expiration queue. + // 2. If it's faulty, it will expire early within the first 14 entries + // of the expiration queue. + stopErr := errors.New("stop") + out := SectorExpiration{} + err = dls.ForEach(s.store, func(dlIdx uint64, dl *miner5.Deadline) error { + partitions, err := dl.PartitionsArray(s.store) + if err != nil { + return err + } + quant := s.State.QuantSpecForDeadline(dlIdx) + var part miner5.Partition + return partitions.ForEach(&part, func(partIdx int64) error { + if found, err := part.Sectors.IsSet(uint64(num)); err != nil { + return err + } else if !found { + return nil + } + if found, err := part.Terminated.IsSet(uint64(num)); err != nil { + return err + } else if found { + // already terminated + return stopErr + } + + q, err := miner5.LoadExpirationQueue(s.store, part.ExpirationsEpochs, quant, miner5.PartitionExpirationAmtBitwidth) + if err != nil { + return err + } + var exp miner5.ExpirationSet + return q.ForEach(&exp, func(epoch int64) error { + if early, err := exp.EarlySectors.IsSet(uint64(num)); err != nil { + return err + } else if early { + out.Early = abi.ChainEpoch(epoch) + return nil + } + if onTime, err := exp.OnTimeSectors.IsSet(uint64(num)); err != nil { + return err + } else if onTime { + out.OnTime = abi.ChainEpoch(epoch) + return stopErr + } + return nil + }) + }) + }) + if err == stopErr { + err = nil + } + if err != nil { + return nil, err + } + if out.Early == 0 && out.OnTime == 0 { + return nil, xerrors.Errorf("failed to find sector %d", num) + } + return &out, nil +} + +func (s *state5) GetPrecommittedSector(num abi.SectorNumber) (*SectorPreCommitOnChainInfo, error) { + info, ok, err := s.State.GetPrecommittedSector(s.store, num) + if !ok || err != nil { + return nil, err + } + + ret := fromV5SectorPreCommitOnChainInfo(*info) + + return &ret, nil +} + +func (s *state5) LoadSectors(snos *bitfield.BitField) ([]*SectorOnChainInfo, error) { + sectors, err := miner5.LoadSectors(s.store, s.State.Sectors) + if err != nil { + return nil, err + } + + // If no sector numbers are specified, load all. + if snos == nil { + infos := make([]*SectorOnChainInfo, 0, sectors.Length()) + var info5 miner5.SectorOnChainInfo + if err := sectors.ForEach(&info5, func(_ int64) error { + info := fromV5SectorOnChainInfo(info5) + infos = append(infos, &info) + return nil + }); err != nil { + return nil, err + } + return infos, nil + } + + // Otherwise, load selected. + infos5, err := sectors.Load(*snos) + if err != nil { + return nil, err + } + infos := make([]*SectorOnChainInfo, len(infos5)) + for i, info5 := range infos5 { + info := fromV5SectorOnChainInfo(*info5) + infos[i] = &info + } + return infos, nil +} + +func (s *state5) IsAllocated(num abi.SectorNumber) (bool, error) { + var allocatedSectors bitfield.BitField + if err := s.store.Get(s.store.Context(), s.State.AllocatedSectors, &allocatedSectors); err != nil { + return false, err + } + + return allocatedSectors.IsSet(uint64(num)) +} + +func (s *state5) LoadDeadline(idx uint64) (Deadline, error) { + dls, err := s.State.LoadDeadlines(s.store) + if err != nil { + return nil, err + } + dl, err := dls.LoadDeadline(s.store, idx) + if err != nil { + return nil, err + } + return &deadline5{*dl, s.store}, nil +} + +func (s *state5) ForEachDeadline(cb func(uint64, Deadline) error) error { + dls, err := s.State.LoadDeadlines(s.store) + if err != nil { + return err + } + return dls.ForEach(s.store, func(i uint64, dl *miner5.Deadline) error { + return cb(i, &deadline5{*dl, s.store}) + }) +} + +func (s *state5) NumDeadlines() (uint64, error) { + return miner5.WPoStPeriodDeadlines, nil +} + +func (s *state5) DeadlinesChanged(other State) (bool, error) { + other5, ok := other.(*state5) + if !ok { + // treat an upgrade as a change, always + return true, nil + } + + return !s.State.Deadlines.Equals(other5.Deadlines), nil +} + +func (s *state5) MinerInfoChanged(other State) (bool, error) { + other0, ok := other.(*state5) + if !ok { + // treat an upgrade as a change, always + return true, nil + } + return !s.State.Info.Equals(other0.State.Info), nil +} + +func (s *state5) Info() (MinerInfo, error) { + info, err := s.State.GetInfo(s.store) + if err != nil { + return MinerInfo{}, err + } + + var pid *peer.ID + if peerID, err := peer.IDFromBytes(info.PeerId); err == nil { + pid = &peerID + } + + mi := MinerInfo{ + Owner: info.Owner, + Worker: info.Worker, + ControlAddresses: info.ControlAddresses, + + NewWorker: address.Undef, + WorkerChangeEpoch: -1, + + PeerId: pid, + Multiaddrs: info.Multiaddrs, + WindowPoStProofType: info.WindowPoStProofType, + SectorSize: info.SectorSize, + WindowPoStPartitionSectors: info.WindowPoStPartitionSectors, + ConsensusFaultElapsed: info.ConsensusFaultElapsed, + } + + if info.PendingWorkerKey != nil { + mi.NewWorker = info.PendingWorkerKey.NewWorker + mi.WorkerChangeEpoch = info.PendingWorkerKey.EffectiveAt + } + + return mi, nil +} + +func (s *state5) DeadlineInfo(epoch abi.ChainEpoch) (*dline.Info, error) { + return s.State.RecordedDeadlineInfo(epoch), nil +} + +func (s *state5) DeadlineCronActive() (bool, error) { + return s.State.DeadlineCronActive, nil +} + +func (s *state5) sectors() (adt.Array, error) { + return adt5.AsArray(s.store, s.Sectors, miner5.SectorsAmtBitwidth) +} + +func (s *state5) decodeSectorOnChainInfo(val *cbg.Deferred) (SectorOnChainInfo, error) { + var si miner5.SectorOnChainInfo + err := si.UnmarshalCBOR(bytes.NewReader(val.Raw)) + if err != nil { + return SectorOnChainInfo{}, err + } + + return fromV5SectorOnChainInfo(si), nil +} + +func (s *state5) precommits() (adt.Map, error) { + return adt5.AsMap(s.store, s.PreCommittedSectors, builtin5.DefaultHamtBitwidth) +} + +func (s *state5) decodeSectorPreCommitOnChainInfo(val *cbg.Deferred) (SectorPreCommitOnChainInfo, error) { + var sp miner5.SectorPreCommitOnChainInfo + err := sp.UnmarshalCBOR(bytes.NewReader(val.Raw)) + if err != nil { + return SectorPreCommitOnChainInfo{}, err + } + + return fromV5SectorPreCommitOnChainInfo(sp), nil +} + +func (d *deadline5) LoadPartition(idx uint64) (Partition, error) { + p, err := d.Deadline.LoadPartition(d.store, idx) + if err != nil { + return nil, err + } + return &partition5{*p, d.store}, nil +} + +func (d *deadline5) ForEachPartition(cb func(uint64, Partition) error) error { + ps, err := d.Deadline.PartitionsArray(d.store) + if err != nil { + return err + } + var part miner5.Partition + return ps.ForEach(&part, func(i int64) error { + return cb(uint64(i), &partition5{part, d.store}) + }) +} + +func (d *deadline5) PartitionsChanged(other Deadline) (bool, error) { + other5, ok := other.(*deadline5) + if !ok { + // treat an upgrade as a change, always + return true, nil + } + + return !d.Deadline.Partitions.Equals(other5.Deadline.Partitions), nil +} + +func (d *deadline5) PartitionsPoSted() (bitfield.BitField, error) { + return d.Deadline.PartitionsPoSted, nil +} + +func (d *deadline5) DisputableProofCount() (uint64, error) { + + ops, err := d.OptimisticProofsSnapshotArray(d.store) + if err != nil { + return 0, err + } + + return ops.Length(), nil + +} + +func (p *partition5) AllSectors() (bitfield.BitField, error) { + return p.Partition.Sectors, nil +} + +func (p *partition5) FaultySectors() (bitfield.BitField, error) { + return p.Partition.Faults, nil +} + +func (p *partition5) RecoveringSectors() (bitfield.BitField, error) { + return p.Partition.Recoveries, nil +} + +func fromV5SectorOnChainInfo(v5 miner5.SectorOnChainInfo) SectorOnChainInfo { + + return SectorOnChainInfo{ + SectorNumber: v5.SectorNumber, + SealProof: v5.SealProof, + SealedCID: v5.SealedCID, + DealIDs: v5.DealIDs, + Activation: v5.Activation, + Expiration: v5.Expiration, + DealWeight: v5.DealWeight, + VerifiedDealWeight: v5.VerifiedDealWeight, + InitialPledge: v5.InitialPledge, + ExpectedDayReward: v5.ExpectedDayReward, + ExpectedStoragePledge: v5.ExpectedStoragePledge, + } + +} + +func fromV5SectorPreCommitOnChainInfo(v5 miner5.SectorPreCommitOnChainInfo) SectorPreCommitOnChainInfo { + + return SectorPreCommitOnChainInfo{ + Info: (SectorPreCommitInfo)(v5.Info), + PreCommitDeposit: v5.PreCommitDeposit, + PreCommitEpoch: v5.PreCommitEpoch, + DealWeight: v5.DealWeight, + VerifiedDealWeight: v5.VerifiedDealWeight, + } + +} diff --git a/chain/actors/builtin/multisig/actor.go.template b/chain/actors/builtin/multisig/actor.go.template index 304c0610c..f741eba93 100644 --- a/chain/actors/builtin/multisig/actor.go.template +++ b/chain/actors/builtin/multisig/actor.go.template @@ -13,7 +13,7 @@ import ( "github.com/ipfs/go-cid" msig0 "github.com/filecoin-project/specs-actors/actors/builtin/multisig" - msig4 "github.com/filecoin-project/specs-actors/v4/actors/builtin/multisig" + msig{{.latestVersion}} "github.com/filecoin-project/specs-actors{{import .latestVersion}}actors/builtin/multisig" {{range .versions}} builtin{{.}} "github.com/filecoin-project/specs-actors{{import .}}actors/builtin" {{end}} @@ -95,7 +95,7 @@ type ProposeReturn = msig{{.latestVersion}}.ProposeReturn type ProposeParams = msig{{.latestVersion}}.ProposeParams func txnParams(id uint64, data *ProposalHashData) ([]byte, error) { - params := msig{{.latestVersion}}.TxnIDParams{ID: msig4.TxnID(id)} + params := msig{{.latestVersion}}.TxnIDParams{ID: msig{{.latestVersion}}.TxnID(id)} if data != nil { if data.Requester.Protocol() != address.ID { return nil, xerrors.Errorf("proposer address must be an ID address, was %s", data.Requester) diff --git a/chain/actors/builtin/multisig/message5.go b/chain/actors/builtin/multisig/message5.go new file mode 100644 index 000000000..9a8110f2c --- /dev/null +++ b/chain/actors/builtin/multisig/message5.go @@ -0,0 +1,71 @@ +package multisig + +import ( + "golang.org/x/xerrors" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + + builtin5 "github.com/filecoin-project/specs-actors/v5/actors/builtin" + init5 "github.com/filecoin-project/specs-actors/v5/actors/builtin/init" + multisig5 "github.com/filecoin-project/specs-actors/v5/actors/builtin/multisig" + + "github.com/filecoin-project/lotus/chain/actors" + init_ "github.com/filecoin-project/lotus/chain/actors/builtin/init" + "github.com/filecoin-project/lotus/chain/types" +) + +type message5 struct{ message0 } + +func (m message5) Create( + signers []address.Address, threshold uint64, + unlockStart, unlockDuration abi.ChainEpoch, + initialAmount abi.TokenAmount, +) (*types.Message, error) { + + lenAddrs := uint64(len(signers)) + + if lenAddrs < threshold { + return nil, xerrors.Errorf("cannot require signing of more addresses than provided for multisig") + } + + if threshold == 0 { + threshold = lenAddrs + } + + if m.from == address.Undef { + return nil, xerrors.Errorf("must provide source address") + } + + // Set up constructor parameters for multisig + msigParams := &multisig5.ConstructorParams{ + Signers: signers, + NumApprovalsThreshold: threshold, + UnlockDuration: unlockDuration, + StartEpoch: unlockStart, + } + + enc, actErr := actors.SerializeParams(msigParams) + if actErr != nil { + return nil, actErr + } + + // new actors are created by invoking 'exec' on the init actor with the constructor params + execParams := &init5.ExecParams{ + CodeCID: builtin5.MultisigActorCodeID, + ConstructorParams: enc, + } + + enc, actErr = actors.SerializeParams(execParams) + if actErr != nil { + return nil, actErr + } + + return &types.Message{ + To: init_.Address, + From: m.from, + Method: builtin5.MethodsInit.Exec, + Params: enc, + Value: initialAmount, + }, nil +} diff --git a/chain/actors/builtin/multisig/multisig.go b/chain/actors/builtin/multisig/multisig.go index 79b1a57d7..eafd418e0 100644 --- a/chain/actors/builtin/multisig/multisig.go +++ b/chain/actors/builtin/multisig/multisig.go @@ -13,7 +13,7 @@ import ( "github.com/ipfs/go-cid" msig0 "github.com/filecoin-project/specs-actors/actors/builtin/multisig" - msig4 "github.com/filecoin-project/specs-actors/v4/actors/builtin/multisig" + msig5 "github.com/filecoin-project/specs-actors/v5/actors/builtin/multisig" builtin0 "github.com/filecoin-project/specs-actors/actors/builtin" @@ -23,6 +23,8 @@ import ( builtin4 "github.com/filecoin-project/specs-actors/v4/actors/builtin" + builtin5 "github.com/filecoin-project/specs-actors/v5/actors/builtin" + "github.com/filecoin-project/lotus/chain/actors" "github.com/filecoin-project/lotus/chain/actors/adt" "github.com/filecoin-project/lotus/chain/actors/builtin" @@ -46,6 +48,10 @@ func init() { builtin.RegisterActorState(builtin4.MultisigActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load4(store, root) }) + + builtin.RegisterActorState(builtin5.MultisigActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { + return load5(store, root) + }) } func Load(store adt.Store, act *types.Actor) (State, error) { @@ -63,6 +69,9 @@ func Load(store adt.Store, act *types.Actor) (State, error) { case builtin4.MultisigActorCodeID: return load4(store, act.Head) + case builtin5.MultisigActorCodeID: + return load5(store, act.Head) + } return nil, xerrors.Errorf("unknown actor code %s", act.Code) } @@ -86,7 +95,7 @@ type State interface { type Transaction = msig0.Transaction -var Methods = builtin4.MethodsMultisig +var Methods = builtin5.MethodsMultisig func Message(version actors.Version, from address.Address) MessageBuilder { switch version { @@ -102,6 +111,9 @@ func Message(version actors.Version, from address.Address) MessageBuilder { case actors.Version4: return message4{message0{from}} + + case actors.Version5: + return message5{message0{from}} default: panic(fmt.Sprintf("unsupported actors version: %d", version)) } @@ -125,12 +137,12 @@ type MessageBuilder interface { } // this type is the same between v0 and v2 -type ProposalHashData = msig4.ProposalHashData -type ProposeReturn = msig4.ProposeReturn -type ProposeParams = msig4.ProposeParams +type ProposalHashData = msig5.ProposalHashData +type ProposeReturn = msig5.ProposeReturn +type ProposeParams = msig5.ProposeParams func txnParams(id uint64, data *ProposalHashData) ([]byte, error) { - params := msig4.TxnIDParams{ID: msig4.TxnID(id)} + params := msig5.TxnIDParams{ID: msig5.TxnID(id)} if data != nil { if data.Requester.Protocol() != address.ID { return nil, xerrors.Errorf("proposer address must be an ID address, was %s", data.Requester) diff --git a/chain/actors/builtin/multisig/v5.go b/chain/actors/builtin/multisig/v5.go new file mode 100644 index 000000000..e879783ba --- /dev/null +++ b/chain/actors/builtin/multisig/v5.go @@ -0,0 +1,96 @@ +package multisig + +import ( + "bytes" + "encoding/binary" + + adt5 "github.com/filecoin-project/specs-actors/v5/actors/util/adt" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + "github.com/ipfs/go-cid" + cbg "github.com/whyrusleeping/cbor-gen" + "golang.org/x/xerrors" + + "github.com/filecoin-project/lotus/chain/actors/adt" + + builtin5 "github.com/filecoin-project/specs-actors/v5/actors/builtin" + + msig5 "github.com/filecoin-project/specs-actors/v5/actors/builtin/multisig" +) + +var _ State = (*state5)(nil) + +func load5(store adt.Store, root cid.Cid) (State, error) { + out := state5{store: store} + err := store.Get(store.Context(), root, &out) + if err != nil { + return nil, err + } + return &out, nil +} + +type state5 struct { + msig5.State + store adt.Store +} + +func (s *state5) LockedBalance(currEpoch abi.ChainEpoch) (abi.TokenAmount, error) { + return s.State.AmountLocked(currEpoch - s.State.StartEpoch), nil +} + +func (s *state5) StartEpoch() (abi.ChainEpoch, error) { + return s.State.StartEpoch, nil +} + +func (s *state5) UnlockDuration() (abi.ChainEpoch, error) { + return s.State.UnlockDuration, nil +} + +func (s *state5) InitialBalance() (abi.TokenAmount, error) { + return s.State.InitialBalance, nil +} + +func (s *state5) Threshold() (uint64, error) { + return s.State.NumApprovalsThreshold, nil +} + +func (s *state5) Signers() ([]address.Address, error) { + return s.State.Signers, nil +} + +func (s *state5) ForEachPendingTxn(cb func(id int64, txn Transaction) error) error { + arr, err := adt5.AsMap(s.store, s.State.PendingTxns, builtin5.DefaultHamtBitwidth) + if err != nil { + return err + } + var out msig5.Transaction + return arr.ForEach(&out, func(key string) error { + txid, n := binary.Varint([]byte(key)) + if n <= 0 { + return xerrors.Errorf("invalid pending transaction key: %v", key) + } + return cb(txid, (Transaction)(out)) //nolint:unconvert + }) +} + +func (s *state5) PendingTxnChanged(other State) (bool, error) { + other5, ok := other.(*state5) + if !ok { + // treat an upgrade as a change, always + return true, nil + } + return !s.State.PendingTxns.Equals(other5.PendingTxns), nil +} + +func (s *state5) transactions() (adt.Map, error) { + return adt5.AsMap(s.store, s.PendingTxns, builtin5.DefaultHamtBitwidth) +} + +func (s *state5) decodeTransaction(val *cbg.Deferred) (Transaction, error) { + var tx msig5.Transaction + if err := tx.UnmarshalCBOR(bytes.NewReader(val.Raw)); err != nil { + return Transaction{}, err + } + return tx, nil +} diff --git a/chain/actors/builtin/paych/message5.go b/chain/actors/builtin/paych/message5.go new file mode 100644 index 000000000..37a2b6f04 --- /dev/null +++ b/chain/actors/builtin/paych/message5.go @@ -0,0 +1,74 @@ +package paych + +import ( + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + + builtin5 "github.com/filecoin-project/specs-actors/v5/actors/builtin" + init5 "github.com/filecoin-project/specs-actors/v5/actors/builtin/init" + paych5 "github.com/filecoin-project/specs-actors/v5/actors/builtin/paych" + + "github.com/filecoin-project/lotus/chain/actors" + init_ "github.com/filecoin-project/lotus/chain/actors/builtin/init" + "github.com/filecoin-project/lotus/chain/types" +) + +type message5 struct{ from address.Address } + +func (m message5) Create(to address.Address, initialAmount abi.TokenAmount) (*types.Message, error) { + params, aerr := actors.SerializeParams(&paych5.ConstructorParams{From: m.from, To: to}) + if aerr != nil { + return nil, aerr + } + enc, aerr := actors.SerializeParams(&init5.ExecParams{ + CodeCID: builtin5.PaymentChannelActorCodeID, + ConstructorParams: params, + }) + if aerr != nil { + return nil, aerr + } + + return &types.Message{ + To: init_.Address, + From: m.from, + Value: initialAmount, + Method: builtin5.MethodsInit.Exec, + Params: enc, + }, nil +} + +func (m message5) Update(paych address.Address, sv *SignedVoucher, secret []byte) (*types.Message, error) { + params, aerr := actors.SerializeParams(&paych5.UpdateChannelStateParams{ + Sv: *sv, + Secret: secret, + }) + if aerr != nil { + return nil, aerr + } + + return &types.Message{ + To: paych, + From: m.from, + Value: abi.NewTokenAmount(0), + Method: builtin5.MethodsPaych.UpdateChannelState, + Params: params, + }, nil +} + +func (m message5) Settle(paych address.Address) (*types.Message, error) { + return &types.Message{ + To: paych, + From: m.from, + Value: abi.NewTokenAmount(0), + Method: builtin5.MethodsPaych.Settle, + }, nil +} + +func (m message5) Collect(paych address.Address) (*types.Message, error) { + return &types.Message{ + To: paych, + From: m.from, + Value: abi.NewTokenAmount(0), + Method: builtin5.MethodsPaych.Collect, + }, nil +} diff --git a/chain/actors/builtin/paych/paych.go b/chain/actors/builtin/paych/paych.go index 30e4408d8..bafd6e94d 100644 --- a/chain/actors/builtin/paych/paych.go +++ b/chain/actors/builtin/paych/paych.go @@ -23,6 +23,8 @@ import ( builtin4 "github.com/filecoin-project/specs-actors/v4/actors/builtin" + builtin5 "github.com/filecoin-project/specs-actors/v5/actors/builtin" + "github.com/filecoin-project/lotus/chain/actors" "github.com/filecoin-project/lotus/chain/actors/adt" "github.com/filecoin-project/lotus/chain/actors/builtin" @@ -46,6 +48,10 @@ func init() { builtin.RegisterActorState(builtin4.PaymentChannelActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load4(store, root) }) + + builtin.RegisterActorState(builtin5.PaymentChannelActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { + return load5(store, root) + }) } // Load returns an abstract copy of payment channel state, irregardless of actor version @@ -64,6 +70,9 @@ func Load(store adt.Store, act *types.Actor) (State, error) { case builtin4.PaymentChannelActorCodeID: return load4(store, act.Head) + case builtin5.PaymentChannelActorCodeID: + return load5(store, act.Head) + } return nil, xerrors.Errorf("unknown actor code %s", act.Code) } @@ -114,7 +123,7 @@ func DecodeSignedVoucher(s string) (*SignedVoucher, error) { return &sv, nil } -var Methods = builtin4.MethodsPaych +var Methods = builtin5.MethodsPaych func Message(version actors.Version, from address.Address) MessageBuilder { switch version { @@ -131,6 +140,9 @@ func Message(version actors.Version, from address.Address) MessageBuilder { case actors.Version4: return message4{from} + case actors.Version5: + return message5{from} + default: panic(fmt.Sprintf("unsupported actors version: %d", version)) } diff --git a/chain/actors/builtin/paych/v5.go b/chain/actors/builtin/paych/v5.go new file mode 100644 index 000000000..f878d858c --- /dev/null +++ b/chain/actors/builtin/paych/v5.go @@ -0,0 +1,104 @@ +package paych + +import ( + "github.com/ipfs/go-cid" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/big" + + "github.com/filecoin-project/lotus/chain/actors/adt" + + paych5 "github.com/filecoin-project/specs-actors/v5/actors/builtin/paych" + adt5 "github.com/filecoin-project/specs-actors/v5/actors/util/adt" +) + +var _ State = (*state5)(nil) + +func load5(store adt.Store, root cid.Cid) (State, error) { + out := state5{store: store} + err := store.Get(store.Context(), root, &out) + if err != nil { + return nil, err + } + return &out, nil +} + +type state5 struct { + paych5.State + store adt.Store + lsAmt *adt5.Array +} + +// Channel owner, who has funded the actor +func (s *state5) From() (address.Address, error) { + return s.State.From, nil +} + +// Recipient of payouts from channel +func (s *state5) To() (address.Address, error) { + return s.State.To, nil +} + +// Height at which the channel can be `Collected` +func (s *state5) SettlingAt() (abi.ChainEpoch, error) { + return s.State.SettlingAt, nil +} + +// Amount successfully redeemed through the payment channel, paid out on `Collect()` +func (s *state5) ToSend() (abi.TokenAmount, error) { + return s.State.ToSend, nil +} + +func (s *state5) getOrLoadLsAmt() (*adt5.Array, error) { + if s.lsAmt != nil { + return s.lsAmt, nil + } + + // Get the lane state from the chain + lsamt, err := adt5.AsArray(s.store, s.State.LaneStates, paych5.LaneStatesAmtBitwidth) + if err != nil { + return nil, err + } + + s.lsAmt = lsamt + return lsamt, nil +} + +// Get total number of lanes +func (s *state5) LaneCount() (uint64, error) { + lsamt, err := s.getOrLoadLsAmt() + if err != nil { + return 0, err + } + return lsamt.Length(), nil +} + +// Iterate lane states +func (s *state5) ForEachLaneState(cb func(idx uint64, dl LaneState) error) error { + // Get the lane state from the chain + lsamt, err := s.getOrLoadLsAmt() + if err != nil { + return err + } + + // Note: we use a map instead of an array to store laneStates because the + // client sets the lane ID (the index) and potentially they could use a + // very large index. + var ls paych5.LaneState + return lsamt.ForEach(&ls, func(i int64) error { + return cb(uint64(i), &laneState5{ls}) + }) +} + +type laneState5 struct { + paych5.LaneState +} + +func (ls *laneState5) Redeemed() (big.Int, error) { + return ls.LaneState.Redeemed, nil +} + +func (ls *laneState5) Nonce() (uint64, error) { + return ls.LaneState.Nonce, nil +} diff --git a/chain/actors/builtin/power/power.go b/chain/actors/builtin/power/power.go index bf530a21a..2cf59ef02 100644 --- a/chain/actors/builtin/power/power.go +++ b/chain/actors/builtin/power/power.go @@ -21,6 +21,8 @@ import ( builtin3 "github.com/filecoin-project/specs-actors/v3/actors/builtin" builtin4 "github.com/filecoin-project/specs-actors/v4/actors/builtin" + + builtin5 "github.com/filecoin-project/specs-actors/v5/actors/builtin" ) func init() { @@ -40,11 +42,15 @@ func init() { builtin.RegisterActorState(builtin4.StoragePowerActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load4(store, root) }) + + builtin.RegisterActorState(builtin5.StoragePowerActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { + return load5(store, root) + }) } var ( - Address = builtin4.StoragePowerActorAddr - Methods = builtin4.MethodsPower + Address = builtin5.StoragePowerActorAddr + Methods = builtin5.MethodsPower ) func Load(store adt.Store, act *types.Actor) (State, error) { @@ -62,6 +68,9 @@ func Load(store adt.Store, act *types.Actor) (State, error) { case builtin4.StoragePowerActorCodeID: return load4(store, act.Head) + case builtin5.StoragePowerActorCodeID: + return load5(store, act.Head) + } return nil, xerrors.Errorf("unknown actor code %s", act.Code) } diff --git a/chain/actors/builtin/power/v5.go b/chain/actors/builtin/power/v5.go new file mode 100644 index 000000000..33a5d3b62 --- /dev/null +++ b/chain/actors/builtin/power/v5.go @@ -0,0 +1,150 @@ +package power + +import ( + "bytes" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + "github.com/ipfs/go-cid" + cbg "github.com/whyrusleeping/cbor-gen" + + "github.com/filecoin-project/lotus/chain/actors/adt" + "github.com/filecoin-project/lotus/chain/actors/builtin" + + builtin5 "github.com/filecoin-project/specs-actors/v5/actors/builtin" + + power5 "github.com/filecoin-project/specs-actors/v5/actors/builtin/power" + adt5 "github.com/filecoin-project/specs-actors/v5/actors/util/adt" +) + +var _ State = (*state5)(nil) + +func load5(store adt.Store, root cid.Cid) (State, error) { + out := state5{store: store} + err := store.Get(store.Context(), root, &out) + if err != nil { + return nil, err + } + return &out, nil +} + +type state5 struct { + power5.State + store adt.Store +} + +func (s *state5) TotalLocked() (abi.TokenAmount, error) { + return s.TotalPledgeCollateral, nil +} + +func (s *state5) TotalPower() (Claim, error) { + return Claim{ + RawBytePower: s.TotalRawBytePower, + QualityAdjPower: s.TotalQualityAdjPower, + }, nil +} + +// Committed power to the network. Includes miners below the minimum threshold. +func (s *state5) TotalCommitted() (Claim, error) { + return Claim{ + RawBytePower: s.TotalBytesCommitted, + QualityAdjPower: s.TotalQABytesCommitted, + }, nil +} + +func (s *state5) MinerPower(addr address.Address) (Claim, bool, error) { + claims, err := s.claims() + if err != nil { + return Claim{}, false, err + } + var claim power5.Claim + ok, err := claims.Get(abi.AddrKey(addr), &claim) + if err != nil { + return Claim{}, false, err + } + return Claim{ + RawBytePower: claim.RawBytePower, + QualityAdjPower: claim.QualityAdjPower, + }, ok, nil +} + +func (s *state5) MinerNominalPowerMeetsConsensusMinimum(a address.Address) (bool, error) { + return s.State.MinerNominalPowerMeetsConsensusMinimum(s.store, a) +} + +func (s *state5) TotalPowerSmoothed() (builtin.FilterEstimate, error) { + return builtin.FromV5FilterEstimate(s.State.ThisEpochQAPowerSmoothed), nil +} + +func (s *state5) MinerCounts() (uint64, uint64, error) { + return uint64(s.State.MinerAboveMinPowerCount), uint64(s.State.MinerCount), nil +} + +func (s *state5) ListAllMiners() ([]address.Address, error) { + claims, err := s.claims() + if err != nil { + return nil, err + } + + var miners []address.Address + err = claims.ForEach(nil, func(k string) error { + a, err := address.NewFromBytes([]byte(k)) + if err != nil { + return err + } + miners = append(miners, a) + return nil + }) + if err != nil { + return nil, err + } + + return miners, nil +} + +func (s *state5) ForEachClaim(cb func(miner address.Address, claim Claim) error) error { + claims, err := s.claims() + if err != nil { + return err + } + + var claim power5.Claim + return claims.ForEach(&claim, func(k string) error { + a, err := address.NewFromBytes([]byte(k)) + if err != nil { + return err + } + return cb(a, Claim{ + RawBytePower: claim.RawBytePower, + QualityAdjPower: claim.QualityAdjPower, + }) + }) +} + +func (s *state5) ClaimsChanged(other State) (bool, error) { + other5, ok := other.(*state5) + if !ok { + // treat an upgrade as a change, always + return true, nil + } + return !s.State.Claims.Equals(other5.State.Claims), nil +} + +func (s *state5) claims() (adt.Map, error) { + return adt5.AsMap(s.store, s.Claims, builtin5.DefaultHamtBitwidth) +} + +func (s *state5) decodeClaim(val *cbg.Deferred) (Claim, error) { + var ci power5.Claim + if err := ci.UnmarshalCBOR(bytes.NewReader(val.Raw)); err != nil { + return Claim{}, err + } + return fromV5Claim(ci), nil +} + +func fromV5Claim(v5 power5.Claim) Claim { + return Claim{ + RawBytePower: v5.RawBytePower, + QualityAdjPower: v5.QualityAdjPower, + } +} diff --git a/chain/actors/builtin/reward/reward.go b/chain/actors/builtin/reward/reward.go index 1037cf741..5f6131334 100644 --- a/chain/actors/builtin/reward/reward.go +++ b/chain/actors/builtin/reward/reward.go @@ -16,6 +16,8 @@ import ( builtin4 "github.com/filecoin-project/specs-actors/v4/actors/builtin" + builtin5 "github.com/filecoin-project/specs-actors/v5/actors/builtin" + "github.com/filecoin-project/lotus/chain/actors/adt" "github.com/filecoin-project/lotus/chain/actors/builtin" "github.com/filecoin-project/lotus/chain/types" @@ -38,11 +40,15 @@ func init() { builtin.RegisterActorState(builtin4.RewardActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { return load4(store, root) }) + + builtin.RegisterActorState(builtin5.RewardActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { + return load5(store, root) + }) } var ( - Address = builtin4.RewardActorAddr - Methods = builtin4.MethodsReward + Address = builtin5.RewardActorAddr + Methods = builtin5.MethodsReward ) func Load(store adt.Store, act *types.Actor) (State, error) { @@ -60,6 +66,9 @@ func Load(store adt.Store, act *types.Actor) (State, error) { case builtin4.RewardActorCodeID: return load4(store, act.Head) + case builtin5.RewardActorCodeID: + return load5(store, act.Head) + } return nil, xerrors.Errorf("unknown actor code %s", act.Code) } diff --git a/chain/actors/builtin/reward/v5.go b/chain/actors/builtin/reward/v5.go new file mode 100644 index 000000000..0dd75de73 --- /dev/null +++ b/chain/actors/builtin/reward/v5.go @@ -0,0 +1,88 @@ +package reward + +import ( + "github.com/filecoin-project/go-state-types/abi" + "github.com/ipfs/go-cid" + + "github.com/filecoin-project/lotus/chain/actors/adt" + "github.com/filecoin-project/lotus/chain/actors/builtin" + + miner5 "github.com/filecoin-project/specs-actors/v5/actors/builtin/miner" + reward5 "github.com/filecoin-project/specs-actors/v5/actors/builtin/reward" + smoothing5 "github.com/filecoin-project/specs-actors/v5/actors/util/smoothing" +) + +var _ State = (*state5)(nil) + +func load5(store adt.Store, root cid.Cid) (State, error) { + out := state5{store: store} + err := store.Get(store.Context(), root, &out) + if err != nil { + return nil, err + } + return &out, nil +} + +type state5 struct { + reward5.State + store adt.Store +} + +func (s *state5) ThisEpochReward() (abi.TokenAmount, error) { + return s.State.ThisEpochReward, nil +} + +func (s *state5) ThisEpochRewardSmoothed() (builtin.FilterEstimate, error) { + + return builtin.FilterEstimate{ + PositionEstimate: s.State.ThisEpochRewardSmoothed.PositionEstimate, + VelocityEstimate: s.State.ThisEpochRewardSmoothed.VelocityEstimate, + }, nil + +} + +func (s *state5) ThisEpochBaselinePower() (abi.StoragePower, error) { + return s.State.ThisEpochBaselinePower, nil +} + +func (s *state5) TotalStoragePowerReward() (abi.TokenAmount, error) { + return s.State.TotalStoragePowerReward, nil +} + +func (s *state5) EffectiveBaselinePower() (abi.StoragePower, error) { + return s.State.EffectiveBaselinePower, nil +} + +func (s *state5) EffectiveNetworkTime() (abi.ChainEpoch, error) { + return s.State.EffectiveNetworkTime, nil +} + +func (s *state5) CumsumBaseline() (reward5.Spacetime, error) { + return s.State.CumsumBaseline, nil +} + +func (s *state5) CumsumRealized() (reward5.Spacetime, error) { + return s.State.CumsumRealized, nil +} + +func (s *state5) InitialPledgeForPower(qaPower abi.StoragePower, networkTotalPledge abi.TokenAmount, networkQAPower *builtin.FilterEstimate, circSupply abi.TokenAmount) (abi.TokenAmount, error) { + return miner5.InitialPledgeForPower( + qaPower, + s.State.ThisEpochBaselinePower, + s.State.ThisEpochRewardSmoothed, + smoothing5.FilterEstimate{ + PositionEstimate: networkQAPower.PositionEstimate, + VelocityEstimate: networkQAPower.VelocityEstimate, + }, + circSupply, + ), nil +} + +func (s *state5) PreCommitDepositForPower(networkQAPower builtin.FilterEstimate, sectorWeight abi.StoragePower) (abi.TokenAmount, error) { + return miner5.PreCommitDepositForPower(s.State.ThisEpochRewardSmoothed, + smoothing5.FilterEstimate{ + PositionEstimate: networkQAPower.PositionEstimate, + VelocityEstimate: networkQAPower.VelocityEstimate, + }, + sectorWeight), nil +} diff --git a/chain/actors/builtin/verifreg/v5.go b/chain/actors/builtin/verifreg/v5.go new file mode 100644 index 000000000..367d38498 --- /dev/null +++ b/chain/actors/builtin/verifreg/v5.go @@ -0,0 +1,58 @@ +package verifreg + +import ( + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + "github.com/ipfs/go-cid" + + "github.com/filecoin-project/lotus/chain/actors" + "github.com/filecoin-project/lotus/chain/actors/adt" + + builtin5 "github.com/filecoin-project/specs-actors/v5/actors/builtin" + verifreg5 "github.com/filecoin-project/specs-actors/v5/actors/builtin/verifreg" + adt5 "github.com/filecoin-project/specs-actors/v5/actors/util/adt" +) + +var _ State = (*state5)(nil) + +func load5(store adt.Store, root cid.Cid) (State, error) { + out := state5{store: store} + err := store.Get(store.Context(), root, &out) + if err != nil { + return nil, err + } + return &out, nil +} + +type state5 struct { + verifreg5.State + store adt.Store +} + +func (s *state5) RootKey() (address.Address, error) { + return s.State.RootKey, nil +} + +func (s *state5) VerifiedClientDataCap(addr address.Address) (bool, abi.StoragePower, error) { + return getDataCap(s.store, actors.Version5, s.verifiedClients, addr) +} + +func (s *state5) VerifierDataCap(addr address.Address) (bool, abi.StoragePower, error) { + return getDataCap(s.store, actors.Version5, s.verifiers, addr) +} + +func (s *state5) ForEachVerifier(cb func(addr address.Address, dcap abi.StoragePower) error) error { + return forEachCap(s.store, actors.Version5, s.verifiers, cb) +} + +func (s *state5) ForEachClient(cb func(addr address.Address, dcap abi.StoragePower) error) error { + return forEachCap(s.store, actors.Version5, s.verifiedClients, cb) +} + +func (s *state5) verifiedClients() (adt.Map, error) { + return adt5.AsMap(s.store, s.VerifiedClients, builtin5.DefaultHamtBitwidth) +} + +func (s *state5) verifiers() (adt.Map, error) { + return adt5.AsMap(s.store, s.Verifiers, builtin5.DefaultHamtBitwidth) +} diff --git a/chain/actors/builtin/verifreg/verifreg.go b/chain/actors/builtin/verifreg/verifreg.go index 32f50a4ae..baca66177 100644 --- a/chain/actors/builtin/verifreg/verifreg.go +++ b/chain/actors/builtin/verifreg/verifreg.go @@ -17,6 +17,8 @@ import ( builtin4 "github.com/filecoin-project/specs-actors/v4/actors/builtin" + builtin5 "github.com/filecoin-project/specs-actors/v5/actors/builtin" + "github.com/filecoin-project/lotus/chain/actors/adt" "github.com/filecoin-project/lotus/chain/actors/builtin" "github.com/filecoin-project/lotus/chain/types" @@ -40,11 +42,15 @@ func init() { return load4(store, root) }) + builtin.RegisterActorState(builtin5.VerifiedRegistryActorCodeID, func(store adt.Store, root cid.Cid) (cbor.Marshaler, error) { + return load5(store, root) + }) + } var ( - Address = builtin4.VerifiedRegistryActorAddr - Methods = builtin4.MethodsVerifiedRegistry + Address = builtin5.VerifiedRegistryActorAddr + Methods = builtin5.MethodsVerifiedRegistry ) func Load(store adt.Store, act *types.Actor) (State, error) { @@ -62,6 +68,9 @@ func Load(store adt.Store, act *types.Actor) (State, error) { case builtin4.VerifiedRegistryActorCodeID: return load4(store, act.Head) + case builtin5.VerifiedRegistryActorCodeID: + return load5(store, act.Head) + } return nil, xerrors.Errorf("unknown actor code %s", act.Code) } diff --git a/chain/actors/policy/policy.go b/chain/actors/policy/policy.go index 164f19a76..191ffb5f5 100644 --- a/chain/actors/policy/policy.go +++ b/chain/actors/policy/policy.go @@ -27,14 +27,19 @@ import ( miner4 "github.com/filecoin-project/specs-actors/v4/actors/builtin/miner" verifreg4 "github.com/filecoin-project/specs-actors/v4/actors/builtin/verifreg" - paych4 "github.com/filecoin-project/specs-actors/v4/actors/builtin/paych" + builtin5 "github.com/filecoin-project/specs-actors/v5/actors/builtin" + market5 "github.com/filecoin-project/specs-actors/v5/actors/builtin/market" + miner5 "github.com/filecoin-project/specs-actors/v5/actors/builtin/miner" + verifreg5 "github.com/filecoin-project/specs-actors/v5/actors/builtin/verifreg" + + paych5 "github.com/filecoin-project/specs-actors/v5/actors/builtin/paych" ) const ( - ChainFinality = miner4.ChainFinality + ChainFinality = miner5.ChainFinality SealRandomnessLookback = ChainFinality - PaychSettleDelay = paych4.SettleDelay - MaxPreCommitRandomnessLookback = builtin4.EpochsInDay + SealRandomnessLookback + PaychSettleDelay = paych5.SettleDelay + MaxPreCommitRandomnessLookback = builtin5.EpochsInDay + SealRandomnessLookback ) // SetSupportedProofTypes sets supported proof types, across all actor versions. @@ -55,6 +60,10 @@ func SetSupportedProofTypes(types ...abi.RegisteredSealProof) { miner4.PreCommitSealProofTypesV7 = make(map[abi.RegisteredSealProof]struct{}, len(types)*2) miner4.PreCommitSealProofTypesV8 = make(map[abi.RegisteredSealProof]struct{}, len(types)) + miner5.PreCommitSealProofTypesV0 = make(map[abi.RegisteredSealProof]struct{}, len(types)) + miner5.PreCommitSealProofTypesV7 = make(map[abi.RegisteredSealProof]struct{}, len(types)*2) + miner5.PreCommitSealProofTypesV8 = make(map[abi.RegisteredSealProof]struct{}, len(types)) + AddSupportedProofTypes(types...) } @@ -84,6 +93,11 @@ func AddSupportedProofTypes(types ...abi.RegisteredSealProof) { miner4.PreCommitSealProofTypesV7[t+abi.RegisteredSealProof_StackedDrg2KiBV1_1] = struct{}{} miner4.PreCommitSealProofTypesV8[t+abi.RegisteredSealProof_StackedDrg2KiBV1_1] = struct{}{} + miner5.PreCommitSealProofTypesV0[t] = struct{}{} + miner5.PreCommitSealProofTypesV7[t] = struct{}{} + miner5.PreCommitSealProofTypesV7[t+abi.RegisteredSealProof_StackedDrg2KiBV1_1] = struct{}{} + miner5.PreCommitSealProofTypesV8[t+abi.RegisteredSealProof_StackedDrg2KiBV1_1] = struct{}{} + } } @@ -100,11 +114,13 @@ func SetPreCommitChallengeDelay(delay abi.ChainEpoch) { miner4.PreCommitChallengeDelay = delay + miner5.PreCommitChallengeDelay = delay + } // TODO: this function shouldn't really exist. Instead, the API should expose the precommit delay. func GetPreCommitChallengeDelay() abi.ChainEpoch { - return miner4.PreCommitChallengeDelay + return miner5.PreCommitChallengeDelay } // SetConsensusMinerMinPower sets the minimum power of an individual miner must @@ -126,6 +142,10 @@ func SetConsensusMinerMinPower(p abi.StoragePower) { policy.ConsensusMinerMinPower = p } + for _, policy := range builtin5.PoStProofPolicies { + policy.ConsensusMinerMinPower = p + } + } // SetMinVerifiedDealSize sets the minimum size of a verified deal. This should @@ -140,6 +160,8 @@ func SetMinVerifiedDealSize(size abi.StoragePower) { verifreg4.MinVerifiedDealSize = size + verifreg5.MinVerifiedDealSize = size + } func GetMaxProveCommitDuration(ver actors.Version, t abi.RegisteredSealProof) abi.ChainEpoch { @@ -161,6 +183,10 @@ func GetMaxProveCommitDuration(ver actors.Version, t abi.RegisteredSealProof) ab return miner4.MaxProveCommitDuration[t] + case actors.Version5: + + return miner5.MaxProveCommitDuration[t] + default: panic("unsupported actors version") } @@ -189,13 +215,17 @@ func DealProviderCollateralBounds( return market4.DealProviderCollateralBounds(size, verified, rawBytePower, qaPower, baselinePower, circulatingFil) + case actors.Version5: + + return market5.DealProviderCollateralBounds(size, verified, rawBytePower, qaPower, baselinePower, circulatingFil) + default: panic("unsupported actors version") } } func DealDurationBounds(pieceSize abi.PaddedPieceSize) (min, max abi.ChainEpoch) { - return market4.DealDurationBounds(pieceSize) + return market5.DealDurationBounds(pieceSize) } // Sets the challenge window and scales the proving period to match (such that @@ -222,6 +252,13 @@ func SetWPoStChallengeWindow(period abi.ChainEpoch) { // scale it if we're scaling the challenge period. miner4.WPoStDisputeWindow = period * 30 + miner5.WPoStChallengeWindow = period + miner5.WPoStProvingPeriod = period * abi.ChainEpoch(miner5.WPoStPeriodDeadlines) + + // by default, this is 2x finality which is 30 periods. + // scale it if we're scaling the challenge period. + miner5.WPoStDisputeWindow = period * 30 + } func GetWinningPoStSectorSetLookback(nwVer network.Version) abi.ChainEpoch { @@ -234,22 +271,22 @@ func GetWinningPoStSectorSetLookback(nwVer network.Version) abi.ChainEpoch { } func GetMaxSectorExpirationExtension() abi.ChainEpoch { - return miner4.MaxSectorExpirationExtension + return miner5.MaxSectorExpirationExtension } // TODO: we'll probably need to abstract over this better in the future. func GetMaxPoStPartitions(p abi.RegisteredPoStProof) (int, error) { - sectorsPerPart, err := builtin4.PoStProofWindowPoStPartitionSectors(p) + sectorsPerPart, err := builtin5.PoStProofWindowPoStPartitionSectors(p) if err != nil { return 0, err } - return int(miner4.AddressedSectorsMax / sectorsPerPart), nil + return int(miner5.AddressedSectorsMax / sectorsPerPart), nil } func GetDefaultSectorSize() abi.SectorSize { // supported sector sizes are the same across versions. - szs := make([]abi.SectorSize, 0, len(miner4.PreCommitSealProofTypesV8)) - for spt := range miner4.PreCommitSealProofTypesV8 { + szs := make([]abi.SectorSize, 0, len(miner5.PreCommitSealProofTypesV8)) + for spt := range miner5.PreCommitSealProofTypesV8 { ss, err := spt.SectorSize() if err != nil { panic(err) @@ -267,10 +304,10 @@ func GetDefaultSectorSize() abi.SectorSize { func GetSectorMaxLifetime(proof abi.RegisteredSealProof, nwVer network.Version) abi.ChainEpoch { if nwVer <= network.Version10 { - return builtin4.SealProofPoliciesV0[proof].SectorMaxLifetime + return builtin5.SealProofPoliciesV0[proof].SectorMaxLifetime } - return builtin4.SealProofPoliciesV11[proof].SectorMaxLifetime + return builtin5.SealProofPoliciesV11[proof].SectorMaxLifetime } func GetAddressedSectorsMax(nwVer network.Version) int { @@ -288,6 +325,9 @@ func GetAddressedSectorsMax(nwVer network.Version) int { case actors.Version4: return miner4.AddressedSectorsMax + case actors.Version5: + return miner5.AddressedSectorsMax + default: panic("unsupported network version") } @@ -313,6 +353,10 @@ func GetDeclarationsMax(nwVer network.Version) int { return miner4.DeclarationsMax + case actors.Version5: + + return miner5.DeclarationsMax + default: panic("unsupported network version") } diff --git a/chain/actors/version.go b/chain/actors/version.go index bd7b708bd..e6ca2e9bd 100644 --- a/chain/actors/version.go +++ b/chain/actors/version.go @@ -13,6 +13,7 @@ const ( Version2 Version = 2 Version3 Version = 3 Version4 Version = 4 + Version5 Version = 5 ) // Converts a network version into an actors adt version. @@ -26,6 +27,8 @@ func VersionForNetwork(version network.Version) Version { return Version3 case network.Version12: return Version4 + case network.Version13: + return Version5 default: panic(fmt.Sprintf("unsupported network version %d", version)) } diff --git a/chain/gen/genesis/util.go b/chain/gen/genesis/util.go index 54cc30cc1..1ebc58121 100644 --- a/chain/gen/genesis/util.go +++ b/chain/gen/genesis/util.go @@ -63,7 +63,7 @@ var GenesisNetworkVersion = func() network.Version { if build.UpgradeIgnitionHeight >= 0 { return network.Version2 } - if build.UpgradeActorsV2Height >= 0 { + if build.UpgradeAssemblyHeight >= 0 { return network.Version3 } if build.UpgradeLiftoffHeight >= 0 { diff --git a/chain/messagepool/selection.go b/chain/messagepool/selection.go index 0a836804f..6c9d506ef 100644 --- a/chain/messagepool/selection.go +++ b/chain/messagepool/selection.go @@ -701,7 +701,7 @@ func (*MessagePool) getGasPerf(gasReward *big.Int, gasLimit int64) float64 { } func isMessageMute(m *types.Message, ts *types.TipSet) bool { - if api.RunningNodeType != api.NodeFull || ts.Height() > build.UpgradeActorsV4Height { + if api.RunningNodeType != api.NodeFull || ts.Height() > build.UpgradeTurboHeight { return false } diff --git a/chain/stmgr/forks.go b/chain/stmgr/forks.go index a7b56f679..de5e91388 100644 --- a/chain/stmgr/forks.go +++ b/chain/stmgr/forks.go @@ -9,6 +9,8 @@ import ( "sync" "time" + "github.com/filecoin-project/specs-actors/v5/actors/migration/nv13" + "github.com/filecoin-project/go-state-types/rt" "github.com/filecoin-project/go-address" @@ -143,7 +145,7 @@ func DefaultUpgradeSchedule() UpgradeSchedule { Network: network.Version3, Migration: UpgradeRefuel, }, { - Height: build.UpgradeActorsV2Height, + Height: build.UpgradeAssemblyHeight, Network: network.Version4, Expensive: true, Migration: UpgradeActorsV2, @@ -172,7 +174,7 @@ func DefaultUpgradeSchedule() UpgradeSchedule { Network: network.Version9, Migration: nil, }, { - Height: build.UpgradeActorsV3Height, + Height: build.UpgradeTrustHeight, Network: network.Version10, Migration: UpgradeActorsV3, PreMigrations: []PreMigration{{ @@ -192,7 +194,7 @@ func DefaultUpgradeSchedule() UpgradeSchedule { Network: network.Version11, Migration: nil, }, { - Height: build.UpgradeActorsV4Height, + Height: build.UpgradeTurboHeight, Network: network.Version12, Migration: UpgradeActorsV4, PreMigrations: []PreMigration{{ @@ -207,7 +209,22 @@ func DefaultUpgradeSchedule() UpgradeSchedule { StopWithin: 5, }}, Expensive: true, - }} + }, { + Height: build.UpgradeHyperdriveHeight, + Network: network.Version13, + Migration: UpgradeActorsV5, + PreMigrations: []PreMigration{{ + PreMigration: PreUpgradeActorsV5, + StartWithin: 120, + DontStartWithin: 60, + StopWithin: 35, + }, { + PreMigration: PreUpgradeActorsV5, + StartWithin: 30, + DontStartWithin: 15, + StopWithin: 5, + }}, + Expensive: true}} for _, u := range updates { if u.Height < 0 { @@ -1053,7 +1070,7 @@ func upgradeActorsV3Common( // Perform the migration newHamtRoot, err := nv10.MigrateStateTree(ctx, store, stateRoot.Actors, epoch, config, migrationLogger{}, cache) if err != nil { - return cid.Undef, xerrors.Errorf("upgrading to actors v2: %w", err) + return cid.Undef, xerrors.Errorf("upgrading to actors v3: %w", err) } // Persist the result. @@ -1139,7 +1156,93 @@ func upgradeActorsV4Common( // Perform the migration newHamtRoot, err := nv12.MigrateStateTree(ctx, store, stateRoot.Actors, epoch, config, migrationLogger{}, cache) if err != nil { - return cid.Undef, xerrors.Errorf("upgrading to actors v2: %w", err) + return cid.Undef, xerrors.Errorf("upgrading to actors v4: %w", err) + } + + // Persist the result. + newRoot, err := store.Put(ctx, &types.StateRoot{ + Version: types.StateTreeVersion3, + Actors: newHamtRoot, + Info: stateRoot.Info, + }) + if err != nil { + return cid.Undef, xerrors.Errorf("failed to persist new state root: %w", err) + } + + // Persist the new tree. + + { + from := buf + to := buf.Read() + + if err := vm.Copy(ctx, from, to, newRoot); err != nil { + return cid.Undef, xerrors.Errorf("copying migrated tree: %w", err) + } + } + + return newRoot, nil +} + +func UpgradeActorsV5(ctx context.Context, sm *StateManager, cache MigrationCache, cb ExecCallback, root cid.Cid, epoch abi.ChainEpoch, ts *types.TipSet) (cid.Cid, error) { + // Use all the CPUs except 3. + workerCount := runtime.NumCPU() - 3 + if workerCount <= 0 { + workerCount = 1 + } + + config := nv13.Config{ + MaxWorkers: uint(workerCount), + JobQueueSize: 1000, + ResultQueueSize: 100, + ProgressLogPeriod: 10 * time.Second, + } + + newRoot, err := upgradeActorsV5Common(ctx, sm, cache, root, epoch, ts, config) + if err != nil { + return cid.Undef, xerrors.Errorf("migrating actors v5 state: %w", err) + } + + return newRoot, nil +} + +func PreUpgradeActorsV5(ctx context.Context, sm *StateManager, cache MigrationCache, root cid.Cid, epoch abi.ChainEpoch, ts *types.TipSet) error { + // Use half the CPUs for pre-migration, but leave at least 3. + workerCount := runtime.NumCPU() + if workerCount <= 4 { + workerCount = 1 + } else { + workerCount /= 2 + } + config := nv13.Config{MaxWorkers: uint(workerCount)} + _, err := upgradeActorsV5Common(ctx, sm, cache, root, epoch, ts, config) + return err +} + +func upgradeActorsV5Common( + ctx context.Context, sm *StateManager, cache MigrationCache, + root cid.Cid, epoch abi.ChainEpoch, ts *types.TipSet, + config nv13.Config, +) (cid.Cid, error) { + buf := blockstore.NewTieredBstore(sm.cs.StateBlockstore(), blockstore.NewMemorySync()) + store := store.ActorStore(ctx, buf) + + // Load the state root. + var stateRoot types.StateRoot + if err := store.Get(ctx, root, &stateRoot); err != nil { + return cid.Undef, xerrors.Errorf("failed to decode state root: %w", err) + } + + if stateRoot.Version != types.StateTreeVersion3 { + return cid.Undef, xerrors.Errorf( + "expected state root version 3 for actors v5 upgrade, got %d", + stateRoot.Version, + ) + } + + // Perform the migration + newHamtRoot, err := nv13.MigrateStateTree(ctx, store, stateRoot.Actors, epoch, config, migrationLogger{}, cache) + if err != nil { + return cid.Undef, xerrors.Errorf("upgrading to actors v5: %w", err) } // Persist the result. diff --git a/chain/stmgr/stmgr.go b/chain/stmgr/stmgr.go index ffbe08474..93832f185 100644 --- a/chain/stmgr/stmgr.go +++ b/chain/stmgr/stmgr.go @@ -1139,8 +1139,8 @@ func (sm *StateManager) GetFilVested(ctx context.Context, height abi.ChainEpoch, } } - // After UpgradeActorsV2Height these funds are accounted for in GetFilReserveDisbursed - if height <= build.UpgradeActorsV2Height { + // After UpgradeAssemblyHeight these funds are accounted for in GetFilReserveDisbursed + if height <= build.UpgradeAssemblyHeight { // continue to use preIgnitionGenInfos, nothing changed at the Ignition epoch vf = big.Add(vf, sm.genesisPledge) // continue to use preIgnitionGenInfos, nothing changed at the Ignition epoch @@ -1263,7 +1263,7 @@ func (sm *StateManager) GetVMCirculatingSupplyDetailed(ctx context.Context, heig } filReserveDisbursed := big.Zero() - if height > build.UpgradeActorsV2Height { + if height > build.UpgradeAssemblyHeight { filReserveDisbursed, err = GetFilReserveDisbursed(ctx, st) if err != nil { return api.CirculatingSupply{}, xerrors.Errorf("failed to calculate filReserveDisbursed: %w", err) diff --git a/chain/stmgr/utils.go b/chain/stmgr/utils.go index be4b9cd4f..ce6d0bf2b 100644 --- a/chain/stmgr/utils.go +++ b/chain/stmgr/utils.go @@ -9,6 +9,8 @@ import ( "runtime" "strings" + exported5 "github.com/filecoin-project/specs-actors/v5/actors/builtin/exported" + "github.com/filecoin-project/go-state-types/big" "github.com/filecoin-project/go-state-types/network" @@ -550,6 +552,7 @@ func init() { actors = append(actors, exported2.BuiltinActors()...) actors = append(actors, exported3.BuiltinActors()...) actors = append(actors, exported4.BuiltinActors()...) + actors = append(actors, exported5.BuiltinActors()...) for _, actor := range actors { exports := actor.Exports() diff --git a/chain/vm/invoker.go b/chain/vm/invoker.go index 8e0e6edd6..126b57090 100644 --- a/chain/vm/invoker.go +++ b/chain/vm/invoker.go @@ -19,6 +19,7 @@ import ( vmr "github.com/filecoin-project/specs-actors/v2/actors/runtime" exported3 "github.com/filecoin-project/specs-actors/v3/actors/builtin/exported" exported4 "github.com/filecoin-project/specs-actors/v4/actors/builtin/exported" + exported5 "github.com/filecoin-project/specs-actors/v5/actors/builtin/exported" "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/exitcode" @@ -66,6 +67,7 @@ func NewActorRegistry() *ActorRegistry { inv.Register(ActorsVersionPredicate(actors.Version2), exported2.BuiltinActors()...) inv.Register(ActorsVersionPredicate(actors.Version3), exported3.BuiltinActors()...) inv.Register(ActorsVersionPredicate(actors.Version4), exported4.BuiltinActors()...) + inv.Register(ActorsVersionPredicate(actors.Version5), exported5.BuiltinActors()...) return inv } diff --git a/chain/vm/mkactor.go b/chain/vm/mkactor.go index 11de7362b..669c1450f 100644 --- a/chain/vm/mkactor.go +++ b/chain/vm/mkactor.go @@ -18,6 +18,7 @@ import ( builtin2 "github.com/filecoin-project/specs-actors/v2/actors/builtin" builtin3 "github.com/filecoin-project/specs-actors/v3/actors/builtin" builtin4 "github.com/filecoin-project/specs-actors/v4/actors/builtin" + builtin5 "github.com/filecoin-project/specs-actors/v5/actors/builtin" "github.com/filecoin-project/go-address" "github.com/filecoin-project/lotus/chain/actors/aerrors" @@ -105,6 +106,8 @@ func newAccountActor(ver actors.Version) *types.Actor { code = builtin3.AccountActorCodeID case actors.Version4: code = builtin4.AccountActorCodeID + case actors.Version5: + code = builtin5.AccountActorCodeID default: panic("unsupported actors version") } diff --git a/cmd/tvx/codenames.go b/cmd/tvx/codenames.go index b9f590914..f8da07e8d 100644 --- a/cmd/tvx/codenames.go +++ b/cmd/tvx/codenames.go @@ -20,7 +20,7 @@ var ProtocolCodenames = []struct { {build.UpgradeSmokeHeight + 1, "smoke"}, {build.UpgradeIgnitionHeight + 1, "ignition"}, {build.UpgradeRefuelHeight + 1, "refuel"}, - {build.UpgradeActorsV2Height + 1, "actorsv2"}, + {build.UpgradeAssemblyHeight + 1, "actorsv2"}, {build.UpgradeTapeHeight + 1, "tape"}, {build.UpgradeLiftoffHeight + 1, "liftoff"}, {build.UpgradeKumquatHeight + 1, "postliftoff"}, diff --git a/cmd/tvx/codenames_test.go b/cmd/tvx/codenames_test.go index bef2e982f..e7136d6cc 100644 --- a/cmd/tvx/codenames_test.go +++ b/cmd/tvx/codenames_test.go @@ -18,7 +18,7 @@ func TestProtocolCodenames(t *testing.T) { t.Fatal("expected breeze codename") } - if height := build.UpgradeActorsV2Height + 1; GetProtocolCodename(abi.ChainEpoch(height)) != "actorsv2" { + if height := build.UpgradeAssemblyHeight + 1; GetProtocolCodename(abi.ChainEpoch(height)) != "actorsv2" { t.Fatal("expected actorsv2 codename") } diff --git a/documentation/en/api-methods.md b/documentation/en/api-methods.md index 493fd7d2b..743432cb0 100644 --- a/documentation/en/api-methods.md +++ b/documentation/en/api-methods.md @@ -4517,7 +4517,7 @@ Inputs: ] ``` -Response: `11` +Response: `13` ### StateReadState StateReadState returns the indicated actor's state. diff --git a/go.mod b/go.mod index 86e6da40d..af2ee9c3e 100644 --- a/go.mod +++ b/go.mod @@ -39,7 +39,7 @@ require ( github.com/filecoin-project/go-multistore v0.0.3 github.com/filecoin-project/go-padreader v0.0.0-20200903213702-ed5fae088b20 github.com/filecoin-project/go-paramfetch v0.0.2-0.20200701152213-3e0f0afdc261 - github.com/filecoin-project/go-state-types v0.1.0 + github.com/filecoin-project/go-state-types v0.1.1-0.20210506134452-99b279731c48 github.com/filecoin-project/go-statemachine v0.0.0-20200925024713-05bd7c71fbfe github.com/filecoin-project/go-statestore v0.1.1 github.com/filecoin-project/go-storedcounter v0.0.0-20200421200003-1c99c62e8a5b @@ -47,6 +47,7 @@ require ( github.com/filecoin-project/specs-actors/v2 v2.3.5-0.20210114162132-5b58b773f4fb github.com/filecoin-project/specs-actors/v3 v3.1.0 github.com/filecoin-project/specs-actors/v4 v4.0.0 + github.com/filecoin-project/specs-actors/v5 v5.0.0-20210510162709-3255bdd9f2bb github.com/filecoin-project/specs-storage v0.1.1-0.20201105051918-5188d9774506 github.com/filecoin-project/test-vectors/schema v0.0.5 github.com/gbrlsnchs/jwt/v3 v3.0.0-beta.1 diff --git a/go.sum b/go.sum index 8bcfafdfc..ecdab7a65 100644 --- a/go.sum +++ b/go.sum @@ -295,6 +295,8 @@ github.com/filecoin-project/go-state-types v0.0.0-20200928172055-2df22083d8ab/go github.com/filecoin-project/go-state-types v0.0.0-20201102161440-c8033295a1fc/go.mod h1:ezYnPf0bNkTsDibL/psSz5dy4B5awOJ/E7P2Saeep8g= github.com/filecoin-project/go-state-types v0.1.0 h1:9r2HCSMMCmyMfGyMKxQtv0GKp6VT/m5GgVk8EhYbLJU= github.com/filecoin-project/go-state-types v0.1.0/go.mod h1:ezYnPf0bNkTsDibL/psSz5dy4B5awOJ/E7P2Saeep8g= +github.com/filecoin-project/go-state-types v0.1.1-0.20210506134452-99b279731c48 h1:Jc4OprDp3bRDxbsrXNHPwJabZJM3iDy+ri8/1e0ZnX4= +github.com/filecoin-project/go-state-types v0.1.1-0.20210506134452-99b279731c48/go.mod h1:ezYnPf0bNkTsDibL/psSz5dy4B5awOJ/E7P2Saeep8g= github.com/filecoin-project/go-statemachine v0.0.0-20200925024713-05bd7c71fbfe h1:dF8u+LEWeIcTcfUcCf3WFVlc81Fr2JKg8zPzIbBDKDw= github.com/filecoin-project/go-statemachine v0.0.0-20200925024713-05bd7c71fbfe/go.mod h1:FGwQgZAt2Gh5mjlwJUlVB62JeYdo+if0xWxSEfBD9ig= github.com/filecoin-project/go-statestore v0.1.0/go.mod h1:LFc9hD+fRxPqiHiaqUEZOinUJB4WARkRfNl10O7kTnI= @@ -314,6 +316,8 @@ github.com/filecoin-project/specs-actors/v3 v3.1.0 h1:s4qiPw8pgypqBGAy853u/zdZJ7 github.com/filecoin-project/specs-actors/v3 v3.1.0/go.mod h1:mpynccOLlIRy0QnR008BwYBwT9fen+sPR13MA1VmMww= github.com/filecoin-project/specs-actors/v4 v4.0.0 h1:vMALksY5G3J5rj3q9rbcyB+f4Tk1xrLqSgdB3jOok4s= github.com/filecoin-project/specs-actors/v4 v4.0.0/go.mod h1:TkHXf/l7Wyw4ZejyXIPS2rK8bBO0rdwhTZyQQgaglng= +github.com/filecoin-project/specs-actors/v5 v5.0.0-20210510162709-3255bdd9f2bb h1:i2ZBHLiNYyyhNlfjfB/TGtGLlb8dgiGiVCDZlGpUtUc= +github.com/filecoin-project/specs-actors/v5 v5.0.0-20210510162709-3255bdd9f2bb/go.mod h1:XAgQWq5pu0MBwx3MI5uJ6fK/Q8jCkZnKNNLxvDcbXew= github.com/filecoin-project/specs-storage v0.1.1-0.20201105051918-5188d9774506 h1:Ur/l2+6qN+lQiqjozWWc5p9UDaAMDZKTlDS98oRnlIw= github.com/filecoin-project/specs-storage v0.1.1-0.20201105051918-5188d9774506/go.mod h1:nJRRM7Aa9XVvygr3W9k6xGF46RWzr2zxF/iGoAIfA/g= github.com/filecoin-project/test-vectors/schema v0.0.5 h1:w3zHQhzM4pYxJDl21avXjOKBLF8egrvwUwjpT8TquDg= diff --git a/lotuspond/front/src/chain/methods.json b/lotuspond/front/src/chain/methods.json index a09d3ec91..12e0e8abf 100644 --- a/lotuspond/front/src/chain/methods.json +++ b/lotuspond/front/src/chain/methods.json @@ -410,5 +410,109 @@ "AddVerifiedClient", "UseBytes", "RestoreBytes" + ], + "fil/5/account": [ + "Send", + "Constructor", + "PubkeyAddress" + ], + "fil/5/cron": [ + "Send", + "Constructor", + "EpochTick" + ], + "fil/5/init": [ + "Send", + "Constructor", + "Exec" + ], + "fil/5/multisig": [ + "Send", + "Constructor", + "Propose", + "Approve", + "Cancel", + "AddSigner", + "RemoveSigner", + "SwapSigner", + "ChangeNumApprovalsThreshold", + "LockBalance" + ], + "fil/5/paymentchannel": [ + "Send", + "Constructor", + "UpdateChannelState", + "Settle", + "Collect" + ], + "fil/5/reward": [ + "Send", + "Constructor", + "AwardBlockReward", + "ThisEpochReward", + "UpdateNetworkKPI" + ], + "fil/5/storagemarket": [ + "Send", + "Constructor", + "AddBalance", + "WithdrawBalance", + "PublishStorageDeals", + "VerifyDealsForActivation", + "ActivateDeals", + "OnMinerSectorsTerminate", + "ComputeDataCommitment", + "CronTick" + ], + "fil/5/storageminer": [ + "Send", + "Constructor", + "ControlAddresses", + "ChangeWorkerAddress", + "ChangePeerID", + "SubmitWindowedPoSt", + "PreCommitSector", + "ProveCommitSector", + "ExtendSectorExpiration", + "TerminateSectors", + "DeclareFaults", + "DeclareFaultsRecovered", + "OnDeferredCronEvent", + "CheckSectorProven", + "ApplyRewards", + "ReportConsensusFault", + "WithdrawBalance", + "ConfirmSectorProofsValid", + "ChangeMultiaddrs", + "CompactPartitions", + "CompactSectorNumbers", + "ConfirmUpdateWorkerKey", + "RepayDebt", + "ChangeOwnerAddress", + "DisputeWindowedPoSt" + ], + "fil/5/storagepower": [ + "Send", + "Constructor", + "CreateMiner", + "UpdateClaimedPower", + "EnrollCronEvent", + "OnEpochTickEnd", + "UpdatePledgeTotal", + "SubmitPoRepForBulkVerify", + "CurrentTotalPower" + ], + "fil/5/system": [ + "Send", + "Constructor" + ], + "fil/5/verifiedregistry": [ + "Send", + "Constructor", + "AddVerifier", + "RemoveVerifier", + "AddVerifiedClient", + "UseBytes", + "RestoreBytes" ] } \ No newline at end of file diff --git a/testplans/lotus-soup/init.go b/testplans/lotus-soup/init.go index 5690e803a..a8d8e7478 100644 --- a/testplans/lotus-soup/init.go +++ b/testplans/lotus-soup/init.go @@ -53,5 +53,5 @@ func init() { build.UpgradeLiftoffHeight = -3 // We need to _run_ this upgrade because genesis doesn't support v2, so // we run it at height 0. - build.UpgradeActorsV2Height = 0 + build.UpgradeAssemblyHeight = 0 } From 506f39b29457b02ffc170663ac065f8f8168d952 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 10 Mar 2021 16:16:44 +0100 Subject: [PATCH 003/160] WIP: Integrate FIP0013 --- api/api_storage.go | 2 + api/apistruct/struct.go | 12 + build/parameters.go | 4 + build/proof-params/srs-inner-product.json | 7 + chain/gen/gen.go | 24 +- chain/gen/genesis/miners.go | 6 +- chain/vm/gas.go | 30 +- chain/vm/gas_v0.go | 7 + chain/vm/invoker.go | 2 +- chain/vm/runtime.go | 8 +- chain/vm/syscalls.go | 44 +-- cli/params.go | 2 +- cmd/lotus-bench/main.go | 4 +- cmd/lotus-seal-worker/main.go | 2 +- cmd/lotus-shed/params.go | 2 +- cmd/lotus-storage-miner/info.go | 2 + cmd/lotus-storage-miner/init.go | 2 +- cmd/lotus-storage-miner/init_restore.go | 2 +- cmd/lotus-storage-miner/sectors.go | 48 ++++ cmd/lotus/daemon.go | 2 +- documentation/en/api-methods-miner.md | 20 ++ extern/filecoin-ffi | 2 +- .../sector-storage/ffiwrapper/sealer_test.go | 103 ++++++- extern/sector-storage/ffiwrapper/types.go | 12 +- .../sector-storage/ffiwrapper/verifier_cgo.go | 22 +- extern/sector-storage/mock/mock.go | 45 ++- extern/storage-sealing/commit_batch.go | 271 ++++++++++++++++++ extern/storage-sealing/fsm.go | 14 + extern/storage-sealing/fsm_events.go | 12 + extern/storage-sealing/sealiface/config.go | 4 + extern/storage-sealing/sealing.go | 10 + extern/storage-sealing/sector_state.go | 108 +++---- extern/storage-sealing/states_sealing.go | 32 +++ go.mod | 4 +- go.sum | 10 +- lotuspond/front/src/chain/methods.json | 3 +- node/config/def.go | 8 + node/impl/storminer.go | 8 + node/modules/storageminer.go | 8 +- storage/sealing.go | 8 + 40 files changed, 783 insertions(+), 133 deletions(-) create mode 100644 build/proof-params/srs-inner-product.json create mode 100644 extern/storage-sealing/commit_batch.go diff --git a/api/api_storage.go b/api/api_storage.go index 9662e8cd8..a9dec3d0e 100644 --- a/api/api_storage.go +++ b/api/api_storage.go @@ -80,6 +80,8 @@ type StorageMiner interface { // SectorTerminatePending returns a list of pending sector terminations to be sent in the next batch message SectorTerminatePending(ctx context.Context) ([]abi.SectorID, error) //perm:admin SectorMarkForUpgrade(ctx context.Context, id abi.SectorNumber) error //perm:admin + SectorCommitFlush(ctx context.Context) (*cid.Cid, error) //perm:admin + SectorCommitPending(ctx context.Context) ([]abi.SectorID, error) //perm:admin // WorkerConnect tells the node to connect to workers RPC WorkerConnect(context.Context, string) error //perm:admin retry:true diff --git a/api/apistruct/struct.go b/api/apistruct/struct.go index 21ed6d56a..6917a2967 100644 --- a/api/apistruct/struct.go +++ b/api/apistruct/struct.go @@ -639,6 +639,10 @@ type StorageMinerStruct struct { SealingSchedDiag func(p0 context.Context, p1 bool) (interface{}, error) `perm:"admin"` + SectorCommitFlush func(p0 context.Context) (*cid.Cid, error) `perm:"admin"` + + SectorCommitPending func(p0 context.Context) ([]abi.SectorID, error) `perm:"admin"` + SectorGetExpectedSealDuration func(p0 context.Context) (time.Duration, error) `perm:"read"` SectorGetSealDelay func(p0 context.Context) (time.Duration, error) `perm:"read"` @@ -1923,6 +1927,14 @@ func (s *StorageMinerStruct) SealingSchedDiag(p0 context.Context, p1 bool) (inte return s.Internal.SealingSchedDiag(p0, p1) } +func (s *StorageMinerStruct) SectorCommitFlush(p0 context.Context) (*cid.Cid, error) { + return s.Internal.SectorCommitFlush(p0) +} + +func (s *StorageMinerStruct) SectorCommitPending(p0 context.Context) ([]abi.SectorID, error) { + return s.Internal.SectorCommitPending(p0) +} + func (s *StorageMinerStruct) SectorGetExpectedSealDuration(p0 context.Context) (time.Duration, error) { return s.Internal.SectorGetExpectedSealDuration(p0) } diff --git a/build/parameters.go b/build/parameters.go index 7d34a7831..b70dad1c1 100644 --- a/build/parameters.go +++ b/build/parameters.go @@ -5,3 +5,7 @@ import rice "github.com/GeertJohan/go.rice" func ParametersJSON() []byte { return rice.MustFindBox("proof-params").MustBytes("parameters.json") } + +func SrsJSON() []byte { + return rice.MustFindBox("proof-params").MustBytes("srs-inner-product.json") +} diff --git a/build/proof-params/srs-inner-product.json b/build/proof-params/srs-inner-product.json new file mode 100644 index 000000000..8566bf5fd --- /dev/null +++ b/build/proof-params/srs-inner-product.json @@ -0,0 +1,7 @@ +{ + "v28-fil-inner-product-v1.srs": { + "cid": "Qmdq44DjcQnFfU3PJcdX7J49GCqcUYszr1TxMbHtAkvQ3g", + "digest": "ae20310138f5ba81451d723f858e3797", + "sector_size": 0 + } +} diff --git a/chain/gen/gen.go b/chain/gen/gen.go index b4e04424c..af2611676 100644 --- a/chain/gen/gen.go +++ b/chain/gen/gen.go @@ -25,7 +25,7 @@ import ( "go.opencensus.io/trace" "golang.org/x/xerrors" - proof2 "github.com/filecoin-project/specs-actors/v2/actors/runtime/proof" + proof5 "github.com/filecoin-project/specs-actors/v5/actors/runtime/proof" "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/blockstore" @@ -51,7 +51,7 @@ const msgsPerBlock = 20 //nolint:deadcode,varcheck var log = logging.Logger("gen") -var ValidWpostForTesting = []proof2.PoStProof{{ +var ValidWpostForTesting = []proof5.PoStProof{{ ProofBytes: []byte("valid proof"), }} @@ -460,7 +460,7 @@ func (cg *ChainGen) NextTipSetFromMinersWithMessages(base *types.TipSet, miners func (cg *ChainGen) makeBlock(parents *types.TipSet, m address.Address, vrfticket *types.Ticket, eticket *types.ElectionProof, bvals []types.BeaconEntry, height abi.ChainEpoch, - wpost []proof2.PoStProof, msgs []*types.SignedMessage) (*types.FullBlock, error) { + wpost []proof5.PoStProof, msgs []*types.SignedMessage) (*types.FullBlock, error) { var ts uint64 if cg.Timestamper != nil { @@ -598,7 +598,7 @@ func (mca mca) WalletSign(ctx context.Context, a address.Address, v []byte) (*cr type WinningPoStProver interface { GenerateCandidates(context.Context, abi.PoStRandomness, uint64) ([]uint64, error) - ComputeProof(context.Context, []proof2.SectorInfo, abi.PoStRandomness) ([]proof2.PoStProof, error) + ComputeProof(context.Context, []proof5.SectorInfo, abi.PoStRandomness) ([]proof5.PoStProof, error) } type wppProvider struct{} @@ -607,7 +607,7 @@ func (wpp *wppProvider) GenerateCandidates(ctx context.Context, _ abi.PoStRandom return []uint64{0}, nil } -func (wpp *wppProvider) ComputeProof(context.Context, []proof2.SectorInfo, abi.PoStRandomness) ([]proof2.PoStProof, error) { +func (wpp *wppProvider) ComputeProof(context.Context, []proof5.SectorInfo, abi.PoStRandomness) ([]proof5.PoStProof, error) { return ValidWpostForTesting, nil } @@ -685,15 +685,23 @@ type genFakeVerifier struct{} var _ ffiwrapper.Verifier = (*genFakeVerifier)(nil) -func (m genFakeVerifier) VerifySeal(svi proof2.SealVerifyInfo) (bool, error) { +func (m genFakeVerifier) VerifySeal(svi proof5.SealVerifyInfo) (bool, error) { return true, nil } -func (m genFakeVerifier) VerifyWinningPoSt(ctx context.Context, info proof2.WinningPoStVerifyInfo) (bool, error) { +func (m genFakeVerifier) VerifyAggregateSeals(aggregate proof5.AggregateSealVerifyProofAndInfos) (bool, error) { panic("not supported") } -func (m genFakeVerifier) VerifyWindowPoSt(ctx context.Context, info proof2.WindowPoStVerifyInfo) (bool, error) { +func (m genFakeVerifier) AggregateSealProofs(proofType abi.RegisteredSealProof, proofs [][]byte) ([]byte, error) { + panic("not supported") +} + +func (m genFakeVerifier) VerifyWinningPoSt(ctx context.Context, info proof5.WinningPoStVerifyInfo) (bool, error) { + panic("not supported") +} + +func (m genFakeVerifier) VerifyWindowPoSt(ctx context.Context, info proof5.WindowPoStVerifyInfo) (bool, error) { panic("not supported") } diff --git a/chain/gen/genesis/miners.go b/chain/gen/genesis/miners.go index 297543886..be33560e5 100644 --- a/chain/gen/genesis/miners.go +++ b/chain/gen/genesis/miners.go @@ -27,7 +27,7 @@ import ( miner0 "github.com/filecoin-project/specs-actors/actors/builtin/miner" power0 "github.com/filecoin-project/specs-actors/actors/builtin/power" reward0 "github.com/filecoin-project/specs-actors/actors/builtin/reward" - runtime2 "github.com/filecoin-project/specs-actors/v2/actors/runtime" + runtime5 "github.com/filecoin-project/specs-actors/v5/actors/runtime" "github.com/filecoin-project/lotus/chain/state" "github.com/filecoin-project/lotus/chain/store" @@ -46,7 +46,7 @@ func MinerAddress(genesisIndex uint64) address.Address { } type fakedSigSyscalls struct { - runtime2.Syscalls + runtime5.Syscalls } func (fss *fakedSigSyscalls) VerifySignature(signature crypto.Signature, signer address.Address, plaintext []byte) error { @@ -54,7 +54,7 @@ func (fss *fakedSigSyscalls) VerifySignature(signature crypto.Signature, signer } func mkFakedSigSyscalls(base vm.SyscallBuilder) vm.SyscallBuilder { - return func(ctx context.Context, rt *vm.Runtime) runtime2.Syscalls { + return func(ctx context.Context, rt *vm.Runtime) runtime5.Syscalls { return &fakedSigSyscalls{ base(ctx, rt), } diff --git a/chain/vm/gas.go b/chain/vm/gas.go index eef431aef..5ecc42345 100644 --- a/chain/vm/gas.go +++ b/chain/vm/gas.go @@ -9,8 +9,8 @@ import ( addr "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/crypto" - vmr2 "github.com/filecoin-project/specs-actors/v2/actors/runtime" - proof2 "github.com/filecoin-project/specs-actors/v2/actors/runtime/proof" + vmr5 "github.com/filecoin-project/specs-actors/v5/actors/runtime" + proof5 "github.com/filecoin-project/specs-actors/v5/actors/runtime/proof" "github.com/ipfs/go-cid" ) @@ -74,8 +74,9 @@ type Pricelist interface { OnVerifySignature(sigType crypto.SigType, planTextSize int) (GasCharge, error) OnHashing(dataSize int) GasCharge OnComputeUnsealedSectorCid(proofType abi.RegisteredSealProof, pieces []abi.PieceInfo) GasCharge - OnVerifySeal(info proof2.SealVerifyInfo) GasCharge - OnVerifyPost(info proof2.WindowPoStVerifyInfo) GasCharge + OnVerifySeal(info proof5.SealVerifyInfo) GasCharge + OnVerifyAggregateSeals() GasCharge + OnVerifyPost(info proof5.WindowPoStVerifyInfo) GasCharge OnVerifyConsensusFault() GasCharge } @@ -111,6 +112,7 @@ var prices = map[abi.ChainEpoch]Pricelist{ hashingBase: 31355, computeUnsealedSectorCidBase: 98647, verifySealBase: 2000, // TODO gas , it VerifySeal syscall is not used + verifyAggregateSealBase: 0, verifyPostLookup: map[abi.RegisteredPoStProof]scalingCost{ abi.RegisteredPoStProof_StackedDrgWindow512MiBV1: { flat: 123861062, @@ -158,7 +160,8 @@ var prices = map[abi.ChainEpoch]Pricelist{ hashingBase: 31355, computeUnsealedSectorCidBase: 98647, - verifySealBase: 2000, // TODO gas , it VerifySeal syscall is not used + verifySealBase: 2000, // TODO gas , it VerifySeal syscall is not used + verifyAggregateSealBase: 400_000_000, // TODO (~40ms, I think) verifyPostLookup: map[abi.RegisteredPoStProof]scalingCost{ abi.RegisteredPoStProof_StackedDrgWindow512MiBV1: { flat: 117680921, @@ -198,7 +201,7 @@ func PricelistByEpoch(epoch abi.ChainEpoch) Pricelist { } type pricedSyscalls struct { - under vmr2.Syscalls + under vmr5.Syscalls pl Pricelist chargeGas func(GasCharge) } @@ -232,7 +235,7 @@ func (ps pricedSyscalls) ComputeUnsealedSectorCID(reg abi.RegisteredSealProof, p } // Verifies a sector seal proof. -func (ps pricedSyscalls) VerifySeal(vi proof2.SealVerifyInfo) error { +func (ps pricedSyscalls) VerifySeal(vi proof5.SealVerifyInfo) error { ps.chargeGas(ps.pl.OnVerifySeal(vi)) defer ps.chargeGas(gasOnActorExec) @@ -240,7 +243,7 @@ func (ps pricedSyscalls) VerifySeal(vi proof2.SealVerifyInfo) error { } // Verifies a proof of spacetime. -func (ps pricedSyscalls) VerifyPoSt(vi proof2.WindowPoStVerifyInfo) error { +func (ps pricedSyscalls) VerifyPoSt(vi proof5.WindowPoStVerifyInfo) error { ps.chargeGas(ps.pl.OnVerifyPost(vi)) defer ps.chargeGas(gasOnActorExec) @@ -257,14 +260,14 @@ func (ps pricedSyscalls) VerifyPoSt(vi proof2.WindowPoStVerifyInfo) error { // the "parent grinding fault", in which case it must be the sibling of h1 (same parent tipset) and one of the // blocks in the parent of h2 (i.e. h2's grandparent). // Returns nil and an error if the headers don't prove a fault. -func (ps pricedSyscalls) VerifyConsensusFault(h1 []byte, h2 []byte, extra []byte) (*vmr2.ConsensusFault, error) { +func (ps pricedSyscalls) VerifyConsensusFault(h1 []byte, h2 []byte, extra []byte) (*vmr5.ConsensusFault, error) { ps.chargeGas(ps.pl.OnVerifyConsensusFault()) defer ps.chargeGas(gasOnActorExec) return ps.under.VerifyConsensusFault(h1, h2, extra) } -func (ps pricedSyscalls) BatchVerifySeals(inp map[address.Address][]proof2.SealVerifyInfo) (map[address.Address][]bool, error) { +func (ps pricedSyscalls) BatchVerifySeals(inp map[address.Address][]proof5.SealVerifyInfo) (map[address.Address][]bool, error) { count := int64(0) for _, svis := range inp { count += int64(len(svis)) @@ -277,3 +280,10 @@ func (ps pricedSyscalls) BatchVerifySeals(inp map[address.Address][]proof2.SealV return ps.under.BatchVerifySeals(inp) } + +func (ps pricedSyscalls) VerifyAggregateSeals(aggregate proof5.AggregateSealVerifyProofAndInfos) error { + ps.chargeGas(ps.pl.OnVerifyAggregateSeals()) + defer ps.chargeGas(gasOnActorExec) + + return ps.under.VerifyAggregateSeals(aggregate) +} diff --git a/chain/vm/gas_v0.go b/chain/vm/gas_v0.go index 7c864b7f9..d54760b69 100644 --- a/chain/vm/gas_v0.go +++ b/chain/vm/gas_v0.go @@ -91,6 +91,7 @@ type pricelistV0 struct { computeUnsealedSectorCidBase int64 verifySealBase int64 + verifyAggregateSealBase int64 verifyPostLookup map[abi.RegisteredPoStProof]scalingCost verifyPostDiscount bool verifyConsensusFault int64 @@ -185,6 +186,12 @@ func (pl *pricelistV0) OnVerifySeal(info proof2.SealVerifyInfo) GasCharge { return newGasCharge("OnVerifySeal", pl.verifySealBase, 0) } +// OnVerifyAggregateSeals +func (pl *pricelistV0) OnVerifyAggregateSeals() GasCharge { + // TODO: this needs more cost tunning + return newGasCharge("OnVerifyAggregateSeals", pl.verifyAggregateSealBase, 0) +} + // OnVerifyPost func (pl *pricelistV0) OnVerifyPost(info proof2.WindowPoStVerifyInfo) GasCharge { sectorSize := "unknown" diff --git a/chain/vm/invoker.go b/chain/vm/invoker.go index 126b57090..4a8032770 100644 --- a/chain/vm/invoker.go +++ b/chain/vm/invoker.go @@ -16,10 +16,10 @@ import ( exported0 "github.com/filecoin-project/specs-actors/actors/builtin/exported" exported2 "github.com/filecoin-project/specs-actors/v2/actors/builtin/exported" - vmr "github.com/filecoin-project/specs-actors/v2/actors/runtime" exported3 "github.com/filecoin-project/specs-actors/v3/actors/builtin/exported" exported4 "github.com/filecoin-project/specs-actors/v4/actors/builtin/exported" exported5 "github.com/filecoin-project/specs-actors/v5/actors/builtin/exported" + vmr "github.com/filecoin-project/specs-actors/v5/actors/runtime" "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/exitcode" diff --git a/chain/vm/runtime.go b/chain/vm/runtime.go index cdb1720de..a3e2f293f 100644 --- a/chain/vm/runtime.go +++ b/chain/vm/runtime.go @@ -16,7 +16,7 @@ import ( "github.com/filecoin-project/go-state-types/network" rtt "github.com/filecoin-project/go-state-types/rt" rt0 "github.com/filecoin-project/specs-actors/actors/runtime" - rt2 "github.com/filecoin-project/specs-actors/v2/actors/runtime" + rt5 "github.com/filecoin-project/specs-actors/v5/actors/runtime" "github.com/ipfs/go-cid" ipldcbor "github.com/ipfs/go-ipld-cbor" "go.opencensus.io/trace" @@ -54,8 +54,8 @@ func (m *Message) ValueReceived() abi.TokenAmount { var EnableGasTracing = false type Runtime struct { - rt2.Message - rt2.Syscalls + rt5.Message + rt5.Syscalls ctx context.Context @@ -136,7 +136,7 @@ func (rt *Runtime) StorePut(x cbor.Marshaler) cid.Cid { } var _ rt0.Runtime = (*Runtime)(nil) -var _ rt2.Runtime = (*Runtime)(nil) +var _ rt5.Runtime = (*Runtime)(nil) func (rt *Runtime) shimCall(f func() interface{}) (rval []byte, aerr aerrors.ActorError) { defer func() { diff --git a/chain/vm/syscalls.go b/chain/vm/syscalls.go index 0bcfe10a7..568197bc8 100644 --- a/chain/vm/syscalls.go +++ b/chain/vm/syscalls.go @@ -26,8 +26,8 @@ import ( "github.com/filecoin-project/lotus/extern/sector-storage/ffiwrapper" "github.com/filecoin-project/lotus/lib/sigs" - runtime2 "github.com/filecoin-project/specs-actors/v2/actors/runtime" - proof2 "github.com/filecoin-project/specs-actors/v2/actors/runtime/proof" + runtime5 "github.com/filecoin-project/specs-actors/v5/actors/runtime" + proof5 "github.com/filecoin-project/specs-actors/v5/actors/runtime/proof" ) func init() { @@ -36,10 +36,10 @@ func init() { // Actual type is defined in chain/types/vmcontext.go because the VMContext interface is there -type SyscallBuilder func(ctx context.Context, rt *Runtime) runtime2.Syscalls +type SyscallBuilder func(ctx context.Context, rt *Runtime) runtime5.Syscalls func Syscalls(verifier ffiwrapper.Verifier) SyscallBuilder { - return func(ctx context.Context, rt *Runtime) runtime2.Syscalls { + return func(ctx context.Context, rt *Runtime) runtime5.Syscalls { return &syscallShim{ ctx: ctx, @@ -90,7 +90,7 @@ func (ss *syscallShim) HashBlake2b(data []byte) [32]byte { // Checks validity of the submitted consensus fault with the two block headers needed to prove the fault // and an optional extra one to check common ancestry (as needed). // Note that the blocks are ordered: the method requires a.Epoch() <= b.Epoch(). -func (ss *syscallShim) VerifyConsensusFault(a, b, extra []byte) (*runtime2.ConsensusFault, error) { +func (ss *syscallShim) VerifyConsensusFault(a, b, extra []byte) (*runtime5.ConsensusFault, error) { // Note that block syntax is not validated. Any validly signed block will be accepted pursuant to the below conditions. // Whether or not it could ever have been accepted in a chain is not checked/does not matter here. // for that reason when checking block parent relationships, rather than instantiating a Tipset to do so @@ -133,14 +133,14 @@ func (ss *syscallShim) VerifyConsensusFault(a, b, extra []byte) (*runtime2.Conse } // (2) check for the consensus faults themselves - var consensusFault *runtime2.ConsensusFault + var consensusFault *runtime5.ConsensusFault // (a) double-fork mining fault if blockA.Height == blockB.Height { - consensusFault = &runtime2.ConsensusFault{ + consensusFault = &runtime5.ConsensusFault{ Target: blockA.Miner, Epoch: blockB.Height, - Type: runtime2.ConsensusFaultDoubleForkMining, + Type: runtime5.ConsensusFaultDoubleForkMining, } } @@ -148,10 +148,10 @@ func (ss *syscallShim) VerifyConsensusFault(a, b, extra []byte) (*runtime2.Conse // strictly speaking no need to compare heights based on double fork mining check above, // but at same height this would be a different fault. if types.CidArrsEqual(blockA.Parents, blockB.Parents) && blockA.Height != blockB.Height { - consensusFault = &runtime2.ConsensusFault{ + consensusFault = &runtime5.ConsensusFault{ Target: blockA.Miner, Epoch: blockB.Height, - Type: runtime2.ConsensusFaultTimeOffsetMining, + Type: runtime5.ConsensusFaultTimeOffsetMining, } } @@ -171,10 +171,10 @@ func (ss *syscallShim) VerifyConsensusFault(a, b, extra []byte) (*runtime2.Conse if types.CidArrsEqual(blockA.Parents, blockC.Parents) && blockA.Height == blockC.Height && types.CidArrsContains(blockB.Parents, blockC.Cid()) && !types.CidArrsContains(blockB.Parents, blockA.Cid()) { - consensusFault = &runtime2.ConsensusFault{ + consensusFault = &runtime5.ConsensusFault{ Target: blockA.Miner, Epoch: blockB.Height, - Type: runtime2.ConsensusFaultParentGrinding, + Type: runtime5.ConsensusFaultParentGrinding, } } } @@ -243,7 +243,7 @@ func (ss *syscallShim) workerKeyAtLookback(height abi.ChainEpoch) (address.Addre return ResolveToKeyAddr(ss.cstate, ss.cst, info.Worker) } -func (ss *syscallShim) VerifyPoSt(proof proof2.WindowPoStVerifyInfo) error { +func (ss *syscallShim) VerifyPoSt(proof proof5.WindowPoStVerifyInfo) error { ok, err := ss.verifier.VerifyWindowPoSt(context.TODO(), proof) if err != nil { return err @@ -254,7 +254,7 @@ func (ss *syscallShim) VerifyPoSt(proof proof2.WindowPoStVerifyInfo) error { return nil } -func (ss *syscallShim) VerifySeal(info proof2.SealVerifyInfo) error { +func (ss *syscallShim) VerifySeal(info proof5.SealVerifyInfo) error { //_, span := trace.StartSpan(ctx, "ValidatePoRep") //defer span.End() @@ -281,6 +281,18 @@ func (ss *syscallShim) VerifySeal(info proof2.SealVerifyInfo) error { return nil } +func (ss *syscallShim) VerifyAggregateSeals(aggregate proof5.AggregateSealVerifyProofAndInfos) error { + ok, err := ss.verifier.VerifyAggregateSeals(aggregate) + if err != nil { + return xerrors.Errorf("failed to verify aggregated PoRep: %w", err) + } + if !ok { + return fmt.Errorf("invalid aggredate proof") + } + + return nil +} + func (ss *syscallShim) VerifySignature(sig crypto.Signature, addr address.Address, input []byte) error { // TODO: in genesis setup, we are currently faking signatures @@ -294,7 +306,7 @@ func (ss *syscallShim) VerifySignature(sig crypto.Signature, addr address.Addres var BatchSealVerifyParallelism = goruntime.NumCPU() -func (ss *syscallShim) BatchVerifySeals(inp map[address.Address][]proof2.SealVerifyInfo) (map[address.Address][]bool, error) { +func (ss *syscallShim) BatchVerifySeals(inp map[address.Address][]proof5.SealVerifyInfo) (map[address.Address][]bool, error) { out := make(map[address.Address][]bool) sema := make(chan struct{}, BatchSealVerifyParallelism) @@ -306,7 +318,7 @@ func (ss *syscallShim) BatchVerifySeals(inp map[address.Address][]proof2.SealVer for i, s := range seals { wg.Add(1) - go func(ma address.Address, ix int, svi proof2.SealVerifyInfo, res []bool) { + go func(ma address.Address, ix int, svi proof5.SealVerifyInfo, res []bool) { defer wg.Done() sema <- struct{}{} diff --git a/cli/params.go b/cli/params.go index 8419507b8..1aa6555c5 100644 --- a/cli/params.go +++ b/cli/params.go @@ -23,7 +23,7 @@ var FetchParamCmd = &cli.Command{ } sectorSize := uint64(sectorSizeInt) - err = paramfetch.GetParams(ReqContext(cctx), build.ParametersJSON(), sectorSize) + err = paramfetch.GetParams(ReqContext(cctx), build.ParametersJSON(), build.SrsJSON(), sectorSize) if err != nil { return xerrors.Errorf("fetching proof parameters: %w", err) } diff --git a/cmd/lotus-bench/main.go b/cmd/lotus-bench/main.go index 81aa09a75..0b8ec6fe3 100644 --- a/cmd/lotus-bench/main.go +++ b/cmd/lotus-bench/main.go @@ -243,7 +243,7 @@ var sealBenchCmd = &cli.Command{ // Only fetch parameters if actually needed skipc2 := c.Bool("skip-commit2") if !skipc2 { - if err := paramfetch.GetParams(lcli.ReqContext(c), build.ParametersJSON(), uint64(sectorSize)); err != nil { + if err := paramfetch.GetParams(lcli.ReqContext(c), build.ParametersJSON(), build.SrsJSON(), uint64(sectorSize)); err != nil { return xerrors.Errorf("getting params: %w", err) } } @@ -738,7 +738,7 @@ var proveCmd = &cli.Command{ return xerrors.Errorf("unmarshalling input file: %w", err) } - if err := paramfetch.GetParams(lcli.ReqContext(c), build.ParametersJSON(), c2in.SectorSize); err != nil { + if err := paramfetch.GetParams(lcli.ReqContext(c), build.ParametersJSON(), build.SrsJSON(), c2in.SectorSize); err != nil { return xerrors.Errorf("getting params: %w", err) } diff --git a/cmd/lotus-seal-worker/main.go b/cmd/lotus-seal-worker/main.go index 24918e52a..5a78c6dac 100644 --- a/cmd/lotus-seal-worker/main.go +++ b/cmd/lotus-seal-worker/main.go @@ -228,7 +228,7 @@ var runCmd = &cli.Command{ } if cctx.Bool("commit") { - if err := paramfetch.GetParams(ctx, build.ParametersJSON(), uint64(ssize)); err != nil { + if err := paramfetch.GetParams(ctx, build.ParametersJSON(), build.SrsJSON(), uint64(ssize)); err != nil { return xerrors.Errorf("get params: %w", err) } } diff --git a/cmd/lotus-shed/params.go b/cmd/lotus-shed/params.go index 3f7e7b6fb..e45d9489c 100644 --- a/cmd/lotus-shed/params.go +++ b/cmd/lotus-shed/params.go @@ -25,7 +25,7 @@ var fetchParamCmd = &cli.Command{ return err } sectorSize := uint64(sectorSizeInt) - err = paramfetch.GetParams(lcli.ReqContext(cctx), build.ParametersJSON(), sectorSize) + err = paramfetch.GetParams(lcli.ReqContext(cctx), build.ParametersJSON(), build.SrsJSON(), sectorSize) if err != nil { return xerrors.Errorf("fetching proof parameters: %w", err) } diff --git a/cmd/lotus-storage-miner/info.go b/cmd/lotus-storage-miner/info.go index 7650de035..7a8835fee 100644 --- a/cmd/lotus-storage-miner/info.go +++ b/cmd/lotus-storage-miner/info.go @@ -295,6 +295,8 @@ var stateList = []stateMeta{ {col: color.FgYellow, state: sealing.Committing}, {col: color.FgYellow, state: sealing.SubmitCommit}, {col: color.FgYellow, state: sealing.CommitWait}, + {col: color.FgYellow, state: sealing.SubmitCommitAggregate}, + {col: color.FgYellow, state: sealing.CommitAggregateWait}, {col: color.FgYellow, state: sealing.FinalizeSector}, {col: color.FgCyan, state: sealing.Terminating}, diff --git a/cmd/lotus-storage-miner/init.go b/cmd/lotus-storage-miner/init.go index 2e38dcc06..bac8444cc 100644 --- a/cmd/lotus-storage-miner/init.go +++ b/cmd/lotus-storage-miner/init.go @@ -143,7 +143,7 @@ var initCmd = &cli.Command{ log.Info("Checking proof parameters") - if err := paramfetch.GetParams(ctx, build.ParametersJSON(), uint64(ssize)); err != nil { + if err := paramfetch.GetParams(ctx, build.ParametersJSON(), build.SrsJSON(), uint64(ssize)); err != nil { return xerrors.Errorf("fetching proof parameters: %w", err) } diff --git a/cmd/lotus-storage-miner/init_restore.go b/cmd/lotus-storage-miner/init_restore.go index 12358e63a..af4c43c95 100644 --- a/cmd/lotus-storage-miner/init_restore.go +++ b/cmd/lotus-storage-miner/init_restore.go @@ -249,7 +249,7 @@ var initRestoreCmd = &cli.Command{ log.Info("Checking proof parameters") - if err := paramfetch.GetParams(ctx, build.ParametersJSON(), uint64(mi.SectorSize)); err != nil { + if err := paramfetch.GetParams(ctx, build.ParametersJSON(), build.SrsJSON(), uint64(mi.SectorSize)); err != nil { return xerrors.Errorf("fetching proof parameters: %w", err) } diff --git a/cmd/lotus-storage-miner/sectors.go b/cmd/lotus-storage-miner/sectors.go index 3791dbf07..8da491841 100644 --- a/cmd/lotus-storage-miner/sectors.go +++ b/cmd/lotus-storage-miner/sectors.go @@ -45,6 +45,7 @@ var sectorsCmd = &cli.Command{ sectorsStartSealCmd, sectorsSealDelayCmd, sectorsCapacityCollateralCmd, + sectorsPendingCommit, }, } @@ -969,6 +970,53 @@ var sectorsUpdateCmd = &cli.Command{ }, } +var sectorsPendingCommit = &cli.Command{ + Name: "pending-commit", + Usage: "list sectors waiting in batch queue", + Flags: []cli.Flag{ + &cli.BoolFlag{ + Name: "publish-now", + Usage: "send a batch now", + }, + }, + Action: func(cctx *cli.Context) error { + api, closer, err := lcli.GetStorageMinerAPI(cctx) + if err != nil { + return err + } + defer closer() + ctx := lcli.ReqContext(cctx) + + if cctx.Bool("publish-now") { + cid, err := api.SectorCommitFlush(ctx) + if err != nil { + return xerrors.Errorf("flush: %w", err) + } + if cid == nil { + return xerrors.Errorf("no sectors to publish") + } + + fmt.Println("sector batch published: ", cid) + return nil + } + + pending, err := api.SectorCommitPending(ctx) + if err != nil { + return xerrors.Errorf("getting pending deals: %w", err) + } + + if len(pending) > 0 { + for _, sector := range pending { + fmt.Println(sector.Number) + } + return nil + } + + fmt.Println("No sectors queued to be committed") + return nil + }, +} + func yesno(b bool) string { if b { return color.GreenString("YES") diff --git a/cmd/lotus/daemon.go b/cmd/lotus/daemon.go index 5a59ec816..644892ee2 100644 --- a/cmd/lotus/daemon.go +++ b/cmd/lotus/daemon.go @@ -231,7 +231,7 @@ var DaemonCmd = &cli.Command{ freshRepo := err != repo.ErrRepoExists if !isLite { - if err := paramfetch.GetParams(lcli.ReqContext(cctx), build.ParametersJSON(), 0); err != nil { + if err := paramfetch.GetParams(lcli.ReqContext(cctx), build.ParametersJSON(), build.SrsJSON(), 0); err != nil { return xerrors.Errorf("fetching proof parameters: %w", err) } } diff --git a/documentation/en/api-methods-miner.md b/documentation/en/api-methods-miner.md index d140d7d85..5ebc44be5 100644 --- a/documentation/en/api-methods-miner.md +++ b/documentation/en/api-methods-miner.md @@ -98,6 +98,8 @@ * [SealingAbort](#SealingAbort) * [SealingSchedDiag](#SealingSchedDiag) * [Sector](#Sector) + * [SectorCommitFlush](#SectorCommitFlush) + * [SectorCommitPending](#SectorCommitPending) * [SectorGetExpectedSealDuration](#SectorGetExpectedSealDuration) * [SectorGetSealDelay](#SectorGetSealDelay) * [SectorMarkForUpgrade](#SectorMarkForUpgrade) @@ -1556,6 +1558,24 @@ Response: `{}` ## Sector +### SectorCommitFlush + + +Perms: admin + +Inputs: `null` + +Response: `null` + +### SectorCommitPending + + +Perms: admin + +Inputs: `null` + +Response: `null` + ### SectorGetExpectedSealDuration SectorGetExpectedSealDuration gets the expected time for a sector to seal diff --git a/extern/filecoin-ffi b/extern/filecoin-ffi index dc4e4e8dc..5f9f082b0 160000 --- a/extern/filecoin-ffi +++ b/extern/filecoin-ffi @@ -1 +1 @@ -Subproject commit dc4e4e8dc9554dedb6f48304f7f0c6328331f9ec +Subproject commit 5f9f082b03a22bbf0f31adcb2bdf7f539cee6b6b diff --git a/extern/sector-storage/ffiwrapper/sealer_test.go b/extern/sector-storage/ffiwrapper/sealer_test.go index 2efcfc6a0..5af6a78e7 100644 --- a/extern/sector-storage/ffiwrapper/sealer_test.go +++ b/extern/sector-storage/ffiwrapper/sealer_test.go @@ -18,6 +18,7 @@ import ( commpffi "github.com/filecoin-project/go-commp-utils/ffiwrapper" proof2 "github.com/filecoin-project/specs-actors/v2/actors/runtime/proof" + proof5 "github.com/filecoin-project/specs-actors/v5/actors/runtime/proof" "github.com/ipfs/go-cid" @@ -83,9 +84,10 @@ func (s *seal) precommit(t *testing.T, sb *Sealer, id storage.SectorRef, done fu s.cids = cids } -func (s *seal) commit(t *testing.T, sb *Sealer, done func()) { +var seed = abi.InteractiveSealRandomness{0, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 9, 8, 7, 6, 45, 3, 2, 1, 0, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 9} + +func (s *seal) commit(t *testing.T, sb *Sealer, done func()) storage.Proof { defer done() - seed := abi.InteractiveSealRandomness{0, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 9, 8, 7, 6, 45, 3, 2, 1, 0, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 9} pc1, err := sb.SealCommit1(context.TODO(), s.ref, s.ticket, seed, []abi.PieceInfo{s.pi}, s.cids) if err != nil { @@ -112,6 +114,8 @@ func (s *seal) commit(t *testing.T, sb *Sealer, done func()) { if !ok { t.Fatal("proof failed to validate") } + + return proof } func (s *seal) unseal(t *testing.T, sb *Sealer, sp *basicfs.Provider, si storage.SectorRef, done func()) { @@ -229,7 +233,12 @@ func getGrothParamFileAndVerifyingKeys(s abi.SectorSize) { panic(err) } - err = paramfetch.GetParams(context.TODO(), dat, uint64(s)) + datSrs, err := ioutil.ReadFile("../../../build/proof-params/srs-inner-product.json") + if err != nil { + panic(err) + } + + err = paramfetch.GetParams(context.TODO(), dat, datSrs, uint64(s)) if err != nil { panic(xerrors.Errorf("failed to acquire Groth parameters for 2KiB sectors: %w", err)) } @@ -462,6 +471,94 @@ func TestSealAndVerify3(t *testing.T) { post(t, sb, []abi.SectorID{si1.ID, si2.ID}, s1, s2, s3) } +func TestSealAndVerifyAggregate(t *testing.T) { + numAgg := 5 + + if testing.Short() { + t.Skip("skipping test in short mode") + } + + defer requireFDsClosed(t, openFDs(t)) + + if runtime.NumCPU() < 10 && os.Getenv("CI") == "" { // don't bother on slow hardware + t.Skip("this is slow") + } + _ = os.Setenv("RUST_LOG", "info") + + getGrothParamFileAndVerifyingKeys(sectorSize) + + cdir, err := ioutil.TempDir("", "sbtest-c-") + if err != nil { + t.Fatal(err) + } + miner := abi.ActorID(123) + + sp := &basicfs.Provider{ + Root: cdir, + } + sb, err := New(sp) + if err != nil { + t.Fatalf("%+v", err) + } + cleanup := func() { + if t.Failed() { + fmt.Printf("not removing %s\n", cdir) + return + } + if err := os.RemoveAll(cdir); err != nil { + t.Error(err) + } + } + defer cleanup() + + avi := proof5.AggregateSealVerifyProofAndInfos{ + Miner: miner, + Infos: make([]proof5.AggregateSealVerifyInfo, numAgg), + } + + toAggregate := make([][]byte, numAgg) + for i := 0; i < numAgg; i++ { + si := storage.SectorRef{ + ID: abi.SectorID{Miner: miner, Number: abi.SectorNumber(i + 1)}, + ProofType: sealProofType, + } + + s := seal{ref: si} + s.precommit(t, sb, si, func() {}) + toAggregate[i] = s.commit(t, sb, func() {}) + + avi.Infos[i] = proof5.AggregateSealVerifyInfo{ + Number: abi.SectorNumber(i + 1), + Randomness: s.ticket, + InteractiveRandomness: seed, + SealedCID: s.cids.Sealed, + UnsealedCID: s.cids.Unsealed, + } + } + + aggStart := time.Now() + + avi.Proof, err = ProofVerifier.AggregateSealProofs(sealProofType, toAggregate) + require.NoError(t, err) + + aggDone := time.Now() + + _, err = ProofVerifier.AggregateSealProofs(sealProofType, toAggregate) + require.NoError(t, err) + + aggHot := time.Now() + + ok, err := ProofVerifier.VerifyAggregateSeals(avi) + require.NoError(t, err) + require.True(t, ok) + + verifDone := time.Now() + + fmt.Printf("Aggregate: %s\n", aggDone.Sub(aggStart).String()) + fmt.Printf("Hot: %s\n", aggHot.Sub(aggDone).String()) + fmt.Printf("Verify: %s\n", verifDone.Sub(aggHot).String()) +} + func BenchmarkWriteWithAlignment(b *testing.B) { bt := abi.UnpaddedPieceSize(2 * 127 * 1024 * 1024) b.SetBytes(int64(bt)) diff --git a/extern/sector-storage/ffiwrapper/types.go b/extern/sector-storage/ffiwrapper/types.go index b7e96636a..9c84794bf 100644 --- a/extern/sector-storage/ffiwrapper/types.go +++ b/extern/sector-storage/ffiwrapper/types.go @@ -4,7 +4,7 @@ import ( "context" "io" - proof2 "github.com/filecoin-project/specs-actors/v2/actors/runtime/proof" + proof5 "github.com/filecoin-project/specs-actors/v5/actors/runtime/proof" "github.com/ipfs/go-cid" @@ -34,11 +34,15 @@ type Storage interface { } type Verifier interface { - VerifySeal(proof2.SealVerifyInfo) (bool, error) - VerifyWinningPoSt(ctx context.Context, info proof2.WinningPoStVerifyInfo) (bool, error) - VerifyWindowPoSt(ctx context.Context, info proof2.WindowPoStVerifyInfo) (bool, error) + VerifySeal(proof5.SealVerifyInfo) (bool, error) + VerifyAggregateSeals(aggregate proof5.AggregateSealVerifyProofAndInfos) (bool, error) + VerifyWinningPoSt(ctx context.Context, info proof5.WinningPoStVerifyInfo) (bool, error) + VerifyWindowPoSt(ctx context.Context, info proof5.WindowPoStVerifyInfo) (bool, error) GenerateWinningPoStSectorChallenge(context.Context, abi.RegisteredPoStProof, abi.ActorID, abi.PoStRandomness, uint64) ([]uint64, error) + + // cheap, makes no sense to put this on the storage interface + AggregateSealProofs(proofType abi.RegisteredSealProof, proofs [][]byte) ([]byte, error) } type SectorProvider interface { diff --git a/extern/sector-storage/ffiwrapper/verifier_cgo.go b/extern/sector-storage/ffiwrapper/verifier_cgo.go index 15e0e6ab3..b31903080 100644 --- a/extern/sector-storage/ffiwrapper/verifier_cgo.go +++ b/extern/sector-storage/ffiwrapper/verifier_cgo.go @@ -10,13 +10,13 @@ import ( ffi "github.com/filecoin-project/filecoin-ffi" "github.com/filecoin-project/go-state-types/abi" - proof2 "github.com/filecoin-project/specs-actors/v2/actors/runtime/proof" + proof5 "github.com/filecoin-project/specs-actors/v5/actors/runtime/proof" "github.com/filecoin-project/specs-storage/storage" "github.com/filecoin-project/lotus/extern/sector-storage/storiface" ) -func (sb *Sealer) GenerateWinningPoSt(ctx context.Context, minerID abi.ActorID, sectorInfo []proof2.SectorInfo, randomness abi.PoStRandomness) ([]proof2.PoStProof, error) { +func (sb *Sealer) GenerateWinningPoSt(ctx context.Context, minerID abi.ActorID, sectorInfo []proof5.SectorInfo, randomness abi.PoStRandomness) ([]proof5.PoStProof, error) { randomness[31] &= 0x3f privsectors, skipped, done, err := sb.pubSectorToPriv(ctx, minerID, sectorInfo, nil, abi.RegisteredSealProof.RegisteredWinningPoStProof) // TODO: FAULTS? if err != nil { @@ -30,7 +30,7 @@ func (sb *Sealer) GenerateWinningPoSt(ctx context.Context, minerID abi.ActorID, return ffi.GenerateWinningPoSt(minerID, privsectors, randomness) } -func (sb *Sealer) GenerateWindowPoSt(ctx context.Context, minerID abi.ActorID, sectorInfo []proof2.SectorInfo, randomness abi.PoStRandomness) ([]proof2.PoStProof, []abi.SectorID, error) { +func (sb *Sealer) GenerateWindowPoSt(ctx context.Context, minerID abi.ActorID, sectorInfo []proof5.SectorInfo, randomness abi.PoStRandomness) ([]proof5.PoStProof, []abi.SectorID, error) { randomness[31] &= 0x3f privsectors, skipped, done, err := sb.pubSectorToPriv(ctx, minerID, sectorInfo, nil, abi.RegisteredSealProof.RegisteredWindowPoStProof) if err != nil { @@ -55,7 +55,7 @@ func (sb *Sealer) GenerateWindowPoSt(ctx context.Context, minerID abi.ActorID, s return proof, faultyIDs, err } -func (sb *Sealer) pubSectorToPriv(ctx context.Context, mid abi.ActorID, sectorInfo []proof2.SectorInfo, faults []abi.SectorNumber, rpt func(abi.RegisteredSealProof) (abi.RegisteredPoStProof, error)) (ffi.SortedPrivateSectorInfo, []abi.SectorID, func(), error) { +func (sb *Sealer) pubSectorToPriv(ctx context.Context, mid abi.ActorID, sectorInfo []proof5.SectorInfo, faults []abi.SectorNumber, rpt func(abi.RegisteredSealProof) (abi.RegisteredPoStProof, error)) (ffi.SortedPrivateSectorInfo, []abi.SectorID, func(), error) { fmap := map[abi.SectorNumber]struct{}{} for _, fault := range faults { fmap[fault] = struct{}{} @@ -111,11 +111,15 @@ type proofVerifier struct{} var ProofVerifier = proofVerifier{} -func (proofVerifier) VerifySeal(info proof2.SealVerifyInfo) (bool, error) { +func (proofVerifier) VerifySeal(info proof5.SealVerifyInfo) (bool, error) { return ffi.VerifySeal(info) } -func (proofVerifier) VerifyWinningPoSt(ctx context.Context, info proof2.WinningPoStVerifyInfo) (bool, error) { +func (proofVerifier) VerifyAggregateSeals(aggregate proof5.AggregateSealVerifyProofAndInfos) (bool, error) { + return ffi.VerifyAggregateSeals(aggregate) +} + +func (proofVerifier) VerifyWinningPoSt(ctx context.Context, info proof5.WinningPoStVerifyInfo) (bool, error) { info.Randomness[31] &= 0x3f _, span := trace.StartSpan(ctx, "VerifyWinningPoSt") defer span.End() @@ -123,7 +127,7 @@ func (proofVerifier) VerifyWinningPoSt(ctx context.Context, info proof2.WinningP return ffi.VerifyWinningPoSt(info) } -func (proofVerifier) VerifyWindowPoSt(ctx context.Context, info proof2.WindowPoStVerifyInfo) (bool, error) { +func (proofVerifier) VerifyWindowPoSt(ctx context.Context, info proof5.WindowPoStVerifyInfo) (bool, error) { info.Randomness[31] &= 0x3f _, span := trace.StartSpan(ctx, "VerifyWindowPoSt") defer span.End() @@ -135,3 +139,7 @@ func (proofVerifier) GenerateWinningPoStSectorChallenge(ctx context.Context, pro randomness[31] &= 0x3f return ffi.GenerateWinningPoStSectorChallenge(proofType, minerID, randomness, eligibleSectorCount) } + +func (v proofVerifier) AggregateSealProofs(proofType abi.RegisteredSealProof, proofs [][]byte) ([]byte, error) { + return ffi.AggregateSealProofs(proofType, proofs) +} diff --git a/extern/sector-storage/mock/mock.go b/extern/sector-storage/mock/mock.go index ae7d54985..ec6020010 100644 --- a/extern/sector-storage/mock/mock.go +++ b/extern/sector-storage/mock/mock.go @@ -9,7 +9,7 @@ import ( "math/rand" "sync" - proof2 "github.com/filecoin-project/specs-actors/v2/actors/runtime/proof" + proof5 "github.com/filecoin-project/specs-actors/v5/actors/runtime/proof" ffiwrapper2 "github.com/filecoin-project/go-commp-utils/ffiwrapper" commcid "github.com/filecoin-project/go-fil-commcid" @@ -300,14 +300,14 @@ func AddOpFinish(ctx context.Context) (context.Context, func()) { } } -func (mgr *SectorMgr) GenerateWinningPoSt(ctx context.Context, minerID abi.ActorID, sectorInfo []proof2.SectorInfo, randomness abi.PoStRandomness) ([]proof2.PoStProof, error) { +func (mgr *SectorMgr) GenerateWinningPoSt(ctx context.Context, minerID abi.ActorID, sectorInfo []proof5.SectorInfo, randomness abi.PoStRandomness) ([]proof5.PoStProof, error) { mgr.lk.Lock() defer mgr.lk.Unlock() return generateFakePoSt(sectorInfo, abi.RegisteredSealProof.RegisteredWinningPoStProof, randomness), nil } -func (mgr *SectorMgr) GenerateWindowPoSt(ctx context.Context, minerID abi.ActorID, sectorInfo []proof2.SectorInfo, randomness abi.PoStRandomness) ([]proof2.PoStProof, []abi.SectorID, error) { +func (mgr *SectorMgr) GenerateWindowPoSt(ctx context.Context, minerID abi.ActorID, sectorInfo []proof5.SectorInfo, randomness abi.PoStRandomness) ([]proof5.PoStProof, []abi.SectorID, error) { mgr.lk.Lock() defer mgr.lk.Unlock() @@ -315,7 +315,8 @@ func (mgr *SectorMgr) GenerateWindowPoSt(ctx context.Context, minerID abi.ActorI return nil, nil, xerrors.Errorf("failed to post (mock)") } - si := make([]proof2.SectorInfo, 0, len(sectorInfo)) + si := make([]proof5.SectorInfo, 0, len(sectorInfo)) + var skipped []abi.SectorID var err error @@ -343,7 +344,7 @@ func (mgr *SectorMgr) GenerateWindowPoSt(ctx context.Context, minerID abi.ActorI return generateFakePoSt(si, abi.RegisteredSealProof.RegisteredWindowPoStProof, randomness), skipped, nil } -func generateFakePoStProof(sectorInfo []proof2.SectorInfo, randomness abi.PoStRandomness) []byte { +func generateFakePoStProof(sectorInfo []proof5.SectorInfo, randomness abi.PoStRandomness) []byte { randomness[31] &= 0x3f hasher := sha256.New() @@ -358,13 +359,13 @@ func generateFakePoStProof(sectorInfo []proof2.SectorInfo, randomness abi.PoStRa } -func generateFakePoSt(sectorInfo []proof2.SectorInfo, rpt func(abi.RegisteredSealProof) (abi.RegisteredPoStProof, error), randomness abi.PoStRandomness) []proof2.PoStProof { +func generateFakePoSt(sectorInfo []proof5.SectorInfo, rpt func(abi.RegisteredSealProof) (abi.RegisteredPoStProof, error), randomness abi.PoStRandomness) []proof5.PoStProof { wp, err := rpt(sectorInfo[0].SealProof) if err != nil { panic(err) } - return []proof2.PoStProof{ + return []proof5.PoStProof{ { PoStProof: wp, ProofBytes: generateFakePoStProof(sectorInfo, randomness), @@ -489,7 +490,7 @@ func (mgr *SectorMgr) ReturnFetch(ctx context.Context, callID storiface.CallID, panic("not supported") } -func (m mockVerif) VerifySeal(svi proof2.SealVerifyInfo) (bool, error) { +func (m mockVerif) VerifySeal(svi proof5.SealVerifyInfo) (bool, error) { plen, err := svi.SealProof.ProofSize() if err != nil { return false, err @@ -501,6 +502,7 @@ func (m mockVerif) VerifySeal(svi proof2.SealVerifyInfo) (bool, error) { // only the first 32 bytes, the rest are 0. for i, b := range svi.Proof[:32] { + // unsealed+sealed-seed*ticket if b != svi.UnsealedCID.Bytes()[i]+svi.SealedCID.Bytes()[31-i]-svi.InteractiveRandomness[i]*svi.Randomness[i] { return false, nil } @@ -509,12 +511,35 @@ func (m mockVerif) VerifySeal(svi proof2.SealVerifyInfo) (bool, error) { return true, nil } -func (m mockVerif) VerifyWinningPoSt(ctx context.Context, info proof2.WinningPoStVerifyInfo) (bool, error) { +func (m mockVerif) VerifyAggregateSeals(aggregate proof5.AggregateSealVerifyProofAndInfos) (bool, error) { + out := make([]byte, 200) + for pi, svi := range aggregate.Infos { + for i := 0; i < 32; i++ { + b := svi.UnsealedCID.Bytes()[i] + svi.SealedCID.Bytes()[31-i] - svi.InteractiveRandomness[i]*svi.Randomness[i] // raw proof byte + b *= uint8(pi) // with aggregate index + out[i] += b + } + } + + return bytes.Equal(aggregate.Proof, out), nil +} + +func (m mockVerif) AggregateSealProofs(proofType abi.RegisteredSealProof, proofs [][]byte) ([]byte, error) { + out := make([]byte, 200) // todo: figure out more real length + for pi, proof := range proofs { + for i := range proof[:32] { + out[i] += proof[i] * uint8(pi) + } + } + return out, nil +} + +func (m mockVerif) VerifyWinningPoSt(ctx context.Context, info proof5.WinningPoStVerifyInfo) (bool, error) { info.Randomness[31] &= 0x3f return true, nil } -func (m mockVerif) VerifyWindowPoSt(ctx context.Context, info proof2.WindowPoStVerifyInfo) (bool, error) { +func (m mockVerif) VerifyWindowPoSt(ctx context.Context, info proof5.WindowPoStVerifyInfo) (bool, error) { if len(info.Proofs) != 1 { return false, xerrors.Errorf("expected 1 proof entry") } diff --git a/extern/storage-sealing/commit_batch.go b/extern/storage-sealing/commit_batch.go new file mode 100644 index 000000000..74f163010 --- /dev/null +++ b/extern/storage-sealing/commit_batch.go @@ -0,0 +1,271 @@ +package sealing + +import ( + "bytes" + "context" + "sort" + "sync" + "time" + + "github.com/ipfs/go-cid" + "golang.org/x/xerrors" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-bitfield" + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/big" + miner5 "github.com/filecoin-project/specs-actors/v5/actors/builtin/miner" + proof5 "github.com/filecoin-project/specs-actors/v5/actors/runtime/proof" + + "github.com/filecoin-project/lotus/api" + "github.com/filecoin-project/lotus/chain/actors/builtin/miner" + "github.com/filecoin-project/lotus/extern/sector-storage/ffiwrapper" +) + +var ( + // TODO: config! + + CommitBatchWait = 5 * time.Minute +) + +type CommitBatcherApi interface { + SendMsg(ctx context.Context, from, to address.Address, method abi.MethodNum, value, maxFee abi.TokenAmount, params []byte) (cid.Cid, error) + StateMinerInfo(context.Context, address.Address, TipSetToken) (miner.MinerInfo, error) +} + +type AggregateInput struct { + // TODO: Something changed in actors, I think this now needs to be AggregateSealVerifyProofAndInfos + info proof5.AggregateSealVerifyInfo + proof []byte +} + +type CommitBatcher struct { + api CommitBatcherApi + maddr address.Address + mctx context.Context + addrSel AddrSel + feeCfg FeeConfig + getConfig GetSealingConfigFunc + verif ffiwrapper.Verifier + + todo map[abi.SectorNumber]AggregateInput + waiting map[abi.SectorNumber][]chan cid.Cid + + notify, stop, stopped chan struct{} + force chan chan *cid.Cid + lk sync.Mutex +} + +func NewCommitBatcher(mctx context.Context, maddr address.Address, api CommitBatcherApi, addrSel AddrSel, feeCfg FeeConfig, getConfig GetSealingConfigFunc, verif ffiwrapper.Verifier) *CommitBatcher { + b := &CommitBatcher{ + api: api, + maddr: maddr, + mctx: mctx, + addrSel: addrSel, + feeCfg: feeCfg, + getConfig: getConfig, + verif: verif, + + todo: map[abi.SectorNumber]AggregateInput{}, + waiting: map[abi.SectorNumber][]chan cid.Cid{}, + + notify: make(chan struct{}, 1), + force: make(chan chan *cid.Cid), + stop: make(chan struct{}), + stopped: make(chan struct{}), + } + + go b.run() + + return b +} + +func (b *CommitBatcher) run() { + var forceRes chan *cid.Cid + var lastMsg *cid.Cid + + for { + if forceRes != nil { + forceRes <- lastMsg + forceRes = nil + } + lastMsg = nil + + var sendAboveMax, sendAboveMin bool + select { + case <-b.stop: + close(b.stopped) + return + case <-b.notify: + sendAboveMax = true + case <-time.After(TerminateBatchWait): + sendAboveMin = true + case fr := <-b.force: // user triggered + forceRes = fr + } + + var err error + lastMsg, err = b.processBatch(sendAboveMax, sendAboveMin) + if err != nil { + log.Warnw("TerminateBatcher processBatch error", "error", err) + } + } +} + +func (b *CommitBatcher) processBatch(notif, after bool) (*cid.Cid, error) { + b.lk.Lock() + defer b.lk.Unlock() + params := miner5.ProveCommitAggregateParams{ + SectorNumbers: bitfield.New(), + } + + total := len(b.todo) + if total == 0 { + return nil, nil // nothing to do + } + + cfg, err := b.getConfig() + if err != nil { + return nil, xerrors.Errorf("getting config: %w", err) + } + + if notif && total < cfg.MaxCommitBatch { + return nil, nil + } + + if after && total < cfg.MinCommitBatch { + return nil, nil + } + + spt := b.todo[0].info.SealProof + proofs := make([][]byte, total) + + for id, p := range b.todo { + if p.info.SealProof != spt { + // todo: handle when we'll have proof upgrade + return nil, xerrors.Errorf("different seal proof types in commit batch: %w", err) + } + + params.SectorNumbers.Set(uint64(id)) + proofs[id] = p.proof + } + + params.AggregateProof, err = b.verif.AggregateSealProofs(spt, proofs) + if err != nil { + return nil, xerrors.Errorf("aggregating proofs: %w", err) + } + + enc := new(bytes.Buffer) + if err := params.MarshalCBOR(enc); err != nil { + return nil, xerrors.Errorf("couldn't serialize TerminateSectors params: %w", err) + } + + mi, err := b.api.StateMinerInfo(b.mctx, b.maddr, nil) + if err != nil { + return nil, xerrors.Errorf("couldn't get miner info: %w", err) + } + + from, _, err := b.addrSel(b.mctx, mi, api.CommitAddr, b.feeCfg.MaxCommitGasFee, b.feeCfg.MaxCommitGasFee) + if err != nil { + return nil, xerrors.Errorf("no good address found: %w", err) + } + + mcid, err := b.api.SendMsg(b.mctx, from, b.maddr, miner.Methods.ProveCommitAggregate, big.Zero(), b.feeCfg.MaxCommitGasFee, enc.Bytes()) + if err != nil { + return nil, xerrors.Errorf("sending message failed: %w", err) + } + + log.Infow("Sent ProveCommitAggregate message", "cid", mcid, "from", from, "sectors", total) + + err = params.SectorNumbers.ForEach(func(us uint64) error { + sn := abi.SectorNumber(us) + + for _, ch := range b.waiting[sn] { + ch <- mcid // buffered + } + delete(b.waiting, sn) + delete(b.todo, sn) + return nil + }) + if err != nil { + return nil, xerrors.Errorf("done sectors foreach: %w", err) + } + + return &mcid, nil +} + +// register commit, wait for batch message, return message CID +func (b *CommitBatcher) AddCommit(ctx context.Context, s abi.SectorNumber, in AggregateInput) (mcid cid.Cid, err error) { + b.lk.Lock() + b.todo[s] = in + + sent := make(chan cid.Cid, 1) + b.waiting[s] = append(b.waiting[s], sent) + + select { + case b.notify <- struct{}{}: + default: // already have a pending notification, don't need more + } + b.lk.Unlock() + + select { + case c := <-sent: + return c, nil + case <-ctx.Done(): + return cid.Undef, ctx.Err() + } +} + +func (b *CommitBatcher) Flush(ctx context.Context) (*cid.Cid, error) { + resCh := make(chan *cid.Cid, 1) + select { + case b.force <- resCh: + select { + case res := <-resCh: + return res, nil + case <-ctx.Done(): + return nil, ctx.Err() + } + case <-ctx.Done(): + return nil, ctx.Err() + } +} + +func (b *CommitBatcher) Pending(ctx context.Context) ([]abi.SectorID, error) { + b.lk.Lock() + defer b.lk.Unlock() + + mid, err := address.IDFromAddress(b.maddr) + if err != nil { + return nil, err + } + + res := make([]abi.SectorID, 0) + for _, s := range b.todo { + res = append(res, abi.SectorID{ + Miner: abi.ActorID(mid), + Number: s.info.Number, + }) + } + + sort.Slice(res, func(i, j int) bool { + if res[i].Miner != res[j].Miner { + return res[i].Miner < res[j].Miner + } + + return res[i].Number < res[j].Number + }) + + return res, nil +} + +func (b *CommitBatcher) Stop(ctx context.Context) error { + close(b.stop) + + select { + case <-b.stopped: + return nil + case <-ctx.Done(): + return ctx.Err() + } +} diff --git a/extern/storage-sealing/fsm.go b/extern/storage-sealing/fsm.go index d14d363e5..367938099 100644 --- a/extern/storage-sealing/fsm.go +++ b/extern/storage-sealing/fsm.go @@ -91,12 +91,22 @@ var fsmPlanners = map[SectorState]func(events []statemachine.Event, state *Secto SubmitCommit: planOne( on(SectorCommitSubmitted{}, CommitWait), on(SectorCommitFailed{}, CommitFailed), + on(SectorSubmitCommitAggregate{}, SubmitCommitAggregate), + ), + SubmitCommitAggregate: planOne( + on(SectorCommitAggregateSent{}, CommitWait), + on(SectorCommitFailed{}, CommitFailed), ), CommitWait: planOne( on(SectorProving{}, FinalizeSector), on(SectorCommitFailed{}, CommitFailed), on(SectorRetrySubmitCommit{}, SubmitCommit), ), + CommitAggregateWait: planOne( + on(SectorProving{}, FinalizeSector), + on(SectorCommitFailed{}, CommitFailed), + on(SectorRetrySubmitCommit{}, SubmitCommit), + ), FinalizeSector: planOne( on(SectorFinalized{}, Proving), @@ -338,8 +348,12 @@ func (m *Sealing) plan(events []statemachine.Event, state *SectorInfo) (func(sta return m.handleCommitting, processed, nil case SubmitCommit: return m.handleSubmitCommit, processed, nil + case CommitAggregateWait: + fallthrough case CommitWait: return m.handleCommitWait, processed, nil + case SubmitCommitAggregate: + return m.handleSubmitCommitAggregate, processed, nil case FinalizeSector: return m.handleFinalizeSector, processed, nil diff --git a/extern/storage-sealing/fsm_events.go b/extern/storage-sealing/fsm_events.go index 8d11b248b..bced1921f 100644 --- a/extern/storage-sealing/fsm_events.go +++ b/extern/storage-sealing/fsm_events.go @@ -233,6 +233,10 @@ func (evt SectorCommitted) apply(state *SectorInfo) { state.Proof = evt.Proof } +type SectorSubmitCommitAggregate struct{} + +func (evt SectorSubmitCommitAggregate) apply(*SectorInfo) {} + type SectorCommitSubmitted struct { Message cid.Cid } @@ -241,6 +245,14 @@ func (evt SectorCommitSubmitted) apply(state *SectorInfo) { state.CommitMessage = &evt.Message } +type SectorCommitAggregateSent struct { + Message cid.Cid +} + +func (evt SectorCommitAggregateSent) apply(state *SectorInfo) { + state.CommitMessage = &evt.Message +} + type SectorProving struct{} func (evt SectorProving) apply(*SectorInfo) {} diff --git a/extern/storage-sealing/sealiface/config.go b/extern/storage-sealing/sealiface/config.go index 7ac5f6160..f62911b70 100644 --- a/extern/storage-sealing/sealiface/config.go +++ b/extern/storage-sealing/sealiface/config.go @@ -17,4 +17,8 @@ type Config struct { WaitDealsDelay time.Duration AlwaysKeepUnsealedCopy bool + + AggregateCommits bool + MinCommitBatch int + MaxCommitBatch int } diff --git a/extern/storage-sealing/sealing.go b/extern/storage-sealing/sealing.go index 8feca3b7b..d990cb02f 100644 --- a/extern/storage-sealing/sealing.go +++ b/extern/storage-sealing/sealing.go @@ -103,6 +103,7 @@ type Sealing struct { stats SectorStats terminator *TerminateBatcher + commiter *CommitBatcher getConfig GetSealingConfigFunc dealInfo *CurrentDealInfoManager @@ -152,6 +153,7 @@ func New(api SealingAPI, fc FeeConfig, events Events, maddr address.Address, ds addrSel: as, terminator: NewTerminationBatcher(context.TODO(), maddr, api, as, fc), + commiter: NewCommitBatcher(context.TODO(), maddr, api, as, fc, gc, verif), getConfig: gc, dealInfo: &CurrentDealInfoManager{api}, @@ -202,6 +204,14 @@ func (m *Sealing) TerminatePending(ctx context.Context) ([]abi.SectorID, error) return m.terminator.Pending(ctx) } +func (m *Sealing) CommitFlush(ctx context.Context) (*cid.Cid, error) { + return m.commiter.Flush(ctx) +} + +func (m *Sealing) CommitPending(ctx context.Context) ([]abi.SectorID, error) { + return m.commiter.Pending(ctx) +} + func (m *Sealing) currentSealProof(ctx context.Context) (abi.RegisteredSealProof, error) { mi, err := m.api.StateMinerInfo(ctx, m.maddr, nil) if err != nil { diff --git a/extern/storage-sealing/sector_state.go b/extern/storage-sealing/sector_state.go index b636614d1..b6b7cbf7b 100644 --- a/extern/storage-sealing/sector_state.go +++ b/extern/storage-sealing/sector_state.go @@ -3,61 +3,69 @@ package sealing type SectorState string var ExistSectorStateList = map[SectorState]struct{}{ - Empty: {}, - WaitDeals: {}, - Packing: {}, - AddPiece: {}, - AddPieceFailed: {}, - GetTicket: {}, - PreCommit1: {}, - PreCommit2: {}, - PreCommitting: {}, - PreCommitWait: {}, - WaitSeed: {}, - Committing: {}, - SubmitCommit: {}, - CommitWait: {}, - FinalizeSector: {}, - Proving: {}, - FailedUnrecoverable: {}, - SealPreCommit1Failed: {}, - SealPreCommit2Failed: {}, - PreCommitFailed: {}, - ComputeProofFailed: {}, - CommitFailed: {}, - PackingFailed: {}, - FinalizeFailed: {}, - DealsExpired: {}, - RecoverDealIDs: {}, - Faulty: {}, - FaultReported: {}, - FaultedFinal: {}, - Terminating: {}, - TerminateWait: {}, - TerminateFinality: {}, - TerminateFailed: {}, - Removing: {}, - RemoveFailed: {}, - Removed: {}, + Empty: {}, + WaitDeals: {}, + Packing: {}, + AddPiece: {}, + AddPieceFailed: {}, + GetTicket: {}, + PreCommit1: {}, + PreCommit2: {}, + PreCommitting: {}, + PreCommitWait: {}, + WaitSeed: {}, + Committing: {}, + SubmitCommit: {}, + CommitWait: {}, + SubmitCommitAggregate: {}, + CommitAggregateWait: {}, + FinalizeSector: {}, + Proving: {}, + FailedUnrecoverable: {}, + SealPreCommit1Failed: {}, + SealPreCommit2Failed: {}, + PreCommitFailed: {}, + ComputeProofFailed: {}, + CommitFailed: {}, + PackingFailed: {}, + FinalizeFailed: {}, + DealsExpired: {}, + RecoverDealIDs: {}, + Faulty: {}, + FaultReported: {}, + FaultedFinal: {}, + Terminating: {}, + TerminateWait: {}, + TerminateFinality: {}, + TerminateFailed: {}, + Removing: {}, + RemoveFailed: {}, + Removed: {}, } const ( UndefinedSectorState SectorState = "" // happy path - Empty SectorState = "Empty" // deprecated - WaitDeals SectorState = "WaitDeals" // waiting for more pieces (deals) to be added to the sector - AddPiece SectorState = "AddPiece" // put deal data (and padding if required) into the sector - Packing SectorState = "Packing" // sector not in sealStore, and not on chain - GetTicket SectorState = "GetTicket" // generate ticket - PreCommit1 SectorState = "PreCommit1" // do PreCommit1 - PreCommit2 SectorState = "PreCommit2" // do PreCommit2 - PreCommitting SectorState = "PreCommitting" // on chain pre-commit - PreCommitWait SectorState = "PreCommitWait" // waiting for precommit to land on chain - WaitSeed SectorState = "WaitSeed" // waiting for seed - Committing SectorState = "Committing" // compute PoRep - SubmitCommit SectorState = "SubmitCommit" // send commit message to the chain - CommitWait SectorState = "CommitWait" // wait for the commit message to land on chain + Empty SectorState = "Empty" // deprecated + WaitDeals SectorState = "WaitDeals" // waiting for more pieces (deals) to be added to the sector + AddPiece SectorState = "AddPiece" // put deal data (and padding if required) into the sector + Packing SectorState = "Packing" // sector not in sealStore, and not on chain + GetTicket SectorState = "GetTicket" // generate ticket + PreCommit1 SectorState = "PreCommit1" // do PreCommit1 + PreCommit2 SectorState = "PreCommit2" // do PreCommit2 + PreCommitting SectorState = "PreCommitting" // on chain pre-commit + PreCommitWait SectorState = "PreCommitWait" // waiting for precommit to land on chain + WaitSeed SectorState = "WaitSeed" // waiting for seed + Committing SectorState = "Committing" // compute PoRep + + // single commit + SubmitCommit SectorState = "SubmitCommit" // send commit message to the chain + CommitWait SectorState = "CommitWait" // wait for the commit message to land on chain + + SubmitCommitAggregate SectorState = "SubmitCommitAggregate" + CommitAggregateWait SectorState = "CommitAggregateWait" + FinalizeSector SectorState = "FinalizeSector" Proving SectorState = "Proving" // error modes @@ -91,7 +99,7 @@ func toStatState(st SectorState) statSectorState { switch st { case UndefinedSectorState, Empty, WaitDeals, AddPiece: return sstStaging - case Packing, GetTicket, PreCommit1, PreCommit2, PreCommitting, PreCommitWait, WaitSeed, Committing, SubmitCommit, CommitWait, FinalizeSector: + case Packing, GetTicket, PreCommit1, PreCommit2, PreCommitting, PreCommitWait, WaitSeed, Committing, SubmitCommit, CommitWait, SubmitCommitAggregate, CommitAggregateWait, FinalizeSector: return sstSealing case Proving, Removed, Removing, Terminating, TerminateWait, TerminateFinality, TerminateFailed: return sstProving diff --git a/extern/storage-sealing/states_sealing.go b/extern/storage-sealing/states_sealing.go index e371ab33f..296d92b9c 100644 --- a/extern/storage-sealing/states_sealing.go +++ b/extern/storage-sealing/states_sealing.go @@ -12,6 +12,7 @@ import ( "github.com/filecoin-project/go-state-types/crypto" "github.com/filecoin-project/go-state-types/exitcode" "github.com/filecoin-project/go-statemachine" + "github.com/filecoin-project/specs-actors/v5/actors/runtime/proof" "github.com/filecoin-project/specs-storage/storage" "github.com/filecoin-project/lotus/api" @@ -452,6 +453,14 @@ func (m *Sealing) handleCommitting(ctx statemachine.Context, sector SectorInfo) } func (m *Sealing) handleSubmitCommit(ctx statemachine.Context, sector SectorInfo) error { + cfg, err := m.getConfig() + if err != nil { + return xerrors.Errorf("getting config: %w", err) + } + if cfg.AggregateCommits { + return ctx.Send(SectorSubmitCommitAggregate{}) + } + tok, _, err := m.api.ChainHead(ctx.Context()) if err != nil { log.Errorf("handleCommitting: api error, not proceeding: %+v", err) @@ -514,6 +523,29 @@ func (m *Sealing) handleSubmitCommit(ctx statemachine.Context, sector SectorInfo }) } +func (m *Sealing) handleSubmitCommitAggregate(ctx statemachine.Context, sector SectorInfo) error { + if sector.CommD == nil || sector.CommR == nil { + return ctx.Send(SectorCommitFailed{xerrors.Errorf("sector had nil commR or commD")}) + } + + mcid, err := m.commiter.AddCommit(ctx.Context(), sector.SectorNumber, AggregateInput{ + info: proof.AggregateSealVerifyInfo{ + Number: sector.SectorNumber, + DealIDs: sector.dealIDs(), + Randomness: sector.TicketValue, + InteractiveRandomness: sector.SeedValue, + SealedCID: *sector.CommR, + UnsealedCID: *sector.CommD, + }, + proof: sector.Proof, // todo: this correct?? + }) + if err != nil { + return ctx.Send(SectorCommitFailed{xerrors.Errorf("queuing commit for aggregation failed: %w", err)}) + } + + return ctx.Send(SectorCommitAggregateSent{mcid}) +} + func (m *Sealing) handleCommitWait(ctx statemachine.Context, sector SectorInfo) error { if sector.CommitMessage == nil { log.Errorf("sector %d entered commit wait state without a message cid", sector.SectorNumber) diff --git a/go.mod b/go.mod index af2ee9c3e..4eb8e0844 100644 --- a/go.mod +++ b/go.mod @@ -38,7 +38,7 @@ require ( github.com/filecoin-project/go-jsonrpc v0.1.4-0.20210217175800-45ea43ac2bec github.com/filecoin-project/go-multistore v0.0.3 github.com/filecoin-project/go-padreader v0.0.0-20200903213702-ed5fae088b20 - github.com/filecoin-project/go-paramfetch v0.0.2-0.20200701152213-3e0f0afdc261 + github.com/filecoin-project/go-paramfetch v0.0.2-0.20210330140417-936748d3f5ec github.com/filecoin-project/go-state-types v0.1.1-0.20210506134452-99b279731c48 github.com/filecoin-project/go-statemachine v0.0.0-20200925024713-05bd7c71fbfe github.com/filecoin-project/go-statestore v0.1.1 @@ -47,7 +47,7 @@ require ( github.com/filecoin-project/specs-actors/v2 v2.3.5-0.20210114162132-5b58b773f4fb github.com/filecoin-project/specs-actors/v3 v3.1.0 github.com/filecoin-project/specs-actors/v4 v4.0.0 - github.com/filecoin-project/specs-actors/v5 v5.0.0-20210510162709-3255bdd9f2bb + github.com/filecoin-project/specs-actors/v5 v5.0.0-20210512015452-4fe3889fff57 github.com/filecoin-project/specs-storage v0.1.1-0.20201105051918-5188d9774506 github.com/filecoin-project/test-vectors/schema v0.0.5 github.com/gbrlsnchs/jwt/v3 v3.0.0-beta.1 diff --git a/go.sum b/go.sum index ecdab7a65..a84e2e471 100644 --- a/go.sum +++ b/go.sum @@ -287,8 +287,8 @@ github.com/filecoin-project/go-multistore v0.0.3 h1:vaRBY4YiA2UZFPK57RNuewypB8u0 github.com/filecoin-project/go-multistore v0.0.3/go.mod h1:kaNqCC4IhU4B1uyr7YWFHd23TL4KM32aChS0jNkyUvQ= github.com/filecoin-project/go-padreader v0.0.0-20200903213702-ed5fae088b20 h1:+/4aUeUoKr6AKfPE3mBhXA5spIV6UcKdTYDPNU2Tdmg= github.com/filecoin-project/go-padreader v0.0.0-20200903213702-ed5fae088b20/go.mod h1:mPn+LRRd5gEKNAtc+r3ScpW2JRU/pj4NBKdADYWHiak= -github.com/filecoin-project/go-paramfetch v0.0.2-0.20200701152213-3e0f0afdc261 h1:A256QonvzRaknIIAuWhe/M2dpV2otzs3NBhi5TWa/UA= -github.com/filecoin-project/go-paramfetch v0.0.2-0.20200701152213-3e0f0afdc261/go.mod h1:fZzmf4tftbwf9S37XRifoJlz7nCjRdIrMGLR07dKLCc= +github.com/filecoin-project/go-paramfetch v0.0.2-0.20210330140417-936748d3f5ec h1:gExwWUiT1TcARkxGneS4nvp9C+wBsKU0bFdg7qFpNco= +github.com/filecoin-project/go-paramfetch v0.0.2-0.20210330140417-936748d3f5ec/go.mod h1:fZzmf4tftbwf9S37XRifoJlz7nCjRdIrMGLR07dKLCc= github.com/filecoin-project/go-state-types v0.0.0-20200903145444-247639ffa6ad/go.mod h1:IQ0MBPnonv35CJHtWSN3YY1Hz2gkPru1Q9qoaYLxx9I= github.com/filecoin-project/go-state-types v0.0.0-20200904021452-1883f36ca2f4/go.mod h1:IQ0MBPnonv35CJHtWSN3YY1Hz2gkPru1Q9qoaYLxx9I= github.com/filecoin-project/go-state-types v0.0.0-20200928172055-2df22083d8ab/go.mod h1:ezYnPf0bNkTsDibL/psSz5dy4B5awOJ/E7P2Saeep8g= @@ -310,14 +310,16 @@ github.com/filecoin-project/specs-actors v0.9.13 h1:rUEOQouefi9fuVY/2HOroROJlZbO github.com/filecoin-project/specs-actors v0.9.13/go.mod h1:TS1AW/7LbG+615j4NsjMK1qlpAwaFsG9w0V2tg2gSao= github.com/filecoin-project/specs-actors/v2 v2.0.1/go.mod h1:v2NZVYinNIKA9acEMBm5wWXxqv5+frFEbekBFemYghY= github.com/filecoin-project/specs-actors/v2 v2.3.2/go.mod h1:UuJQLoTx/HPvvWeqlIFmC/ywlOLHNe8SNQ3OunFbu2Y= +github.com/filecoin-project/specs-actors/v2 v2.3.4/go.mod h1:UuJQLoTx/HPvvWeqlIFmC/ywlOLHNe8SNQ3OunFbu2Y= github.com/filecoin-project/specs-actors/v2 v2.3.5-0.20210114162132-5b58b773f4fb h1:orr/sMzrDZUPAveRE+paBdu1kScIUO5zm+HYeh+VlhA= github.com/filecoin-project/specs-actors/v2 v2.3.5-0.20210114162132-5b58b773f4fb/go.mod h1:LljnY2Mn2homxZsmokJZCpRuhOPxfXhvcek5gWkmqAc= +github.com/filecoin-project/specs-actors/v3 v3.0.4-0.20210227000520-b3317b86f4d1/go.mod h1:oMcmEed6B7H/wHabM3RQphTIhq0ibAKsbpYs+bQ/uxQ= github.com/filecoin-project/specs-actors/v3 v3.1.0 h1:s4qiPw8pgypqBGAy853u/zdZJ7K9cTZdM1rTiSonHrg= github.com/filecoin-project/specs-actors/v3 v3.1.0/go.mod h1:mpynccOLlIRy0QnR008BwYBwT9fen+sPR13MA1VmMww= github.com/filecoin-project/specs-actors/v4 v4.0.0 h1:vMALksY5G3J5rj3q9rbcyB+f4Tk1xrLqSgdB3jOok4s= github.com/filecoin-project/specs-actors/v4 v4.0.0/go.mod h1:TkHXf/l7Wyw4ZejyXIPS2rK8bBO0rdwhTZyQQgaglng= -github.com/filecoin-project/specs-actors/v5 v5.0.0-20210510162709-3255bdd9f2bb h1:i2ZBHLiNYyyhNlfjfB/TGtGLlb8dgiGiVCDZlGpUtUc= -github.com/filecoin-project/specs-actors/v5 v5.0.0-20210510162709-3255bdd9f2bb/go.mod h1:XAgQWq5pu0MBwx3MI5uJ6fK/Q8jCkZnKNNLxvDcbXew= +github.com/filecoin-project/specs-actors/v5 v5.0.0-20210512015452-4fe3889fff57 h1:N6IBsnGXfAMXd677G6EiOKewFwQ7Ulcuupi4U6wYmXE= +github.com/filecoin-project/specs-actors/v5 v5.0.0-20210512015452-4fe3889fff57/go.mod h1:283yBMMUSDB2abcjP/hhrwTkhb9h3sfM6KGrep/ZlBI= github.com/filecoin-project/specs-storage v0.1.1-0.20201105051918-5188d9774506 h1:Ur/l2+6qN+lQiqjozWWc5p9UDaAMDZKTlDS98oRnlIw= github.com/filecoin-project/specs-storage v0.1.1-0.20201105051918-5188d9774506/go.mod h1:nJRRM7Aa9XVvygr3W9k6xGF46RWzr2zxF/iGoAIfA/g= github.com/filecoin-project/test-vectors/schema v0.0.5 h1:w3zHQhzM4pYxJDl21avXjOKBLF8egrvwUwjpT8TquDg= diff --git a/lotuspond/front/src/chain/methods.json b/lotuspond/front/src/chain/methods.json index 12e0e8abf..f90e91914 100644 --- a/lotuspond/front/src/chain/methods.json +++ b/lotuspond/front/src/chain/methods.json @@ -281,7 +281,8 @@ "ConfirmUpdateWorkerKey", "RepayDebt", "ChangeOwnerAddress", - "DisputeWindowedPoSt" + "DisputeWindowedPoSt", + "ProveCommitAggregate" ], "fil/3/storagepower": [ "Send", diff --git a/node/config/def.go b/node/config/def.go index 63099516b..94e87be3b 100644 --- a/node/config/def.go +++ b/node/config/def.go @@ -82,6 +82,10 @@ type SealingConfig struct { AlwaysKeepUnsealedCopy bool + AggregateCommits bool + MinCommitBatch int + MaxCommitBatch int + // Keep this many sectors in sealing pipeline, start CC if needed // todo TargetSealingSectors uint64 @@ -237,6 +241,10 @@ func DefaultStorageMiner() *StorageMiner { MaxSealingSectorsForDeals: 0, WaitDealsDelay: Duration(time.Hour * 6), AlwaysKeepUnsealedCopy: true, + + AggregateCommits: true, + MinCommitBatch: 5, // todo: base this on some real numbers + MaxCommitBatch: 400, }, Storage: sectorstorage.SealerConfig{ diff --git a/node/impl/storminer.go b/node/impl/storminer.go index cad886e2d..8766ba154 100644 --- a/node/impl/storminer.go +++ b/node/impl/storminer.go @@ -378,6 +378,14 @@ func (sm *StorageMinerAPI) SectorMarkForUpgrade(ctx context.Context, id abi.Sect return sm.Miner.MarkForUpgrade(id) } +func (sm *StorageMinerAPI) SectorCommitFlush(ctx context.Context) (*cid.Cid, error) { + return sm.Miner.CommitFlush(ctx) +} + +func (sm *StorageMinerAPI) SectorCommitPending(ctx context.Context) ([]abi.SectorID, error) { + return sm.Miner.CommitPending(ctx) +} + func (sm *StorageMinerAPI) WorkerConnect(ctx context.Context, url string) error { w, err := connectRemoteWorker(ctx, sm, url) if err != nil { diff --git a/node/modules/storageminer.go b/node/modules/storageminer.go index 1781d0493..1d89a0c4b 100644 --- a/node/modules/storageminer.go +++ b/node/modules/storageminer.go @@ -99,7 +99,7 @@ func GetParams(spt abi.RegisteredSealProof) error { } // TODO: We should fetch the params for the actual proof type, not just based on the size. - if err := paramfetch.GetParams(context.TODO(), build.ParametersJSON(), uint64(ssize)); err != nil { + if err := paramfetch.GetParams(context.TODO(), build.ParametersJSON(), build.SrsJSON(), uint64(ssize)); err != nil { return xerrors.Errorf("fetching proof parameters: %w", err) } @@ -824,6 +824,9 @@ func NewSetSealConfigFunc(r repo.LockedRepo) (dtypes.SetSealingConfigFunc, error MaxSealingSectorsForDeals: cfg.MaxSealingSectorsForDeals, WaitDealsDelay: config.Duration(cfg.WaitDealsDelay), AlwaysKeepUnsealedCopy: cfg.AlwaysKeepUnsealedCopy, + AggregateCommits: cfg.AggregateCommits, + MinCommitBatch: cfg.MinCommitBatch, + MaxCommitBatch: cfg.MaxCommitBatch, } }) return @@ -839,6 +842,9 @@ func NewGetSealConfigFunc(r repo.LockedRepo) (dtypes.GetSealingConfigFunc, error MaxSealingSectorsForDeals: cfg.Sealing.MaxSealingSectorsForDeals, WaitDealsDelay: time.Duration(cfg.Sealing.WaitDealsDelay), AlwaysKeepUnsealedCopy: cfg.Sealing.AlwaysKeepUnsealedCopy, + AggregateCommits: cfg.Sealing.AggregateCommits, + MinCommitBatch: cfg.Sealing.MinCommitBatch, + MaxCommitBatch: cfg.Sealing.MaxCommitBatch, } }) return diff --git a/storage/sealing.go b/storage/sealing.go index 8981c3738..b3d38909b 100644 --- a/storage/sealing.go +++ b/storage/sealing.go @@ -59,6 +59,14 @@ func (m *Miner) TerminatePending(ctx context.Context) ([]abi.SectorID, error) { return m.sealing.TerminatePending(ctx) } +func (m *Miner) CommitFlush(ctx context.Context) (*cid.Cid, error) { + return m.sealing.CommitFlush(ctx) +} + +func (m *Miner) CommitPending(ctx context.Context) ([]abi.SectorID, error) { + return m.sealing.CommitPending(ctx) +} + func (m *Miner) MarkForUpgrade(id abi.SectorNumber) error { return m.sealing.MarkForUpgrade(id) } From 5f8c80533a18b9e16f6414ff1ca71be970f7b944 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Mon, 17 May 2021 13:15:07 -0400 Subject: [PATCH 004/160] Update to latest actors and FFI --- extern/filecoin-ffi | 2 +- go.mod | 2 +- go.sum | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/extern/filecoin-ffi b/extern/filecoin-ffi index 5f9f082b0..941bf0917 160000 --- a/extern/filecoin-ffi +++ b/extern/filecoin-ffi @@ -1 +1 @@ -Subproject commit 5f9f082b03a22bbf0f31adcb2bdf7f539cee6b6b +Subproject commit 941bf0917e155dc9a5886e0508e4f900039df9dc diff --git a/go.mod b/go.mod index 4eb8e0844..98e9d3691 100644 --- a/go.mod +++ b/go.mod @@ -47,7 +47,7 @@ require ( github.com/filecoin-project/specs-actors/v2 v2.3.5-0.20210114162132-5b58b773f4fb github.com/filecoin-project/specs-actors/v3 v3.1.0 github.com/filecoin-project/specs-actors/v4 v4.0.0 - github.com/filecoin-project/specs-actors/v5 v5.0.0-20210512015452-4fe3889fff57 + github.com/filecoin-project/specs-actors/v5 v5.0.0-20210517165532-c7cff61d07fb github.com/filecoin-project/specs-storage v0.1.1-0.20201105051918-5188d9774506 github.com/filecoin-project/test-vectors/schema v0.0.5 github.com/gbrlsnchs/jwt/v3 v3.0.0-beta.1 diff --git a/go.sum b/go.sum index a84e2e471..1c811efb6 100644 --- a/go.sum +++ b/go.sum @@ -318,8 +318,8 @@ github.com/filecoin-project/specs-actors/v3 v3.1.0 h1:s4qiPw8pgypqBGAy853u/zdZJ7 github.com/filecoin-project/specs-actors/v3 v3.1.0/go.mod h1:mpynccOLlIRy0QnR008BwYBwT9fen+sPR13MA1VmMww= github.com/filecoin-project/specs-actors/v4 v4.0.0 h1:vMALksY5G3J5rj3q9rbcyB+f4Tk1xrLqSgdB3jOok4s= github.com/filecoin-project/specs-actors/v4 v4.0.0/go.mod h1:TkHXf/l7Wyw4ZejyXIPS2rK8bBO0rdwhTZyQQgaglng= -github.com/filecoin-project/specs-actors/v5 v5.0.0-20210512015452-4fe3889fff57 h1:N6IBsnGXfAMXd677G6EiOKewFwQ7Ulcuupi4U6wYmXE= -github.com/filecoin-project/specs-actors/v5 v5.0.0-20210512015452-4fe3889fff57/go.mod h1:283yBMMUSDB2abcjP/hhrwTkhb9h3sfM6KGrep/ZlBI= +github.com/filecoin-project/specs-actors/v5 v5.0.0-20210517165532-c7cff61d07fb h1:818gGdeEC+7aHGl2X7ptdtYuqoEgRsY3jwz+DvUYUFk= +github.com/filecoin-project/specs-actors/v5 v5.0.0-20210517165532-c7cff61d07fb/go.mod h1:283yBMMUSDB2abcjP/hhrwTkhb9h3sfM6KGrep/ZlBI= github.com/filecoin-project/specs-storage v0.1.1-0.20201105051918-5188d9774506 h1:Ur/l2+6qN+lQiqjozWWc5p9UDaAMDZKTlDS98oRnlIw= github.com/filecoin-project/specs-storage v0.1.1-0.20201105051918-5188d9774506/go.mod h1:nJRRM7Aa9XVvygr3W9k6xGF46RWzr2zxF/iGoAIfA/g= github.com/filecoin-project/test-vectors/schema v0.0.5 h1:w3zHQhzM4pYxJDl21avXjOKBLF8egrvwUwjpT8TquDg= From 8f42f375cf1d9d90b0b0d6e48ab98904dbaf4410 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 17 May 2021 19:55:44 +0200 Subject: [PATCH 005/160] Update ffi --- extern/filecoin-ffi | 2 +- go.sum | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/extern/filecoin-ffi b/extern/filecoin-ffi index 941bf0917..525851103 160000 --- a/extern/filecoin-ffi +++ b/extern/filecoin-ffi @@ -1 +1 @@ -Subproject commit 941bf0917e155dc9a5886e0508e4f900039df9dc +Subproject commit 525851103fcf548dff1d4db6b5a1a2a6d9e10833 diff --git a/go.sum b/go.sum index 1c811efb6..c281e0fdb 100644 --- a/go.sum +++ b/go.sum @@ -318,6 +318,7 @@ github.com/filecoin-project/specs-actors/v3 v3.1.0 h1:s4qiPw8pgypqBGAy853u/zdZJ7 github.com/filecoin-project/specs-actors/v3 v3.1.0/go.mod h1:mpynccOLlIRy0QnR008BwYBwT9fen+sPR13MA1VmMww= github.com/filecoin-project/specs-actors/v4 v4.0.0 h1:vMALksY5G3J5rj3q9rbcyB+f4Tk1xrLqSgdB3jOok4s= github.com/filecoin-project/specs-actors/v4 v4.0.0/go.mod h1:TkHXf/l7Wyw4ZejyXIPS2rK8bBO0rdwhTZyQQgaglng= +github.com/filecoin-project/specs-actors/v5 v5.0.0-20210512015452-4fe3889fff57/go.mod h1:283yBMMUSDB2abcjP/hhrwTkhb9h3sfM6KGrep/ZlBI= github.com/filecoin-project/specs-actors/v5 v5.0.0-20210517165532-c7cff61d07fb h1:818gGdeEC+7aHGl2X7ptdtYuqoEgRsY3jwz+DvUYUFk= github.com/filecoin-project/specs-actors/v5 v5.0.0-20210517165532-c7cff61d07fb/go.mod h1:283yBMMUSDB2abcjP/hhrwTkhb9h3sfM6KGrep/ZlBI= github.com/filecoin-project/specs-storage v0.1.1-0.20201105051918-5188d9774506 h1:Ur/l2+6qN+lQiqjozWWc5p9UDaAMDZKTlDS98oRnlIw= From 6278bdc69a2fa614a38ac1a24f07460cf3550212 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 17 May 2021 20:47:41 +0200 Subject: [PATCH 006/160] Make things build --- chain/actors/policy/policy.go | 4 ++++ chain/gen/gen.go | 2 +- cmd/lotus-bench/caching_verifier.go | 9 +++++++++ extern/sector-storage/ffiwrapper/sealer_test.go | 5 +++-- extern/sector-storage/ffiwrapper/types.go | 2 +- extern/sector-storage/ffiwrapper/verifier_cgo.go | 4 ++-- extern/sector-storage/mock/mock.go | 2 +- extern/storage-sealing/commit_batch.go | 14 ++++++-------- extern/storage-sealing/states_sealing.go | 1 - storage/wdpost_run_test.go | 9 +++++++++ 10 files changed, 36 insertions(+), 16 deletions(-) diff --git a/chain/actors/policy/policy.go b/chain/actors/policy/policy.go index 191ffb5f5..113544c05 100644 --- a/chain/actors/policy/policy.go +++ b/chain/actors/policy/policy.go @@ -302,6 +302,10 @@ func GetDefaultSectorSize() abi.SectorSize { return szs[0] } +func GetDefaultAggregationProof() abi.RegisteredAggregationProof { + return abi.RegisteredAggregationProof_SnarkPackV1 +} + func GetSectorMaxLifetime(proof abi.RegisteredSealProof, nwVer network.Version) abi.ChainEpoch { if nwVer <= network.Version10 { return builtin5.SealProofPoliciesV0[proof].SectorMaxLifetime diff --git a/chain/gen/gen.go b/chain/gen/gen.go index af2611676..32b238433 100644 --- a/chain/gen/gen.go +++ b/chain/gen/gen.go @@ -693,7 +693,7 @@ func (m genFakeVerifier) VerifyAggregateSeals(aggregate proof5.AggregateSealVeri panic("not supported") } -func (m genFakeVerifier) AggregateSealProofs(proofType abi.RegisteredSealProof, proofs [][]byte) ([]byte, error) { +func (m genFakeVerifier) AggregateSealProofs(proofType abi.RegisteredSealProof, rap abi.RegisteredAggregationProof, proofs [][]byte) ([]byte, error) { panic("not supported") } diff --git a/cmd/lotus-bench/caching_verifier.go b/cmd/lotus-bench/caching_verifier.go index 5b434c762..55786c585 100644 --- a/cmd/lotus-bench/caching_verifier.go +++ b/cmd/lotus-bench/caching_verifier.go @@ -8,6 +8,7 @@ import ( "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/lotus/extern/sector-storage/ffiwrapper" proof2 "github.com/filecoin-project/specs-actors/v2/actors/runtime/proof" + proof5 "github.com/filecoin-project/specs-actors/v5/actors/runtime/proof" "github.com/ipfs/go-datastore" "github.com/minio/blake2b-simd" cbg "github.com/whyrusleeping/cbor-gen" @@ -96,4 +97,12 @@ func (cv *cachingVerifier) GenerateWinningPoStSectorChallenge(ctx context.Contex return cv.backend.GenerateWinningPoStSectorChallenge(ctx, proofType, a, rnd, u) } +func (cv cachingVerifier) VerifyAggregateSeals(aggregate proof5.AggregateSealVerifyProofAndInfos) (bool, error) { + return cv.backend.VerifyAggregateSeals(aggregate) +} + +func (cv cachingVerifier) AggregateSealProofs(proofType abi.RegisteredSealProof, rap abi.RegisteredAggregationProof, proofs [][]byte) ([]byte, error) { + return cv.backend.AggregateSealProofs(proofType, rap, proofs) +} + var _ ffiwrapper.Verifier = (*cachingVerifier)(nil) diff --git a/extern/sector-storage/ffiwrapper/sealer_test.go b/extern/sector-storage/ffiwrapper/sealer_test.go index 5af6a78e7..a1b27cc87 100644 --- a/extern/sector-storage/ffiwrapper/sealer_test.go +++ b/extern/sector-storage/ffiwrapper/sealer_test.go @@ -32,6 +32,7 @@ import ( ffi "github.com/filecoin-project/filecoin-ffi" + "github.com/filecoin-project/lotus/chain/actors/policy" "github.com/filecoin-project/lotus/extern/sector-storage/ffiwrapper/basicfs" "github.com/filecoin-project/lotus/extern/sector-storage/storiface" "github.com/filecoin-project/lotus/extern/storage-sealing/lib/nullreader" @@ -538,12 +539,12 @@ func TestSealAndVerifyAggregate(t *testing.T) { aggStart := time.Now() - avi.Proof, err = ProofVerifier.AggregateSealProofs(sealProofType, toAggregate) + avi.Proof, err = ProofVerifier.AggregateSealProofs(sealProofType, policy.GetDefaultAggregationProof(), toAggregate) require.NoError(t, err) aggDone := time.Now() - _, err = ProofVerifier.AggregateSealProofs(sealProofType, toAggregate) + _, err = ProofVerifier.AggregateSealProofs(sealProofType, policy.GetDefaultAggregationProof(), toAggregate) require.NoError(t, err) aggHot := time.Now() diff --git a/extern/sector-storage/ffiwrapper/types.go b/extern/sector-storage/ffiwrapper/types.go index 9c84794bf..aa0658397 100644 --- a/extern/sector-storage/ffiwrapper/types.go +++ b/extern/sector-storage/ffiwrapper/types.go @@ -42,7 +42,7 @@ type Verifier interface { GenerateWinningPoStSectorChallenge(context.Context, abi.RegisteredPoStProof, abi.ActorID, abi.PoStRandomness, uint64) ([]uint64, error) // cheap, makes no sense to put this on the storage interface - AggregateSealProofs(proofType abi.RegisteredSealProof, proofs [][]byte) ([]byte, error) + AggregateSealProofs(proofType abi.RegisteredSealProof, rap abi.RegisteredAggregationProof, proofs [][]byte) ([]byte, error) } type SectorProvider interface { diff --git a/extern/sector-storage/ffiwrapper/verifier_cgo.go b/extern/sector-storage/ffiwrapper/verifier_cgo.go index b31903080..2650fba02 100644 --- a/extern/sector-storage/ffiwrapper/verifier_cgo.go +++ b/extern/sector-storage/ffiwrapper/verifier_cgo.go @@ -140,6 +140,6 @@ func (proofVerifier) GenerateWinningPoStSectorChallenge(ctx context.Context, pro return ffi.GenerateWinningPoStSectorChallenge(proofType, minerID, randomness, eligibleSectorCount) } -func (v proofVerifier) AggregateSealProofs(proofType abi.RegisteredSealProof, proofs [][]byte) ([]byte, error) { - return ffi.AggregateSealProofs(proofType, proofs) +func (v proofVerifier) AggregateSealProofs(proofType abi.RegisteredSealProof, rap abi.RegisteredAggregationProof, proofs [][]byte) ([]byte, error) { + return ffi.AggregateSealProofs(proofType, rap, proofs) } diff --git a/extern/sector-storage/mock/mock.go b/extern/sector-storage/mock/mock.go index ec6020010..8a70ed7bd 100644 --- a/extern/sector-storage/mock/mock.go +++ b/extern/sector-storage/mock/mock.go @@ -524,7 +524,7 @@ func (m mockVerif) VerifyAggregateSeals(aggregate proof5.AggregateSealVerifyProo return bytes.Equal(aggregate.Proof, out), nil } -func (m mockVerif) AggregateSealProofs(proofType abi.RegisteredSealProof, proofs [][]byte) ([]byte, error) { +func (m mockVerif) AggregateSealProofs(proofType abi.RegisteredSealProof, rap abi.RegisteredAggregationProof, proofs [][]byte) ([]byte, error) { out := make([]byte, 200) // todo: figure out more real length for pi, proof := range proofs { for i := range proof[:32] { diff --git a/extern/storage-sealing/commit_batch.go b/extern/storage-sealing/commit_batch.go index 74f163010..8b5d9b543 100644 --- a/extern/storage-sealing/commit_batch.go +++ b/extern/storage-sealing/commit_batch.go @@ -28,13 +28,16 @@ var ( CommitBatchWait = 5 * time.Minute ) +const arp = abi.RegisteredAggregationProof_SnarkPackV1 + type CommitBatcherApi interface { SendMsg(ctx context.Context, from, to address.Address, method abi.MethodNum, value, maxFee abi.TokenAmount, params []byte) (cid.Cid, error) StateMinerInfo(context.Context, address.Address, TipSetToken) (miner.MinerInfo, error) } type AggregateInput struct { - // TODO: Something changed in actors, I think this now needs to be AggregateSealVerifyProofAndInfos + spt abi.RegisteredSealProof + // TODO: Something changed in actors, I think this now needs to be AggregateSealVerifyProofAndInfos todo ?? info proof5.AggregateSealVerifyInfo proof []byte } @@ -137,20 +140,15 @@ func (b *CommitBatcher) processBatch(notif, after bool) (*cid.Cid, error) { return nil, nil } - spt := b.todo[0].info.SealProof + spt := b.todo[0].spt proofs := make([][]byte, total) for id, p := range b.todo { - if p.info.SealProof != spt { - // todo: handle when we'll have proof upgrade - return nil, xerrors.Errorf("different seal proof types in commit batch: %w", err) - } - params.SectorNumbers.Set(uint64(id)) proofs[id] = p.proof } - params.AggregateProof, err = b.verif.AggregateSealProofs(spt, proofs) + params.AggregateProof, err = b.verif.AggregateSealProofs(spt, arp, proofs) if err != nil { return nil, xerrors.Errorf("aggregating proofs: %w", err) } diff --git a/extern/storage-sealing/states_sealing.go b/extern/storage-sealing/states_sealing.go index 296d92b9c..9975b1a37 100644 --- a/extern/storage-sealing/states_sealing.go +++ b/extern/storage-sealing/states_sealing.go @@ -531,7 +531,6 @@ func (m *Sealing) handleSubmitCommitAggregate(ctx statemachine.Context, sector S mcid, err := m.commiter.AddCommit(ctx.Context(), sector.SectorNumber, AggregateInput{ info: proof.AggregateSealVerifyInfo{ Number: sector.SectorNumber, - DealIDs: sector.dealIDs(), Randomness: sector.TicketValue, InteractiveRandomness: sector.SeedValue, SealedCID: *sector.CommR, diff --git a/storage/wdpost_run_test.go b/storage/wdpost_run_test.go index 4bf30e3e9..2e412880a 100644 --- a/storage/wdpost_run_test.go +++ b/storage/wdpost_run_test.go @@ -23,6 +23,7 @@ import ( miner2 "github.com/filecoin-project/specs-actors/v2/actors/builtin/miner" proof2 "github.com/filecoin-project/specs-actors/v2/actors/runtime/proof" tutils "github.com/filecoin-project/specs-actors/v2/support/testing" + proof5 "github.com/filecoin-project/specs-actors/v5/actors/runtime/proof" "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/build" @@ -144,6 +145,14 @@ func (m mockVerif) VerifyWindowPoSt(ctx context.Context, info proof2.WindowPoStV return true, nil } +func (m mockVerif) AggregateSealProofs(proofType abi.RegisteredSealProof, rap abi.RegisteredAggregationProof, proofs [][]byte) ([]byte, error) { + panic("implement me") +} + +func (m mockVerif) VerifyAggregateSeals(aggregate proof5.AggregateSealVerifyProofAndInfos) (bool, error) { + panic("implement me") +} + func (m mockVerif) VerifySeal(proof2.SealVerifyInfo) (bool, error) { panic("implement me") } From 578bef4f832a2846ab17107d28adb01983b1d9e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 17 May 2021 20:56:28 +0200 Subject: [PATCH 007/160] Update gen --- build/openrpc/full.json.gz | Bin 22804 -> 22810 bytes build/openrpc/miner.json.gz | Bin 7828 -> 7890 bytes build/openrpc/worker.json.gz | Bin 2573 -> 2577 bytes chain/actors/policy/policy.go.template | 4 ++++ lotuspond/front/src/chain/methods.json | 7 ++++--- 5 files changed, 8 insertions(+), 3 deletions(-) diff --git a/build/openrpc/full.json.gz b/build/openrpc/full.json.gz index 65c5eddd3a304996f9bd099df40cf3a6af605706..1aad25356f8daa0c721ab56cf6c7f47c4f4b8fea 100644 GIT binary patch literal 22810 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

-fDhK`xeCY=8+2=BLD= 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 aoq1Bg5PEs^fro>8dAZSs4}Rl`1ot1Is*~CP diff --git a/build/openrpc/miner.json.gz b/build/openrpc/miner.json.gz index 13b9917c830cc37a79050bcbd7dc7f844fdcb58f..cb24beaf8ac4ba08dd0136ac203ee80b0e01e7ec 100644 GIT binary patch delta 7877 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 literal 7828 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 diff --git a/build/openrpc/worker.json.gz b/build/openrpc/worker.json.gz index 798a6376cacd1d613567d6c963b3e734655e62e7..2214ad85af5e8b7f0c47a81106e161959b55c203 100644 GIT binary patch delta 2463 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 Date: Mon, 17 May 2021 22:02:23 +0200 Subject: [PATCH 008/160] Get PreCommitting to work --- build/params_2k.go | 20 ++++++------ chain/state/statetree.go | 13 ++++++++ chain/stmgr/forks.go | 2 +- chain/types/state.go | 4 ++- storage/adapter_storage_miner.go | 54 +++++++++++++++++++++++++++----- 5 files changed, 74 insertions(+), 19 deletions(-) diff --git a/build/params_2k.go b/build/params_2k.go index d93b26468..8ead218f5 100644 --- a/build/params_2k.go +++ b/build/params_2k.go @@ -24,22 +24,22 @@ var UpgradeIgnitionHeight = abi.ChainEpoch(-2) var UpgradeRefuelHeight = abi.ChainEpoch(-3) var UpgradeTapeHeight = abi.ChainEpoch(-4) -var UpgradeAssemblyHeight = abi.ChainEpoch(10) +var UpgradeAssemblyHeight = abi.ChainEpoch(5) var UpgradeLiftoffHeight = abi.ChainEpoch(-5) -var UpgradeKumquatHeight = abi.ChainEpoch(15) -var UpgradeCalicoHeight = abi.ChainEpoch(20) -var UpgradePersianHeight = abi.ChainEpoch(25) -var UpgradeOrangeHeight = abi.ChainEpoch(27) -var UpgradeClausHeight = abi.ChainEpoch(30) +var UpgradeKumquatHeight = abi.ChainEpoch(6) +var UpgradeCalicoHeight = abi.ChainEpoch(7) +var UpgradePersianHeight = abi.ChainEpoch(8) +var UpgradeOrangeHeight = abi.ChainEpoch(9) +var UpgradeClausHeight = abi.ChainEpoch(10) -var UpgradeTrustHeight = abi.ChainEpoch(35) +var UpgradeTrustHeight = abi.ChainEpoch(11) -var UpgradeNorwegianHeight = abi.ChainEpoch(40) +var UpgradeNorwegianHeight = abi.ChainEpoch(12) -var UpgradeTurboHeight = abi.ChainEpoch(45) +var UpgradeTurboHeight = abi.ChainEpoch(13) -var UpgradeHyperdriveHeight = abi.ChainEpoch(50) +var UpgradeHyperdriveHeight = abi.ChainEpoch(14) var DrandSchedule = map[abi.ChainEpoch]DrandEnum{ 0: DrandMainnet, diff --git a/chain/state/statetree.go b/chain/state/statetree.go index 33a8116df..d783fff69 100644 --- a/chain/state/statetree.go +++ b/chain/state/statetree.go @@ -25,6 +25,7 @@ import ( states2 "github.com/filecoin-project/specs-actors/v2/actors/states" states3 "github.com/filecoin-project/specs-actors/v3/actors/states" states4 "github.com/filecoin-project/specs-actors/v4/actors/states" + states5 "github.com/filecoin-project/specs-actors/v5/actors/states" ) var log = logging.Logger("statetree") @@ -191,6 +192,12 @@ func NewStateTree(cst cbor.IpldStore, ver types.StateTreeVersion) (*StateTree, e return nil, xerrors.Errorf("failed to create state tree: %w", err) } hamt = tree.Map + case types.StateTreeVersion4: + tree, err := states5.NewTree(store) + if err != nil { + return nil, xerrors.Errorf("failed to create state tree: %w", err) + } + hamt = tree.Map default: return nil, xerrors.Errorf("unsupported state tree version: %d", ver) } @@ -246,6 +253,12 @@ func LoadStateTree(cst cbor.IpldStore, c cid.Cid) (*StateTree, error) { if tree != nil { hamt = tree.Map } + case types.StateTreeVersion4: + var tree *states5.Tree + tree, err = states5.LoadTree(store, root.Actors) + if tree != nil { + hamt = tree.Map + } default: return nil, xerrors.Errorf("unsupported state tree version: %d", root.Version) } diff --git a/chain/stmgr/forks.go b/chain/stmgr/forks.go index de5e91388..ee5a26dea 100644 --- a/chain/stmgr/forks.go +++ b/chain/stmgr/forks.go @@ -1247,7 +1247,7 @@ func upgradeActorsV5Common( // Persist the result. newRoot, err := store.Put(ctx, &types.StateRoot{ - Version: types.StateTreeVersion3, + Version: types.StateTreeVersion4, Actors: newHamtRoot, Info: stateRoot.Info, }) diff --git a/chain/types/state.go b/chain/types/state.go index b561aab71..c8f8f1cd9 100644 --- a/chain/types/state.go +++ b/chain/types/state.go @@ -13,8 +13,10 @@ const ( StateTreeVersion1 // StateTreeVersion2 corresponds to actors v3. StateTreeVersion2 - // StateTreeVersion3 corresponds to actors >= v4. + // StateTreeVersion3 corresponds to actors v4. StateTreeVersion3 + // StateTreeVersion4 corresponds to actors v5. + StateTreeVersion4 ) type StateRoot struct { diff --git a/storage/adapter_storage_miner.go b/storage/adapter_storage_miner.go index 41d7461a8..f4d1e0038 100644 --- a/storage/adapter_storage_miner.go +++ b/storage/adapter_storage_miner.go @@ -16,6 +16,7 @@ import ( "github.com/filecoin-project/go-state-types/network" market2 "github.com/filecoin-project/specs-actors/v2/actors/builtin/market" + market5 "github.com/filecoin-project/specs-actors/v5/actors/builtin/market" "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/blockstore" @@ -146,10 +147,36 @@ func (s SealingAPIAdapter) StateComputeDataCommitment(ctx context.Context, maddr return cid.Undef, xerrors.Errorf("failed to unmarshal TipSetToken to TipSetKey: %w", err) } - ccparams, err := actors.SerializeParams(&market2.ComputeDataCommitmentParams{ - DealIDs: deals, - SectorType: sectorType, - }) + ts, err := s.delegate.ChainGetTipSet(ctx, tsk) + if err != nil { + return cid.Cid{}, err + } + + // using parent ts because the migration won't be run on the first nv13 + // tipset we apply StateCall to (because we don't run migrations in StateCall + // and just apply to parent state) + nv, err := s.delegate.StateNetworkVersion(ctx, ts.Parents()) + if err != nil { + return cid.Cid{}, err + } + + var ccparams []byte + if nv < network.Version13 { + ccparams, err = actors.SerializeParams(&market2.ComputeDataCommitmentParams{ + DealIDs: deals, + SectorType: sectorType, + }) + } else { + ccparams, err = actors.SerializeParams(&market5.ComputeDataCommitmentParams{ + Inputs: []*market5.SectorDataSpec{ + { + DealIDs: deals, + SectorType: sectorType, + }, + }, + }) + } + if err != nil { return cid.Undef, xerrors.Errorf("computing params for ComputeDataCommitment: %w", err) } @@ -169,12 +196,25 @@ func (s SealingAPIAdapter) StateComputeDataCommitment(ctx context.Context, maddr return cid.Undef, xerrors.Errorf("receipt for ComputeDataCommitment had exit code %d", r.MsgRct.ExitCode) } - var c cbg.CborCid - if err := c.UnmarshalCBOR(bytes.NewReader(r.MsgRct.Return)); err != nil { + if nv < network.Version13 { + var c cbg.CborCid + if err := c.UnmarshalCBOR(bytes.NewReader(r.MsgRct.Return)); err != nil { + return cid.Undef, xerrors.Errorf("failed to unmarshal CBOR to CborCid: %w", err) + } + + return cid.Cid(c), nil + } + + var cr market5.ComputeDataCommitmentReturn + if err := cr.UnmarshalCBOR(bytes.NewReader(r.MsgRct.Return)); err != nil { return cid.Undef, xerrors.Errorf("failed to unmarshal CBOR to CborCid: %w", err) } - return cid.Cid(c), nil + if len(cr.CommDs) != 1 { + return cid.Undef, xerrors.Errorf("CommD output must have 1 entry") + } + + return cid.Cid(cr.CommDs[0]), nil } func (s SealingAPIAdapter) StateSectorPreCommitInfo(ctx context.Context, maddr address.Address, sectorNumber abi.SectorNumber, tok sealing.TipSetToken) (*miner.SectorPreCommitOnChainInfo, error) { From ba2032c642b212747eef8afa63d1778033e3f899 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 17 May 2021 22:51:29 +0200 Subject: [PATCH 009/160] Fix some aggregation bugs --- api/test/window_post.go | 6 ++++++ extern/storage-sealing/commit_batch.go | 4 ++-- extern/storage-sealing/states_sealing.go | 11 ++++++++++- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/api/test/window_post.go b/api/test/window_post.go index c987fa1f9..b6804c401 100644 --- a/api/test/window_post.go +++ b/api/test/window_post.go @@ -188,6 +188,12 @@ func pledgeSectors(t *testing.T, ctx context.Context, miner TestStorageNode, n, } for len(toCheck) > 0 { + cb, err := miner.SectorCommitFlush(ctx) + require.NoError(t, err) + if cb != nil { + fmt.Printf("BATCH: %s\n", *cb) + } + for n := range toCheck { st, err := miner.SectorsStatus(ctx, n, false) require.NoError(t, err) diff --git a/extern/storage-sealing/commit_batch.go b/extern/storage-sealing/commit_batch.go index 8b5d9b543..ad0b00e88 100644 --- a/extern/storage-sealing/commit_batch.go +++ b/extern/storage-sealing/commit_batch.go @@ -141,11 +141,11 @@ func (b *CommitBatcher) processBatch(notif, after bool) (*cid.Cid, error) { } spt := b.todo[0].spt - proofs := make([][]byte, total) + proofs := make([][]byte, 0, total) for id, p := range b.todo { params.SectorNumbers.Set(uint64(id)) - proofs[id] = p.proof + proofs = append(proofs, p.proof) } params.AggregateProof, err = b.verif.AggregateSealProofs(spt, arp, proofs) diff --git a/extern/storage-sealing/states_sealing.go b/extern/storage-sealing/states_sealing.go index 9975b1a37..739e8c946 100644 --- a/extern/storage-sealing/states_sealing.go +++ b/extern/storage-sealing/states_sealing.go @@ -11,6 +11,7 @@ import ( "github.com/filecoin-project/go-state-types/big" "github.com/filecoin-project/go-state-types/crypto" "github.com/filecoin-project/go-state-types/exitcode" + "github.com/filecoin-project/go-state-types/network" "github.com/filecoin-project/go-statemachine" "github.com/filecoin-project/specs-actors/v5/actors/runtime/proof" "github.com/filecoin-project/specs-storage/storage" @@ -457,8 +458,16 @@ func (m *Sealing) handleSubmitCommit(ctx statemachine.Context, sector SectorInfo if err != nil { return xerrors.Errorf("getting config: %w", err) } + if cfg.AggregateCommits { - return ctx.Send(SectorSubmitCommitAggregate{}) + nv, err := m.api.StateNetworkVersion(ctx.Context(), nil) + if err != nil { + return xerrors.Errorf("getting network version: %w", err) + } + + if nv >= network.Version13 { + return ctx.Send(SectorSubmitCommitAggregate{}) + } } tok, _, err := m.api.ChainHead(ctx.Context()) From febf7cf28f9b8c4c9649dfb7b66381ecd3a12c97 Mon Sep 17 00:00:00 2001 From: vyzo Date: Tue, 18 May 2021 12:02:53 +0300 Subject: [PATCH 010/160] sane config defaults --- node/config/def.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/node/config/def.go b/node/config/def.go index 94e87be3b..ba4ec7ce1 100644 --- a/node/config/def.go +++ b/node/config/def.go @@ -243,8 +243,8 @@ func DefaultStorageMiner() *StorageMiner { AlwaysKeepUnsealedCopy: true, AggregateCommits: true, - MinCommitBatch: 5, // todo: base this on some real numbers - MaxCommitBatch: 400, + MinCommitBatch: 1, // we must have at least one proof to aggregate + MaxCommitBatch: 204, // this is the maximum aggregation per FIP13 }, Storage: sectorstorage.SealerConfig{ From 357c0868b7ec4ce4a14f1ad9919dc9fbf10fe51e Mon Sep 17 00:00:00 2001 From: vyzo Date: Tue, 18 May 2021 12:20:19 +0300 Subject: [PATCH 011/160] proper config for termination batching and commit wait --- extern/storage-sealing/commit_batch.go | 13 ++++--- extern/storage-sealing/sealiface/config.go | 5 +++ extern/storage-sealing/sealing.go | 2 +- extern/storage-sealing/terminate_batch.go | 41 +++++++++++----------- node/config/def.go | 10 ++++-- 5 files changed, 39 insertions(+), 32 deletions(-) diff --git a/extern/storage-sealing/commit_batch.go b/extern/storage-sealing/commit_batch.go index ad0b00e88..6b3cef6d5 100644 --- a/extern/storage-sealing/commit_batch.go +++ b/extern/storage-sealing/commit_batch.go @@ -22,12 +22,6 @@ import ( "github.com/filecoin-project/lotus/extern/sector-storage/ffiwrapper" ) -var ( - // TODO: config! - - CommitBatchWait = 5 * time.Minute -) - const arp = abi.RegisteredAggregationProof_SnarkPackV1 type CommitBatcherApi interface { @@ -87,6 +81,11 @@ func (b *CommitBatcher) run() { var forceRes chan *cid.Cid var lastMsg *cid.Cid + cfg, err := b.getConfig() + if err != nil { + panic(err) + } + for { if forceRes != nil { forceRes <- lastMsg @@ -101,7 +100,7 @@ func (b *CommitBatcher) run() { return case <-b.notify: sendAboveMax = true - case <-time.After(TerminateBatchWait): + case <-time.After(cfg.CommitBatchWait): sendAboveMin = true case fr := <-b.force: // user triggered forceRes = fr diff --git a/extern/storage-sealing/sealiface/config.go b/extern/storage-sealing/sealiface/config.go index f62911b70..1c0945db2 100644 --- a/extern/storage-sealing/sealiface/config.go +++ b/extern/storage-sealing/sealiface/config.go @@ -21,4 +21,9 @@ type Config struct { AggregateCommits bool MinCommitBatch int MaxCommitBatch int + CommitBatchWait time.Duration + + TerminateBatchMax uint64 + TerminateBatchMin uint64 + TerminateBatchWait time.Duration } diff --git a/extern/storage-sealing/sealing.go b/extern/storage-sealing/sealing.go index d990cb02f..35ff15f51 100644 --- a/extern/storage-sealing/sealing.go +++ b/extern/storage-sealing/sealing.go @@ -152,7 +152,7 @@ func New(api SealingAPI, fc FeeConfig, events Events, maddr address.Address, ds notifee: notifee, addrSel: as, - terminator: NewTerminationBatcher(context.TODO(), maddr, api, as, fc), + terminator: NewTerminationBatcher(context.TODO(), maddr, api, as, fc, gc), commiter: NewCommitBatcher(context.TODO(), maddr, api, as, fc, gc, verif), getConfig: gc, diff --git a/extern/storage-sealing/terminate_batch.go b/extern/storage-sealing/terminate_batch.go index 0e96e8384..3833109a1 100644 --- a/extern/storage-sealing/terminate_batch.go +++ b/extern/storage-sealing/terminate_batch.go @@ -21,14 +21,6 @@ import ( "github.com/filecoin-project/lotus/chain/actors/builtin/miner" ) -var ( - // TODO: config - - TerminateBatchMax uint64 = 100 // adjust based on real-world gas numbers, actors limit at 10k - TerminateBatchMin uint64 = 1 - TerminateBatchWait = 5 * time.Minute -) - type TerminateBatcherApi interface { StateSectorPartition(ctx context.Context, maddr address.Address, sectorNumber abi.SectorNumber, tok TipSetToken) (*SectorLocation, error) SendMsg(ctx context.Context, from, to address.Address, method abi.MethodNum, value, maxFee abi.TokenAmount, params []byte) (cid.Cid, error) @@ -38,11 +30,12 @@ type TerminateBatcherApi interface { } type TerminateBatcher struct { - api TerminateBatcherApi - maddr address.Address - mctx context.Context - addrSel AddrSel - feeCfg FeeConfig + api TerminateBatcherApi + maddr address.Address + mctx context.Context + addrSel AddrSel + feeCfg FeeConfig + getConfig GetSealingConfigFunc todo map[SectorLocation]*bitfield.BitField // MinerSectorLocation -> BitField @@ -53,13 +46,14 @@ type TerminateBatcher struct { lk sync.Mutex } -func NewTerminationBatcher(mctx context.Context, maddr address.Address, api TerminateBatcherApi, addrSel AddrSel, feeCfg FeeConfig) *TerminateBatcher { +func NewTerminationBatcher(mctx context.Context, maddr address.Address, api TerminateBatcherApi, addrSel AddrSel, feeCfg FeeConfig, getConfig GetSealingConfigFunc) *TerminateBatcher { b := &TerminateBatcher{ - api: api, - maddr: maddr, - mctx: mctx, - addrSel: addrSel, - feeCfg: feeCfg, + api: api, + maddr: maddr, + mctx: mctx, + addrSel: addrSel, + feeCfg: feeCfg, + getConfig: getConfig, todo: map[SectorLocation]*bitfield.BitField{}, waiting: map[abi.SectorNumber][]chan cid.Cid{}, @@ -113,6 +107,11 @@ func (b *TerminateBatcher) processBatch(notif, after bool) (*cid.Cid, error) { return nil, xerrors.Errorf("getting proving deadline info failed: %w", err) } + cfg, err := b.getConfig() + if err != nil { + return nil, xerrors.Errorf("getting sealing config: %W", err) + } + b.lk.Lock() defer b.lk.Unlock() params := miner2.TerminateSectorsParams{} @@ -193,11 +192,11 @@ func (b *TerminateBatcher) processBatch(notif, after bool) (*cid.Cid, error) { return nil, nil // nothing to do } - if notif && total < TerminateBatchMax { + if notif && total < cfg.TerminateBatchMax { return nil, nil } - if after && total < TerminateBatchMin { + if after && total < cfg.TerminateBatchMin { return nil, nil } diff --git a/node/config/def.go b/node/config/def.go index ba4ec7ce1..6419127d0 100644 --- a/node/config/def.go +++ b/node/config/def.go @@ -242,9 +242,13 @@ func DefaultStorageMiner() *StorageMiner { WaitDealsDelay: Duration(time.Hour * 6), AlwaysKeepUnsealedCopy: true, - AggregateCommits: true, - MinCommitBatch: 1, // we must have at least one proof to aggregate - MaxCommitBatch: 204, // this is the maximum aggregation per FIP13 + AggregateCommits: true, + MinCommitBatch: 1, // we must have at least one proof to aggregate + MaxCommitBatch: 204, // this is the maximum aggregation per FIP13 + CommitBatchWait: time.Day, // this can be up to 6 days + TerminateBatchMin: 1, // same as above + TerminateBatchMax: 204, // same as above + TerminateBatchWait: time.Day, // this can be up to 6 days }, Storage: sectorstorage.SealerConfig{ From fe9311e43562b6dff5e485a72556608abe579e5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 18 May 2021 12:46:13 +0200 Subject: [PATCH 012/160] update ffi --- extern/filecoin-ffi | 2 +- extern/sector-storage/ffiwrapper/sealer_test.go | 11 +++++++---- extern/sector-storage/ffiwrapper/types.go | 2 +- extern/sector-storage/ffiwrapper/verifier_cgo.go | 4 ++-- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/extern/filecoin-ffi b/extern/filecoin-ffi index 525851103..178ac15a5 160000 --- a/extern/filecoin-ffi +++ b/extern/filecoin-ffi @@ -1 +1 @@ -Subproject commit 525851103fcf548dff1d4db6b5a1a2a6d9e10833 +Subproject commit 178ac15a537626cac07d8af68bffc603011c0310 diff --git a/extern/sector-storage/ffiwrapper/sealer_test.go b/extern/sector-storage/ffiwrapper/sealer_test.go index a1b27cc87..172641bf7 100644 --- a/extern/sector-storage/ffiwrapper/sealer_test.go +++ b/extern/sector-storage/ffiwrapper/sealer_test.go @@ -513,8 +513,11 @@ func TestSealAndVerifyAggregate(t *testing.T) { defer cleanup() avi := proof5.AggregateSealVerifyProofAndInfos{ - Miner: miner, - Infos: make([]proof5.AggregateSealVerifyInfo, numAgg), + Miner: miner, + SealProof: sealProofType, + AggregateProof: policy.GetDefaultAggregationProof(), + Proof: nil, + Infos: make([]proof5.AggregateSealVerifyInfo, numAgg), } toAggregate := make([][]byte, numAgg) @@ -539,12 +542,12 @@ func TestSealAndVerifyAggregate(t *testing.T) { aggStart := time.Now() - avi.Proof, err = ProofVerifier.AggregateSealProofs(sealProofType, policy.GetDefaultAggregationProof(), toAggregate) + avi.Proof, err = ProofVerifier.AggregateSealProofs(avi, toAggregate) require.NoError(t, err) aggDone := time.Now() - _, err = ProofVerifier.AggregateSealProofs(sealProofType, policy.GetDefaultAggregationProof(), toAggregate) + _, err = ProofVerifier.AggregateSealProofs(avi, toAggregate) require.NoError(t, err) aggHot := time.Now() diff --git a/extern/sector-storage/ffiwrapper/types.go b/extern/sector-storage/ffiwrapper/types.go index aa0658397..99efa7521 100644 --- a/extern/sector-storage/ffiwrapper/types.go +++ b/extern/sector-storage/ffiwrapper/types.go @@ -42,7 +42,7 @@ type Verifier interface { GenerateWinningPoStSectorChallenge(context.Context, abi.RegisteredPoStProof, abi.ActorID, abi.PoStRandomness, uint64) ([]uint64, error) // cheap, makes no sense to put this on the storage interface - AggregateSealProofs(proofType abi.RegisteredSealProof, rap abi.RegisteredAggregationProof, proofs [][]byte) ([]byte, error) + AggregateSealProofs(aggregateInfo proof5.AggregateSealVerifyProofAndInfos, proofs [][]byte) ([]byte, error) } type SectorProvider interface { diff --git a/extern/sector-storage/ffiwrapper/verifier_cgo.go b/extern/sector-storage/ffiwrapper/verifier_cgo.go index 2650fba02..650155305 100644 --- a/extern/sector-storage/ffiwrapper/verifier_cgo.go +++ b/extern/sector-storage/ffiwrapper/verifier_cgo.go @@ -140,6 +140,6 @@ func (proofVerifier) GenerateWinningPoStSectorChallenge(ctx context.Context, pro return ffi.GenerateWinningPoStSectorChallenge(proofType, minerID, randomness, eligibleSectorCount) } -func (v proofVerifier) AggregateSealProofs(proofType abi.RegisteredSealProof, rap abi.RegisteredAggregationProof, proofs [][]byte) ([]byte, error) { - return ffi.AggregateSealProofs(proofType, rap, proofs) +func (v proofVerifier) AggregateSealProofs(aggregateInfo proof5.AggregateSealVerifyProofAndInfos, proofs [][]byte) ([]byte, error) { + return ffi.AggregateSealProofs(aggregateInfo, proofs) } From 74bad49068956231baf4c48bc69447aa52a95d2c Mon Sep 17 00:00:00 2001 From: vyzo Date: Tue, 18 May 2021 14:30:47 +0300 Subject: [PATCH 013/160] correctly handle commit batch timer --- extern/storage-sealing/commit_batch.go | 88 +++++++++++++++++++++- extern/storage-sealing/sealiface/config.go | 1 + extern/storage-sealing/states_sealing.go | 2 +- node/config/def.go | 7 +- 4 files changed, 90 insertions(+), 8 deletions(-) diff --git a/extern/storage-sealing/commit_batch.go b/extern/storage-sealing/commit_batch.go index 6b3cef6d5..b61d5cdde 100644 --- a/extern/storage-sealing/commit_batch.go +++ b/extern/storage-sealing/commit_batch.go @@ -18,6 +18,7 @@ import ( proof5 "github.com/filecoin-project/specs-actors/v5/actors/runtime/proof" "github.com/filecoin-project/lotus/api" + "github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/chain/actors/builtin/miner" "github.com/filecoin-project/lotus/extern/sector-storage/ffiwrapper" ) @@ -27,6 +28,8 @@ const arp = abi.RegisteredAggregationProof_SnarkPackV1 type CommitBatcherApi interface { SendMsg(ctx context.Context, from, to address.Address, method abi.MethodNum, value, maxFee abi.TokenAmount, params []byte) (cid.Cid, error) StateMinerInfo(context.Context, address.Address, TipSetToken) (miner.MinerInfo, error) + StateMarketStorageDeal(context.Context, abi.DealID, TipSetToken) (*api.MarketDeal, error) + ChainHead(ctx context.Context) (TipSetToken, abi.ChainEpoch, error) } type AggregateInput struct { @@ -45,6 +48,7 @@ type CommitBatcher struct { getConfig GetSealingConfigFunc verif ffiwrapper.Verifier + sectors map[abi.SectorNumber]SectorInfo todo map[abi.SectorNumber]AggregateInput waiting map[abi.SectorNumber][]chan cid.Cid @@ -100,7 +104,7 @@ func (b *CommitBatcher) run() { return case <-b.notify: sendAboveMax = true - case <-time.After(cfg.CommitBatchWait): + case <-time.After(b.batchWait(cfg.CommitBatchWait, cfg.CommitBatchSlack)): sendAboveMin = true case fr := <-b.force: // user triggered forceRes = fr @@ -114,6 +118,79 @@ func (b *CommitBatcher) run() { } } +func (b *CommitBatcher) batchWait(maxWait, slack time.Duration) time.Duration { + now := time.Now() + + b.lk.Lock() + defer b.lk.Unlock() + + var deadline time.Time + for sn := range b.todo { + sectorDeadline := b.getSectorDeadline(sn) + if deadline.IsZero() || (!sectorDeadline.IsZero() && sectorDeadline.Before(deadline)) { + deadline = sectorDeadline + } + } + for sn := range b.waiting { + sectorDeadline := b.getSectorDeadline(sn) + if deadline.IsZero() || (!sectorDeadline.IsZero() && sectorDeadline.Before(deadline)) { + deadline = sectorDeadline + } + } + + if deadline.IsZero() { + return maxWait + } + + deadline = deadline.Add(-slack) + if deadline.Before(now) { + return time.Nanosecond // can't return 0 + } + + wait := deadline.Sub(now) + if wait > maxWait { + wait = maxWait + } + + return wait +} + +func (b *CommitBatcher) getSectorDeadline(sn abi.SectorNumber) time.Time { + si, ok := b.sectors[sn] + if !ok { + return time.Time{} + } + + tok, curEpoch, err := b.api.ChainHead(b.mctx) + if err != nil { + log.Errorf("getting chain head: %s", err) + return time.Time{} + } + + deadlineEpoch := si.TicketEpoch + for _, p := range si.Pieces { + if p.DealInfo == nil { + continue + } + + proposal, err := b.api.StateMarketStorageDealProposal(b.mctx, p.DealInfo.DealID, tok) + if err != nil { + log.Errorf("getting deal proposal for %d: %s", p.DealInfo.DealID, err) + continue + } + + if proposal.StartEpoch < deadlineEpoch { + deadlineEpoch = proposal.StartEpoch + } + } + + if deadlineEpoch <= curEpoch { + return time.Now() + } + + return time.Duration(deadlineEpoch-curEpoch) * time.Duration(build.BlockDelaySecs) * time.Second +} + func (b *CommitBatcher) processBatch(notif, after bool) (*cid.Cid, error) { b.lk.Lock() defer b.lk.Unlock() @@ -182,6 +259,7 @@ func (b *CommitBatcher) processBatch(notif, after bool) (*cid.Cid, error) { } delete(b.waiting, sn) delete(b.todo, sn) + delete(b.sectors, sn) return nil }) if err != nil { @@ -192,12 +270,14 @@ func (b *CommitBatcher) processBatch(notif, after bool) (*cid.Cid, error) { } // register commit, wait for batch message, return message CID -func (b *CommitBatcher) AddCommit(ctx context.Context, s abi.SectorNumber, in AggregateInput) (mcid cid.Cid, err error) { +func (b *CommitBatcher) AddCommit(ctx context.Context, s SectorInfo, in AggregateInput) (mcid cid.Cid, err error) { + sn := s.SectorNumber b.lk.Lock() - b.todo[s] = in + b.sectors[sn] = s + b.todo[sn] = in sent := make(chan cid.Cid, 1) - b.waiting[s] = append(b.waiting[s], sent) + b.waiting[sn] = append(b.waiting[sn], sent) select { case b.notify <- struct{}{}: diff --git a/extern/storage-sealing/sealiface/config.go b/extern/storage-sealing/sealiface/config.go index 1c0945db2..f9784b642 100644 --- a/extern/storage-sealing/sealiface/config.go +++ b/extern/storage-sealing/sealiface/config.go @@ -22,6 +22,7 @@ type Config struct { MinCommitBatch int MaxCommitBatch int CommitBatchWait time.Duration + CommitBatchSlack time.Duration TerminateBatchMax uint64 TerminateBatchMin uint64 diff --git a/extern/storage-sealing/states_sealing.go b/extern/storage-sealing/states_sealing.go index 739e8c946..6e95fbd69 100644 --- a/extern/storage-sealing/states_sealing.go +++ b/extern/storage-sealing/states_sealing.go @@ -537,7 +537,7 @@ func (m *Sealing) handleSubmitCommitAggregate(ctx statemachine.Context, sector S return ctx.Send(SectorCommitFailed{xerrors.Errorf("sector had nil commR or commD")}) } - mcid, err := m.commiter.AddCommit(ctx.Context(), sector.SectorNumber, AggregateInput{ + mcid, err := m.commiter.AddCommit(ctx.Context(), sector, AggregateInput{ info: proof.AggregateSealVerifyInfo{ Number: sector.SectorNumber, Randomness: sector.TicketValue, diff --git a/node/config/def.go b/node/config/def.go index 6419127d0..553aabb1e 100644 --- a/node/config/def.go +++ b/node/config/def.go @@ -246,9 +246,10 @@ func DefaultStorageMiner() *StorageMiner { MinCommitBatch: 1, // we must have at least one proof to aggregate MaxCommitBatch: 204, // this is the maximum aggregation per FIP13 CommitBatchWait: time.Day, // this can be up to 6 days - TerminateBatchMin: 1, // same as above - TerminateBatchMax: 204, // same as above - TerminateBatchWait: time.Day, // this can be up to 6 days + CommitBatchSlack: 8 * time.Hour, + TerminateBatchMin: 1, + TerminateBatchMax: 100, + TerminateBatchWait: 5 * time.Minute, }, Storage: sectorstorage.SealerConfig{ From 6b3e04b9b16aeb9679c21331ae6349d93f705cc8 Mon Sep 17 00:00:00 2001 From: vyzo Date: Tue, 18 May 2021 15:28:51 +0300 Subject: [PATCH 014/160] cache sector deadlines. --- extern/storage-sealing/commit_batch.go | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/extern/storage-sealing/commit_batch.go b/extern/storage-sealing/commit_batch.go index b61d5cdde..b23832fe1 100644 --- a/extern/storage-sealing/commit_batch.go +++ b/extern/storage-sealing/commit_batch.go @@ -48,9 +48,9 @@ type CommitBatcher struct { getConfig GetSealingConfigFunc verif ffiwrapper.Verifier - sectors map[abi.SectorNumber]SectorInfo - todo map[abi.SectorNumber]AggregateInput - waiting map[abi.SectorNumber][]chan cid.Cid + deadlines map[abi.SectorNumber]time.Time + todo map[abi.SectorNumber]AggregateInput + waiting map[abi.SectorNumber][]chan cid.Cid notify, stop, stopped chan struct{} force chan chan *cid.Cid @@ -67,8 +67,9 @@ func NewCommitBatcher(mctx context.Context, maddr address.Address, api CommitBat getConfig: getConfig, verif: verif, - todo: map[abi.SectorNumber]AggregateInput{}, - waiting: map[abi.SectorNumber][]chan cid.Cid{}, + deadlines: map[abi.SectorNumber]time.Time{}, + todo: map[abi.SectorNumber]AggregateInput{}, + waiting: map[abi.SectorNumber][]chan cid.Cid{}, notify: make(chan struct{}, 1), force: make(chan chan *cid.Cid), @@ -126,13 +127,13 @@ func (b *CommitBatcher) batchWait(maxWait, slack time.Duration) time.Duration { var deadline time.Time for sn := range b.todo { - sectorDeadline := b.getSectorDeadline(sn) + sectorDeadline := b.deadlines[sn] if deadline.IsZero() || (!sectorDeadline.IsZero() && sectorDeadline.Before(deadline)) { deadline = sectorDeadline } } for sn := range b.waiting { - sectorDeadline := b.getSectorDeadline(sn) + sectorDeadline := b.deadlines[sn] if deadline.IsZero() || (!sectorDeadline.IsZero() && sectorDeadline.Before(deadline)) { deadline = sectorDeadline } @@ -155,12 +156,7 @@ func (b *CommitBatcher) batchWait(maxWait, slack time.Duration) time.Duration { return wait } -func (b *CommitBatcher) getSectorDeadline(sn abi.SectorNumber) time.Time { - si, ok := b.sectors[sn] - if !ok { - return time.Time{} - } - +func (b *CommitBatcher) getSectorDeadline(si SectorInfo) time.Time { tok, curEpoch, err := b.api.ChainHead(b.mctx) if err != nil { log.Errorf("getting chain head: %s", err) @@ -259,7 +255,7 @@ func (b *CommitBatcher) processBatch(notif, after bool) (*cid.Cid, error) { } delete(b.waiting, sn) delete(b.todo, sn) - delete(b.sectors, sn) + delete(b.deadlines, sn) return nil }) if err != nil { @@ -273,7 +269,7 @@ func (b *CommitBatcher) processBatch(notif, after bool) (*cid.Cid, error) { func (b *CommitBatcher) AddCommit(ctx context.Context, s SectorInfo, in AggregateInput) (mcid cid.Cid, err error) { sn := s.SectorNumber b.lk.Lock() - b.sectors[sn] = s + b.deadlines[sn] = b.getSectorDeadline(s) b.todo[sn] = in sent := make(chan cid.Cid, 1) From 7512748b56e2e6f03f90297794fdde4e7825ea1c Mon Sep 17 00:00:00 2001 From: vyzo Date: Tue, 18 May 2021 15:31:52 +0300 Subject: [PATCH 015/160] wire in sealing config values --- node/modules/storageminer.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/node/modules/storageminer.go b/node/modules/storageminer.go index 1d89a0c4b..66052600e 100644 --- a/node/modules/storageminer.go +++ b/node/modules/storageminer.go @@ -827,6 +827,11 @@ func NewSetSealConfigFunc(r repo.LockedRepo) (dtypes.SetSealingConfigFunc, error AggregateCommits: cfg.AggregateCommits, MinCommitBatch: cfg.MinCommitBatch, MaxCommitBatch: cfg.MaxCommitBatch, + CommitBatchWait: cfg.CommitBatchWait, + CommitBatchSlack: cfg.CommitBatchSlack, + TerminateBatchMax: cfg.TerminateBatchMax, + TerminateBatchMin: cfg.TerminateBatchMin, + TerminateBatchWait: cfg.TerminateBatchWait, } }) return @@ -845,6 +850,11 @@ func NewGetSealConfigFunc(r repo.LockedRepo) (dtypes.GetSealingConfigFunc, error AggregateCommits: cfg.Sealing.AggregateCommits, MinCommitBatch: cfg.Sealing.MinCommitBatch, MaxCommitBatch: cfg.Sealing.MaxCommitBatch, + CommitBatchWait: cfg.Sealing.CommitBatchWait, + CommitBatchSlack: cfg.Sealing.CommitBatchSlack, + TerminateBatchMax: cfg.Sealing.TerminateBatchMax, + TerminateBatchMin: cfg.Sealing.TerminateBatchMin, + TerminateBatchWait: cfg.Sealing.TerminateBatchWait, } }) return From c544f4ce441f02c89b162c2352d5552ee650a390 Mon Sep 17 00:00:00 2001 From: vyzo Date: Tue, 18 May 2021 16:59:11 +0300 Subject: [PATCH 016/160] avoid extraneous rpc call for storage start epoch --- extern/storage-sealing/commit_batch.go | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/extern/storage-sealing/commit_batch.go b/extern/storage-sealing/commit_batch.go index b23832fe1..ba9d988ca 100644 --- a/extern/storage-sealing/commit_batch.go +++ b/extern/storage-sealing/commit_batch.go @@ -28,7 +28,6 @@ const arp = abi.RegisteredAggregationProof_SnarkPackV1 type CommitBatcherApi interface { SendMsg(ctx context.Context, from, to address.Address, method abi.MethodNum, value, maxFee abi.TokenAmount, params []byte) (cid.Cid, error) StateMinerInfo(context.Context, address.Address, TipSetToken) (miner.MinerInfo, error) - StateMarketStorageDeal(context.Context, abi.DealID, TipSetToken) (*api.MarketDeal, error) ChainHead(ctx context.Context) (TipSetToken, abi.ChainEpoch, error) } @@ -169,14 +168,9 @@ func (b *CommitBatcher) getSectorDeadline(si SectorInfo) time.Time { continue } - proposal, err := b.api.StateMarketStorageDealProposal(b.mctx, p.DealInfo.DealID, tok) - if err != nil { - log.Errorf("getting deal proposal for %d: %s", p.DealInfo.DealID, err) - continue - } - - if proposal.StartEpoch < deadlineEpoch { - deadlineEpoch = proposal.StartEpoch + startEpoch := p.DealInfo.DealSchedule.StartEpoch + if startEpoch < deadlineEpoch { + deadlineEpoch = startEpoch } } From c7ba083fa4159deb060125235220996a67d4387e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 18 May 2021 16:51:06 +0200 Subject: [PATCH 017/160] Import precommit batcher --- api/api_storage.go | 9 ++- api/apistruct/struct.go | 12 ++++ chain/vm/invoker.go | 2 +- extern/storage-sealing/commit_batch.go | 66 ++++++++++++---------- extern/storage-sealing/sealiface/config.go | 6 ++ extern/storage-sealing/sealing.go | 18 ++++-- extern/storage-sealing/terminate_batch.go | 8 ++- node/config/def.go | 31 ++++++++-- node/impl/storminer.go | 8 +++ node/modules/storageminer.go | 24 +++++--- storage/sealing.go | 8 +++ 11 files changed, 140 insertions(+), 52 deletions(-) diff --git a/api/api_storage.go b/api/api_storage.go index a9dec3d0e..f9a5ccdd8 100644 --- a/api/api_storage.go +++ b/api/api_storage.go @@ -80,8 +80,13 @@ type StorageMiner interface { // SectorTerminatePending returns a list of pending sector terminations to be sent in the next batch message SectorTerminatePending(ctx context.Context) ([]abi.SectorID, error) //perm:admin SectorMarkForUpgrade(ctx context.Context, id abi.SectorNumber) error //perm:admin - SectorCommitFlush(ctx context.Context) (*cid.Cid, error) //perm:admin - SectorCommitPending(ctx context.Context) ([]abi.SectorID, error) //perm:admin + // SectorPreCommitFlush immediately sends a PreCommit message with sectors batched for PreCommit. + // Returns null if message wasn't sent + SectorPreCommitFlush(ctx context.Context) (*cid.Cid, error) //perm:admin + // SectorPreCommitPending returns a list of pending PreCommit sectors to be sent in the next batch message + SectorPreCommitPending(ctx context.Context) ([]abi.SectorID, error) //perm:admin + SectorCommitFlush(ctx context.Context) (*cid.Cid, error) //perm:admin + SectorCommitPending(ctx context.Context) ([]abi.SectorID, error) //perm:admin // WorkerConnect tells the node to connect to workers RPC WorkerConnect(context.Context, string) error //perm:admin retry:true diff --git a/api/apistruct/struct.go b/api/apistruct/struct.go index 6917a2967..13375cf72 100644 --- a/api/apistruct/struct.go +++ b/api/apistruct/struct.go @@ -649,6 +649,10 @@ type StorageMinerStruct struct { SectorMarkForUpgrade func(p0 context.Context, p1 abi.SectorNumber) error `perm:"admin"` + SectorPreCommitFlush func(p0 context.Context) (*cid.Cid, error) `perm:"admin"` + + SectorPreCommitPending func(p0 context.Context) ([]abi.SectorID, error) `perm:"admin"` + SectorRemove func(p0 context.Context, p1 abi.SectorNumber) error `perm:"admin"` SectorSetExpectedSealDuration func(p0 context.Context, p1 time.Duration) error `perm:"write"` @@ -1947,6 +1951,14 @@ func (s *StorageMinerStruct) SectorMarkForUpgrade(p0 context.Context, p1 abi.Sec return s.Internal.SectorMarkForUpgrade(p0, p1) } +func (s *StorageMinerStruct) SectorPreCommitFlush(p0 context.Context) (*cid.Cid, error) { + return s.Internal.SectorPreCommitFlush(p0) +} + +func (s *StorageMinerStruct) SectorPreCommitPending(p0 context.Context) ([]abi.SectorID, error) { + return s.Internal.SectorPreCommitPending(p0) +} + func (s *StorageMinerStruct) SectorRemove(p0 context.Context, p1 abi.SectorNumber) error { return s.Internal.SectorRemove(p0, p1) } diff --git a/chain/vm/invoker.go b/chain/vm/invoker.go index 4a8032770..e4b154031 100644 --- a/chain/vm/invoker.go +++ b/chain/vm/invoker.go @@ -155,7 +155,7 @@ func (*ActorRegistry) transform(instance invokee) (nativeCode, error) { "vmr.Runtime, ") } if !runtimeType.Implements(t.In(0)) { - return nil, newErr("first arguemnt should be vmr.Runtime") + return nil, newErr("first argument should be vmr.Runtime") } if t.In(1).Kind() != reflect.Ptr { return nil, newErr("second argument should be of kind reflect.Ptr") diff --git a/extern/storage-sealing/commit_batch.go b/extern/storage-sealing/commit_batch.go index ba9d988ca..5628acdc0 100644 --- a/extern/storage-sealing/commit_batch.go +++ b/extern/storage-sealing/commit_batch.go @@ -155,32 +155,6 @@ func (b *CommitBatcher) batchWait(maxWait, slack time.Duration) time.Duration { return wait } -func (b *CommitBatcher) getSectorDeadline(si SectorInfo) time.Time { - tok, curEpoch, err := b.api.ChainHead(b.mctx) - if err != nil { - log.Errorf("getting chain head: %s", err) - return time.Time{} - } - - deadlineEpoch := si.TicketEpoch - for _, p := range si.Pieces { - if p.DealInfo == nil { - continue - } - - startEpoch := p.DealInfo.DealSchedule.StartEpoch - if startEpoch < deadlineEpoch { - deadlineEpoch = startEpoch - } - } - - if deadlineEpoch <= curEpoch { - return time.Now() - } - - return time.Duration(deadlineEpoch-curEpoch) * time.Duration(build.BlockDelaySecs) * time.Second -} - func (b *CommitBatcher) processBatch(notif, after bool) (*cid.Cid, error) { b.lk.Lock() defer b.lk.Unlock() @@ -208,20 +182,27 @@ func (b *CommitBatcher) processBatch(notif, after bool) (*cid.Cid, error) { spt := b.todo[0].spt proofs := make([][]byte, 0, total) + infos := make([]proof5.AggregateSealVerifyInfo, 0, total) for id, p := range b.todo { params.SectorNumbers.Set(uint64(id)) proofs = append(proofs, p.proof) + infos = append(infos, p.info) } - params.AggregateProof, err = b.verif.AggregateSealProofs(spt, arp, proofs) + params.AggregateProof, err = b.verif.AggregateSealProofs(proof5.AggregateSealVerifyProofAndInfos{ + Miner: 0, + SealProof: spt, + AggregateProof: arp, + Infos: infos, + }, proofs) if err != nil { return nil, xerrors.Errorf("aggregating proofs: %w", err) } enc := new(bytes.Buffer) if err := params.MarshalCBOR(enc); err != nil { - return nil, xerrors.Errorf("couldn't serialize TerminateSectors params: %w", err) + return nil, xerrors.Errorf("couldn't serialize ProveCommitAggregateParams: %w", err) } mi, err := b.api.StateMinerInfo(b.mctx, b.maddr, nil) @@ -261,9 +242,16 @@ func (b *CommitBatcher) processBatch(notif, after bool) (*cid.Cid, error) { // register commit, wait for batch message, return message CID func (b *CommitBatcher) AddCommit(ctx context.Context, s SectorInfo, in AggregateInput) (mcid cid.Cid, err error) { + _, curEpoch, err := b.api.ChainHead(b.mctx) + if err != nil { + log.Errorf("getting chain head: %s", err) + return cid.Undef, nil + } + sn := s.SectorNumber + b.lk.Lock() - b.deadlines[sn] = b.getSectorDeadline(s) + b.deadlines[sn] = getSectorDeadline(curEpoch, s) b.todo[sn] = in sent := make(chan cid.Cid, 1) @@ -336,3 +324,23 @@ func (b *CommitBatcher) Stop(ctx context.Context) error { return ctx.Err() } } + +func getSectorDeadline(curEpoch abi.ChainEpoch, si SectorInfo) time.Time { + deadlineEpoch := si.TicketEpoch + for _, p := range si.Pieces { + if p.DealInfo == nil { + continue + } + + startEpoch := p.DealInfo.DealSchedule.StartEpoch + if startEpoch < deadlineEpoch { + deadlineEpoch = startEpoch + } + } + + if deadlineEpoch <= curEpoch { + return time.Now() + } + + return time.Now().Add(time.Duration(deadlineEpoch-curEpoch) * time.Duration(build.BlockDelaySecs) * time.Second) +} diff --git a/extern/storage-sealing/sealiface/config.go b/extern/storage-sealing/sealiface/config.go index f9784b642..54ba2ef58 100644 --- a/extern/storage-sealing/sealiface/config.go +++ b/extern/storage-sealing/sealiface/config.go @@ -18,6 +18,12 @@ type Config struct { AlwaysKeepUnsealedCopy bool + BatchPreCommits bool + MaxPreCommitBatch int + MinPreCommitBatch int + PreCommitBatchWait time.Duration + PreCommitBatchSlack time.Duration + AggregateCommits bool MinCommitBatch int MaxCommitBatch int diff --git a/extern/storage-sealing/sealing.go b/extern/storage-sealing/sealing.go index 35ff15f51..ede281e39 100644 --- a/extern/storage-sealing/sealing.go +++ b/extern/storage-sealing/sealing.go @@ -102,8 +102,9 @@ type Sealing struct { stats SectorStats - terminator *TerminateBatcher - commiter *CommitBatcher + terminator *TerminateBatcher + precommiter *PreCommitBatcher + commiter *CommitBatcher getConfig GetSealingConfigFunc dealInfo *CurrentDealInfoManager @@ -152,8 +153,9 @@ func New(api SealingAPI, fc FeeConfig, events Events, maddr address.Address, ds notifee: notifee, addrSel: as, - terminator: NewTerminationBatcher(context.TODO(), maddr, api, as, fc, gc), - commiter: NewCommitBatcher(context.TODO(), maddr, api, as, fc, gc, verif), + terminator: NewTerminationBatcher(context.TODO(), maddr, api, as, fc, gc), + precommiter: NewPreCommitBatcher(context.TODO(), maddr, api, as, fc, gc), + commiter: NewCommitBatcher(context.TODO(), maddr, api, as, fc, gc, verif), getConfig: gc, dealInfo: &CurrentDealInfoManager{api}, @@ -204,6 +206,14 @@ func (m *Sealing) TerminatePending(ctx context.Context) ([]abi.SectorID, error) return m.terminator.Pending(ctx) } +func (m *Sealing) SectorPreCommitFlush(ctx context.Context) (*cid.Cid, error) { + return m.precommiter.Flush(ctx) +} + +func (m *Sealing) SectorPreCommitPending(ctx context.Context) ([]abi.SectorID, error) { + return m.precommiter.Pending(ctx) +} + func (m *Sealing) CommitFlush(ctx context.Context) (*cid.Cid, error) { return m.commiter.Flush(ctx) } diff --git a/extern/storage-sealing/terminate_batch.go b/extern/storage-sealing/terminate_batch.go index 3833109a1..2bb2dc76a 100644 --- a/extern/storage-sealing/terminate_batch.go +++ b/extern/storage-sealing/terminate_batch.go @@ -80,6 +80,11 @@ func (b *TerminateBatcher) run() { } lastMsg = nil + cfg, err := b.getConfig() + if err != nil { + log.Warnw("TerminateBatcher getconfig error", "error", err) + } + var sendAboveMax, sendAboveMin bool select { case <-b.stop: @@ -87,13 +92,12 @@ func (b *TerminateBatcher) run() { return case <-b.notify: sendAboveMax = true - case <-time.After(TerminateBatchWait): + case <-time.After(cfg.TerminateBatchWait): sendAboveMin = true case fr := <-b.force: // user triggered forceRes = fr } - var err error lastMsg, err = b.processBatch(sendAboveMax, sendAboveMin) if err != nil { log.Warnw("TerminateBatcher processBatch error", "error", err) diff --git a/node/config/def.go b/node/config/def.go index 553aabb1e..207419c6c 100644 --- a/node/config/def.go +++ b/node/config/def.go @@ -82,9 +82,21 @@ type SealingConfig struct { AlwaysKeepUnsealedCopy bool + BatchPreCommits bool + MaxPreCommitBatch int + MinPreCommitBatch int + PreCommitBatchWait Duration + PreCommitBatchSlack Duration + AggregateCommits bool MinCommitBatch int MaxCommitBatch int + CommitBatchWait Duration + CommitBatchSlack Duration + + TerminateBatchMax uint64 + TerminateBatchMin uint64 + TerminateBatchWait Duration // Keep this many sectors in sealing pipeline, start CC if needed // todo TargetSealingSectors uint64 @@ -242,14 +254,21 @@ func DefaultStorageMiner() *StorageMiner { WaitDealsDelay: Duration(time.Hour * 6), AlwaysKeepUnsealedCopy: true, - AggregateCommits: true, - MinCommitBatch: 1, // we must have at least one proof to aggregate - MaxCommitBatch: 204, // this is the maximum aggregation per FIP13 - CommitBatchWait: time.Day, // this can be up to 6 days - CommitBatchSlack: 8 * time.Hour, + BatchPreCommits: true, + MinPreCommitBatch: 1, // we must have at least one proof to aggregate + MaxPreCommitBatch: 204, // todo max? + PreCommitBatchWait: Duration(24 * time.Hour), // this can be up to 6 days + PreCommitBatchSlack: Duration(8 * time.Hour), + + AggregateCommits: true, + MinCommitBatch: 1, // we must have at least one proof to aggregate + MaxCommitBatch: 204, // this is the maximum aggregation per FIP13 + CommitBatchWait: Duration(24 * time.Hour), // this can be up to 6 days + CommitBatchSlack: Duration(8 * time.Hour), + TerminateBatchMin: 1, TerminateBatchMax: 100, - TerminateBatchWait: 5 * time.Minute, + TerminateBatchWait: Duration(5 * time.Minute), }, Storage: sectorstorage.SealerConfig{ diff --git a/node/impl/storminer.go b/node/impl/storminer.go index 8766ba154..8660f1efb 100644 --- a/node/impl/storminer.go +++ b/node/impl/storminer.go @@ -374,6 +374,14 @@ func (sm *StorageMinerAPI) SectorTerminatePending(ctx context.Context) ([]abi.Se return sm.Miner.TerminatePending(ctx) } +func (sm *StorageMinerAPI) SectorPreCommitFlush(ctx context.Context) (*cid.Cid, error) { + return sm.Miner.SectorPreCommitFlush(ctx) +} + +func (sm *StorageMinerAPI) SectorPreCommitPending(ctx context.Context) ([]abi.SectorID, error) { + return sm.Miner.SectorPreCommitPending(ctx) +} + func (sm *StorageMinerAPI) SectorMarkForUpgrade(ctx context.Context, id abi.SectorNumber) error { return sm.Miner.MarkForUpgrade(id) } diff --git a/node/modules/storageminer.go b/node/modules/storageminer.go index 66052600e..6458bfe69 100644 --- a/node/modules/storageminer.go +++ b/node/modules/storageminer.go @@ -847,14 +847,22 @@ func NewGetSealConfigFunc(r repo.LockedRepo) (dtypes.GetSealingConfigFunc, error MaxSealingSectorsForDeals: cfg.Sealing.MaxSealingSectorsForDeals, WaitDealsDelay: time.Duration(cfg.Sealing.WaitDealsDelay), AlwaysKeepUnsealedCopy: cfg.Sealing.AlwaysKeepUnsealedCopy, - AggregateCommits: cfg.Sealing.AggregateCommits, - MinCommitBatch: cfg.Sealing.MinCommitBatch, - MaxCommitBatch: cfg.Sealing.MaxCommitBatch, - CommitBatchWait: cfg.Sealing.CommitBatchWait, - CommitBatchSlack: cfg.Sealing.CommitBatchSlack, - TerminateBatchMax: cfg.Sealing.TerminateBatchMax, - TerminateBatchMin: cfg.Sealing.TerminateBatchMin, - TerminateBatchWait: cfg.Sealing.TerminateBatchWait, + + BatchPreCommits: cfg.Sealing.BatchPreCommits, + MinPreCommitBatch: cfg.Sealing.MinPreCommitBatch, + MaxPreCommitBatch: cfg.Sealing.MaxPreCommitBatch, + PreCommitBatchWait: time.Duration(cfg.Sealing.PreCommitBatchWait), + PreCommitBatchSlack: time.Duration(cfg.Sealing.PreCommitBatchSlack), + + AggregateCommits: cfg.Sealing.AggregateCommits, + MinCommitBatch: cfg.Sealing.MinCommitBatch, + MaxCommitBatch: cfg.Sealing.MaxCommitBatch, + CommitBatchWait: time.Duration(cfg.Sealing.CommitBatchWait), + CommitBatchSlack: time.Duration(cfg.Sealing.CommitBatchSlack), + + TerminateBatchMax: cfg.Sealing.TerminateBatchMax, + TerminateBatchMin: cfg.Sealing.TerminateBatchMin, + TerminateBatchWait: time.Duration(cfg.Sealing.TerminateBatchWait), } }) return diff --git a/storage/sealing.go b/storage/sealing.go index b3d38909b..cd215f238 100644 --- a/storage/sealing.go +++ b/storage/sealing.go @@ -59,6 +59,14 @@ func (m *Miner) TerminatePending(ctx context.Context) ([]abi.SectorID, error) { return m.sealing.TerminatePending(ctx) } +func (m *Miner) SectorPreCommitFlush(ctx context.Context) (*cid.Cid, error) { + return m.sealing.SectorPreCommitFlush(ctx) +} + +func (m *Miner) SectorPreCommitPending(ctx context.Context) ([]abi.SectorID, error) { + return m.sealing.SectorPreCommitPending(ctx) +} + func (m *Miner) CommitFlush(ctx context.Context) (*cid.Cid, error) { return m.sealing.CommitFlush(ctx) } From 81b5d8c671d48ce7babe4f6982d684211bab6e66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 18 May 2021 16:53:49 +0200 Subject: [PATCH 018/160] Make things build with both batchers --- chain/gen/gen.go | 2 +- node/modules/storageminer.go | 24 ++++++++++++++++-------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/chain/gen/gen.go b/chain/gen/gen.go index 32b238433..b9173d781 100644 --- a/chain/gen/gen.go +++ b/chain/gen/gen.go @@ -693,7 +693,7 @@ func (m genFakeVerifier) VerifyAggregateSeals(aggregate proof5.AggregateSealVeri panic("not supported") } -func (m genFakeVerifier) AggregateSealProofs(proofType abi.RegisteredSealProof, rap abi.RegisteredAggregationProof, proofs [][]byte) ([]byte, error) { +func (m genFakeVerifier) AggregateSealProofs(ai proof5.AggregateSealVerifyProofAndInfos, proofs [][]byte) ([]byte, error) { panic("not supported") } diff --git a/node/modules/storageminer.go b/node/modules/storageminer.go index 6458bfe69..8a9a99175 100644 --- a/node/modules/storageminer.go +++ b/node/modules/storageminer.go @@ -824,14 +824,22 @@ func NewSetSealConfigFunc(r repo.LockedRepo) (dtypes.SetSealingConfigFunc, error MaxSealingSectorsForDeals: cfg.MaxSealingSectorsForDeals, WaitDealsDelay: config.Duration(cfg.WaitDealsDelay), AlwaysKeepUnsealedCopy: cfg.AlwaysKeepUnsealedCopy, - AggregateCommits: cfg.AggregateCommits, - MinCommitBatch: cfg.MinCommitBatch, - MaxCommitBatch: cfg.MaxCommitBatch, - CommitBatchWait: cfg.CommitBatchWait, - CommitBatchSlack: cfg.CommitBatchSlack, - TerminateBatchMax: cfg.TerminateBatchMax, - TerminateBatchMin: cfg.TerminateBatchMin, - TerminateBatchWait: cfg.TerminateBatchWait, + + BatchPreCommits: cfg.BatchPreCommits, + MinPreCommitBatch: cfg.MinPreCommitBatch, + MaxPreCommitBatch: cfg.MaxPreCommitBatch, + PreCommitBatchWait: config.Duration(cfg.PreCommitBatchWait), + PreCommitBatchSlack: config.Duration(cfg.PreCommitBatchSlack), + + AggregateCommits: cfg.AggregateCommits, + MinCommitBatch: cfg.MinCommitBatch, + MaxCommitBatch: cfg.MaxCommitBatch, + CommitBatchWait: config.Duration(cfg.CommitBatchWait), + CommitBatchSlack: config.Duration(cfg.CommitBatchSlack), + + TerminateBatchMax: cfg.TerminateBatchMax, + TerminateBatchMin: cfg.TerminateBatchMin, + TerminateBatchWait: config.Duration(cfg.TerminateBatchWait), } }) return From d92c5e100173a1aa74485357aea892b5dfbe6058 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 18 May 2021 16:54:55 +0200 Subject: [PATCH 019/160] Missing precommit batcher --- extern/storage-sealing/precommit_batch.go | 298 ++++++++++++++++++++++ 1 file changed, 298 insertions(+) create mode 100644 extern/storage-sealing/precommit_batch.go diff --git a/extern/storage-sealing/precommit_batch.go b/extern/storage-sealing/precommit_batch.go new file mode 100644 index 000000000..93846fbef --- /dev/null +++ b/extern/storage-sealing/precommit_batch.go @@ -0,0 +1,298 @@ +package sealing + +import ( + "bytes" + "context" + "sort" + "sync" + "time" + + "github.com/ipfs/go-cid" + "golang.org/x/xerrors" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/big" + miner0 "github.com/filecoin-project/specs-actors/actors/builtin/miner" + miner5 "github.com/filecoin-project/specs-actors/v5/actors/builtin/miner" + + "github.com/filecoin-project/lotus/api" + "github.com/filecoin-project/lotus/chain/actors/builtin/miner" +) + +var ( + // TODO: config + + PreCommitBatchMax uint64 = 100 // adjust based on real-world gas numbers, actors limit at 10k + PreCommitBatchMin uint64 = 1 + PreCommitBatchWait = 5 * time.Minute +) + +type PreCommitBatcherApi interface { + SendMsg(ctx context.Context, from, to address.Address, method abi.MethodNum, value, maxFee abi.TokenAmount, params []byte) (cid.Cid, error) + StateMinerInfo(context.Context, address.Address, TipSetToken) (miner.MinerInfo, error) + ChainHead(ctx context.Context) (TipSetToken, abi.ChainEpoch, error) +} + +type PreCommitBatcher struct { + api PreCommitBatcherApi + maddr address.Address + mctx context.Context + addrSel AddrSel + feeCfg FeeConfig + getConfig GetSealingConfigFunc + + deadlines map[abi.SectorNumber]time.Time + todo map[abi.SectorNumber]*miner0.SectorPreCommitInfo + waiting map[abi.SectorNumber][]chan cid.Cid + + notify, stop, stopped chan struct{} + force chan chan *cid.Cid + lk sync.Mutex +} + +func NewPreCommitBatcher(mctx context.Context, maddr address.Address, api PreCommitBatcherApi, addrSel AddrSel, feeCfg FeeConfig, getConfig GetSealingConfigFunc) *PreCommitBatcher { + b := &PreCommitBatcher{ + api: api, + maddr: maddr, + mctx: mctx, + addrSel: addrSel, + feeCfg: feeCfg, + getConfig: getConfig, + + deadlines: map[abi.SectorNumber]time.Time{}, + todo: map[abi.SectorNumber]*miner0.SectorPreCommitInfo{}, + waiting: map[abi.SectorNumber][]chan cid.Cid{}, + + notify: make(chan struct{}, 1), + force: make(chan chan *cid.Cid), + stop: make(chan struct{}), + stopped: make(chan struct{}), + } + + go b.run() + + return b +} + +func (b *PreCommitBatcher) run() { + var forceRes chan *cid.Cid + var lastMsg *cid.Cid + + cfg, err := b.getConfig() + if err != nil { + panic(err) + } + + for { + if forceRes != nil { + forceRes <- lastMsg + forceRes = nil + } + lastMsg = nil + + var sendAboveMax, sendAboveMin bool + select { + case <-b.stop: + close(b.stopped) + return + case <-b.notify: + sendAboveMax = true + case <-time.After(b.batchWait(cfg.PreCommitBatchWait, cfg.PreCommitBatchSlack)): + sendAboveMin = true + case fr := <-b.force: // user triggered + forceRes = fr + } + + var err error + lastMsg, err = b.processBatch(sendAboveMax, sendAboveMin) + if err != nil { + log.Warnw("TerminateBatcher processBatch error", "error", err) + } + } +} + +func (b *PreCommitBatcher) batchWait(maxWait, slack time.Duration) time.Duration { + now := time.Now() + + b.lk.Lock() + defer b.lk.Unlock() + + var deadline time.Time + for sn := range b.todo { + sectorDeadline := b.deadlines[sn] + if deadline.IsZero() || (!sectorDeadline.IsZero() && sectorDeadline.Before(deadline)) { + deadline = sectorDeadline + } + } + for sn := range b.waiting { + sectorDeadline := b.deadlines[sn] + if deadline.IsZero() || (!sectorDeadline.IsZero() && sectorDeadline.Before(deadline)) { + deadline = sectorDeadline + } + } + + if deadline.IsZero() { + return maxWait + } + + deadline = deadline.Add(-slack) + if deadline.Before(now) { + return time.Nanosecond // can't return 0 + } + + wait := deadline.Sub(now) + if wait > maxWait { + wait = maxWait + } + + return wait +} + +func (b *PreCommitBatcher) processBatch(notif, after bool) (*cid.Cid, error) { + b.lk.Lock() + defer b.lk.Unlock() + params := miner5.PreCommitSectorBatchParams{} + + total := len(b.todo) + if total == 0 { + return nil, nil // nothing to do + } + + cfg, err := b.getConfig() + if err != nil { + return nil, xerrors.Errorf("getting config: %w", err) + } + + if notif && total < cfg.MaxPreCommitBatch { + return nil, nil + } + + if after && total < cfg.MinPreCommitBatch { + return nil, nil + } + + for _, p := range b.todo { + params.Sectors = append(params.Sectors, p) + } + + enc := new(bytes.Buffer) + if err := params.MarshalCBOR(enc); err != nil { + return nil, xerrors.Errorf("couldn't serialize PreCommitSectorBatchParams: %w", err) + } + + mi, err := b.api.StateMinerInfo(b.mctx, b.maddr, nil) + if err != nil { + return nil, xerrors.Errorf("couldn't get miner info: %w", err) + } + + from, _, err := b.addrSel(b.mctx, mi, api.PreCommitAddr, b.feeCfg.MaxPreCommitGasFee, b.feeCfg.MaxPreCommitGasFee) + if err != nil { + return nil, xerrors.Errorf("no good address found: %w", err) + } + + mcid, err := b.api.SendMsg(b.mctx, from, b.maddr, miner.Methods.PreCommitSectorBatch, big.Zero(), b.feeCfg.MaxPreCommitGasFee, enc.Bytes()) + if err != nil { + return nil, xerrors.Errorf("sending message failed: %w", err) + } + + log.Infow("Sent ProveCommitAggregate message", "cid", mcid, "from", from, "sectors", total) + + for _, sector := range params.Sectors { + sn := sector.SectorNumber + + for _, ch := range b.waiting[sn] { + ch <- mcid // buffered + } + delete(b.waiting, sn) + delete(b.todo, sn) + delete(b.deadlines, sn) + } + + return &mcid, nil +} + +// register PreCommit, wait for batch message, return message CID +func (b *PreCommitBatcher) AddPreCommit(ctx context.Context, s SectorInfo, in *miner0.SectorPreCommitInfo) (mcid cid.Cid, err error) { + _, curEpoch, err := b.api.ChainHead(b.mctx) + if err != nil { + log.Errorf("getting chain head: %s", err) + return cid.Undef, nil + } + + sn := s.SectorNumber + + b.lk.Lock() + b.deadlines[sn] = getSectorDeadline(curEpoch, s) + b.todo[sn] = in + + sent := make(chan cid.Cid, 1) + b.waiting[sn] = append(b.waiting[sn], sent) + + select { + case b.notify <- struct{}{}: + default: // already have a pending notification, don't need more + } + b.lk.Unlock() + + select { + case c := <-sent: + return c, nil + case <-ctx.Done(): + return cid.Undef, ctx.Err() + } +} + +func (b *PreCommitBatcher) Flush(ctx context.Context) (*cid.Cid, error) { + resCh := make(chan *cid.Cid, 1) + select { + case b.force <- resCh: + select { + case res := <-resCh: + return res, nil + case <-ctx.Done(): + return nil, ctx.Err() + } + case <-ctx.Done(): + return nil, ctx.Err() + } +} + +func (b *PreCommitBatcher) Pending(ctx context.Context) ([]abi.SectorID, error) { + b.lk.Lock() + defer b.lk.Unlock() + + mid, err := address.IDFromAddress(b.maddr) + if err != nil { + return nil, err + } + + res := make([]abi.SectorID, 0) + for _, s := range b.todo { + res = append(res, abi.SectorID{ + Miner: abi.ActorID(mid), + Number: s.SectorNumber, + }) + } + + sort.Slice(res, func(i, j int) bool { + if res[i].Miner != res[j].Miner { + return res[i].Miner < res[j].Miner + } + + return res[i].Number < res[j].Number + }) + + return res, nil +} + +func (b *PreCommitBatcher) Stop(ctx context.Context) error { + close(b.stop) + + select { + case <-b.stopped: + return nil + case <-ctx.Done(): + return ctx.Err() + } +} From f66b9c56634930c2e4018dd1c3ebf040403f4b26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 18 May 2021 17:21:10 +0200 Subject: [PATCH 020/160] Maybe working precommit batching --- cmd/lotus-storage-miner/info.go | 2 + extern/storage-sealing/fsm.go | 7 ++- extern/storage-sealing/fsm_events.go | 4 ++ extern/storage-sealing/precommit_batch.go | 37 +++++++----- extern/storage-sealing/sector_state.go | 27 +++++---- extern/storage-sealing/states_sealing.go | 72 ++++++++++++++++------- 6 files changed, 99 insertions(+), 50 deletions(-) diff --git a/cmd/lotus-storage-miner/info.go b/cmd/lotus-storage-miner/info.go index 7a8835fee..0fe14f1ff 100644 --- a/cmd/lotus-storage-miner/info.go +++ b/cmd/lotus-storage-miner/info.go @@ -291,6 +291,8 @@ var stateList = []stateMeta{ {col: color.FgYellow, state: sealing.PreCommit2}, {col: color.FgYellow, state: sealing.PreCommitting}, {col: color.FgYellow, state: sealing.PreCommitWait}, + {col: color.FgYellow, state: sealing.SubmitPreCommitBatch}, + {col: color.FgYellow, state: sealing.PreCommitBatchWait}, {col: color.FgYellow, state: sealing.WaitSeed}, {col: color.FgYellow, state: sealing.Committing}, {col: color.FgYellow, state: sealing.SubmitCommit}, diff --git a/extern/storage-sealing/fsm.go b/extern/storage-sealing/fsm.go index 367938099..ab1cefc1e 100644 --- a/extern/storage-sealing/fsm.go +++ b/extern/storage-sealing/fsm.go @@ -71,6 +71,7 @@ var fsmPlanners = map[SectorState]func(events []statemachine.Event, state *Secto on(SectorSealPreCommit1Failed{}, SealPreCommit1Failed), ), PreCommitting: planOne( + on(SectorPreCommitBatch{}, SubmitPreCommitBatch), on(SectorSealPreCommit1Failed{}, SealPreCommit1Failed), on(SectorPreCommitted{}, PreCommitWait), on(SectorChainPreCommitFailed{}, PreCommitFailed), @@ -340,6 +341,8 @@ func (m *Sealing) plan(events []statemachine.Event, state *SectorInfo) (func(sta return m.handlePreCommit2, processed, nil case PreCommitting: return m.handlePreCommitting, processed, nil + case SubmitPreCommitBatch: + return m.handleSubmitPreCommitBatch, processed, nil case PreCommitWait: return m.handlePreCommitWait, processed, nil case WaitSeed: @@ -348,12 +351,12 @@ func (m *Sealing) plan(events []statemachine.Event, state *SectorInfo) (func(sta return m.handleCommitting, processed, nil case SubmitCommit: return m.handleSubmitCommit, processed, nil + case SubmitCommitAggregate: + return m.handleSubmitCommitAggregate, processed, nil case CommitAggregateWait: fallthrough case CommitWait: return m.handleCommitWait, processed, nil - case SubmitCommitAggregate: - return m.handleSubmitCommitAggregate, processed, nil case FinalizeSector: return m.handleFinalizeSector, processed, nil diff --git a/extern/storage-sealing/fsm_events.go b/extern/storage-sealing/fsm_events.go index bced1921f..2ae08130d 100644 --- a/extern/storage-sealing/fsm_events.go +++ b/extern/storage-sealing/fsm_events.go @@ -150,6 +150,10 @@ func (evt SectorPreCommit2) apply(state *SectorInfo) { state.CommR = &commr } +type SectorPreCommitBatch struct{} + +func (evt SectorPreCommitBatch) apply(*SectorInfo) {} + type SectorPreCommitLanded struct { TipSet TipSetToken } diff --git a/extern/storage-sealing/precommit_batch.go b/extern/storage-sealing/precommit_batch.go index 93846fbef..2016c6d8f 100644 --- a/extern/storage-sealing/precommit_batch.go +++ b/extern/storage-sealing/precommit_batch.go @@ -20,20 +20,17 @@ import ( "github.com/filecoin-project/lotus/chain/actors/builtin/miner" ) -var ( - // TODO: config - - PreCommitBatchMax uint64 = 100 // adjust based on real-world gas numbers, actors limit at 10k - PreCommitBatchMin uint64 = 1 - PreCommitBatchWait = 5 * time.Minute -) - type PreCommitBatcherApi interface { SendMsg(ctx context.Context, from, to address.Address, method abi.MethodNum, value, maxFee abi.TokenAmount, params []byte) (cid.Cid, error) StateMinerInfo(context.Context, address.Address, TipSetToken) (miner.MinerInfo, error) ChainHead(ctx context.Context) (TipSetToken, abi.ChainEpoch, error) } +type preCommitEntry struct { + deposit abi.TokenAmount + pci *miner0.SectorPreCommitInfo +} + type PreCommitBatcher struct { api PreCommitBatcherApi maddr address.Address @@ -43,7 +40,7 @@ type PreCommitBatcher struct { getConfig GetSealingConfigFunc deadlines map[abi.SectorNumber]time.Time - todo map[abi.SectorNumber]*miner0.SectorPreCommitInfo + todo map[abi.SectorNumber]*preCommitEntry waiting map[abi.SectorNumber][]chan cid.Cid notify, stop, stopped chan struct{} @@ -61,7 +58,7 @@ func NewPreCommitBatcher(mctx context.Context, maddr address.Address, api PreCom getConfig: getConfig, deadlines: map[abi.SectorNumber]time.Time{}, - todo: map[abi.SectorNumber]*miner0.SectorPreCommitInfo{}, + todo: map[abi.SectorNumber]*preCommitEntry{}, waiting: map[abi.SectorNumber][]chan cid.Cid{}, notify: make(chan struct{}, 1), @@ -172,8 +169,11 @@ func (b *PreCommitBatcher) processBatch(notif, after bool) (*cid.Cid, error) { return nil, nil } + deposit := big.Zero() + for _, p := range b.todo { - params.Sectors = append(params.Sectors, p) + params.Sectors = append(params.Sectors, p.pci) + deposit = big.Add(deposit, p.deposit) } enc := new(bytes.Buffer) @@ -186,12 +186,14 @@ func (b *PreCommitBatcher) processBatch(notif, after bool) (*cid.Cid, error) { return nil, xerrors.Errorf("couldn't get miner info: %w", err) } - from, _, err := b.addrSel(b.mctx, mi, api.PreCommitAddr, b.feeCfg.MaxPreCommitGasFee, b.feeCfg.MaxPreCommitGasFee) + goodFunds := big.Add(deposit, b.feeCfg.MaxPreCommitGasFee) + + from, _, err := b.addrSel(b.mctx, mi, api.PreCommitAddr, goodFunds, deposit) if err != nil { return nil, xerrors.Errorf("no good address found: %w", err) } - mcid, err := b.api.SendMsg(b.mctx, from, b.maddr, miner.Methods.PreCommitSectorBatch, big.Zero(), b.feeCfg.MaxPreCommitGasFee, enc.Bytes()) + mcid, err := b.api.SendMsg(b.mctx, from, b.maddr, miner.Methods.PreCommitSectorBatch, deposit, b.feeCfg.MaxPreCommitGasFee, enc.Bytes()) if err != nil { return nil, xerrors.Errorf("sending message failed: %w", err) } @@ -213,7 +215,7 @@ func (b *PreCommitBatcher) processBatch(notif, after bool) (*cid.Cid, error) { } // register PreCommit, wait for batch message, return message CID -func (b *PreCommitBatcher) AddPreCommit(ctx context.Context, s SectorInfo, in *miner0.SectorPreCommitInfo) (mcid cid.Cid, err error) { +func (b *PreCommitBatcher) AddPreCommit(ctx context.Context, s SectorInfo, deposit abi.TokenAmount, in *miner0.SectorPreCommitInfo) (mcid cid.Cid, err error) { _, curEpoch, err := b.api.ChainHead(b.mctx) if err != nil { log.Errorf("getting chain head: %s", err) @@ -224,7 +226,10 @@ func (b *PreCommitBatcher) AddPreCommit(ctx context.Context, s SectorInfo, in *m b.lk.Lock() b.deadlines[sn] = getSectorDeadline(curEpoch, s) - b.todo[sn] = in + b.todo[sn] = &preCommitEntry{ + deposit: deposit, + pci: in, + } sent := make(chan cid.Cid, 1) b.waiting[sn] = append(b.waiting[sn], sent) @@ -271,7 +276,7 @@ func (b *PreCommitBatcher) Pending(ctx context.Context) ([]abi.SectorID, error) for _, s := range b.todo { res = append(res, abi.SectorID{ Miner: abi.ActorID(mid), - Number: s.SectorNumber, + Number: s.pci.SectorNumber, }) } diff --git a/extern/storage-sealing/sector_state.go b/extern/storage-sealing/sector_state.go index b6b7cbf7b..23c7695e7 100644 --- a/extern/storage-sealing/sector_state.go +++ b/extern/storage-sealing/sector_state.go @@ -13,6 +13,8 @@ var ExistSectorStateList = map[SectorState]struct{}{ PreCommit2: {}, PreCommitting: {}, PreCommitWait: {}, + SubmitPreCommitBatch: {}, + PreCommitBatchWait: {}, WaitSeed: {}, Committing: {}, SubmitCommit: {}, @@ -47,17 +49,22 @@ const ( UndefinedSectorState SectorState = "" // happy path - Empty SectorState = "Empty" // deprecated - WaitDeals SectorState = "WaitDeals" // waiting for more pieces (deals) to be added to the sector - AddPiece SectorState = "AddPiece" // put deal data (and padding if required) into the sector - Packing SectorState = "Packing" // sector not in sealStore, and not on chain - GetTicket SectorState = "GetTicket" // generate ticket - PreCommit1 SectorState = "PreCommit1" // do PreCommit1 - PreCommit2 SectorState = "PreCommit2" // do PreCommit2 + Empty SectorState = "Empty" // deprecated + WaitDeals SectorState = "WaitDeals" // waiting for more pieces (deals) to be added to the sector + AddPiece SectorState = "AddPiece" // put deal data (and padding if required) into the sector + Packing SectorState = "Packing" // sector not in sealStore, and not on chain + GetTicket SectorState = "GetTicket" // generate ticket + PreCommit1 SectorState = "PreCommit1" // do PreCommit1 + PreCommit2 SectorState = "PreCommit2" // do PreCommit2 + PreCommitting SectorState = "PreCommitting" // on chain pre-commit PreCommitWait SectorState = "PreCommitWait" // waiting for precommit to land on chain - WaitSeed SectorState = "WaitSeed" // waiting for seed - Committing SectorState = "Committing" // compute PoRep + + SubmitPreCommitBatch SectorState = "SubmitPreCommitBatch" + PreCommitBatchWait SectorState = "PreCommitBatchWait" + + WaitSeed SectorState = "WaitSeed" // waiting for seed + Committing SectorState = "Committing" // compute PoRep // single commit SubmitCommit SectorState = "SubmitCommit" // send commit message to the chain @@ -99,7 +106,7 @@ func toStatState(st SectorState) statSectorState { switch st { case UndefinedSectorState, Empty, WaitDeals, AddPiece: return sstStaging - case Packing, GetTicket, PreCommit1, PreCommit2, PreCommitting, PreCommitWait, WaitSeed, Committing, SubmitCommit, CommitWait, SubmitCommitAggregate, CommitAggregateWait, FinalizeSector: + case Packing, GetTicket, PreCommit1, PreCommit2, PreCommitting, PreCommitWait, SubmitPreCommitBatch, PreCommitBatchWait, WaitSeed, Committing, SubmitCommit, CommitWait, SubmitCommitAggregate, CommitAggregateWait, FinalizeSector: return sstSealing case Proving, Removed, Removing, Terminating, TerminateWait, TerminateFinality, TerminateFailed: return sstProving diff --git a/extern/storage-sealing/states_sealing.go b/extern/storage-sealing/states_sealing.go index 6e95fbd69..607f10aba 100644 --- a/extern/storage-sealing/states_sealing.go +++ b/extern/storage-sealing/states_sealing.go @@ -226,56 +226,50 @@ func (m *Sealing) remarkForUpgrade(sid abi.SectorNumber) { } } -func (m *Sealing) handlePreCommitting(ctx statemachine.Context, sector SectorInfo) error { +func (m *Sealing) preCommitParams(ctx statemachine.Context, sector SectorInfo) (*miner.SectorPreCommitInfo, big.Int, TipSetToken, error) { tok, height, err := m.api.ChainHead(ctx.Context()) if err != nil { log.Errorf("handlePreCommitting: api error, not proceeding: %+v", err) - return nil - } - - mi, err := m.api.StateMinerInfo(ctx.Context(), m.maddr, tok) - if err != nil { - log.Errorf("handlePreCommitting: api error, not proceeding: %+v", err) - return nil + return nil, big.Zero(), nil, nil } if err := checkPrecommit(ctx.Context(), m.Address(), sector, tok, height, m.api); err != nil { switch err := err.(type) { case *ErrApi: log.Errorf("handlePreCommitting: api error, not proceeding: %+v", err) - return nil + return nil, big.Zero(), nil, nil case *ErrBadCommD: // TODO: Should this just back to packing? (not really needed since handlePreCommit1 will do that too) - return ctx.Send(SectorSealPreCommit1Failed{xerrors.Errorf("bad CommD error: %w", err)}) + return nil, big.Zero(), nil, ctx.Send(SectorSealPreCommit1Failed{xerrors.Errorf("bad CommD error: %w", err)}) case *ErrExpiredTicket: - return ctx.Send(SectorSealPreCommit1Failed{xerrors.Errorf("ticket expired: %w", err)}) + return nil, big.Zero(), nil, ctx.Send(SectorSealPreCommit1Failed{xerrors.Errorf("ticket expired: %w", err)}) case *ErrBadTicket: - return ctx.Send(SectorSealPreCommit1Failed{xerrors.Errorf("bad ticket: %w", err)}) + return nil, big.Zero(), nil, ctx.Send(SectorSealPreCommit1Failed{xerrors.Errorf("bad ticket: %w", err)}) case *ErrInvalidDeals: log.Warnf("invalid deals in sector %d: %v", sector.SectorNumber, err) - return ctx.Send(SectorInvalidDealIDs{Return: RetPreCommitting}) + return nil, big.Zero(), nil, ctx.Send(SectorInvalidDealIDs{Return: RetPreCommitting}) case *ErrExpiredDeals: - return ctx.Send(SectorDealsExpired{xerrors.Errorf("sector deals expired: %w", err)}) + return nil, big.Zero(), nil, ctx.Send(SectorDealsExpired{xerrors.Errorf("sector deals expired: %w", err)}) case *ErrPrecommitOnChain: - return ctx.Send(SectorPreCommitLanded{TipSet: tok}) // we re-did precommit + return nil, big.Zero(), nil, ctx.Send(SectorPreCommitLanded{TipSet: tok}) // we re-did precommit case *ErrSectorNumberAllocated: log.Errorf("handlePreCommitFailed: sector number already allocated, not proceeding: %+v", err) // TODO: check if the sector is committed (not sure how we'd end up here) - return nil + return nil, big.Zero(), nil, nil default: - return xerrors.Errorf("checkPrecommit sanity check error: %w", err) + return nil, big.Zero(), nil, xerrors.Errorf("checkPrecommit sanity check error: %w", err) } } expiration, err := m.pcp.Expiration(ctx.Context(), sector.Pieces...) if err != nil { - return ctx.Send(SectorSealPreCommit1Failed{xerrors.Errorf("handlePreCommitting: failed to compute pre-commit expiry: %w", err)}) + return nil, big.Zero(), nil, ctx.Send(SectorSealPreCommit1Failed{xerrors.Errorf("handlePreCommitting: failed to compute pre-commit expiry: %w", err)}) } // Sectors must last _at least_ MinSectorExpiration + MaxSealDuration. // TODO: The "+10" allows the pre-commit to take 10 blocks to be accepted. nv, err := m.api.StateNetworkVersion(ctx.Context(), tok) if err != nil { - return ctx.Send(SectorSealPreCommit1Failed{xerrors.Errorf("failed to get network version: %w", err)}) + return nil, big.Zero(), nil, ctx.Send(SectorSealPreCommit1Failed{xerrors.Errorf("failed to get network version: %w", err)}) } msd := policy.GetMaxProveCommitDuration(actors.VersionForNetwork(nv), sector.SectorType) @@ -297,17 +291,33 @@ func (m *Sealing) handlePreCommitting(ctx statemachine.Context, sector SectorInf depositMinimum := m.tryUpgradeSector(ctx.Context(), params) + collateral, err := m.api.StateMinerPreCommitDepositForPower(ctx.Context(), m.maddr, *params, tok) + if err != nil { + return nil, big.Zero(), nil, xerrors.Errorf("getting initial pledge collateral: %w", err) + } + + deposit := big.Max(depositMinimum, collateral) + + return params, deposit, tok, nil +} + +func (m *Sealing) handlePreCommitting(ctx statemachine.Context, sector SectorInfo) error { + params, deposit, tok, err := m.preCommitParams(ctx, sector) + if err != nil { + return err + } + enc := new(bytes.Buffer) if err := params.MarshalCBOR(enc); err != nil { return ctx.Send(SectorChainPreCommitFailed{xerrors.Errorf("could not serialize pre-commit sector parameters: %w", err)}) } - collateral, err := m.api.StateMinerPreCommitDepositForPower(ctx.Context(), m.maddr, *params, tok) + mi, err := m.api.StateMinerInfo(ctx.Context(), m.maddr, tok) if err != nil { - return xerrors.Errorf("getting initial pledge collateral: %w", err) + log.Errorf("handlePreCommitting: api error, not proceeding: %+v", err) + return nil } - deposit := big.Max(depositMinimum, collateral) goodFunds := big.Add(deposit, m.feeCfg.MaxPreCommitGasFee) from, _, err := m.addrSel(ctx.Context(), mi, api.PreCommitAddr, goodFunds, deposit) @@ -327,6 +337,24 @@ func (m *Sealing) handlePreCommitting(ctx statemachine.Context, sector SectorInf return ctx.Send(SectorPreCommitted{Message: mcid, PreCommitDeposit: deposit, PreCommitInfo: *params}) } +func (m *Sealing) handleSubmitPreCommitBatch(ctx statemachine.Context, sector SectorInfo) error { + if sector.CommD == nil || sector.CommR == nil { + return ctx.Send(SectorCommitFailed{xerrors.Errorf("sector had nil commR or commD")}) + } + + params, deposit, _, err := m.preCommitParams(ctx, sector) + if err != nil { + return err + } + + mcid, err := m.precommiter.AddPreCommit(ctx.Context(), sector, deposit, params) + if err != nil { + return ctx.Send(SectorCommitFailed{xerrors.Errorf("queuing commit for aggregation failed: %w", err)}) + } + + return ctx.Send(SectorCommitAggregateSent{mcid}) +} + func (m *Sealing) handlePreCommitWait(ctx statemachine.Context, sector SectorInfo) error { if sector.PreCommitMessage == nil { return ctx.Send(SectorChainPreCommitFailed{xerrors.Errorf("precommit message was nil")}) From 1946d2ffd423bd70e543788f966275c1604d88d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 18 May 2021 17:37:52 +0200 Subject: [PATCH 021/160] Wire up Precommit Batching --- build/openrpc/full.json.gz | Bin 22810 -> 22809 bytes build/openrpc/miner.json.gz | Bin 7890 -> 7952 bytes build/openrpc/worker.json.gz | Bin 2577 -> 2577 bytes documentation/en/api-methods-miner.md | 23 ++++++++++++++++++++++ extern/storage-sealing/commit_batch.go | 2 +- extern/storage-sealing/fsm.go | 10 ++++++++++ extern/storage-sealing/fsm_events.go | 8 ++++++++ extern/storage-sealing/precommit_batch.go | 2 +- extern/storage-sealing/states_sealing.go | 20 +++++++++++++++++-- 9 files changed, 61 insertions(+), 4 deletions(-) diff --git a/build/openrpc/full.json.gz b/build/openrpc/full.json.gz index 1aad25356f8daa0c721ab56cf6c7f47c4f4b8fea..a81cb15650063bac74bd3fb4aa2cd03f77604826 100644 GIT binary patch literal 22809 zcmbT7LxUhZm~PAJvTb(Rwr$(CZQHhO+qP}nuA2U4e!;z2Bw1#YoJrJ;A<^>pd_>wIxg|k3 z(Xzlk_j<$I0J05Xg(8KG+lzINOWqxr(*HqV&91EQ_CqhcB6;J%2kwsxLkILajDi?~ zfM$`v84uBg9peRl7{&8_`ryWuE{&V^@a*=I&)YoUlY&RDPz5MQBbrzmr0)?1iJFQ6 zARcW4oDl&g+`VW(8UxG+nu-Gu41$2bcqW2-_g)SH4+PlS#|Ien^I*j%1Omq|=fM{| znx{Ab*b%_))As`_TD=X9>o3ol2pc!Xn9&JNGGu-~akg0>s9u0NK4oD=cnD?XS4`Vx zB+h_Epk#jJ=LSmyhDabh!<88X7$>l@wz0PHzI;6zcz)>_nfCfD-8DR`g4)eQ|01>?R`Dv#ou_ zL$vyNz0f;||4q;H{k_!XW+y%Sw5ZUx>o*Ha>uc!2bm&Dh!S!aH_PUjhk6p$Sl)}%% zL5lrIi#lH5kJ-7JI4mhG8wE|Y{W%{UELQ_oT>APs&2{oQzmq+JoP4i#BSn+N%O<)g zmmQnEra(a~lI6R1ayv&cUI+?#hJA@SR(xf(M-O z5(2^9j}Y_?^X@yE*$;zoe_)3asdY*xAT{JJ6aqN=gtm|I@=IJ{=}V7$G7wr<@}&dG zWd^;9#TGI7HEVb6-0vubMO9N?V4 zl19$ozTVV!eme{_i7|H-cp-oafO4q7HBlkS&*ZOzE&aGYaTzS<5kBB!7_bTDsNsH< zB4ZOs@d?d~UE{)*ztWrGkCi-;5AqlgJQ@#_1?*jc}R_FP@-D`kJMMh$Sm%UERET_Pm{f14TVVR0V=^5-9s*7N6RR72U0z6ac z4!CN;@&eU7Tj)zQD(UnR&OzpqE?Tl#iAW+AMvHYNu7p(&nO(=u;9@uznQ?@fx>Z2o zPJ`5T7thVsjcH!`>e}9DibFv9+ZI=a-fBR=H~?>ZbkUT6!HAIX2QL9cEf7n0D4w|&e4#11>8d8h$9D^l z#z#K!%TegE7UE4p{FJt})Dx_21* z`0E#Fcz1sB29f2}1yVY?x@w9i)Bkx`S^2$qNNM*1cokR~c{tdZ*eGac_j7;ex_&S( zdbfYrT$O71@=<6-p!s?CwK#ttZ*%f-b#U?K!N+5tWVMCOd+eQ;9`I2CPe0D*CyF7c z!$HCmp(^8@qsUlQo{=w9IAIY|LC9he66jajrx^L5I`+pl0uHX;_Q!^j#Dw!~Bu^GS zyG7KcG%gq431U!2dG;Pz6X;cOj21oPpllF-m(s9Gn-`CGylII1B+C`$-t3e19XhZq z5c!^+MS)fne`WJ2-SB+?@W$m#%G*_GTV8ucUbzA(S|TT);bL9FymMM%gMu*Z@a7@C zT$dod7`4k1TSxqTSO#8F(+ag$sy*x8#wy1=z9hT6rk}8gH*W?#sjC^#jN*>rV&_P; zN^2`Ca%{yulchaAt3WO0qDxxZ+p@5c!nV_N@#M4;MoPU8{s|>V%m?jTQX#v4e_ft1 z5H5^NlYy`+51Kvk>0=_I{aQ`be_@q{m|Xv>a}(39NW?t)bzv9mZ7cxHm|W2=VqxcG zPUnjO(D;Hss;Nc6Nr}bc#WU1Y)Hw6X4*kQ-YtrSeMwGn+DG>v&x2Lxq?iVxux-s3}VeI;Kj)M0g*|MB_-Ta{*)y(*^k;yA|n!Bl_G zSEboms8EhvRb+65GG>b9J` zCYVED1U(ul@Pr6^K1dIeGfIg#7eF~B&y3PndUeItJCJI94RP=5m^?81FmKU^)wCnk zoFu3NUDX@?^}c&&aFgxXx&aK_!v`g>&?RtzqH1dgx$`_^j0rYzKb$ zM?|^`>-2~c`&{@5Y^AscadPzhRd7v|DI64@6`z2BAH4jDy#2p0VG5eS_%E-=hrf|{ zk%8XIZohhbeZSov&ljhvwLh~;OurG6l8X;$n4mrH(08M%++S!1dcVeaWwCAqFK`UL zx4mC4qYLD|`sY;;qX}omy+yDvu5Utg4V{jUmu_T)(C@y#935nxhrGJCS8tq7uA*PU zErZtBJG-)L)vWEp-M&5CQT!CyP1daDk@gK-BxMKs#fWf|-H3vo@*Z!{fh`hir-Sw< z==8J%_vcCYGyk^iMW{Ai}CwWaV})3^*YMilha#EWRCb4X|d} zt`i*UrctM*R^n9X71wwlRXW^SwI0O--KILX#K?y6guT0g0{eXXD$biusxbNVn}$Qx zHv33FSNn46Lf(7XqpU1N0yJa41D47J?z~#gbVCb#vd`Ky=QEsnw-PH^z@?PGybh8 zZR91)>|dZF!o}X~6JA{ykfa?ux}cqCpw#i|+c!j40STv|CBSiQNiwD$dL8YaTuXRF zKm0#*1|QdcL#geCi8l}Ai%5e^GKx21mx}n+(k+XwajvlE6_+!wffx#yY6xRR{p@@a zslYoR8G; z_o8#UE6VbVDvRl&bF<^pIzvg18$vyEIs+d~otXu)CZ;{+3hKIqXRasGB@NMuCw8P) zU(O3Gr*~wEb6Zfu`g@gShg&xqWgA1l2@h1l;YhkNZs12@sbyj=Sysbr>IInEfYIj` zNO8KEOJJ&EPNy~{4b2yJJ}UuD!|X?c$VsDpf`#M|CVoWyNHL}K)kmIKdq}J|E4WJj zOIaCrqGJuSOpSfD(h0}Oeg%x5EXk*Jw$`FDYV6SWBcuDd4K*mdvYhpE1L*^hcqc$m zQ=l(weu9|h(i>_0b_#nByz~3HyyiGtvVvptvJ&(tb{P*o$r)mkmE+uy>pqCA2tatT zBESqn2e9c02Qfh1_BhCDaO87VZ7!6J8u-+L))Ns|Tbx*c_fWsq298;VgvEo~*D0xu z(%hMxWF3vCm_qxjq|RX@oVuuW491#utX!95;BH0?L5!T4?Ko*^E^yBALqfFeTJ1AUxDi|4|hz&W+HXw#A4Jn; ztt_psnhcLN=PWs_y1&&k$x~S<2l;ytP;S%s`-MCtU>>+uO1 zfa3xi}O_H8#-|v}%~l z<>?jRj)3d#@UL)~}p|y_{_A z8$P44=jYGdp1wYxpT80EuGBV(l=p{gV%a-X`AW8zO)Kev9^oK^XQ*JJ5Gki+Fy#}? zN}`o&T4qq|Fo;+USL^bBDwM8_d0Z%#CC(85lcLIppvT+%XeFTuM0xT+&hDwBf0TTG z0^@U6C#Pn=wczBlAJnaB+x^hc-OuEx4O15rtqpo`)D5J^wcuuBh@l&SKZ9vdhdvWn zDMe=PcgbDC$1u{ebKJ6<XN#e!FBQ zq!=jJc&XIeFdkZ`Ptbionf}$Ywq7wolnqlq-f1LjZRp@Gw-z;(-h4E((qfV{z=*gP z70|&U(RP*C5EDri6~qfZ;CAg;F2msHA5sms>X@%?>xix$m>q8G{EdOn^u~^cZzM8G zpE-G>I*KSA&7$mviGAeQAq4ObO_M`gw)gvBHyw0x!|uuh@t-62(cwr9Y{vJCzNpI0 zeP2kk+>(T~%E~FvSQ}{vg2P@-sGsQ5yQOc}Tf2-a$T#e9RK5mMSDN)D~Y=42w| zBbm6x$5S#U?(}P>}{&6e0?iH#lq#K4CRq1B*Nx_*;p^c=T@ftKc64 z1&93Z7LFFVXxk)mZIEE)YS@1BI`g*wQ@mq9ZQog*9+$P|QV`R1RUs6`VR%oP(u1h- zd0Jk#vr#3zYc>OgwA$vKs^+xk(<4dt>a}WT!{fg5!$c7NURM>9wlm%U{p8;n^Yq=l%93it3>$H zgS|}sLQ){Dcqgu|^^n(n+r2iv&msAon38%KQY2rUh2wq7R2s0cbNx!QpWTs+=}GY0 z=r!%bdw5Wlw+Cv#F%>F9<%WDNX4Mf?-+R-&v%P-v3X8EIzfrfrpP8AdS-bRG!^--) z^!u}^xW13<64q+SH5iH{1_AX{nuS0`hpa>+Nd_0?bBlN%huGbKlNo2Y{w{hOI)p>f zNU`ME4Ng>(K#j}Sv$f22m|i}IUsOh(`WuX!EM%?JpKf;+`=BC9 zXt`D+J?Vbei2M2T#v_!&=L03OwXb%wKQ?`}#l0dFk7jo~HaOdoS%z=Zbj_Ipwc%np zdl-GT5R!95=YZ9mLQ-FLFo^-`6j4?^rWElsY2_s?y{74$irUzLI_QqNfP2nz@g#~s zdN6$>c_%n_9g(TF>>?b_-HarYNgqG0g5zhWDO zm%h1uVHwvG{GI)o!*gYHITiV0xtMzBjnX#jbUf}6DL?6D`&1ZNK6%K{% zXK-NqijQ6Nr?&HW=<}c><}F_JyQ4(6Y0(up*RR%Z;-Q%ejRay%_@biR67Iyr$Jse-^gLm_xcLkh=| zWY2P1E-B|KoFm#XxY3U54-h57X+HMv;;_4#O;#woJTAwO8=cf6f=&3a>d?ZZhB+04 zvn-`+x-K`xFuR;;*Tn&P8tLQffx|vtLK^J3C**La4!MLeg|}^4_WY*sm|B9_1)&j_ zPEZsxUtuklb_??R)+sBFzL{1xtZzyGdTXTG6N%eBBU(~FJwtv#?NhC{k6|beWAvM9LkJ_ zSz=Yn5I1kQ+sWAo=r7Fwg)9?@nwgps+fm16Q?ry$tKb_1mC9RD$s)|G3YD1cx6M`a z!Je)|P$nx?P1)yAYU?r&mA7s&N9@r)9;s#$w(|lr1M8$0Zn22QO`0`aJ7WwGzWL|w zI|J(n77n)Y_ZTY`W5xH;52svOa^KQu%D#}HdGpR^3Jd6@DRCmD1;wJ1g^Js))rfTk zEPeuc=HzVf$aLR3#>@8V&RQzjqr}>_n9(7m%TlX%Pxj9xl|14snXyx%Qlefn+JFrO z(&D@<2l`srde*@#ko*fx>`owh*z4)0M}k$2Vv6hw$2tHLvjcGL#|niLO0J|R(C63I z)~}wcN_~3a9@5>!B*Q0*b50|Hwv>fNWY2&kEj>TzH(@2BPpcw<3&{y*ou(U7 z7ppj|!PJ0@@z(|>PwTC9&)ZI@YxN3ZSuyo>c& zoTD6Z9VZ{sG@Y~LOn>EE8)5TYk%F)J1xg%n1nMR`6Ku->L5!e)WWV?PIJChUoE6@oIUszk36} z2X}e3zy6Jthrh1%zTdF;4v`onQ02PLN6r(sKtd{ znfO3l=pYTFTdoE*B6>=hf@99x#roK(+{fU!Q@a*TpQUklj{c=`c}PL9`k#yH7Wt?{ zlj_Gg;k)x!ByRb+*n9UF>7Jow7SN9{3B?)!>ABcZ1mHwL ze&B=0{$E=lY6G5wF-&*eCF*Tn17}R1pTHzJ^Toa@;G%&CievvE)1PGCXJm)QneoX# zAth`XBXOGY)XB{KVFaGtzj1nxy{h0yv0t+w-=Dg_7tfx_>&PL4bE1cuErG7w6Z6?= zQ*Uq2-_zc2t6^5V(Otc}QnsyRiXKEaAwScDM5@(s{Au(z54P zG&~qzS-#VTTNd3foxgT63GphaS%yFw*6HudXwS>Fg#NL56PpVYI-YAk+2@~20Lylg zFCTc6r$Mj=?@kb4-*p9@yet(XWARt>4-z2wo4as5BiGZD)q5Es^oQRcXD3;gQQw}O z^?TQ|o0zvqtI$ohuHM`_Ot@bpS56BPvdNyiV|om_nHBMqCg zwv)on49@I#0UIu1i<_`+qL-MpTGV=7dm-IJlouUPgZ0;MV06Cr2kU;XdVdxI`pP6p zb%bjYH~n)R9r}p(ylyq|n9xf~6h1j?W(8q@ClKwO>KA2|G%n}onF+BsWv{Lotk-nT z!=Jse0!rz&Ru;=HFy?y>;R8hbAscyj)phwBsXr^M*Ba*Ul^x}-pVY}Bk&0Q&~+-I^`qh3mNYL`%W8Sd#~%U1L#B5%MB6cy zzhW8L7=hH?F_mSU0S|Dngotv@_t48RgJzuF%M!6G2zH`K*c|N7x$h#Ig%BV-piFWDHx%otTvjheg;&jf5wi~H8y0lQ?|Qy zJ4be+W1{ZgAC3a#IVu{!D$9sIj$e1sq+rd3nws5Zs)0ecC6cP+M(^#ToVnh!;bapb zd3rmyQ?n7IM$n!l(tMQoCS1UdzsNg^i=u(IoEYkK7Q)ojy{#LTTA)ynXf+~frBj;@ zS4*&pHo;5(bqdA{`y92vr_OWpm-wlQZc=$CM6sfb3FHdZ;8ub{9U+zx80wo56uItP z5=zW))Q*`+>$0o~NtS&FM&kwGjQ$-tHK^*Nsi!DthV2}!ZBw3D$p7$8;$;V+XdJZ# zWE>f|{X9+=Hd4P`gi4Q|0sv9EV*K|z=>EMBdTyudNhlzDN00!PcttrBNI#e8uRkz4c3NwI}K=Vp(l~aoo25 zq8!q@q@aAD<_3N^`o?;w&2$2hYnev@2nK@Y?Fv=8wiE=TN$567VBLiD8|cDZW^lI- zg?amCZ%64TwxuKD7WM$3Y#k8p#Xa*JG(MZLpLximn$mEXQne+I?v*Rm)8bse?0r=1bCS zP<$;${TKJA+Q-$DE5{Ov9@4bl>)+}Rt6~~gf*Q^xU~}Ec8!cM2fLm}#U6Tx@$wLQ^ zWC8xHXeUnr=E(}xg+Clx~5jmol{{`cE|7C|jh$mw* z$yg@`Zn#=tEBXSf93*5FF}WQ|(MH2BzgF3;IRk*xt)#g+1ihrG7GoPUTJ7SXn0511XkqAl8nI?3 zL4#fb9l73fTZ&7r`es{Xx5&wr^Yqfv;>s{PbDr()6g~jpai-@IFh_!%HgSr@`Mm=g z3Bggsba#$(e%-w;^&?5JKB_q+Mfm3#vcW`BTUuLjw~|Z7G1vT(mu{6@_e}vyOqJde z1(=4)Ke<=HCOF4Z?4o*`wAW9OnCat2!b{2eGprp;xcYNg8`5)I;G5=m2a z2CpgNMr7F?LX|ap!vb$=ReEIjPc&F(wxnYFjao`}H5wgyiT5;nafu!fib7XqKYx^G zs%u%+YHec!+jE9uE&KBZl0k&DO3Dpvm2^)fvr7_-3NmM^@^SS?nomWxxpT|`Pav`i zXn-D)*}3T{O%QKv4V=Jp{-fK>(OgJROc)Tncjs#DH*B zbWt)@Y2z5f+0E4XK1T9yca>P&G^PNVBS-q;yq;)a_S8FwP&%u0j<}?qY3qZLZo*G< zFtF#2t2b!Z3eKXF)FQp~DAT`5ghpwZHfD$UgIAY;|5kjqUUZG+xlZv@W1*kGy@@Nm zYGsk;AMYxfOg2q^c)gm0j?`KVIWW5W05Kb7zwn1NC7GoERFoO4OaJbAOlE15T{Y>I zm6s72RaNm*#FM^cU{zTht?E+AwOVPP1=UFH+zyL2p5(^n60~run_uJ1?7~Q~2%s1c z+1t;Sh2p=s!TNs83Ra%E6C-zLG}7&oig4cM6Wegp@NFEqsxhOVJ-v7#Q9CYn zWaG-ZOpu;lj+9%~Zr>dT>sXGO8yfMN*fuOKy;rI)M4QzFCG8g*zK1H}YXpL=X(C){ zfbLJ_Nt>_Uy8X&29JyJL_HT*L}xOWRq8+Q+aTeP3#z+M?8$=RvEk(d zfVp$zcm43<_pt|{F!0f*!B+mCV_#_ zz(KK*-2~+FW@IWcc7spIwCnxKd&tqrc{i^^02c0%3HXRUS(gn|W{>@jbXaXHsam#1 zvc3Q@dNBk-9yv#D&_5+&Tc+l4BAuun&g;fnomu3@u~F;cnpk0p73{;wT8x5|BYwbe z*NWDClcBQd1tW`R_n{%cAVYJq&?us40vSoaQ8rY94Q|RY9BY0UkBwyc&pd&!r|XeM zWl{!YGN@)UdUSRZUF#&w_2LU+O&a%+ zP&3xmXEPq{!<&k!{5(@b6FLJ+d5@FFn;P=B78mbIRp#{t^20}749k*7=}L!G6?Qdx zFKJB!{sE`L6;t|G9;P_&s{81%E=}X+*9H@|+E0kLv>Ht>orUWbwHM>BY(>HCWSzYscauyNG`ov<$~Zj#yzg zWh1_bJ5x+QWI)swwt8LK7Repxp(auYy~b|wtXQ-0Z0vu1jw2xNz<6|8=CDfDtFyj) z)+`R4{J$z=l;D~4WsgR)jHD(KeCwwowg!spS++r&1imwn-!F*7OHxZtp%yv16bfI; z@+GM^n%y862m!Lp75FQx{4wglMaRE35xvr#53%OgAM4d$OjDTZ2HzUghhQg6v zOuw?DPk)cmMdZiOCnz+;s65e@r}%>0-DjhcgjUTKzG-2%#3fwds4=VcJPfh1YRlon zJ$iVlL~Sj;@o>&_s?gL?l#AibCcE$yb5Bi}{s&qkVG{t4#J#c#5(B7^Tx?tJtH;W` zgwY)y3P@rk3}NMUow!tRqx_<->O^_*MN6L6&{DNeF~cUMi}yw8cY&)m#SGLn~!* zZ^Y<~hV+oqHsFi=tm>YEsWHt9S&(DV>`zW2UO=*+Qd!C&U+V|XUA!OLp2lM$T0$1x z;n^A&hsxUQaWZXpYxuiIiFHPOg^FoVdqSLWX5C9t^}p1l2)a;ogvJ85HELets$=8+NDe&=hQL| zG+MZJO8GBvLM9dRCl&A0OGL2Lw|lybk^FR1u0aISus0=81S)Vehe)SGp>uq%_Cx|1 z8tCvoZm4HUfF~wbHL<7;et@XPx4pcqXV4=3s=sJ~`*A&FB~2&oZL-=TC&RM21@Mcm^{lGLzYup#+dUz_4Pamr%e2W?-N_V53e zi?nwWqs!sb4(-#fdFbN?Xu4y>3sHvs#!h_AZc+{^ne&d=6;^y=(xSTTZ&jDEUQH=k zMhFrP@~i|>iNZ_7d_(C0JOW4}LwN8QQ%bxV^35)b@ue+cD%L2j7S%$|EJal&70fO<^Xu>vVJ>@jYCL^bnv2_IQWHw%6Z&@ z8rYMx*Ih&k@z33Ua|+0}+?ZmzBMs3YJRnRoZ9EY2KL}gf!XNRLT3d4~TU#2RRvK=9 zEzR^R6l1NW3!82FmQHReTXwxE-!SLh*IIUw|Ajd2m$GTG?10w5Yi;)9(O>t%XDm9o zbiSNG%1Md4W=lKNiwR_hZTapP!L1Sd`bQ(*i?h{I%B^Tq)h??4M01`GQru@D)-KN1 z(tDHxyF_r{ekvMy9#z}Gq1jzvV8~Mf<%iPqPR%`o&=(CXBmC`yh6EUgc5~uT&eRg^ zlYF4HsiKs2*EoAN-}1>0tv}YW5n3tX5uSOu+iRQV&&7lA{yVsxv^G}Jn2u5kAq`gIIK>IX zrrsC;RQ5KcJ=1M&yWgmT1M9L=;-L^OrsCQAy-{@a<99=|ClzpF(@9g^TlRGKoEao1 za_ERcNQ+7!^|?YQWFz)S0!fBhwPrS1z;ABJQrW@{z|(^UJyWf6Hi?wfe0u=_ zwZ@Lxaq29^$d2sT zB4^MjQvDIalc(yKp_mq3P^n!MN9{feE>anwXr;Qg*`Zb?`1Z;B{F&R}1_z8H#x83* z5KQh((6#e+C_M6ed#^6sBkzLAIuOt@YKNR%A85?Z$0mG~kbBqVSlQ^rz2f6MyiB^^-ks zXv0lN#P?n%X_SHu8&X+PN0%{b{=?W+O;rsgpqbLNa8?p#Bz?ZA)!aVzS+Pe~KSXX4fC+EVeBC+mpXSL!pB;6ZsP* zDbehSUkrj0fh^>WOjO`(ID1Zhi-0o!H+j4VIGr$JU@*t^5xNOQ5sk9V`~q%tyI_R= zE2UIDUtnP(U-YIB$Ztu7?s`Gf&iQqEmpkYS`^*0IN_?-2(((=uYHVeQYoaAA<(iOF z4e9W&)Q@QA6_leYJ5-Qvn)S~;l8<2o9_m4CccERjXi=~kKD1B8!gN@#4XAh%2kWVI zmMvdvS+@DR3~CKv49)wB-D1_3^1ZVh#6TawKSy#TU*aHI`qPp(pRXBegVW(1bLO&r zXxDA+Fu=Uj@R@xh1^jZ;rrWEWsd5ciaz5*RLRj2iBOix?vYq{G#(PjuVOSm0%(3!O zq<+}}UbD8|WS7(%5u(_I?z$|^m3FC+scAoqz+OvY+U%f03C@H4X18Z(+#mX=;lWl* z;!3-<{l^w2)6xb&3zB@U=TtoBo6`c%wn?tHoBIhfR8>sH8ZW$Q%e9e6HJ@va`r@fr zp9#7S+-9ySEy^j%76H&yR2*pWG+m>qPD7g zXFGBGKDXA@yjnT7!^9X}*h|9k!gcJYQe8m%_5uVm!tV<+Fvrpq&GfAdnvbf~0oNn|u{3 zZPfq>G%p8@-Y#?)1PNZwgE43ahVMZVsz_!pj;NlCAvLoZ+|gPt;RbQxgL9%aPn&rC zTX`k!YBIFh|7_cYjB2uQNd@7~i>XGF8P+)h5oZ93R**7-(sD(9BlkJoGnOsXP)X2y z*@T0rb*b?&W%9*>-_TgR@1OU{Y@T%iNWo7`+M$M^#||K@ZC=zE6Qr=SFEBy(?;;Mf z{U;L_Q4i8V*Ntc6u~L?OltbRhVh6>E?}tuGVC{JXfDzL~v`^Rra_07;aZ=$MA*nN%W6E|&j1$}4}Y4>lI+Q1o2v!kJFv`DEn z{h7Ou^;TCHbMnvCsW}=&hn`gb#C#9W%;T(2(StF%8{!4AJhI^<*S{t(bZRG(*WVlD2|d8Fw!l54i%hT%v^J^(h$tFNA-2!S72z5iIuB9pB98+Z@Vpq%X9AgpG2 zS(2zp2Y2Sz1`6!Pei*>E%>n+jfMlG_NIeYUl8jD}=_Csq3hs6knMCPqr!xHJ4ol2Nin}@BIRt+32Jx`@&kKgZ7%Xqv)_Yjj0FuYFy zq4PfGHIRALpPDM0{Um=f3WBaPC=D|l4Nims zk<+4T3+SLZI{H#F$_4+R=+p{!*-P8P&$F6d;R^`6KC?DfRvvxn&T~J3+$}KpT?#mX z96$ogC2b9{fE{TV03F7{ixa|(U)cnpOKicRqbwBy_6aDw^%T^n0ZRm=7wzKj{B;;` zV^T)yl(uo&}szgtJ^ke)t7~0YpUz zFac)9lv9S8yBo+Uo(WC;2cvtK=b>L{n_W>xLzgrR01gcB;j9#A3;+I;M1}qK#446y7h@plgGh$PQyEp31IIA6syNtF}|AFO@kOd#0 z3y2m%xw;%~2;0tI;Chrrh-d=7E&IY&nbG!ZH0l&1B07lHS#!h8%t!=2*bvN{m=`D^ zJx9d-;F~;7NmrJ9Av1y*_&+jP@l`Z{W7K0FPw;J6@*>Oy@~8yU@#^7(ht5&~TwOYkV?*u5iolua};WT(A>l#i!o;^*`L zigRLLu*a|&L}Yl4VT(eB*Y(kjm|%aOUx4O?}5H4X70pr*WitMdTtD2mm#tXU9i$;Oew# zX;D9J;R5K5_x_)53H4ij2zy4nn0&%OAvIYOp{=$)c@j0h5Vo=;G1J34EOf3|dWY*K z&JO_i&xWx#Ffjl=;0K~RMNpto0op|N^pwc>E?|_58<2t@>2;|W+(vf|CkYQh3 z(#Ik?R>;E;R!xK^p=pMMCZE6s!^bPMXaI80fHsg&9hDc_^J_Tn<}lb0Nvcx;nFnu% zX@ONxt8>(i!sL7l3>k|^`BxAZw%*}fx#XU0so+5BA`wPkhY(o3UCmQEi?HI+bNN4ovDqSZeD(Qs&cP5~D`LVvr!FRe%d{Mhdk+|afp_)xx%d`HhgSWLiH0gZh z07^lNgUn@!l{PAINmh%~l{|oVY3FG+d5QC2X`pA@srmTy1x+L8fH}pbpg%)LS)Dee ze$$Em7&-jEe^)KCMoMM2j`qzd+USu{<`XHlMckYAu!=mj4GcPkQ(ckYW{>%7(Sg+1 zaTLfqAc^$of|F$A1#B&_7+~Tu7$L+4_EQxT) z-{hY(M)MJkvf$tENo@P80-SQuLJIKAs<5c)<&gm$UaDit>%J#dj&}&exhKAQeH|Q& zG>}hxx&(A#U7LO6#LiN5hYz?tg9-TvaPhv!@^En`C^|TK*}HqS`FeXbf%7DDP)-4n z@IoHLVFnWzH=s!MU8An`vN^i?t?rkU3(`yq)42cTAd9fkm;jq}`bRxrC9Aspt*7Pq z7l-Q|r-lg;s24t7(3E<8E)&v7Ru{86%gxuZfx^n`CFJ~U5Hc~k2}svB|K8-B!G%5Y z(vFHNha2z8s%7APSRe(hO0PJ&I~_!_Y>NwQ30U*_pdX}hcc-#7NSMjBM0|B)3UgB> zW~;!AEI{W}7bj%ei`9y6>(j@>Q~M7oK_W)U7#1voYt7Lm(K@c~BzhK1 zLgndZsY|x(B5%IR7tFT4ia6{~R)4!s3D~i2k{SC9ima2f% zfcCA4`t7t?k7pM>q3RmUl+{&+q?C-==pP}`y4|O#!Lw(yM&YoJonh5N+$AN7(%B*c z35;Q0-Dhw=X(MgK+qJ2RaY4w9 zL*1RRDC1!C~$#ugw%9Z+0%K@C#A9$phTRRN&X zBrBb;O)+M^fm)n^^{Hn0sz5?MMYe$)uoxaa-Vekh)~Ml*wrbG7(AJM}A&Il)CuE#v z0v(9EosGGm0dq>>dFU?RAg@vXDY4$XT)&}iPB~Q1b;m7 z+*-DNtsusF7RF=Q4&gwjKIpatB*9zQ767XcW6p_`E9d+uu;F*|qkPSmuyh15P5tYx z@Jed1f{pLHMKIHU?;W-ow8*;{?%CnuS`5s*5Kk=|0{FnWPv5Qhtg-2w5=^#@>XOdR zuaepOyKSIY)Pv$UEU@*k@OG}1F&@U zMTTqH&RolSK6F!;_6EI!z=yF%KsX~lS^wrv#nZWJNy^}U&XBe;-ywoW?rszd{{$@6 zvBNdpT3-fjSWsUva&@lR;;A{1vNWzuBR!y-K!2Br7+l8O&{3!x<1^ihoU}fz%o^}e zi>HFjKEM?fbaVtYfLpON>$1T!F|5p#Wl+Q5$)Tk#YPrkOAud*74^)`f=qt3*XQ1Q~ zHVjeNx69UdAoQazYnFIpU0ziam!Jmkikta>+Wxgpb9tRdW4P>ucbASAJ2QtP}f z{?N?Vj*jjB&$o}-T}6JHqZgUafuT~@f65HGn@6?DN9+Goa#n9qxL>p%x=UKRduWi9 zl5QBfyHUD9LK=~nVE_@3?(XhJnjr@15|Bpn@O_@&xjQ%K=KT}iz4uz5wFYWRRp-Ro z#i}=++b&n9vNk-3X$hSstpc;-+5;6a6e1N&q?5M+U)XcM->^FDYiiD=;rIYGDqU~p zQf^vH`lhEWi)PdfN8?=Wo0N`i0s;t|e6V@M%K9ql+EHBgIvnjADt@?qD37BmuGq?! zSp7KWzP|$Zl+R9}8Kq|}s2ddOVRW|WusBFoFE((H(uVp}WKnmA+9k$KZj*s`b~Y1GDF_6ET+&mxYu}8~zL7bXr-@KAuU=KL5}#?a1I-qZJwJS8t3I z*+9(bxJ-?;J2Z>>TtMsz?&b-mR47u%S9ie(Dq5E3{vBbO1v4EgBv*(e6$TZ(%U#if z2EU+(q_Q$JHWQZIM&x>$7H&hmt3tX)TDZR{xPOb}qssCVYFb>MYz^UJXFj5zP+v6X zG5*o!3>H`nZ-sWbRuYOnJHrJvBzf(0?Q=h3=Z$lX-COI1xI;O`s%maKeKF2(?GT|G zN1~hE3}hC(tG6$zKgxKn5mBj*IwKw>nD@RtK7s?HOrF?hBwnPsjDR>Tg5@J>efho@ z@j8!Q-8K>nTkXc4rtxnIw+Xau9wUKM2+o-%5`%vIijYcjNS2&8O3*rFE=DY|<^Mjm z33D;k*cO?`ZGL&x{`2?y&Am#kV3K+A5+R`#5q_|OMWx8d5;8S*2dgG|JY!L;;06G* z=a2}2(Gfhgm~E1VLC`->lk8H|d%gvw zfEjhcr$N7rQHBDn9S)`w=g@{s$bK3eJI^TH^4MgIp8rG&~tNB0cBJWv-_X}7CkP(OQeJY+o5X6HD(vNUmauzxht(k{64 zjkoEB)9X^mbtl>sa~!9a#J3=w6Bu1P0New{h=^|jV=A$~t>zs27Bu2LNK0J{dlh>{ zfmf2nUaK|?1r-xd{S`}X8P|@^8?*#TosisLw>jLuicCM#&(fTLAup=2#-BE+C(sTn zJ*Dqw8lbZmRJfNPpDRXl_*BS_QFZUZ(*@!jiFM!=y;%S$yaEmT}sHk#tkk-U;ezb=nMWi=PFe!_7nTCBd}U z8!K#D&+FO|gu+&pJWyD=M6-*xTGYe$)>E?hVboK6;=6rJwyvS$kaLUPU7p^XmS#T1 zkJ33BtO6SVd!rSaO(|Ni7U4&!kH7SvA9!PB}?=OxE{Ic693Z5Eef^z@Z=(SsdXZrHQr=j3NN~=FwRHUQkItA*o zAv%poB(MiM7uN#=8!-mmQfm+2NShZMAIUMK9I3SYZFbk@#SkNboUF)fLy5Qtj$_^L zq+#u8-)*7$>;N&*#fviYHv97G`(60z0q^zzIkOt=^~{?y$BYIiS3)FaYp3s9)7agG z;&a(gYSlBBEnQ8kHhtQ{+;*miO%=|uheR_sHQk?Lo-z!6d^0{WE#tRTs#J^CD=-41 zs6O@2b=b#vzLO6Ihhyxh{r{zc_9rYcU!UAEXQZ%@1(yC zHB!Y5j+qEzpy@!&7y;{2zfDTufIbTBL`?L#ckFM=x^#WlL@3hqBLta9>OIZ`IEhTsKVjJZR`Itvtv!Yw`jo z5bim`KmXxW(xkf`rMnSE-N7Outb9Mb)*w(|rA-!*Z7}wijF5~GDYFl1@&=haEJ(_4 z$3Ny$iwL>iCU;csws##lkPxI)MTY(t2NC~H;4SI_F&@GBLf2X~+=r)L+lroJYX+TH zNfGZgdo(*;P!NLIs%|{K;6|3*KsIcR$n@nf!je&yUm8%7L0hzhficixCIx%K`VXBm z4;nyWR(4*b!Iz1RzHdW3Lt^Ymgs6WBu=q2BzKjjZ;U)sr!Gnx1MijL_ze`~uLQD#u z;V`vh6^^oB;mOc@vsTcE^O0E~(MD${ViQ{`8T}bwk@5Fu(tFy0Tw~W0R{Z5uck`%U zsq>ttAJi9BWRpM>aEzVQ#Eyxk?A^06+OiK(ywH-V72 zLJ;0>q6-angX`7M`LF+p%x8j3X7K-~%3Lw`WN&t0><}Zh8<;HFHgvvYp06dh1qNkf zJ`sx!4cA)fWTZHw3O4x~_l2lXss!n)lZFxvk`xJNjfMswsnJpt(`{Af=S9ly`HrKm z!X3Y^WOPnQv2Vpfd?)b836y6uVjo@h z1$%Wu4Ge5PVPoDDdLqlQ^gf@iiAWkHXFPLyVEU5SSVeL<;g-d*E6;VKGuFZhxlQ%H zNzB0U$-FDWQ-zr-BdbK0m06(%G-?~zS?ESv6He={>LgqA`(RFsr#Y{?hTj_%Q3QSIKl6h|EZ6z z_e4T1jC|faHBii5yv~g3-^TqaCVu+&?&t6GqVe?mKJPtAe+BUz_gw_U%Uv1r0tv&u zo7umd+v!Bk%)gC?rK0=7m`)bl^Oe|W<7~d~XL8LC{&|?e{&QZH1o|<~7gWk+A7L$- z;w-NAvw}vJWTNfdPG^C=qWSO&_N66Y&TCb#dWY>zdab=t4bI$v>}5g|LRDOZ^B}Kk zcx)jl_qV76a(icXsf;lO9> z;lvlKwm*FbDVwt|fE1sU)0oeL-Mie*ba8Zl!3-EGmb zc0YD-%Nm9~z*p#^X&`Cr4wQC(kn7;8TWT5;YcERJUb55JEAyINhblDXp;^$Ti`2BU zF~rHb4r|n+&E+M42UH9#@iOY z^T~f%C@vQF-&OnA$@y5@)e|uIrN5l9ESqgiq2z=bm?KpD))0A9NJJ?#Bs2y(m7+eR zuSJGhPloFw+lGVF6c^R0Ehw261t{ePcQ{H3XMC!>`|dyRco;@=*uG zUwr;IFRUlxaDjLdajTj!>oVt-rOb zN74M!w^{P{ksDHD@HB~Lbhp5q(k=L}VlZ;^O``2CjH%Er6ez`lmDmWF|F8uMX9bIW zNBg-yBgdafCOW(;y*8&o$)gNXbw_g#MegAG6b9PI=TK+O%qE;TwO8rU`V-B-x`-gg z8=Xn%WMt`dpgE@L4y^A0EC5F`yt5as%iHd|Lv0Bb zj)e#YZRM{Z4=%XL(t|(j6>R{)7j2E|qi@eT!tDamX=2V9kUBe6JbP!85s$^6 zZ1m?;qKkZoY-|Xw1==?T9siIx{LV4Y!kC~e99&#z_}iXOuaT~2FAR#}WF>j*LdMNu zwjs61oe-DZdA4El@a&XbayqIU!`cC~qR(4v%wM@GB3tK+zSk})&&4ifdFSY!J7Tdh z?-u-8Op^gM-+;9Xtbm0vs|~rtWhMDo;zdLzvuND@v5%4&^ol#m95C$^Eai|lfOUH2 zuQNDdz8_KL6Vwq%rcPcI{1Ag;!Y?YxE_r0F33aKFojt^lA0c)Ffp~`YK)0hQ+bvH# zn-$GVM}b}TqmJcV4}97Poo&12dii`W3eHwC!!@@!=oGqZ?)XR1-19 z0terxI?D_{9a-;&Y7e0{4JR)A2msOVn;pFR5p0e7d0I^6P_1FgzvQG2E2mOR?0QEm zNQq!;oC^6RGS0CniedXKCVRV?Egjrj#I$q(&$rat8J8B+JW{d}Q`yr=dO2@=!(AVbPmgW9v~QM^UFP()jW&32Bo(F^kk! z%q4h5f92hRIi(auhWn*H9BX5KkZu$PqWuq}-<3ciSrn99UBZQUqsSCnLc+k4_yfeHrIqu*~t z2A6!NEBu_eUN#@XZ#Y6a?Q+nLp$)uleL!UvdB2y*2p9w8^@$`t0*VP*X_N^asHc7i z!gRhE3|M|$+6F9DH}Gb-TH4VsGFI_@{h~zPJ>X z$+x=nO7ags3-ld6QJeold2NWC$s3$R1-W&Z3h$&A-%f+_VU;sB2kRa|G}?=1;8DG% zXxX8NZC`_UD#xL~&Az;ngskJi3bpnq2B2S2eC$}lTrs=tZX&X-{EZ+l+9|g>g{9fN;H?aaX@ski)2i6B%vA- zxvqdz%ddO#rDYSJ#|j+s6FDC!MQ*mN>|gwxew8%68}8uXN(PmKh|IgaMemAwix{}$ z!vLPWAkOTB*Al=1oN^(nLk{IjU69qYUE0=SbI2dki-j>l_If6M6P0$uia}uTT*;5S zuv1aDS-gWP*u|AtrYT3ct*~9;>W|@4$y_l{NZ4^Z%0i!ECZidHMAEQ_3k6bLDAuQ3 z#2S@<>WM*k*hp|RO8?E9V_{)q=zDXN5gG;|T79h&gPj;YseCr8l>br;bcV~*)LBvCZYG7b zNUj+fht(pl2fuC<^8__2Obm@+N&jWMRetfBM*#KrT)dU=NQzCfYM1^Bk*!E$eq?9K zikEMN?c%9I=UJWES_@F=Q-I61@%p-Bieny*BOg1y4u50($X>EL^{P&(7jn}8dlIK{ zf#D}VJ%z?HUA;rI+B~EoN8JtV&LUXgFiV1ZefN9KEjrV`^_U}t?hiRKRaT1SDntKk zEWFx9k=3D8n~b<+C-QXH*uc_L(Ijr4-ErskYsZnLNpy$d!mn8se69Glx0h~(Z#T^u z>VkzYoy;sH5p=YO>3ll$V9Y$Y);zaIxLr%FL!aXSZDrtpM3XXCn{*+Ea z^)8TxiN(P>yX)?PCaoX3s#FN7Yn_8kEqSA7WjMS?Oslo^sr*_~{o@*C5_eWWN2dU5 z;uc)EQ{UCy$j`1Dv8dm;D7_q>zM~0&j=4`W+vB29*ZnjtZC=}U8uVWjPUhn6IhvPX zrO1*@*D4&v`31-S*aGW_!@`6`ATY@=ST@7aGsAcK$~AsT!gMnwrNW)2*P&%%pFJ2H z*P?vN|GD}6!}$JIYeN8g-_Rq4I^jXb3|P&3L-F(ej~8wL;N|54UDTa12pRBy0JV*l A00000 literal 22810 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

-fDhK`xeCY=8+2=BLD= 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 GjR62v<$f&y literal 7890 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 diff --git a/build/openrpc/worker.json.gz b/build/openrpc/worker.json.gz index 2214ad85af5e8b7f0c47a81106e161959b55c203..5bb69aa4919c86a4700e9fe7a69472082c5d4cc8 100644 GIT binary patch literal 2577 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 network.Version13 { + return ctx.Send(SectorPreCommitBatch{}) + } + } + params, deposit, tok, err := m.preCommitParams(ctx, sector) if err != nil { return err @@ -349,10 +365,10 @@ func (m *Sealing) handleSubmitPreCommitBatch(ctx statemachine.Context, sector Se mcid, err := m.precommiter.AddPreCommit(ctx.Context(), sector, deposit, params) if err != nil { - return ctx.Send(SectorCommitFailed{xerrors.Errorf("queuing commit for aggregation failed: %w", err)}) + return ctx.Send(SectorCommitFailed{xerrors.Errorf("queuing precommit batch failed: %w", err)}) } - return ctx.Send(SectorCommitAggregateSent{mcid}) + return ctx.Send(SectorPreCommitBatchSent{mcid}) } func (m *Sealing) handlePreCommitWait(ctx statemachine.Context, sector SectorInfo) error { From 9690bc882c3ce9309c00efaf39cf190390bc2ee5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 18 May 2021 18:41:42 +0200 Subject: [PATCH 022/160] Test to trigger batching logic --- api/test/window_post.go | 46 ++++++++++++++++++++++++++++++ extern/sector-storage/mock/mock.go | 2 +- node/node_test.go | 6 ++++ 3 files changed, 53 insertions(+), 1 deletion(-) diff --git a/api/test/window_post.go b/api/test/window_post.go index b6804c401..e508fb5c5 100644 --- a/api/test/window_post.go +++ b/api/test/window_post.go @@ -117,6 +117,52 @@ func TestSDRUpgrade(t *testing.T, b APIBuilder, blocktime time.Duration) { <-done } +func TestPledgeBatching(t *testing.T, b APIBuilder, blocktime time.Duration, nSectors int) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + n, sn := b(t, []FullNodeOpts{FullNodeWithLatestActorsAt(-1)}, OneMiner) + client := n[0].FullNode.(*impl.FullNodeAPI) + miner := sn[0] + + addrinfo, err := client.NetAddrsListen(ctx) + if err != nil { + t.Fatal(err) + } + + if err := miner.NetConnect(ctx, addrinfo); err != nil { + t.Fatal(err) + } + build.Clock.Sleep(time.Second) + + mine := int64(1) + done := make(chan struct{}) + go func() { + defer close(done) + for atomic.LoadInt64(&mine) != 0 { + build.Clock.Sleep(blocktime) + if err := sn[0].MineOne(ctx, bminer.MineReq{Done: func(bool, abi.ChainEpoch, error) { + + }}); err != nil { + t.Error(err) + } + } + }() + + for { + h, err := client.ChainHead(ctx) + require.NoError(t, err) + if h.Height() > 10 { + break + } + } + + pledgeSectors(t, ctx, miner, nSectors, 0, nil) + + atomic.StoreInt64(&mine, 0) + <-done +} + func TestPledgeSector(t *testing.T, b APIBuilder, blocktime time.Duration, nSectors int) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() diff --git a/extern/sector-storage/mock/mock.go b/extern/sector-storage/mock/mock.go index 8a70ed7bd..279d50d77 100644 --- a/extern/sector-storage/mock/mock.go +++ b/extern/sector-storage/mock/mock.go @@ -524,7 +524,7 @@ func (m mockVerif) VerifyAggregateSeals(aggregate proof5.AggregateSealVerifyProo return bytes.Equal(aggregate.Proof, out), nil } -func (m mockVerif) AggregateSealProofs(proofType abi.RegisteredSealProof, rap abi.RegisteredAggregationProof, proofs [][]byte) ([]byte, error) { +func (m mockVerif) AggregateSealProofs(aggregateInfo proof5.AggregateSealVerifyProofAndInfos, proofs [][]byte) ([]byte, error) { out := make([]byte, 200) // todo: figure out more real length for pi, proof := range proofs { for i := range proof[:32] { diff --git a/node/node_test.go b/node/node_test.go index 91348647d..5db7e355f 100644 --- a/node/node_test.go +++ b/node/node_test.go @@ -148,6 +148,12 @@ func TestPledgeSectors(t *testing.T) { }) } +func TestPledgeBatching(t *testing.T) { + t.Run("100", func(t *testing.T) { + test.TestPledgeBatching(t, builder.MockSbBuilder, 50*time.Millisecond, 100) + }) +} + func TestTapeFix(t *testing.T) { logging.SetLogLevel("miner", "ERROR") logging.SetLogLevel("chainstore", "ERROR") From e400bdf87a7763428a45e33a6b22d69816a56940 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 18 May 2021 18:58:41 +0200 Subject: [PATCH 023/160] Order proofs before aggregation --- extern/sector-storage/mock/mock.go | 14 ++++++++++++++ extern/storage-sealing/commit_batch.go | 9 ++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/extern/sector-storage/mock/mock.go b/extern/sector-storage/mock/mock.go index 279d50d77..c7844befd 100644 --- a/extern/sector-storage/mock/mock.go +++ b/extern/sector-storage/mock/mock.go @@ -516,11 +516,18 @@ func (m mockVerif) VerifyAggregateSeals(aggregate proof5.AggregateSealVerifyProo for pi, svi := range aggregate.Infos { for i := 0; i < 32; i++ { b := svi.UnsealedCID.Bytes()[i] + svi.SealedCID.Bytes()[31-i] - svi.InteractiveRandomness[i]*svi.Randomness[i] // raw proof byte + b *= uint8(pi) // with aggregate index out[i] += b } } + var sis []abi.SectorNumber + for _, info := range aggregate.Infos { + sis = append(sis, info.Number) + } + fmt.Printf("VERSIS %+v\n", sis) + return bytes.Equal(aggregate.Proof, out), nil } @@ -531,6 +538,13 @@ func (m mockVerif) AggregateSealProofs(aggregateInfo proof5.AggregateSealVerifyP out[i] += proof[i] * uint8(pi) } } + + var sis []abi.SectorNumber + for _, info := range aggregateInfo.Infos { + sis = append(sis, info.Number) + } + fmt.Printf("AGGSIS %+v\n", sis) + return out, nil } diff --git a/extern/storage-sealing/commit_batch.go b/extern/storage-sealing/commit_batch.go index 34e386a49..e560ecba7 100644 --- a/extern/storage-sealing/commit_batch.go +++ b/extern/storage-sealing/commit_batch.go @@ -186,10 +186,17 @@ func (b *CommitBatcher) processBatch(notif, after bool) (*cid.Cid, error) { for id, p := range b.todo { params.SectorNumbers.Set(uint64(id)) - proofs = append(proofs, p.proof) infos = append(infos, p.info) } + sort.Slice(infos, func(i, j int) bool { + return infos[i].Number < infos[j].Number + }) + + for _, info := range infos { + proofs = append(proofs, b.todo[info.Number].proof) + } + params.AggregateProof, err = b.verif.AggregateSealProofs(proof5.AggregateSealVerifyProofAndInfos{ Miner: 0, SealProof: spt, From 0419c64a06ee6709a21b13e590b4483bb8c53eaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 18 May 2021 19:47:30 +0200 Subject: [PATCH 024/160] CLI for precommit batching --- cmd/lotus-shed/cron-count.go | 99 ++ cmd/lotus-storage-miner/sectors.go | 62 +- documentation/en/cli-lotus-miner.md | 1864 ++++++++++++++++++++++++ extern/storage-sealing/commit_batch.go | 3 +- 4 files changed, 2024 insertions(+), 4 deletions(-) create mode 100644 cmd/lotus-shed/cron-count.go create mode 100644 documentation/en/cli-lotus-miner.md diff --git a/cmd/lotus-shed/cron-count.go b/cmd/lotus-shed/cron-count.go new file mode 100644 index 000000000..622f38791 --- /dev/null +++ b/cmd/lotus-shed/cron-count.go @@ -0,0 +1,99 @@ +package main + +import ( + "fmt" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/lotus/build" + lcli "github.com/filecoin-project/lotus/cli" + "github.com/urfave/cli/v2" + "golang.org/x/xerrors" +) + +var cronWcCmd = &cli.Command{ + Name: "cron-wc", + Description: "cron stats", + Subcommands: []*cli.Command{ + minerDeadlineCronCountCmd, + }, +} + +var minerDeadlineCronCountCmd = &cli.Command{ + Name: "deadline", + Description: "list all addresses of miners with active deadline crons", + Action: func(c *cli.Context) error { + return countDeadlineCrons(c) + }, + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "tipset", + Usage: "specify tipset state to search on (pass comma separated array of cids)", + }, + }, +} + +func findDeadlineCrons(c *cli.Context) (map[address.Address]struct{}, error) { + api, acloser, err := lcli.GetFullNodeAPI(c) + if err != nil { + return nil, err + } + defer acloser() + ctx := lcli.ReqContext(c) + + ts, err := lcli.LoadTipSet(ctx, c, api) + if err != nil { + return nil, err + } + if ts == nil { + ts, err = api.ChainHead(ctx) + if err != nil { + return nil, err + } + } + + mAddrs, err := api.StateListMiners(ctx, ts.Key()) + if err != nil { + return nil, err + } + activeMiners := make(map[address.Address]struct{}) + for _, mAddr := range mAddrs { + // All miners have active cron before v4. + // v4 upgrade epoch is last epoch running v3 epoch and api.StateReadState reads + // parent state, so v4 state isn't read until upgrade epoch + 2 + if ts.Height() <= build.UpgradeTurboHeight+1 { + activeMiners[mAddr] = struct{}{} + continue + } + st, err := api.StateReadState(ctx, mAddr, ts.Key()) + if err != nil { + return nil, err + } + minerState, ok := st.State.(map[string]interface{}) + if !ok { + return nil, xerrors.Errorf("internal error: failed to cast miner state to expected map type") + } + + activeDlineIface, ok := minerState["DeadlineCronActive"] + if !ok { + return nil, xerrors.Errorf("miner %s had no deadline state, is this a v3 state root?", mAddr) + } + active := activeDlineIface.(bool) + if active { + activeMiners[mAddr] = struct{}{} + } + } + + return activeMiners, nil +} + +func countDeadlineCrons(c *cli.Context) error { + activeMiners, err := findDeadlineCrons(c) + if err != nil { + return err + } + for addr := range activeMiners { + fmt.Printf("%s\n", addr) + } + + return nil +} diff --git a/cmd/lotus-storage-miner/sectors.go b/cmd/lotus-storage-miner/sectors.go index 8da491841..2bb44b4f4 100644 --- a/cmd/lotus-storage-miner/sectors.go +++ b/cmd/lotus-storage-miner/sectors.go @@ -45,7 +45,7 @@ var sectorsCmd = &cli.Command{ sectorsStartSealCmd, sectorsSealDelayCmd, sectorsCapacityCollateralCmd, - sectorsPendingCommit, + sectorsBatching, }, } @@ -970,9 +970,18 @@ var sectorsUpdateCmd = &cli.Command{ }, } -var sectorsPendingCommit = &cli.Command{ +var sectorsBatching = &cli.Command{ + Name: "batching", + Usage: "manage batch sector operations", + Subcommands: []*cli.Command{ + sectorsBatchingPendingCommit, + sectorsBatchingPendingPreCommit, + }, +} + +var sectorsBatchingPendingCommit = &cli.Command{ Name: "pending-commit", - Usage: "list sectors waiting in batch queue", + Usage: "list sectors waiting in commit batch queue", Flags: []cli.Flag{ &cli.BoolFlag{ Name: "publish-now", @@ -1017,6 +1026,53 @@ var sectorsPendingCommit = &cli.Command{ }, } +var sectorsBatchingPendingPreCommit = &cli.Command{ + Name: "pending-precommit", + Usage: "list sectors waiting in precommit batch queue", + Flags: []cli.Flag{ + &cli.BoolFlag{ + Name: "publish-now", + Usage: "send a batch now", + }, + }, + Action: func(cctx *cli.Context) error { + api, closer, err := lcli.GetStorageMinerAPI(cctx) + if err != nil { + return err + } + defer closer() + ctx := lcli.ReqContext(cctx) + + if cctx.Bool("publish-now") { + cid, err := api.SectorPreCommitFlush(ctx) + if err != nil { + return xerrors.Errorf("flush: %w", err) + } + if cid == nil { + return xerrors.Errorf("no sectors to publish") + } + + fmt.Println("sector batch published: ", cid) + return nil + } + + pending, err := api.SectorPreCommitPending(ctx) + if err != nil { + return xerrors.Errorf("getting pending deals: %w", err) + } + + if len(pending) > 0 { + for _, sector := range pending { + fmt.Println(sector.Number) + } + return nil + } + + fmt.Println("No sectors queued to be committed") + return nil + }, +} + func yesno(b bool) string { if b { return color.GreenString("YES") diff --git a/documentation/en/cli-lotus-miner.md b/documentation/en/cli-lotus-miner.md new file mode 100644 index 000000000..dfa9072c9 --- /dev/null +++ b/documentation/en/cli-lotus-miner.md @@ -0,0 +1,1864 @@ +# lotus-miner +``` +NAME: + lotus-miner - Filecoin decentralized storage network miner + +USAGE: + lotus-miner [global options] command [command options] [arguments...] + +VERSION: + 1.11.0-dev + +COMMANDS: + init Initialize a lotus miner repo + run Start a lotus miner process + stop Stop a running lotus miner + config Output default configuration + backup Create node metadata backup + version Print version + help, h Shows a list of commands or help for one command + CHAIN: + actor manipulate the miner actor + info Print miner info + DEVELOPER: + auth Manage RPC permissions + log Manage logging + wait-api Wait for lotus api to come online + fetch-params Fetch proving parameters + MARKET: + storage-deals Manage storage deals and related configuration + retrieval-deals Manage retrieval deals and related configuration + data-transfers Manage data transfers + NETWORK: + net Manage P2P Network + RETRIEVAL: + pieces interact with the piecestore + STORAGE: + sectors interact with sector store + proving View proving information + storage manage sector storage + sealing interact with sealing pipeline + +GLOBAL OPTIONS: + --actor value, -a value specify other actor to check state for (read only) + --color (default: false) + --miner-repo value, --storagerepo value Specify miner repo path. flag(storagerepo) and env(LOTUS_STORAGE_PATH) are DEPRECATION, will REMOVE SOON (default: "~/.lotusminer") [$LOTUS_MINER_PATH, $LOTUS_STORAGE_PATH] + --help, -h show help (default: false) + --version, -v print the version (default: false) +``` + +## lotus-miner init +``` +NAME: + lotus-miner init - Initialize a lotus miner repo + +USAGE: + lotus-miner init command [command options] [arguments...] + +COMMANDS: + restore Initialize a lotus miner repo from a backup + help, h Shows a list of commands or help for one command + +OPTIONS: + --actor value specify the address of an already created miner actor + --create-worker-key create separate worker key (default: false) + --worker value, -w value worker key to use (overrides --create-worker-key) + --owner value, -o value owner key to use + --sector-size value specify sector size to use (default: "32GiB") + --pre-sealed-sectors value specify set of presealed sectors for starting as a genesis miner + --pre-sealed-metadata value specify the metadata file for the presealed sectors + --nosync don't check full-node sync status (default: false) + --symlink-imported-sectors attempt to symlink to presealed sectors instead of copying them into place (default: false) + --no-local-storage don't use storageminer repo for sector storage (default: false) + --gas-premium value set gas premium for initialization messages in AttoFIL (default: "0") + --from value select which address to send actor creation message from + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +### lotus-miner init restore +``` +NAME: + lotus-miner init restore - Initialize a lotus miner repo from a backup + +USAGE: + lotus-miner init restore [command options] [backupFile] + +OPTIONS: + --nosync don't check full-node sync status (default: false) + --config value config file (config.toml) + --storage-config value storage paths config (storage.json) + --help, -h show help (default: false) + +``` + +## lotus-miner run +``` +NAME: + lotus-miner run - Start a lotus miner process + +USAGE: + lotus-miner run [command options] [arguments...] + +OPTIONS: + --miner-api value 2345 + --enable-gpu-proving enable use of GPU for mining operations (default: true) + --nosync don't check full-node sync status (default: false) + --manage-fdlimit manage open file limit (default: true) + --help, -h show help (default: false) + +``` + +## lotus-miner stop +``` +NAME: + lotus-miner stop - Stop a running lotus miner + +USAGE: + lotus-miner stop [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +## lotus-miner config +``` +NAME: + lotus-miner config - Output default configuration + +USAGE: + lotus-miner config [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +## lotus-miner backup +``` +NAME: + lotus-miner backup - Create node metadata backup + +USAGE: + lotus-miner backup [command options] [backup file path] + +DESCRIPTION: + The backup command writes a copy of node metadata under the specified path + +Online backups: +For security reasons, the daemon must be have LOTUS_BACKUP_BASE_PATH env var set +to a path where backup files are supposed to be saved, and the path specified in +this command must be within this base path + +OPTIONS: + --offline create backup without the node running (default: false) + --help, -h show help (default: false) + +``` + +## lotus-miner version +``` +NAME: + lotus-miner version - Print version + +USAGE: + lotus-miner version [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +## lotus-miner actor +``` +NAME: + lotus-miner actor - manipulate the miner actor + +USAGE: + lotus-miner actor command [command options] [arguments...] + +COMMANDS: + set-addrs set addresses that your miner can be publicly dialed on + withdraw withdraw available balance + repay-debt pay down a miner's debt + set-peer-id set the peer id of your miner + set-owner Set owner address (this command should be invoked twice, first with the old owner as the senderAddress, and then with the new owner) + control Manage control addresses + propose-change-worker Propose a worker address change + confirm-change-worker Confirm a worker address change + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +### lotus-miner actor set-addrs +``` +NAME: + lotus-miner actor set-addrs - set addresses that your miner can be publicly dialed on + +USAGE: + lotus-miner actor set-addrs [command options] [arguments...] + +OPTIONS: + --gas-limit value set gas limit (default: 0) + --unset unset address (default: false) + --help, -h show help (default: false) + +``` + +### lotus-miner actor withdraw +``` +NAME: + lotus-miner actor withdraw - withdraw available balance + +USAGE: + lotus-miner actor withdraw [command options] [amount (FIL)] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus-miner actor repay-debt +``` +NAME: + lotus-miner actor repay-debt - pay down a miner's debt + +USAGE: + lotus-miner actor repay-debt [command options] [amount (FIL)] + +OPTIONS: + --from value optionally specify the account to send funds from + --help, -h show help (default: false) + +``` + +### lotus-miner actor set-peer-id +``` +NAME: + lotus-miner actor set-peer-id - set the peer id of your miner + +USAGE: + lotus-miner actor set-peer-id [command options] [arguments...] + +OPTIONS: + --gas-limit value set gas limit (default: 0) + --help, -h show help (default: false) + +``` + +### lotus-miner actor set-owner +``` +NAME: + lotus-miner actor set-owner - Set owner address (this command should be invoked twice, first with the old owner as the senderAddress, and then with the new owner) + +USAGE: + lotus-miner actor set-owner [command options] [newOwnerAddress senderAddress] + +OPTIONS: + --really-do-it Actually send transaction performing the action (default: false) + --help, -h show help (default: false) + +``` + +### lotus-miner actor control +``` +NAME: + lotus-miner actor control - Manage control addresses + +USAGE: + lotus-miner actor control command [command options] [arguments...] + +COMMANDS: + list Get currently set control addresses + set Set control address(-es) + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +#### lotus-miner actor control list +``` +NAME: + lotus-miner actor control list - Get currently set control addresses + +USAGE: + lotus-miner actor control list [command options] [arguments...] + +OPTIONS: + --verbose (default: false) + --color (default: true) + --help, -h show help (default: false) + +``` + +#### lotus-miner actor control set +``` +NAME: + lotus-miner actor control set - Set control address(-es) + +USAGE: + lotus-miner actor control set [command options] [...address] + +OPTIONS: + --really-do-it Actually send transaction performing the action (default: false) + --help, -h show help (default: false) + +``` + +### lotus-miner actor propose-change-worker +``` +NAME: + lotus-miner actor propose-change-worker - Propose a worker address change + +USAGE: + lotus-miner actor propose-change-worker [command options] [address] + +OPTIONS: + --really-do-it Actually send transaction performing the action (default: false) + --help, -h show help (default: false) + +``` + +### lotus-miner actor confirm-change-worker +``` +NAME: + lotus-miner actor confirm-change-worker - Confirm a worker address change + +USAGE: + lotus-miner actor confirm-change-worker [command options] [address] + +OPTIONS: + --really-do-it Actually send transaction performing the action (default: false) + --help, -h show help (default: false) + +``` + +## lotus-miner info +``` +NAME: + lotus-miner info - Print miner info + +USAGE: + lotus-miner info command [command options] [arguments...] + +COMMANDS: + all dump all related miner info + help, h Shows a list of commands or help for one command + +OPTIONS: + --hide-sectors-info hide sectors info (default: false) + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +### lotus-miner info all +``` +NAME: + lotus-miner info all - dump all related miner info + +USAGE: + lotus-miner info all [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +## lotus-miner auth +``` +NAME: + lotus-miner auth - Manage RPC permissions + +USAGE: + lotus-miner auth command [command options] [arguments...] + +COMMANDS: + create-token Create token + api-info Get token with API info required to connect to this node + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +### lotus-miner auth create-token +``` +NAME: + lotus-miner auth create-token - Create token + +USAGE: + lotus-miner auth create-token [command options] [arguments...] + +OPTIONS: + --perm value permission to assign to the token, one of: read, write, sign, admin + --help, -h show help (default: false) + +``` + +### lotus-miner auth api-info +``` +NAME: + lotus-miner auth api-info - Get token with API info required to connect to this node + +USAGE: + lotus-miner auth api-info [command options] [arguments...] + +OPTIONS: + --perm value permission to assign to the token, one of: read, write, sign, admin + --help, -h show help (default: false) + +``` + +## lotus-miner log +``` +NAME: + lotus-miner log - Manage logging + +USAGE: + lotus-miner log command [command options] [arguments...] + +COMMANDS: + list List log systems + set-level Set log level + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +### lotus-miner log list +``` +NAME: + lotus-miner log list - List log systems + +USAGE: + lotus-miner log list [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus-miner log set-level +``` +NAME: + lotus-miner log set-level - Set log level + +USAGE: + lotus-miner log set-level [command options] [level] + +DESCRIPTION: + Set the log level for logging systems: + + The system flag can be specified multiple times. + + eg) log set-level --system chain --system chainxchg debug + + Available Levels: + debug + info + warn + error + + Environment Variables: + GOLOG_LOG_LEVEL - Default log level for all log systems + GOLOG_LOG_FMT - Change output log format (json, nocolor) + GOLOG_FILE - Write logs to file + GOLOG_OUTPUT - Specify whether to output to file, stderr, stdout or a combination, i.e. file+stderr + + +OPTIONS: + --system value limit to log system + --help, -h show help (default: false) + +``` + +## lotus-miner wait-api +``` +NAME: + lotus-miner wait-api - Wait for lotus api to come online + +USAGE: + lotus-miner wait-api [command options] [arguments...] + +CATEGORY: + DEVELOPER + +OPTIONS: + --help, -h show help (default: false) + +``` + +## lotus-miner fetch-params +``` +NAME: + lotus-miner fetch-params - Fetch proving parameters + +USAGE: + lotus-miner fetch-params [command options] [sectorSize] + +CATEGORY: + DEVELOPER + +OPTIONS: + --help, -h show help (default: false) + +``` + +## lotus-miner storage-deals +``` +NAME: + lotus-miner storage-deals - Manage storage deals and related configuration + +USAGE: + lotus-miner storage-deals command [command options] [arguments...] + +COMMANDS: + import-data Manually import data for a deal + list List all deals for this miner + selection Configure acceptance criteria for storage deal proposals + set-ask Configure the miner's ask + get-ask Print the miner's ask + set-blocklist Set the miner's list of blocklisted piece CIDs + get-blocklist List the contents of the miner's piece CID blocklist + reset-blocklist Remove all entries from the miner's piece CID blocklist + set-seal-duration Set the expected time, in minutes, that you expect sealing sectors to take. Deals that start before this duration will be rejected. + pending-publish list deals waiting in publish queue + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +### lotus-miner storage-deals import-data +``` +NAME: + lotus-miner storage-deals import-data - Manually import data for a deal + +USAGE: + lotus-miner storage-deals import-data [command options] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus-miner storage-deals list +``` +NAME: + lotus-miner storage-deals list - List all deals for this miner + +USAGE: + lotus-miner storage-deals list [command options] [arguments...] + +OPTIONS: + --verbose, -v (default: false) + --watch watch deal updates in real-time, rather than a one time list (default: false) + --help, -h show help (default: false) + +``` + +### lotus-miner storage-deals selection +``` +NAME: + lotus-miner storage-deals selection - Configure acceptance criteria for storage deal proposals + +USAGE: + lotus-miner storage-deals selection command [command options] [arguments...] + +COMMANDS: + list List storage deal proposal selection criteria + reset Reset storage deal proposal selection criteria to default values + reject Configure criteria which necessitate automatic rejection + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +#### lotus-miner storage-deals selection list +``` +NAME: + lotus-miner storage-deals selection list - List storage deal proposal selection criteria + +USAGE: + lotus-miner storage-deals selection list [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +#### lotus-miner storage-deals selection reset +``` +NAME: + lotus-miner storage-deals selection reset - Reset storage deal proposal selection criteria to default values + +USAGE: + lotus-miner storage-deals selection reset [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +#### lotus-miner storage-deals selection reject +``` +NAME: + lotus-miner storage-deals selection reject - Configure criteria which necessitate automatic rejection + +USAGE: + lotus-miner storage-deals selection reject [command options] [arguments...] + +OPTIONS: + --online (default: false) + --offline (default: false) + --verified (default: false) + --unverified (default: false) + --help, -h show help (default: false) + +``` + +### lotus-miner storage-deals set-ask +``` +NAME: + lotus-miner storage-deals set-ask - Configure the miner's ask + +USAGE: + lotus-miner storage-deals set-ask [command options] [arguments...] + +OPTIONS: + --price PRICE Set the price of the ask for unverified deals (specified as FIL / GiB / Epoch) to PRICE. + --verified-price PRICE Set the price of the ask for verified deals (specified as FIL / GiB / Epoch) to PRICE + --min-piece-size SIZE Set minimum piece size (w/bit-padding, in bytes) in ask to SIZE (default: 256B) + --max-piece-size SIZE Set maximum piece size (w/bit-padding, in bytes) in ask to SIZE (default: miner sector size) + --help, -h show help (default: false) + +``` + +### lotus-miner storage-deals get-ask +``` +NAME: + lotus-miner storage-deals get-ask - Print the miner's ask + +USAGE: + lotus-miner storage-deals get-ask [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus-miner storage-deals set-blocklist +``` +NAME: + lotus-miner storage-deals set-blocklist - Set the miner's list of blocklisted piece CIDs + +USAGE: + lotus-miner storage-deals set-blocklist [command options] [ (optional, will read from stdin if omitted)] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus-miner storage-deals get-blocklist +``` +NAME: + lotus-miner storage-deals get-blocklist - List the contents of the miner's piece CID blocklist + +USAGE: + lotus-miner storage-deals get-blocklist [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus-miner storage-deals reset-blocklist +``` +NAME: + lotus-miner storage-deals reset-blocklist - Remove all entries from the miner's piece CID blocklist + +USAGE: + lotus-miner storage-deals reset-blocklist [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus-miner storage-deals set-seal-duration +``` +NAME: + lotus-miner storage-deals set-seal-duration - Set the expected time, in minutes, that you expect sealing sectors to take. Deals that start before this duration will be rejected. + +USAGE: + lotus-miner storage-deals set-seal-duration [command options] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus-miner storage-deals pending-publish +``` +NAME: + lotus-miner storage-deals pending-publish - list deals waiting in publish queue + +USAGE: + lotus-miner storage-deals pending-publish [command options] [arguments...] + +OPTIONS: + --publish-now send a publish message now (default: false) + --help, -h show help (default: false) + +``` + +## lotus-miner retrieval-deals +``` +NAME: + lotus-miner retrieval-deals - Manage retrieval deals and related configuration + +USAGE: + lotus-miner retrieval-deals command [command options] [arguments...] + +COMMANDS: + selection Configure acceptance criteria for retrieval deal proposals + list List all active retrieval deals for this miner + set-ask Configure the provider's retrieval ask + get-ask Get the provider's current retrieval ask + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +### lotus-miner retrieval-deals selection +``` +NAME: + lotus-miner retrieval-deals selection - Configure acceptance criteria for retrieval deal proposals + +USAGE: + lotus-miner retrieval-deals selection command [command options] [arguments...] + +COMMANDS: + list List retrieval deal proposal selection criteria + reset Reset retrieval deal proposal selection criteria to default values + reject Configure criteria which necessitate automatic rejection + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +#### lotus-miner retrieval-deals selection list +``` +NAME: + lotus-miner retrieval-deals selection list - List retrieval deal proposal selection criteria + +USAGE: + lotus-miner retrieval-deals selection list [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +#### lotus-miner retrieval-deals selection reset +``` +NAME: + lotus-miner retrieval-deals selection reset - Reset retrieval deal proposal selection criteria to default values + +USAGE: + lotus-miner retrieval-deals selection reset [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +#### lotus-miner retrieval-deals selection reject +``` +NAME: + lotus-miner retrieval-deals selection reject - Configure criteria which necessitate automatic rejection + +USAGE: + lotus-miner retrieval-deals selection reject [command options] [arguments...] + +OPTIONS: + --online (default: false) + --offline (default: false) + --help, -h show help (default: false) + +``` + +### lotus-miner retrieval-deals list +``` +NAME: + lotus-miner retrieval-deals list - List all active retrieval deals for this miner + +USAGE: + lotus-miner retrieval-deals list [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus-miner retrieval-deals set-ask +``` +NAME: + lotus-miner retrieval-deals set-ask - Configure the provider's retrieval ask + +USAGE: + lotus-miner retrieval-deals set-ask [command options] [arguments...] + +OPTIONS: + --price value Set the price of the ask for retrievals (FIL/GiB) + --unseal-price value Set the price to unseal + --payment-interval value Set the payment interval (in bytes) for retrieval (default: 1MiB) + --payment-interval-increase value Set the payment interval increase (in bytes) for retrieval (default: 1MiB) + --help, -h show help (default: false) + +``` + +### lotus-miner retrieval-deals get-ask +``` +NAME: + lotus-miner retrieval-deals get-ask - Get the provider's current retrieval ask + +USAGE: + lotus-miner retrieval-deals get-ask [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +## lotus-miner data-transfers +``` +NAME: + lotus-miner data-transfers - Manage data transfers + +USAGE: + lotus-miner data-transfers command [command options] [arguments...] + +COMMANDS: + list List ongoing data transfers for this miner + restart Force restart a stalled data transfer + cancel Force cancel a data transfer + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +### lotus-miner data-transfers list +``` +NAME: + lotus-miner data-transfers list - List ongoing data transfers for this miner + +USAGE: + lotus-miner data-transfers list [command options] [arguments...] + +OPTIONS: + --verbose, -v print verbose transfer details (default: false) + --color use color in display output (default: true) + --completed show completed data transfers (default: false) + --watch watch deal updates in real-time, rather than a one time list (default: false) + --show-failed show failed/cancelled transfers (default: false) + --help, -h show help (default: false) + +``` + +### lotus-miner data-transfers restart +``` +NAME: + lotus-miner data-transfers restart - Force restart a stalled data transfer + +USAGE: + lotus-miner data-transfers restart [command options] [arguments...] + +OPTIONS: + --peerid value narrow to transfer with specific peer + --initiator specify only transfers where peer is/is not initiator (default: false) + --help, -h show help (default: false) + +``` + +### lotus-miner data-transfers cancel +``` +NAME: + lotus-miner data-transfers cancel - Force cancel a data transfer + +USAGE: + lotus-miner data-transfers cancel [command options] [arguments...] + +OPTIONS: + --peerid value narrow to transfer with specific peer + --initiator specify only transfers where peer is/is not initiator (default: false) + --cancel-timeout value time to wait for cancel to be sent to client (default: 5s) + --help, -h show help (default: false) + +``` + +## lotus-miner net +``` +NAME: + lotus-miner net - Manage P2P Network + +USAGE: + lotus-miner net command [command options] [arguments...] + +COMMANDS: + peers Print peers + connect Connect to a peer + listen List listen addresses + id Get node identity + findpeer Find the addresses of a given peerID + scores Print peers' pubsub scores + reachability Print information about reachability from the internet + bandwidth Print bandwidth usage information + block Manage network connection gating rules + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +### lotus-miner net peers +``` +NAME: + lotus-miner net peers - Print peers + +USAGE: + lotus-miner net peers [command options] [arguments...] + +OPTIONS: + --agent, -a Print agent name (default: false) + --extended, -x Print extended peer information in json (default: false) + --help, -h show help (default: false) + +``` + +### lotus-miner net connect +``` +NAME: + lotus-miner net connect - Connect to a peer + +USAGE: + lotus-miner net connect [command options] [peerMultiaddr|minerActorAddress] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus-miner net listen +``` +NAME: + lotus-miner net listen - List listen addresses + +USAGE: + lotus-miner net listen [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus-miner net id +``` +NAME: + lotus-miner net id - Get node identity + +USAGE: + lotus-miner net id [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus-miner net findpeer +``` +NAME: + lotus-miner net findpeer - Find the addresses of a given peerID + +USAGE: + lotus-miner net findpeer [command options] [peerId] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus-miner net scores +``` +NAME: + lotus-miner net scores - Print peers' pubsub scores + +USAGE: + lotus-miner net scores [command options] [arguments...] + +OPTIONS: + --extended, -x print extended peer scores in json (default: false) + --help, -h show help (default: false) + +``` + +### lotus-miner net reachability +``` +NAME: + lotus-miner net reachability - Print information about reachability from the internet + +USAGE: + lotus-miner net reachability [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus-miner net bandwidth +``` +NAME: + lotus-miner net bandwidth - Print bandwidth usage information + +USAGE: + lotus-miner net bandwidth [command options] [arguments...] + +OPTIONS: + --by-peer list bandwidth usage by peer (default: false) + --by-protocol list bandwidth usage by protocol (default: false) + --help, -h show help (default: false) + +``` + +### lotus-miner net block +``` +NAME: + lotus-miner net block - Manage network connection gating rules + +USAGE: + lotus-miner net block command [command options] [arguments...] + +COMMANDS: + add Add connection gating rules + remove Remove connection gating rules + list list connection gating rules + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +#### lotus-miner net block add +``` +NAME: + lotus-miner net block add - Add connection gating rules + +USAGE: + lotus-miner net block add command [command options] [arguments...] + +COMMANDS: + peer Block a peer + ip Block an IP address + subnet Block an IP subnet + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +##### lotus-miner net block add peer +``` +NAME: + lotus-miner net block add peer - Block a peer + +USAGE: + lotus-miner net block add peer [command options] ... + +OPTIONS: + --help, -h show help (default: false) + +``` + +##### lotus-miner net block add ip +``` +NAME: + lotus-miner net block add ip - Block an IP address + +USAGE: + lotus-miner net block add ip [command options] ... + +OPTIONS: + --help, -h show help (default: false) + +``` + +##### lotus-miner net block add subnet +``` +NAME: + lotus-miner net block add subnet - Block an IP subnet + +USAGE: + lotus-miner net block add subnet [command options] ... + +OPTIONS: + --help, -h show help (default: false) + +``` + +#### lotus-miner net block remove +``` +NAME: + lotus-miner net block remove - Remove connection gating rules + +USAGE: + lotus-miner net block remove command [command options] [arguments...] + +COMMANDS: + peer Unblock a peer + ip Unblock an IP address + subnet Unblock an IP subnet + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +##### lotus-miner net block remove peer +``` +NAME: + lotus-miner net block remove peer - Unblock a peer + +USAGE: + lotus-miner net block remove peer [command options] ... + +OPTIONS: + --help, -h show help (default: false) + +``` + +##### lotus-miner net block remove ip +``` +NAME: + lotus-miner net block remove ip - Unblock an IP address + +USAGE: + lotus-miner net block remove ip [command options] ... + +OPTIONS: + --help, -h show help (default: false) + +``` + +##### lotus-miner net block remove subnet +``` +NAME: + lotus-miner net block remove subnet - Unblock an IP subnet + +USAGE: + lotus-miner net block remove subnet [command options] ... + +OPTIONS: + --help, -h show help (default: false) + +``` + +#### lotus-miner net block list +``` +NAME: + lotus-miner net block list - list connection gating rules + +USAGE: + lotus-miner net block list [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +## lotus-miner pieces +``` +NAME: + lotus-miner pieces - interact with the piecestore + +USAGE: + lotus-miner pieces command [command options] [arguments...] + +DESCRIPTION: + The piecestore is a database that tracks and manages data that is made available to the retrieval market + +COMMANDS: + list-pieces list registered pieces + list-cids list registered payload CIDs + piece-info get registered information for a given piece CID + cid-info get registered information for a given payload CID + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +### lotus-miner pieces list-pieces +``` +NAME: + lotus-miner pieces list-pieces - list registered pieces + +USAGE: + lotus-miner pieces list-pieces [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus-miner pieces list-cids +``` +NAME: + lotus-miner pieces list-cids - list registered payload CIDs + +USAGE: + lotus-miner pieces list-cids [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus-miner pieces piece-info +``` +NAME: + lotus-miner pieces piece-info - get registered information for a given piece CID + +USAGE: + lotus-miner pieces piece-info [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus-miner pieces cid-info +``` +NAME: + lotus-miner pieces cid-info - get registered information for a given payload CID + +USAGE: + lotus-miner pieces cid-info [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +## lotus-miner sectors +``` +NAME: + lotus-miner sectors - interact with sector store + +USAGE: + lotus-miner sectors command [command options] [arguments...] + +COMMANDS: + status Get the seal status of a sector by its number + list List sectors + refs List References to sectors + update-state ADVANCED: manually update the state of a sector, this may aid in error recovery + pledge store random data in a sector + extend Extend sector expiration + terminate Terminate sector on-chain then remove (WARNING: This means losing power and collateral for the removed sector) + remove Forcefully remove a sector (WARNING: This means losing power and collateral for the removed sector (use 'terminate' for lower penalty)) + mark-for-upgrade Mark a committed capacity sector for replacement by a sector with deals + seal Manually start sealing a sector (filling any unused space with junk) + set-seal-delay Set the time, in minutes, that a new sector waits for deals before sealing starts + get-cc-collateral Get the collateral required to pledge a committed capacity sector + batching manage batch sector operations + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +### lotus-miner sectors status +``` +NAME: + lotus-miner sectors status - Get the seal status of a sector by its number + +USAGE: + lotus-miner sectors status [command options] + +OPTIONS: + --log display event log (default: false) + --on-chain-info show sector on chain info (default: false) + --help, -h show help (default: false) + +``` + +### lotus-miner sectors list +``` +NAME: + lotus-miner sectors list - List sectors + +USAGE: + lotus-miner sectors list [command options] [arguments...] + +OPTIONS: + --show-removed show removed sectors (default: false) + --color, -c (default: true) + --fast don't show on-chain info for better performance (default: false) + --events display number of events the sector has received (default: false) + --seal-time display how long it took for the sector to be sealed (default: false) + --states value filter sectors by a comma-separated list of states + --help, -h show help (default: false) + +``` + +### lotus-miner sectors refs +``` +NAME: + lotus-miner sectors refs - List References to sectors + +USAGE: + lotus-miner sectors refs [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus-miner sectors update-state +``` +NAME: + lotus-miner sectors update-state - ADVANCED: manually update the state of a sector, this may aid in error recovery + +USAGE: + lotus-miner sectors update-state [command options] + +OPTIONS: + --really-do-it pass this flag if you know what you are doing (default: false) + --help, -h show help (default: false) + +``` + +### lotus-miner sectors pledge +``` +NAME: + lotus-miner sectors pledge - store random data in a sector + +USAGE: + lotus-miner sectors pledge [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus-miner sectors extend +``` +NAME: + lotus-miner sectors extend - Extend sector expiration + +USAGE: + lotus-miner sectors extend [command options] + +OPTIONS: + --new-expiration value new expiration epoch (default: 0) + --v1-sectors renews all v1 sectors up to the maximum possible lifetime (default: false) + --tolerance value when extending v1 sectors, don't try to extend sectors by fewer than this number of epochs (default: 20160) + --expiration-cutoff value when extending v1 sectors, skip sectors whose current expiration is more than epochs from now (infinity if unspecified) (default: 0) + + --help, -h show help (default: false) + +``` + +### lotus-miner sectors terminate +``` +NAME: + lotus-miner sectors terminate - Terminate sector on-chain then remove (WARNING: This means losing power and collateral for the removed sector) + +USAGE: + lotus-miner sectors terminate command [command options] + +COMMANDS: + flush Send a terminate message if there are sectors queued for termination + pending List sector numbers of sectors pending termination + help, h Shows a list of commands or help for one command + +OPTIONS: + --really-do-it pass this flag if you know what you are doing (default: false) + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +#### lotus-miner sectors terminate flush +``` +NAME: + lotus-miner sectors terminate flush - Send a terminate message if there are sectors queued for termination + +USAGE: + lotus-miner sectors terminate flush [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +#### lotus-miner sectors terminate pending +``` +NAME: + lotus-miner sectors terminate pending - List sector numbers of sectors pending termination + +USAGE: + lotus-miner sectors terminate pending [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus-miner sectors remove +``` +NAME: + lotus-miner sectors remove - Forcefully remove a sector (WARNING: This means losing power and collateral for the removed sector (use 'terminate' for lower penalty)) + +USAGE: + lotus-miner sectors remove [command options] + +OPTIONS: + --really-do-it pass this flag if you know what you are doing (default: false) + --help, -h show help (default: false) + +``` + +### lotus-miner sectors mark-for-upgrade +``` +NAME: + lotus-miner sectors mark-for-upgrade - Mark a committed capacity sector for replacement by a sector with deals + +USAGE: + lotus-miner sectors mark-for-upgrade [command options] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus-miner sectors seal +``` +NAME: + lotus-miner sectors seal - Manually start sealing a sector (filling any unused space with junk) + +USAGE: + lotus-miner sectors seal [command options] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus-miner sectors set-seal-delay +``` +NAME: + lotus-miner sectors set-seal-delay - Set the time, in minutes, that a new sector waits for deals before sealing starts + +USAGE: + lotus-miner sectors set-seal-delay [command options] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus-miner sectors get-cc-collateral +``` +NAME: + lotus-miner sectors get-cc-collateral - Get the collateral required to pledge a committed capacity sector + +USAGE: + lotus-miner sectors get-cc-collateral [command options] [arguments...] + +OPTIONS: + --expiration value the epoch when the sector will expire (default: 0) + --help, -h show help (default: false) + +``` + +### lotus-miner sectors batching +``` +NAME: + lotus-miner sectors batching - manage batch sector operations + +USAGE: + lotus-miner sectors batching command [command options] [arguments...] + +COMMANDS: + pending-commit list sectors waiting in commit batch queue + pending-precommit list sectors waiting in precommit batch queue + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +#### lotus-miner sectors batching pending-commit +``` +NAME: + lotus-miner sectors batching pending-commit - list sectors waiting in commit batch queue + +USAGE: + lotus-miner sectors batching pending-commit [command options] [arguments...] + +OPTIONS: + --publish-now send a batch now (default: false) + --help, -h show help (default: false) + +``` + +#### lotus-miner sectors batching pending-precommit +``` +NAME: + lotus-miner sectors batching pending-precommit - list sectors waiting in precommit batch queue + +USAGE: + lotus-miner sectors batching pending-precommit [command options] [arguments...] + +OPTIONS: + --publish-now send a batch now (default: false) + --help, -h show help (default: false) + +``` + +## lotus-miner proving +``` +NAME: + lotus-miner proving - View proving information + +USAGE: + lotus-miner proving command [command options] [arguments...] + +COMMANDS: + info View current state information + deadlines View the current proving period deadlines information + deadline View the current proving period deadline information by its index + faults View the currently known proving faulty sectors information + check Check sectors provable + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +### lotus-miner proving info +``` +NAME: + lotus-miner proving info - View current state information + +USAGE: + lotus-miner proving info [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus-miner proving deadlines +``` +NAME: + lotus-miner proving deadlines - View the current proving period deadlines information + +USAGE: + lotus-miner proving deadlines [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus-miner proving deadline +``` +NAME: + lotus-miner proving deadline - View the current proving period deadline information by its index + +USAGE: + lotus-miner proving deadline [command options] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus-miner proving faults +``` +NAME: + lotus-miner proving faults - View the currently known proving faulty sectors information + +USAGE: + lotus-miner proving faults [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus-miner proving check +``` +NAME: + lotus-miner proving check - Check sectors provable + +USAGE: + lotus-miner proving check [command options] + +OPTIONS: + --only-bad print only bad sectors (default: false) + --slow run slower checks (default: false) + --help, -h show help (default: false) + +``` + +## lotus-miner storage +``` +NAME: + lotus-miner storage - manage sector storage + +USAGE: + lotus-miner storage command [command options] [arguments...] + +DESCRIPTION: + Sectors can be stored across many filesystem paths. These +commands provide ways to manage the storage the miner will used to store sectors +long term for proving (references as 'store') as well as how sectors will be +stored while moving through the sealing pipeline (references as 'seal'). + +COMMANDS: + attach attach local storage path + list list local storage paths + find find sector in the storage system + cleanup trigger cleanup actions + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +### lotus-miner storage attach +``` +NAME: + lotus-miner storage attach - attach local storage path + +USAGE: + lotus-miner storage attach [command options] [arguments...] + +DESCRIPTION: + Storage can be attached to the miner using this command. The storage volume +list is stored local to the miner in $LOTUS_MINER_PATH/storage.json. We do not +recommend manually modifying this value without further understanding of the +storage system. + +Each storage volume contains a configuration file which describes the +capabilities of the volume. When the '--init' flag is provided, this file will +be created using the additional flags. + +Weight +A high weight value means data will be more likely to be stored in this path + +Seal +Data for the sealing process will be stored here + +Store +Finalized sectors that will be moved here for long term storage and be proven +over time + + +OPTIONS: + --init initialize the path first (default: false) + --weight value (for init) path weight (default: 10) + --seal (for init) use path for sealing (default: false) + --store (for init) use path for long-term storage (default: false) + --max-storage value (for init) limit storage space for sectors (expensive for very large paths!) + --help, -h show help (default: false) + +``` + +### lotus-miner storage list +``` +NAME: + lotus-miner storage list - list local storage paths + +USAGE: + lotus-miner storage list command [command options] [arguments...] + +COMMANDS: + sectors get list of all sector files + help, h Shows a list of commands or help for one command + +OPTIONS: + --color (default: false) + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +#### lotus-miner storage list sectors +``` +NAME: + lotus-miner storage list sectors - get list of all sector files + +USAGE: + lotus-miner storage list sectors [command options] [arguments...] + +OPTIONS: + --color (default: true) + --help, -h show help (default: false) + +``` + +### lotus-miner storage find +``` +NAME: + lotus-miner storage find - find sector in the storage system + +USAGE: + lotus-miner storage find [command options] [sector number] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus-miner storage cleanup +``` +NAME: + lotus-miner storage cleanup - trigger cleanup actions + +USAGE: + lotus-miner storage cleanup [command options] [arguments...] + +OPTIONS: + --removed cleanup remaining files from removed sectors (default: true) + --help, -h show help (default: false) + +``` + +## lotus-miner sealing +``` +NAME: + lotus-miner sealing - interact with sealing pipeline + +USAGE: + lotus-miner sealing command [command options] [arguments...] + +COMMANDS: + jobs list running jobs + workers list workers + sched-diag Dump internal scheduler state + abort Abort a running job + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +### lotus-miner sealing jobs +``` +NAME: + lotus-miner sealing jobs - list running jobs + +USAGE: + lotus-miner sealing jobs [command options] [arguments...] + +OPTIONS: + --color (default: false) + --show-ret-done show returned but not consumed calls (default: false) + --help, -h show help (default: false) + +``` + +### lotus-miner sealing workers +``` +NAME: + lotus-miner sealing workers - list workers + +USAGE: + lotus-miner sealing workers [command options] [arguments...] + +OPTIONS: + --color (default: false) + --help, -h show help (default: false) + +``` + +### lotus-miner sealing sched-diag +``` +NAME: + lotus-miner sealing sched-diag - Dump internal scheduler state + +USAGE: + lotus-miner sealing sched-diag [command options] [arguments...] + +OPTIONS: + --force-sched (default: false) + --help, -h show help (default: false) + +``` + +### lotus-miner sealing abort +``` +NAME: + lotus-miner sealing abort - Abort a running job + +USAGE: + lotus-miner sealing abort [command options] [callid] + +OPTIONS: + --help, -h show help (default: false) + +``` diff --git a/extern/storage-sealing/commit_batch.go b/extern/storage-sealing/commit_batch.go index e560ecba7..060a92fff 100644 --- a/extern/storage-sealing/commit_batch.go +++ b/extern/storage-sealing/commit_batch.go @@ -20,6 +20,7 @@ import ( "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/chain/actors/builtin/miner" + "github.com/filecoin-project/lotus/chain/actors/policy" "github.com/filecoin-project/lotus/extern/sector-storage/ffiwrapper" ) @@ -333,7 +334,7 @@ func (b *CommitBatcher) Stop(ctx context.Context) error { } func getSectorDeadline(curEpoch abi.ChainEpoch, si SectorInfo) time.Time { - deadlineEpoch := si.TicketEpoch + deadlineEpoch := si.TicketEpoch + policy.MaxPreCommitRandomnessLookback for _, p := range si.Pieces { if p.DealInfo == nil { continue From dd393b470faf1eea850946b1a3b42faefac4e689 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 18 May 2021 20:34:23 +0200 Subject: [PATCH 025/160] Fix aggregation inputs --- api/test/window_post.go | 2 +- extern/sector-storage/mock/mock.go | 2 +- extern/storage-sealing/commit_batch.go | 10 +++++++--- extern/storage-sealing/states_sealing.go | 2 +- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/api/test/window_post.go b/api/test/window_post.go index e508fb5c5..df520c63d 100644 --- a/api/test/window_post.go +++ b/api/test/window_post.go @@ -121,7 +121,7 @@ func TestPledgeBatching(t *testing.T, b APIBuilder, blocktime time.Duration, nSe ctx, cancel := context.WithCancel(context.Background()) defer cancel() - n, sn := b(t, []FullNodeOpts{FullNodeWithLatestActorsAt(-1)}, OneMiner) + n, sn := b(t, []FullNodeOpts{FullNodeWithLatestActorsAt(-1)}, OneMiner) client := n[0].FullNode.(*impl.FullNodeAPI) miner := sn[0] diff --git a/extern/sector-storage/mock/mock.go b/extern/sector-storage/mock/mock.go index c7844befd..bb968b474 100644 --- a/extern/sector-storage/mock/mock.go +++ b/extern/sector-storage/mock/mock.go @@ -517,7 +517,7 @@ func (m mockVerif) VerifyAggregateSeals(aggregate proof5.AggregateSealVerifyProo for i := 0; i < 32; i++ { b := svi.UnsealedCID.Bytes()[i] + svi.SealedCID.Bytes()[31-i] - svi.InteractiveRandomness[i]*svi.Randomness[i] // raw proof byte - b *= uint8(pi) // with aggregate index + b *= uint8(pi) // with aggregate index out[i] += b } } diff --git a/extern/storage-sealing/commit_batch.go b/extern/storage-sealing/commit_batch.go index 060a92fff..f086c2ad6 100644 --- a/extern/storage-sealing/commit_batch.go +++ b/extern/storage-sealing/commit_batch.go @@ -181,7 +181,6 @@ func (b *CommitBatcher) processBatch(notif, after bool) (*cid.Cid, error) { return nil, nil } - spt := b.todo[0].spt proofs := make([][]byte, 0, total) infos := make([]proof5.AggregateSealVerifyInfo, 0, total) @@ -198,9 +197,14 @@ func (b *CommitBatcher) processBatch(notif, after bool) (*cid.Cid, error) { proofs = append(proofs, b.todo[info.Number].proof) } + mid, err := address.IDFromAddress(b.maddr) + if err != nil { + return nil, xerrors.Errorf("getting miner id: %w", err) + } + params.AggregateProof, err = b.verif.AggregateSealProofs(proof5.AggregateSealVerifyProofAndInfos{ - Miner: 0, - SealProof: spt, + Miner: abi.ActorID(mid), + SealProof: b.todo[infos[0].Number].spt, AggregateProof: arp, Infos: infos, }, proofs) diff --git a/extern/storage-sealing/states_sealing.go b/extern/storage-sealing/states_sealing.go index 19eb31e64..bec9ad51b 100644 --- a/extern/storage-sealing/states_sealing.go +++ b/extern/storage-sealing/states_sealing.go @@ -3,7 +3,6 @@ package sealing import ( "bytes" "context" - "github.com/ipfs/go-cid" "golang.org/x/xerrors" @@ -590,6 +589,7 @@ func (m *Sealing) handleSubmitCommitAggregate(ctx statemachine.Context, sector S UnsealedCID: *sector.CommD, }, proof: sector.Proof, // todo: this correct?? + spt: sector.SectorType, }) if err != nil { return ctx.Send(SectorCommitFailed{xerrors.Errorf("queuing commit for aggregation failed: %w", err)}) From 5112b9fe2b0529ac62bdf7f368c71b8507fa92ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 18 May 2021 21:04:47 +0200 Subject: [PATCH 026/160] Lower default batch slack --- chain/actors/policy/policy.go | 2 +- node/config/def.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/chain/actors/policy/policy.go b/chain/actors/policy/policy.go index 113544c05..1a95c4635 100644 --- a/chain/actors/policy/policy.go +++ b/chain/actors/policy/policy.go @@ -39,7 +39,7 @@ const ( ChainFinality = miner5.ChainFinality SealRandomnessLookback = ChainFinality PaychSettleDelay = paych5.SettleDelay - MaxPreCommitRandomnessLookback = builtin5.EpochsInDay + SealRandomnessLookback + MaxPreCommitRandomnessLookback = builtin5.EpochsInDay + SealRandomnessLookback // todo fix ) // SetSupportedProofTypes sets supported proof types, across all actor versions. diff --git a/node/config/def.go b/node/config/def.go index 207419c6c..0bc4a81e8 100644 --- a/node/config/def.go +++ b/node/config/def.go @@ -258,13 +258,13 @@ func DefaultStorageMiner() *StorageMiner { MinPreCommitBatch: 1, // we must have at least one proof to aggregate MaxPreCommitBatch: 204, // todo max? PreCommitBatchWait: Duration(24 * time.Hour), // this can be up to 6 days - PreCommitBatchSlack: Duration(8 * time.Hour), + PreCommitBatchSlack: Duration(3 * time.Hour), AggregateCommits: true, MinCommitBatch: 1, // we must have at least one proof to aggregate MaxCommitBatch: 204, // this is the maximum aggregation per FIP13 CommitBatchWait: Duration(24 * time.Hour), // this can be up to 6 days - CommitBatchSlack: Duration(8 * time.Hour), + CommitBatchSlack: Duration(1 * time.Hour), TerminateBatchMin: 1, TerminateBatchMax: 100, From 2a0c0e379b9adc35fbde1a903267b25aff81b159 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 19 May 2021 14:32:41 +0200 Subject: [PATCH 027/160] Working default batching config --- node/config/def.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/node/config/def.go b/node/config/def.go index 0bc4a81e8..c0372a1e3 100644 --- a/node/config/def.go +++ b/node/config/def.go @@ -6,6 +6,8 @@ import ( "github.com/ipfs/go-cid" + miner5 "github.com/filecoin-project/specs-actors/v5/actors/builtin/miner" + "github.com/filecoin-project/lotus/chain/types" sectorstorage "github.com/filecoin-project/lotus/extern/sector-storage" ) @@ -256,13 +258,13 @@ func DefaultStorageMiner() *StorageMiner { BatchPreCommits: true, MinPreCommitBatch: 1, // we must have at least one proof to aggregate - MaxPreCommitBatch: 204, // todo max? + MaxPreCommitBatch: miner5.PreCommitSectorBatchMaxSize, // PreCommitBatchWait: Duration(24 * time.Hour), // this can be up to 6 days PreCommitBatchSlack: Duration(3 * time.Hour), AggregateCommits: true, MinCommitBatch: 1, // we must have at least one proof to aggregate - MaxCommitBatch: 204, // this is the maximum aggregation per FIP13 + MaxCommitBatch: miner5.MaxAggregatedSectors, // this is the maximum aggregation per FIP13 CommitBatchWait: Duration(24 * time.Hour), // this can be up to 6 days CommitBatchSlack: Duration(1 * time.Hour), From eafaf6d23620d4dd28780446a6ab6afdb07a9b1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 19 May 2021 14:33:15 +0200 Subject: [PATCH 028/160] Don't block on batching in node tests --- api/test/window_post.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/api/test/window_post.go b/api/test/window_post.go index df520c63d..767aff4d6 100644 --- a/api/test/window_post.go +++ b/api/test/window_post.go @@ -234,10 +234,16 @@ func pledgeSectors(t *testing.T, ctx context.Context, miner TestStorageNode, n, } for len(toCheck) > 0 { + pcb, err := miner.SectorPreCommitFlush(ctx) + require.NoError(t, err) + if pcb != nil { + fmt.Printf("PRECOMMIT BATCH: %s\n", *pcb) + } + cb, err := miner.SectorCommitFlush(ctx) require.NoError(t, err) if cb != nil { - fmt.Printf("BATCH: %s\n", *cb) + fmt.Printf("COMMIT BATCH: %s\n", *cb) } for n := range toCheck { From a5677d1b7a4099bd39bd17bd8a80aea98da207bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 19 May 2021 15:20:23 +0200 Subject: [PATCH 029/160] ffiwrapper: Separate Prover interface --- chain/gen/gen.go | 4 ---- cmd/lotus-bench/caching_verifier.go | 4 ---- .../sector-storage/ffiwrapper/prover_cgo.go | 18 +++++++++++++++++ extern/sector-storage/ffiwrapper/types.go | 6 +++++- .../sector-storage/ffiwrapper/verifier_cgo.go | 4 ---- extern/sector-storage/mock/mock.go | 20 ++++++++++--------- extern/storage-sealing/commit_batch.go | 8 ++++---- extern/storage-sealing/sealing.go | 4 ++-- node/builder.go | 1 + node/modules/storageminer.go | 4 +++- node/test/builder.go | 3 +++ storage/miner.go | 6 ++++-- 12 files changed, 51 insertions(+), 31 deletions(-) create mode 100644 extern/sector-storage/ffiwrapper/prover_cgo.go diff --git a/chain/gen/gen.go b/chain/gen/gen.go index b9173d781..27feaeaaa 100644 --- a/chain/gen/gen.go +++ b/chain/gen/gen.go @@ -693,10 +693,6 @@ func (m genFakeVerifier) VerifyAggregateSeals(aggregate proof5.AggregateSealVeri panic("not supported") } -func (m genFakeVerifier) AggregateSealProofs(ai proof5.AggregateSealVerifyProofAndInfos, proofs [][]byte) ([]byte, error) { - panic("not supported") -} - func (m genFakeVerifier) VerifyWinningPoSt(ctx context.Context, info proof5.WinningPoStVerifyInfo) (bool, error) { panic("not supported") } diff --git a/cmd/lotus-bench/caching_verifier.go b/cmd/lotus-bench/caching_verifier.go index 55786c585..f4cc0f837 100644 --- a/cmd/lotus-bench/caching_verifier.go +++ b/cmd/lotus-bench/caching_verifier.go @@ -101,8 +101,4 @@ func (cv cachingVerifier) VerifyAggregateSeals(aggregate proof5.AggregateSealVer return cv.backend.VerifyAggregateSeals(aggregate) } -func (cv cachingVerifier) AggregateSealProofs(proofType abi.RegisteredSealProof, rap abi.RegisteredAggregationProof, proofs [][]byte) ([]byte, error) { - return cv.backend.AggregateSealProofs(proofType, rap, proofs) -} - var _ ffiwrapper.Verifier = (*cachingVerifier)(nil) diff --git a/extern/sector-storage/ffiwrapper/prover_cgo.go b/extern/sector-storage/ffiwrapper/prover_cgo.go new file mode 100644 index 000000000..3ad73c81c --- /dev/null +++ b/extern/sector-storage/ffiwrapper/prover_cgo.go @@ -0,0 +1,18 @@ +//+build cgo + +package ffiwrapper + +import ( + ffi "github.com/filecoin-project/filecoin-ffi" + proof5 "github.com/filecoin-project/specs-actors/v5/actors/runtime/proof" +) + +var ProofProver = proofProver{} + +var _ Prover = ProofProver + +type proofProver struct{} + +func (v proofProver) AggregateSealProofs(aggregateInfo proof5.AggregateSealVerifyProofAndInfos, proofs [][]byte) ([]byte, error) { + return ffi.AggregateSealProofs(aggregateInfo, proofs) +} diff --git a/extern/sector-storage/ffiwrapper/types.go b/extern/sector-storage/ffiwrapper/types.go index 99efa7521..a5b2fdf1f 100644 --- a/extern/sector-storage/ffiwrapper/types.go +++ b/extern/sector-storage/ffiwrapper/types.go @@ -40,8 +40,12 @@ type Verifier interface { VerifyWindowPoSt(ctx context.Context, info proof5.WindowPoStVerifyInfo) (bool, error) GenerateWinningPoStSectorChallenge(context.Context, abi.RegisteredPoStProof, abi.ActorID, abi.PoStRandomness, uint64) ([]uint64, error) +} + +// Prover contains cheap proving-related methods +type Prover interface { + // TODO: move GenerateWinningPoStSectorChallenge from the Verifier interface to here - // cheap, makes no sense to put this on the storage interface AggregateSealProofs(aggregateInfo proof5.AggregateSealVerifyProofAndInfos, proofs [][]byte) ([]byte, error) } diff --git a/extern/sector-storage/ffiwrapper/verifier_cgo.go b/extern/sector-storage/ffiwrapper/verifier_cgo.go index 650155305..95724bb7c 100644 --- a/extern/sector-storage/ffiwrapper/verifier_cgo.go +++ b/extern/sector-storage/ffiwrapper/verifier_cgo.go @@ -139,7 +139,3 @@ func (proofVerifier) GenerateWinningPoStSectorChallenge(ctx context.Context, pro randomness[31] &= 0x3f return ffi.GenerateWinningPoStSectorChallenge(proofType, minerID, randomness, eligibleSectorCount) } - -func (v proofVerifier) AggregateSealProofs(aggregateInfo proof5.AggregateSealVerifyProofAndInfos, proofs [][]byte) ([]byte, error) { - return ffi.AggregateSealProofs(aggregateInfo, proofs) -} diff --git a/extern/sector-storage/mock/mock.go b/extern/sector-storage/mock/mock.go index bb968b474..bdd9e14cd 100644 --- a/extern/sector-storage/mock/mock.go +++ b/extern/sector-storage/mock/mock.go @@ -34,7 +34,7 @@ type SectorMgr struct { lk sync.Mutex } -type mockVerif struct{} +type mockVerifProver struct{} func NewMockSectorMgr(genesisSectors []abi.SectorID) *SectorMgr { sectors := make(map[abi.SectorID]*sectorState) @@ -490,7 +490,7 @@ func (mgr *SectorMgr) ReturnFetch(ctx context.Context, callID storiface.CallID, panic("not supported") } -func (m mockVerif) VerifySeal(svi proof5.SealVerifyInfo) (bool, error) { +func (m mockVerifProver) VerifySeal(svi proof5.SealVerifyInfo) (bool, error) { plen, err := svi.SealProof.ProofSize() if err != nil { return false, err @@ -511,7 +511,7 @@ func (m mockVerif) VerifySeal(svi proof5.SealVerifyInfo) (bool, error) { return true, nil } -func (m mockVerif) VerifyAggregateSeals(aggregate proof5.AggregateSealVerifyProofAndInfos) (bool, error) { +func (m mockVerifProver) VerifyAggregateSeals(aggregate proof5.AggregateSealVerifyProofAndInfos) (bool, error) { out := make([]byte, 200) for pi, svi := range aggregate.Infos { for i := 0; i < 32; i++ { @@ -531,7 +531,7 @@ func (m mockVerif) VerifyAggregateSeals(aggregate proof5.AggregateSealVerifyProo return bytes.Equal(aggregate.Proof, out), nil } -func (m mockVerif) AggregateSealProofs(aggregateInfo proof5.AggregateSealVerifyProofAndInfos, proofs [][]byte) ([]byte, error) { +func (m mockVerifProver) AggregateSealProofs(aggregateInfo proof5.AggregateSealVerifyProofAndInfos, proofs [][]byte) ([]byte, error) { out := make([]byte, 200) // todo: figure out more real length for pi, proof := range proofs { for i := range proof[:32] { @@ -548,12 +548,12 @@ func (m mockVerif) AggregateSealProofs(aggregateInfo proof5.AggregateSealVerifyP return out, nil } -func (m mockVerif) VerifyWinningPoSt(ctx context.Context, info proof5.WinningPoStVerifyInfo) (bool, error) { +func (m mockVerifProver) VerifyWinningPoSt(ctx context.Context, info proof5.WinningPoStVerifyInfo) (bool, error) { info.Randomness[31] &= 0x3f return true, nil } -func (m mockVerif) VerifyWindowPoSt(ctx context.Context, info proof5.WindowPoStVerifyInfo) (bool, error) { +func (m mockVerifProver) VerifyWindowPoSt(ctx context.Context, info proof5.WindowPoStVerifyInfo) (bool, error) { if len(info.Proofs) != 1 { return false, xerrors.Errorf("expected 1 proof entry") } @@ -567,15 +567,17 @@ func (m mockVerif) VerifyWindowPoSt(ctx context.Context, info proof5.WindowPoStV return true, nil } -func (m mockVerif) GenerateDataCommitment(pt abi.RegisteredSealProof, pieces []abi.PieceInfo) (cid.Cid, error) { +func (m mockVerifProver) GenerateDataCommitment(pt abi.RegisteredSealProof, pieces []abi.PieceInfo) (cid.Cid, error) { return ffiwrapper.GenerateUnsealedCID(pt, pieces) } -func (m mockVerif) GenerateWinningPoStSectorChallenge(ctx context.Context, proofType abi.RegisteredPoStProof, minerID abi.ActorID, randomness abi.PoStRandomness, eligibleSectorCount uint64) ([]uint64, error) { +func (m mockVerifProver) GenerateWinningPoStSectorChallenge(ctx context.Context, proofType abi.RegisteredPoStProof, minerID abi.ActorID, randomness abi.PoStRandomness, eligibleSectorCount uint64) ([]uint64, error) { return []uint64{0}, nil } -var MockVerifier = mockVerif{} +var MockVerifier = mockVerifProver{} +var MockProver = mockVerifProver{} var _ storage.Sealer = &SectorMgr{} var _ ffiwrapper.Verifier = MockVerifier +var _ ffiwrapper.Prover = MockProver diff --git a/extern/storage-sealing/commit_batch.go b/extern/storage-sealing/commit_batch.go index f086c2ad6..72a56e797 100644 --- a/extern/storage-sealing/commit_batch.go +++ b/extern/storage-sealing/commit_batch.go @@ -46,7 +46,7 @@ type CommitBatcher struct { addrSel AddrSel feeCfg FeeConfig getConfig GetSealingConfigFunc - verif ffiwrapper.Verifier + prover ffiwrapper.Prover deadlines map[abi.SectorNumber]time.Time todo map[abi.SectorNumber]AggregateInput @@ -57,7 +57,7 @@ type CommitBatcher struct { lk sync.Mutex } -func NewCommitBatcher(mctx context.Context, maddr address.Address, api CommitBatcherApi, addrSel AddrSel, feeCfg FeeConfig, getConfig GetSealingConfigFunc, verif ffiwrapper.Verifier) *CommitBatcher { +func NewCommitBatcher(mctx context.Context, maddr address.Address, api CommitBatcherApi, addrSel AddrSel, feeCfg FeeConfig, getConfig GetSealingConfigFunc, prov ffiwrapper.Prover) *CommitBatcher { b := &CommitBatcher{ api: api, maddr: maddr, @@ -65,7 +65,7 @@ func NewCommitBatcher(mctx context.Context, maddr address.Address, api CommitBat addrSel: addrSel, feeCfg: feeCfg, getConfig: getConfig, - verif: verif, + prover: prov, deadlines: map[abi.SectorNumber]time.Time{}, todo: map[abi.SectorNumber]AggregateInput{}, @@ -202,7 +202,7 @@ func (b *CommitBatcher) processBatch(notif, after bool) (*cid.Cid, error) { return nil, xerrors.Errorf("getting miner id: %w", err) } - params.AggregateProof, err = b.verif.AggregateSealProofs(proof5.AggregateSealVerifyProofAndInfos{ + params.AggregateProof, err = b.prover.AggregateSealProofs(proof5.AggregateSealVerifyProofAndInfos{ Miner: abi.ActorID(mid), SealProof: b.todo[infos[0].Number].spt, AggregateProof: arp, diff --git a/extern/storage-sealing/sealing.go b/extern/storage-sealing/sealing.go index ede281e39..fc452cc6f 100644 --- a/extern/storage-sealing/sealing.go +++ b/extern/storage-sealing/sealing.go @@ -132,7 +132,7 @@ type pendingPiece struct { accepted func(abi.SectorNumber, abi.UnpaddedPieceSize, error) } -func New(api SealingAPI, fc FeeConfig, events Events, maddr address.Address, ds datastore.Batching, sealer sectorstorage.SectorManager, sc SectorIDCounter, verif ffiwrapper.Verifier, pcp PreCommitPolicy, gc GetSealingConfigFunc, notifee SectorStateNotifee, as AddrSel) *Sealing { +func New(api SealingAPI, fc FeeConfig, events Events, maddr address.Address, ds datastore.Batching, sealer sectorstorage.SectorManager, sc SectorIDCounter, verif ffiwrapper.Verifier, prov ffiwrapper.Prover, pcp PreCommitPolicy, gc GetSealingConfigFunc, notifee SectorStateNotifee, as AddrSel) *Sealing { s := &Sealing{ api: api, feeCfg: fc, @@ -155,7 +155,7 @@ func New(api SealingAPI, fc FeeConfig, events Events, maddr address.Address, ds terminator: NewTerminationBatcher(context.TODO(), maddr, api, as, fc, gc), precommiter: NewPreCommitBatcher(context.TODO(), maddr, api, as, fc, gc), - commiter: NewCommitBatcher(context.TODO(), maddr, api, as, fc, gc, verif), + commiter: NewCommitBatcher(context.TODO(), maddr, api, as, fc, gc, prov), getConfig: gc, dealInfo: &CurrentDealInfoManager{api}, diff --git a/node/builder.go b/node/builder.go index c884b169b..ce00fc18d 100644 --- a/node/builder.go +++ b/node/builder.go @@ -379,6 +379,7 @@ var MinerNode = Options( // Sector storage: Proofs Override(new(ffiwrapper.Verifier), ffiwrapper.ProofVerifier), + Override(new(ffiwrapper.Prover), ffiwrapper.ProofProver), Override(new(storage2.Prover), From(new(sectorstorage.SectorManager))), // Sealing diff --git a/node/modules/storageminer.go b/node/modules/storageminer.go index 8a9a99175..122bec519 100644 --- a/node/modules/storageminer.go +++ b/node/modules/storageminer.go @@ -202,6 +202,7 @@ type StorageMinerParams struct { Sealer sectorstorage.SectorManager SectorIDCounter sealing.SectorIDCounter Verifier ffiwrapper.Verifier + Prover ffiwrapper.Prover GetSealingConfigFn dtypes.GetSealingConfigFunc Journal journal.Journal AddrSel *storage.AddressSelector @@ -218,6 +219,7 @@ func StorageMiner(fc config.MinerFeeConfig) func(params StorageMinerParams) (*st h = params.Host sc = params.SectorIDCounter verif = params.Verifier + prover = params.Prover gsd = params.GetSealingConfigFn j = params.Journal as = params.AddrSel @@ -235,7 +237,7 @@ func StorageMiner(fc config.MinerFeeConfig) func(params StorageMinerParams) (*st return nil, err } - sm, err := storage.NewMiner(api, maddr, h, ds, sealer, sc, verif, gsd, fc, j, as) + sm, err := storage.NewMiner(api, maddr, h, ds, sealer, sc, verif, prover, gsd, fc, j, as) if err != nil { return nil, err } diff --git a/node/test/builder.go b/node/test/builder.go index cd0ecc55b..174f07592 100644 --- a/node/test/builder.go +++ b/node/test/builder.go @@ -463,6 +463,7 @@ func mockSbBuilderOpts(t *testing.T, fullOpts []test.FullNodeOpts, storage []tes node.Test(), node.Override(new(ffiwrapper.Verifier), mock.MockVerifier), + node.Override(new(ffiwrapper.Prover), mock.MockProver), // so that we subscribe to pubsub topics immediately node.Override(new(dtypes.Bootstrapper), dtypes.Bootstrapper(true)), @@ -486,6 +487,7 @@ func mockSbBuilderOpts(t *testing.T, fullOpts []test.FullNodeOpts, storage []tes return mock.NewMockSectorMgr(nil), nil }), node.Override(new(ffiwrapper.Verifier), mock.MockVerifier), + node.Override(new(ffiwrapper.Prover), mock.MockProver), node.Unset(new(*sectorstorage.Manager)), )) } @@ -524,6 +526,7 @@ func mockSbBuilderOpts(t *testing.T, fullOpts []test.FullNodeOpts, storage []tes return mock.NewMockSectorMgr(sectors), nil }), node.Override(new(ffiwrapper.Verifier), mock.MockVerifier), + node.Override(new(ffiwrapper.Prover), mock.MockProver), node.Unset(new(*sectorstorage.Manager)), opts, )) diff --git a/storage/miner.go b/storage/miner.go index 52be4d7b8..1c1a9a0bc 100644 --- a/storage/miner.go +++ b/storage/miner.go @@ -48,6 +48,7 @@ type Miner struct { ds datastore.Batching sc sealing.SectorIDCounter verif ffiwrapper.Verifier + prover ffiwrapper.Prover addrSel *AddressSelector maddr address.Address @@ -116,7 +117,7 @@ type storageMinerApi interface { WalletHas(context.Context, address.Address) (bool, error) } -func NewMiner(api storageMinerApi, maddr address.Address, h host.Host, ds datastore.Batching, sealer sectorstorage.SectorManager, sc sealing.SectorIDCounter, verif ffiwrapper.Verifier, gsd dtypes.GetSealingConfigFunc, feeCfg config.MinerFeeConfig, journal journal.Journal, as *AddressSelector) (*Miner, error) { +func NewMiner(api storageMinerApi, maddr address.Address, h host.Host, ds datastore.Batching, sealer sectorstorage.SectorManager, sc sealing.SectorIDCounter, verif ffiwrapper.Verifier, prover ffiwrapper.Prover, gsd dtypes.GetSealingConfigFunc, feeCfg config.MinerFeeConfig, journal journal.Journal, as *AddressSelector) (*Miner, error) { m := &Miner{ api: api, feeCfg: feeCfg, @@ -125,6 +126,7 @@ func NewMiner(api storageMinerApi, maddr address.Address, h host.Host, ds datast ds: ds, sc: sc, verif: verif, + prover: prover, addrSel: as, maddr: maddr, @@ -161,7 +163,7 @@ func (m *Miner) Run(ctx context.Context) error { return m.addrSel.AddressFor(ctx, m.api, mi, use, goodFunds, minFunds) } - m.sealing = sealing.New(adaptedAPI, fc, NewEventsAdapter(evts), m.maddr, m.ds, m.sealer, m.sc, m.verif, &pcp, sealing.GetSealingConfigFunc(m.getSealConfig), m.handleSealingNotifications, as) + m.sealing = sealing.New(adaptedAPI, fc, NewEventsAdapter(evts), m.maddr, m.ds, m.sealer, m.sc, m.verif, m.prover, &pcp, sealing.GetSealingConfigFunc(m.getSealConfig), m.handleSealingNotifications, as) go m.sealing.Run(ctx) //nolint:errcheck // logged intside the function From 2a1b359edec34da00ef748c435e5118c5c871626 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 19 May 2021 15:20:34 +0200 Subject: [PATCH 030/160] config: fmt --- node/config/def.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/node/config/def.go b/node/config/def.go index c0372a1e3..c0aa82a59 100644 --- a/node/config/def.go +++ b/node/config/def.go @@ -257,15 +257,15 @@ func DefaultStorageMiner() *StorageMiner { AlwaysKeepUnsealedCopy: true, BatchPreCommits: true, - MinPreCommitBatch: 1, // we must have at least one proof to aggregate - MaxPreCommitBatch: miner5.PreCommitSectorBatchMaxSize, // - PreCommitBatchWait: Duration(24 * time.Hour), // this can be up to 6 days + MinPreCommitBatch: 1, // we must have at least one proof to aggregate + MaxPreCommitBatch: miner5.PreCommitSectorBatchMaxSize, // + PreCommitBatchWait: Duration(24 * time.Hour), // this can be up to 6 days PreCommitBatchSlack: Duration(3 * time.Hour), AggregateCommits: true, - MinCommitBatch: 1, // we must have at least one proof to aggregate - MaxCommitBatch: miner5.MaxAggregatedSectors, // this is the maximum aggregation per FIP13 - CommitBatchWait: Duration(24 * time.Hour), // this can be up to 6 days + MinCommitBatch: 1, // we must have at least one proof to aggregate + MaxCommitBatch: miner5.MaxAggregatedSectors, // this is the maximum aggregation per FIP13 + CommitBatchWait: Duration(24 * time.Hour), // this can be up to 6 days CommitBatchSlack: Duration(1 * time.Hour), TerminateBatchMin: 1, From e088c71b9a3473996ddd6b52746d90b76b8f8523 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 19 May 2021 20:07:20 +0200 Subject: [PATCH 031/160] marketadapter: Handle batch sealing messages --- api/test/deals.go | 2 + api/test/window_post.go | 26 +++--- .../sector-storage/ffiwrapper/sealer_test.go | 4 +- .../storageadapter/ondealsectorcommitted.go | 79 ++++++++++++++----- 4 files changed, 79 insertions(+), 32 deletions(-) diff --git a/api/test/deals.go b/api/test/deals.go index 7a9454bae..e3432ff0d 100644 --- a/api/test/deals.go +++ b/api/test/deals.go @@ -435,6 +435,8 @@ func startSealingWaiting(t *testing.T, ctx context.Context, miner TestStorageNod require.NoError(t, miner.SectorStartSealing(ctx, snum)) } } + + flushSealingBatches(t, ctx, miner) } func testRetrieval(t *testing.T, ctx context.Context, client api.FullNode, fcid cid.Cid, piece *cid.Cid, carExport bool, data []byte) { diff --git a/api/test/window_post.go b/api/test/window_post.go index 767aff4d6..48fe3fd6c 100644 --- a/api/test/window_post.go +++ b/api/test/window_post.go @@ -201,6 +201,20 @@ func TestPledgeSector(t *testing.T, b APIBuilder, blocktime time.Duration, nSect <-done } +func flushSealingBatches(t *testing.T, ctx context.Context, miner TestStorageNode) { + pcb, err := miner.SectorPreCommitFlush(ctx) + require.NoError(t, err) + if pcb != nil { + fmt.Printf("PRECOMMIT BATCH: %s\n", *pcb) + } + + cb, err := miner.SectorCommitFlush(ctx) + require.NoError(t, err) + if cb != nil { + fmt.Printf("COMMIT BATCH: %s\n", *cb) + } +} + func pledgeSectors(t *testing.T, ctx context.Context, miner TestStorageNode, n, existing int, blockNotif <-chan struct{}) { for i := 0; i < n; i++ { if i%3 == 0 && blockNotif != nil { @@ -234,17 +248,7 @@ func pledgeSectors(t *testing.T, ctx context.Context, miner TestStorageNode, n, } for len(toCheck) > 0 { - pcb, err := miner.SectorPreCommitFlush(ctx) - require.NoError(t, err) - if pcb != nil { - fmt.Printf("PRECOMMIT BATCH: %s\n", *pcb) - } - - cb, err := miner.SectorCommitFlush(ctx) - require.NoError(t, err) - if cb != nil { - fmt.Printf("COMMIT BATCH: %s\n", *cb) - } + flushSealingBatches(t, ctx, miner) for n := range toCheck { st, err := miner.SectorsStatus(ctx, n, false) diff --git a/extern/sector-storage/ffiwrapper/sealer_test.go b/extern/sector-storage/ffiwrapper/sealer_test.go index 172641bf7..df657f097 100644 --- a/extern/sector-storage/ffiwrapper/sealer_test.go +++ b/extern/sector-storage/ffiwrapper/sealer_test.go @@ -542,12 +542,12 @@ func TestSealAndVerifyAggregate(t *testing.T) { aggStart := time.Now() - avi.Proof, err = ProofVerifier.AggregateSealProofs(avi, toAggregate) + avi.Proof, err = ProofProver.AggregateSealProofs(avi, toAggregate) require.NoError(t, err) aggDone := time.Now() - _, err = ProofVerifier.AggregateSealProofs(avi, toAggregate) + _, err = ProofProver.AggregateSealProofs(avi, toAggregate) require.NoError(t, err) aggHot := time.Now() diff --git a/markets/storageadapter/ondealsectorcommitted.go b/markets/storageadapter/ondealsectorcommitted.go index b5f9c7510..f9ae201b3 100644 --- a/markets/storageadapter/ondealsectorcommitted.go +++ b/markets/storageadapter/ondealsectorcommitted.go @@ -12,6 +12,7 @@ import ( "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-fil-markets/storagemarket" "github.com/filecoin-project/go-state-types/abi" + miner5 "github.com/filecoin-project/specs-actors/v5/actors/builtin/miner" "github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/chain/actors/builtin/market" @@ -109,7 +110,7 @@ func (mgr *SectorCommittedManager) OnDealSectorPreCommitted(ctx context.Context, // Watch for a pre-commit message to the provider. matchEvent := func(msg *types.Message) (bool, error) { - matched := msg.To == provider && msg.Method == miner.Methods.PreCommitSector + matched := msg.To == provider && (msg.Method == miner.Methods.PreCommitSector || msg.Method == miner.Methods.PreCommitSectorBatch) return matched, nil } @@ -137,12 +138,6 @@ func (mgr *SectorCommittedManager) OnDealSectorPreCommitted(ctx context.Context, return true, nil } - // Extract the message parameters - var params miner.SectorPreCommitInfo - if err := params.UnmarshalCBOR(bytes.NewReader(msg.Params)); err != nil { - return false, xerrors.Errorf("unmarshal pre commit: %w", err) - } - // When there is a reorg, the deal ID may change, so get the // current deal ID from the publish message CID res, err := mgr.dealInfo.GetCurrentDealInfo(ctx, ts.Key().Bytes(), &proposal, publishCid) @@ -150,13 +145,40 @@ func (mgr *SectorCommittedManager) OnDealSectorPreCommitted(ctx context.Context, return false, err } - // Check through the deal IDs associated with this message - for _, did := range params.DealIDs { - if did == res.DealID { - // Found the deal ID in this message. Callback with the sector ID. - cb(params.SectorNumber, false, nil) - return false, nil + // Extract the message parameters + switch msg.Method { + case miner.Methods.PreCommitSector: + var params miner.SectorPreCommitInfo + if err := params.UnmarshalCBOR(bytes.NewReader(msg.Params)); err != nil { + return false, xerrors.Errorf("unmarshal pre commit: %w", err) } + + // Check through the deal IDs associated with this message + for _, did := range params.DealIDs { + if did == res.DealID { + // Found the deal ID in this message. Callback with the sector ID. + cb(params.SectorNumber, false, nil) + return false, nil + } + } + case miner.Methods.PreCommitSectorBatch: + var params miner5.PreCommitSectorBatchParams + if err := params.UnmarshalCBOR(bytes.NewReader(msg.Params)); err != nil { + return false, xerrors.Errorf("unmarshal pre commit: %w", err) + } + + for _, precommit := range params.Sectors { + // Check through the deal IDs associated with this message + for _, did := range precommit.DealIDs { + if did == res.DealID { + // Found the deal ID in this message. Callback with the sector ID. + cb(precommit.SectorNumber, false, nil) + return false, nil + } + } + } + default: + return false, xerrors.Errorf("unexpected method %d", msg.Method) } // Didn't find the deal ID in this message, so keep looking @@ -207,16 +229,35 @@ func (mgr *SectorCommittedManager) OnDealSectorCommitted(ctx context.Context, pr // Match a prove-commit sent to the provider with the given sector number matchEvent := func(msg *types.Message) (matched bool, err error) { - if msg.To != provider || msg.Method != miner.Methods.ProveCommitSector { + if msg.To != provider { return false, nil } - var params miner.ProveCommitSectorParams - if err := params.UnmarshalCBOR(bytes.NewReader(msg.Params)); err != nil { - return false, xerrors.Errorf("failed to unmarshal prove commit sector params: %w", err) - } + switch msg.Method { + case miner.Methods.ProveCommitSector: + var params miner.ProveCommitSectorParams + if err := params.UnmarshalCBOR(bytes.NewReader(msg.Params)); err != nil { + return false, xerrors.Errorf("failed to unmarshal prove commit sector params: %w", err) + } - return params.SectorNumber == sectorNumber, nil + return params.SectorNumber == sectorNumber, nil + + case miner.Methods.ProveCommitAggregate: + var params miner5.ProveCommitAggregateParams + if err := params.UnmarshalCBOR(bytes.NewReader(msg.Params)); err != nil { + return false, xerrors.Errorf("failed to unmarshal prove commit sector params: %w", err) + } + + set, err := params.SectorNumbers.IsSet(uint64(sectorNumber)) + if err != nil { + return false, xerrors.Errorf("checking if sectorNumber is set in commit aggregate message: %w", err) + } + + return set, nil + + default: + return false, nil + } } // The deal must be accepted by the deal proposal start epoch, so timeout From 51139361c73d94b936a126cba0bc6e90bf998eb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 19 May 2021 20:34:50 +0200 Subject: [PATCH 032/160] sealing: Handle full batches correctly --- extern/storage-sealing/commit_batch.go | 7 ++++++- extern/storage-sealing/precommit_batch.go | 5 +++++ extern/storage-sealing/terminate_batch.go | 2 +- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/extern/storage-sealing/commit_batch.go b/extern/storage-sealing/commit_batch.go index 72a56e797..f1cbb15ad 100644 --- a/extern/storage-sealing/commit_batch.go +++ b/extern/storage-sealing/commit_batch.go @@ -194,6 +194,11 @@ func (b *CommitBatcher) processBatch(notif, after bool) (*cid.Cid, error) { }) for _, info := range infos { + if len(infos) >= cfg.MaxCommitBatch { + log.Infow("commit batch full") + break + } + proofs = append(proofs, b.todo[info.Number].proof) } @@ -232,7 +237,7 @@ func (b *CommitBatcher) processBatch(notif, after bool) (*cid.Cid, error) { return nil, xerrors.Errorf("sending message failed: %w", err) } - log.Infow("Sent ProveCommitAggregate message", "cid", mcid, "from", from, "sectors", total) + log.Infow("Sent ProveCommitAggregate message", "cid", mcid, "from", from, "todo", total, "sectors", len(infos)) err = params.SectorNumbers.ForEach(func(us uint64) error { sn := abi.SectorNumber(us) diff --git a/extern/storage-sealing/precommit_batch.go b/extern/storage-sealing/precommit_batch.go index 4430fdda9..f16e5c5ee 100644 --- a/extern/storage-sealing/precommit_batch.go +++ b/extern/storage-sealing/precommit_batch.go @@ -172,6 +172,11 @@ func (b *PreCommitBatcher) processBatch(notif, after bool) (*cid.Cid, error) { deposit := big.Zero() for _, p := range b.todo { + if len(params.Sectors) >= cfg.MaxPreCommitBatch { + log.Infow("precommit batch full") + break + } + params.Sectors = append(params.Sectors, p.pci) deposit = big.Add(deposit, p.deposit) } diff --git a/extern/storage-sealing/terminate_batch.go b/extern/storage-sealing/terminate_batch.go index 2bb2dc76a..d545f443f 100644 --- a/extern/storage-sealing/terminate_batch.go +++ b/extern/storage-sealing/terminate_batch.go @@ -183,7 +183,7 @@ func (b *TerminateBatcher) processBatch(notif, after bool) (*cid.Cid, error) { Sectors: toTerminate, }) - if total >= uint64(miner.AddressedSectorsMax) { + if total >= uint64(miner.AddressedSectorsMax) || total >= cfg.TerminateBatchMax { break } From 44bf9bf903a71e590e92182d2f1201957b1ce59f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 20 May 2021 10:39:00 +0200 Subject: [PATCH 033/160] tests: Better state logging in pledgeSectors --- api/test/window_post.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/api/test/window_post.go b/api/test/window_post.go index 48fe3fd6c..1e8090f11 100644 --- a/api/test/window_post.go +++ b/api/test/window_post.go @@ -247,12 +247,15 @@ func pledgeSectors(t *testing.T, ctx context.Context, miner TestStorageNode, n, toCheck[number] = struct{}{} } + for len(toCheck) > 0 { flushSealingBatches(t, ctx, miner) + states := map[api.SectorState]int{} for n := range toCheck { st, err := miner.SectorsStatus(ctx, n, false) require.NoError(t, err) + states[st.State]++ if st.State == api.SectorState(sealing.Proving) { delete(toCheck, n) } @@ -261,8 +264,9 @@ func pledgeSectors(t *testing.T, ctx context.Context, miner TestStorageNode, n, } } + build.Clock.Sleep(100 * time.Millisecond) - fmt.Printf("WaitSeal: %d\n", len(s)) + fmt.Printf("WaitSeal: %d %+v\n", len(s), states) } } From 7edffcd37b00e497b16b29a52c0e66bac0587ea0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 20 May 2021 10:55:01 +0200 Subject: [PATCH 034/160] Update ffi --- extern/filecoin-ffi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/filecoin-ffi b/extern/filecoin-ffi index 178ac15a5..58771ba4d 160000 --- a/extern/filecoin-ffi +++ b/extern/filecoin-ffi @@ -1 +1 @@ -Subproject commit 178ac15a537626cac07d8af68bffc603011c0310 +Subproject commit 58771ba4d942badc306925160a945022ad335161 From 678812f35b8056ce8fae47c475193964517c72f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 20 May 2021 11:33:03 +0200 Subject: [PATCH 035/160] gofmt, lint --- api/test/window_post.go | 2 -- chain/actors/policy/policy.go | 2 +- cmd/lotus-shed/main.go | 1 + go.sum | 3 --- 4 files changed, 2 insertions(+), 6 deletions(-) diff --git a/api/test/window_post.go b/api/test/window_post.go index 1e8090f11..04e709b86 100644 --- a/api/test/window_post.go +++ b/api/test/window_post.go @@ -247,7 +247,6 @@ func pledgeSectors(t *testing.T, ctx context.Context, miner TestStorageNode, n, toCheck[number] = struct{}{} } - for len(toCheck) > 0 { flushSealingBatches(t, ctx, miner) @@ -264,7 +263,6 @@ func pledgeSectors(t *testing.T, ctx context.Context, miner TestStorageNode, n, } } - build.Clock.Sleep(100 * time.Millisecond) fmt.Printf("WaitSeal: %d %+v\n", len(s), states) } diff --git a/chain/actors/policy/policy.go b/chain/actors/policy/policy.go index 1a95c4635..113544c05 100644 --- a/chain/actors/policy/policy.go +++ b/chain/actors/policy/policy.go @@ -39,7 +39,7 @@ const ( ChainFinality = miner5.ChainFinality SealRandomnessLookback = ChainFinality PaychSettleDelay = paych5.SettleDelay - MaxPreCommitRandomnessLookback = builtin5.EpochsInDay + SealRandomnessLookback // todo fix + MaxPreCommitRandomnessLookback = builtin5.EpochsInDay + SealRandomnessLookback ) // SetSupportedProofTypes sets supported proof types, across all actor versions. diff --git a/cmd/lotus-shed/main.go b/cmd/lotus-shed/main.go index ebe4f014a..0e0a13a55 100644 --- a/cmd/lotus-shed/main.go +++ b/cmd/lotus-shed/main.go @@ -20,6 +20,7 @@ func main() { base32Cmd, base16Cmd, bitFieldCmd, + cronWcCmd, frozenMinersCmd, keyinfoCmd, jwtCmd, diff --git a/go.sum b/go.sum index c281e0fdb..3ef546ba4 100644 --- a/go.sum +++ b/go.sum @@ -293,7 +293,6 @@ github.com/filecoin-project/go-state-types v0.0.0-20200903145444-247639ffa6ad/go github.com/filecoin-project/go-state-types v0.0.0-20200904021452-1883f36ca2f4/go.mod h1:IQ0MBPnonv35CJHtWSN3YY1Hz2gkPru1Q9qoaYLxx9I= github.com/filecoin-project/go-state-types v0.0.0-20200928172055-2df22083d8ab/go.mod h1:ezYnPf0bNkTsDibL/psSz5dy4B5awOJ/E7P2Saeep8g= github.com/filecoin-project/go-state-types v0.0.0-20201102161440-c8033295a1fc/go.mod h1:ezYnPf0bNkTsDibL/psSz5dy4B5awOJ/E7P2Saeep8g= -github.com/filecoin-project/go-state-types v0.1.0 h1:9r2HCSMMCmyMfGyMKxQtv0GKp6VT/m5GgVk8EhYbLJU= github.com/filecoin-project/go-state-types v0.1.0/go.mod h1:ezYnPf0bNkTsDibL/psSz5dy4B5awOJ/E7P2Saeep8g= github.com/filecoin-project/go-state-types v0.1.1-0.20210506134452-99b279731c48 h1:Jc4OprDp3bRDxbsrXNHPwJabZJM3iDy+ri8/1e0ZnX4= github.com/filecoin-project/go-state-types v0.1.1-0.20210506134452-99b279731c48/go.mod h1:ezYnPf0bNkTsDibL/psSz5dy4B5awOJ/E7P2Saeep8g= @@ -310,10 +309,8 @@ github.com/filecoin-project/specs-actors v0.9.13 h1:rUEOQouefi9fuVY/2HOroROJlZbO github.com/filecoin-project/specs-actors v0.9.13/go.mod h1:TS1AW/7LbG+615j4NsjMK1qlpAwaFsG9w0V2tg2gSao= github.com/filecoin-project/specs-actors/v2 v2.0.1/go.mod h1:v2NZVYinNIKA9acEMBm5wWXxqv5+frFEbekBFemYghY= github.com/filecoin-project/specs-actors/v2 v2.3.2/go.mod h1:UuJQLoTx/HPvvWeqlIFmC/ywlOLHNe8SNQ3OunFbu2Y= -github.com/filecoin-project/specs-actors/v2 v2.3.4/go.mod h1:UuJQLoTx/HPvvWeqlIFmC/ywlOLHNe8SNQ3OunFbu2Y= github.com/filecoin-project/specs-actors/v2 v2.3.5-0.20210114162132-5b58b773f4fb h1:orr/sMzrDZUPAveRE+paBdu1kScIUO5zm+HYeh+VlhA= github.com/filecoin-project/specs-actors/v2 v2.3.5-0.20210114162132-5b58b773f4fb/go.mod h1:LljnY2Mn2homxZsmokJZCpRuhOPxfXhvcek5gWkmqAc= -github.com/filecoin-project/specs-actors/v3 v3.0.4-0.20210227000520-b3317b86f4d1/go.mod h1:oMcmEed6B7H/wHabM3RQphTIhq0ibAKsbpYs+bQ/uxQ= github.com/filecoin-project/specs-actors/v3 v3.1.0 h1:s4qiPw8pgypqBGAy853u/zdZJ7K9cTZdM1rTiSonHrg= github.com/filecoin-project/specs-actors/v3 v3.1.0/go.mod h1:mpynccOLlIRy0QnR008BwYBwT9fen+sPR13MA1VmMww= github.com/filecoin-project/specs-actors/v4 v4.0.0 h1:vMALksY5G3J5rj3q9rbcyB+f4Tk1xrLqSgdB3jOok4s= From bb889a5976ed8828014fba7e5f46667ab63c10a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 20 May 2021 12:11:11 +0200 Subject: [PATCH 036/160] sealing: Fix max commit batch size check --- extern/storage-sealing/commit_batch.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/extern/storage-sealing/commit_batch.go b/extern/storage-sealing/commit_batch.go index f1cbb15ad..53f572712 100644 --- a/extern/storage-sealing/commit_batch.go +++ b/extern/storage-sealing/commit_batch.go @@ -185,6 +185,11 @@ func (b *CommitBatcher) processBatch(notif, after bool) (*cid.Cid, error) { infos := make([]proof5.AggregateSealVerifyInfo, 0, total) for id, p := range b.todo { + if len(infos) >= cfg.MaxCommitBatch { + log.Infow("commit batch full") + break + } + params.SectorNumbers.Set(uint64(id)) infos = append(infos, p.info) } @@ -194,11 +199,6 @@ func (b *CommitBatcher) processBatch(notif, after bool) (*cid.Cid, error) { }) for _, info := range infos { - if len(infos) >= cfg.MaxCommitBatch { - log.Infow("commit batch full") - break - } - proofs = append(proofs, b.todo[info.Number].proof) } From d71334cc2416768ca05c27e08680f85bde451811 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 20 May 2021 18:04:07 +0200 Subject: [PATCH 037/160] Address self-review --- api/test/pledge.go | 258 ++++++++++++++++++ api/test/window_post.go | 242 ---------------- .../misc/actors_version_checklist.md | 2 +- extern/storage-sealing/states_sealing.go | 5 +- storage/wdpost_run_test.go | 4 - 5 files changed, 262 insertions(+), 249 deletions(-) create mode 100644 api/test/pledge.go diff --git a/api/test/pledge.go b/api/test/pledge.go new file mode 100644 index 000000000..49df97ea8 --- /dev/null +++ b/api/test/pledge.go @@ -0,0 +1,258 @@ +package test + +import ( + "context" + "fmt" + "sort" + "strings" + "sync/atomic" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/network" + + "github.com/filecoin-project/lotus/api" + "github.com/filecoin-project/lotus/build" + sealing "github.com/filecoin-project/lotus/extern/storage-sealing" + bminer "github.com/filecoin-project/lotus/miner" + "github.com/filecoin-project/lotus/node/impl" +) + +func TestSDRUpgrade(t *testing.T, b APIBuilder, blocktime time.Duration) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + n, sn := b(t, []FullNodeOpts{FullNodeWithSDRAt(500, 1000)}, OneMiner) + client := n[0].FullNode.(*impl.FullNodeAPI) + miner := sn[0] + + addrinfo, err := client.NetAddrsListen(ctx) + if err != nil { + t.Fatal(err) + } + + if err := miner.NetConnect(ctx, addrinfo); err != nil { + t.Fatal(err) + } + build.Clock.Sleep(time.Second) + + pledge := make(chan struct{}) + mine := int64(1) + done := make(chan struct{}) + go func() { + defer close(done) + round := 0 + for atomic.LoadInt64(&mine) != 0 { + build.Clock.Sleep(blocktime) + if err := sn[0].MineOne(ctx, bminer.MineReq{Done: func(bool, abi.ChainEpoch, error) { + + }}); err != nil { + t.Error(err) + } + + // 3 sealing rounds: before, during after. + if round >= 3 { + continue + } + + head, err := client.ChainHead(ctx) + assert.NoError(t, err) + + // rounds happen every 100 blocks, with a 50 block offset. + if head.Height() >= abi.ChainEpoch(round*500+50) { + round++ + pledge <- struct{}{} + + ver, err := client.StateNetworkVersion(ctx, head.Key()) + assert.NoError(t, err) + switch round { + case 1: + assert.Equal(t, network.Version6, ver) + case 2: + assert.Equal(t, network.Version7, ver) + case 3: + assert.Equal(t, network.Version8, ver) + } + } + + } + }() + + // before. + pledgeSectors(t, ctx, miner, 9, 0, pledge) + + s, err := miner.SectorsList(ctx) + require.NoError(t, err) + sort.Slice(s, func(i, j int) bool { + return s[i] < s[j] + }) + + for i, id := range s { + info, err := miner.SectorsStatus(ctx, id, true) + require.NoError(t, err) + expectProof := abi.RegisteredSealProof_StackedDrg2KiBV1 + if i >= 3 { + // after + expectProof = abi.RegisteredSealProof_StackedDrg2KiBV1_1 + } + assert.Equal(t, expectProof, info.SealProof, "sector %d, id %d", i, id) + } + + atomic.StoreInt64(&mine, 0) + <-done +} + +func TestPledgeBatching(t *testing.T, b APIBuilder, blocktime time.Duration, nSectors int) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + n, sn := b(t, []FullNodeOpts{FullNodeWithLatestActorsAt(-1)}, OneMiner) + client := n[0].FullNode.(*impl.FullNodeAPI) + miner := sn[0] + + addrinfo, err := client.NetAddrsListen(ctx) + if err != nil { + t.Fatal(err) + } + + if err := miner.NetConnect(ctx, addrinfo); err != nil { + t.Fatal(err) + } + build.Clock.Sleep(time.Second) + + mine := int64(1) + done := make(chan struct{}) + go func() { + defer close(done) + for atomic.LoadInt64(&mine) != 0 { + build.Clock.Sleep(blocktime) + if err := sn[0].MineOne(ctx, bminer.MineReq{Done: func(bool, abi.ChainEpoch, error) { + + }}); err != nil { + t.Error(err) + } + } + }() + + for { + h, err := client.ChainHead(ctx) + require.NoError(t, err) + if h.Height() > 10 { + break + } + } + + pledgeSectors(t, ctx, miner, nSectors, 0, nil) + + atomic.StoreInt64(&mine, 0) + <-done +} + +func TestPledgeSector(t *testing.T, b APIBuilder, blocktime time.Duration, nSectors int) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + n, sn := b(t, OneFull, OneMiner) + client := n[0].FullNode.(*impl.FullNodeAPI) + miner := sn[0] + + addrinfo, err := client.NetAddrsListen(ctx) + if err != nil { + t.Fatal(err) + } + + if err := miner.NetConnect(ctx, addrinfo); err != nil { + t.Fatal(err) + } + build.Clock.Sleep(time.Second) + + mine := int64(1) + done := make(chan struct{}) + go func() { + defer close(done) + for atomic.LoadInt64(&mine) != 0 { + build.Clock.Sleep(blocktime) + if err := sn[0].MineOne(ctx, bminer.MineReq{Done: func(bool, abi.ChainEpoch, error) { + + }}); err != nil { + t.Error(err) + } + } + }() + + pledgeSectors(t, ctx, miner, nSectors, 0, nil) + + atomic.StoreInt64(&mine, 0) + <-done +} + +func flushSealingBatches(t *testing.T, ctx context.Context, miner TestStorageNode) { + pcb, err := miner.SectorPreCommitFlush(ctx) + require.NoError(t, err) + if pcb != nil { + fmt.Printf("PRECOMMIT BATCH: %s\n", *pcb) + } + + cb, err := miner.SectorCommitFlush(ctx) + require.NoError(t, err) + if cb != nil { + fmt.Printf("COMMIT BATCH: %s\n", *cb) + } +} + +func pledgeSectors(t *testing.T, ctx context.Context, miner TestStorageNode, n, existing int, blockNotif <-chan struct{}) { + for i := 0; i < n; i++ { + if i%3 == 0 && blockNotif != nil { + <-blockNotif + log.Errorf("WAIT") + } + log.Errorf("PLEDGING %d", i) + _, err := miner.PledgeSector(ctx) + require.NoError(t, err) + } + + for { + s, err := miner.SectorsList(ctx) // Note - the test builder doesn't import genesis sectors into FSM + require.NoError(t, err) + fmt.Printf("Sectors: %d\n", len(s)) + if len(s) >= n+existing { + break + } + + build.Clock.Sleep(100 * time.Millisecond) + } + + fmt.Printf("All sectors is fsm\n") + + s, err := miner.SectorsList(ctx) + require.NoError(t, err) + + toCheck := map[abi.SectorNumber]struct{}{} + for _, number := range s { + toCheck[number] = struct{}{} + } + + for len(toCheck) > 0 { + flushSealingBatches(t, ctx, miner) + + states := map[api.SectorState]int{} + for n := range toCheck { + st, err := miner.SectorsStatus(ctx, n, false) + require.NoError(t, err) + states[st.State]++ + if st.State == api.SectorState(sealing.Proving) { + delete(toCheck, n) + } + if strings.Contains(string(st.State), "Fail") { + t.Fatal("sector in a failed state", st.State) + } + } + + build.Clock.Sleep(100 * time.Millisecond) + fmt.Printf("WaitSeal: %d %+v\n", len(s), states) + } +} diff --git a/api/test/window_post.go b/api/test/window_post.go index 04e709b86..128a22641 100644 --- a/api/test/window_post.go +++ b/api/test/window_post.go @@ -3,14 +3,9 @@ package test import ( "context" "fmt" - "sort" - "sync/atomic" - - "strings" "testing" "time" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/filecoin-project/go-address" @@ -18,7 +13,6 @@ import ( "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/crypto" "github.com/filecoin-project/go-state-types/dline" - "github.com/filecoin-project/go-state-types/network" "github.com/filecoin-project/lotus/extern/sector-storage/mock" sealing "github.com/filecoin-project/lotus/extern/storage-sealing" proof3 "github.com/filecoin-project/specs-actors/v3/actors/runtime/proof" @@ -29,245 +23,9 @@ import ( "github.com/filecoin-project/lotus/chain/actors" minerActor "github.com/filecoin-project/lotus/chain/actors/builtin/miner" "github.com/filecoin-project/lotus/chain/types" - bminer "github.com/filecoin-project/lotus/miner" "github.com/filecoin-project/lotus/node/impl" ) -func TestSDRUpgrade(t *testing.T, b APIBuilder, blocktime time.Duration) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - n, sn := b(t, []FullNodeOpts{FullNodeWithSDRAt(500, 1000)}, OneMiner) - client := n[0].FullNode.(*impl.FullNodeAPI) - miner := sn[0] - - addrinfo, err := client.NetAddrsListen(ctx) - if err != nil { - t.Fatal(err) - } - - if err := miner.NetConnect(ctx, addrinfo); err != nil { - t.Fatal(err) - } - build.Clock.Sleep(time.Second) - - pledge := make(chan struct{}) - mine := int64(1) - done := make(chan struct{}) - go func() { - defer close(done) - round := 0 - for atomic.LoadInt64(&mine) != 0 { - build.Clock.Sleep(blocktime) - if err := sn[0].MineOne(ctx, bminer.MineReq{Done: func(bool, abi.ChainEpoch, error) { - - }}); err != nil { - t.Error(err) - } - - // 3 sealing rounds: before, during after. - if round >= 3 { - continue - } - - head, err := client.ChainHead(ctx) - assert.NoError(t, err) - - // rounds happen every 100 blocks, with a 50 block offset. - if head.Height() >= abi.ChainEpoch(round*500+50) { - round++ - pledge <- struct{}{} - - ver, err := client.StateNetworkVersion(ctx, head.Key()) - assert.NoError(t, err) - switch round { - case 1: - assert.Equal(t, network.Version6, ver) - case 2: - assert.Equal(t, network.Version7, ver) - case 3: - assert.Equal(t, network.Version8, ver) - } - } - - } - }() - - // before. - pledgeSectors(t, ctx, miner, 9, 0, pledge) - - s, err := miner.SectorsList(ctx) - require.NoError(t, err) - sort.Slice(s, func(i, j int) bool { - return s[i] < s[j] - }) - - for i, id := range s { - info, err := miner.SectorsStatus(ctx, id, true) - require.NoError(t, err) - expectProof := abi.RegisteredSealProof_StackedDrg2KiBV1 - if i >= 3 { - // after - expectProof = abi.RegisteredSealProof_StackedDrg2KiBV1_1 - } - assert.Equal(t, expectProof, info.SealProof, "sector %d, id %d", i, id) - } - - atomic.StoreInt64(&mine, 0) - <-done -} - -func TestPledgeBatching(t *testing.T, b APIBuilder, blocktime time.Duration, nSectors int) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - n, sn := b(t, []FullNodeOpts{FullNodeWithLatestActorsAt(-1)}, OneMiner) - client := n[0].FullNode.(*impl.FullNodeAPI) - miner := sn[0] - - addrinfo, err := client.NetAddrsListen(ctx) - if err != nil { - t.Fatal(err) - } - - if err := miner.NetConnect(ctx, addrinfo); err != nil { - t.Fatal(err) - } - build.Clock.Sleep(time.Second) - - mine := int64(1) - done := make(chan struct{}) - go func() { - defer close(done) - for atomic.LoadInt64(&mine) != 0 { - build.Clock.Sleep(blocktime) - if err := sn[0].MineOne(ctx, bminer.MineReq{Done: func(bool, abi.ChainEpoch, error) { - - }}); err != nil { - t.Error(err) - } - } - }() - - for { - h, err := client.ChainHead(ctx) - require.NoError(t, err) - if h.Height() > 10 { - break - } - } - - pledgeSectors(t, ctx, miner, nSectors, 0, nil) - - atomic.StoreInt64(&mine, 0) - <-done -} - -func TestPledgeSector(t *testing.T, b APIBuilder, blocktime time.Duration, nSectors int) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - n, sn := b(t, OneFull, OneMiner) - client := n[0].FullNode.(*impl.FullNodeAPI) - miner := sn[0] - - addrinfo, err := client.NetAddrsListen(ctx) - if err != nil { - t.Fatal(err) - } - - if err := miner.NetConnect(ctx, addrinfo); err != nil { - t.Fatal(err) - } - build.Clock.Sleep(time.Second) - - mine := int64(1) - done := make(chan struct{}) - go func() { - defer close(done) - for atomic.LoadInt64(&mine) != 0 { - build.Clock.Sleep(blocktime) - if err := sn[0].MineOne(ctx, bminer.MineReq{Done: func(bool, abi.ChainEpoch, error) { - - }}); err != nil { - t.Error(err) - } - } - }() - - pledgeSectors(t, ctx, miner, nSectors, 0, nil) - - atomic.StoreInt64(&mine, 0) - <-done -} - -func flushSealingBatches(t *testing.T, ctx context.Context, miner TestStorageNode) { - pcb, err := miner.SectorPreCommitFlush(ctx) - require.NoError(t, err) - if pcb != nil { - fmt.Printf("PRECOMMIT BATCH: %s\n", *pcb) - } - - cb, err := miner.SectorCommitFlush(ctx) - require.NoError(t, err) - if cb != nil { - fmt.Printf("COMMIT BATCH: %s\n", *cb) - } -} - -func pledgeSectors(t *testing.T, ctx context.Context, miner TestStorageNode, n, existing int, blockNotif <-chan struct{}) { - for i := 0; i < n; i++ { - if i%3 == 0 && blockNotif != nil { - <-blockNotif - log.Errorf("WAIT") - } - log.Errorf("PLEDGING %d", i) - _, err := miner.PledgeSector(ctx) - require.NoError(t, err) - } - - for { - s, err := miner.SectorsList(ctx) // Note - the test builder doesn't import genesis sectors into FSM - require.NoError(t, err) - fmt.Printf("Sectors: %d\n", len(s)) - if len(s) >= n+existing { - break - } - - build.Clock.Sleep(100 * time.Millisecond) - } - - fmt.Printf("All sectors is fsm\n") - - s, err := miner.SectorsList(ctx) - require.NoError(t, err) - - toCheck := map[abi.SectorNumber]struct{}{} - for _, number := range s { - toCheck[number] = struct{}{} - } - - for len(toCheck) > 0 { - flushSealingBatches(t, ctx, miner) - - states := map[api.SectorState]int{} - for n := range toCheck { - st, err := miner.SectorsStatus(ctx, n, false) - require.NoError(t, err) - states[st.State]++ - if st.State == api.SectorState(sealing.Proving) { - delete(toCheck, n) - } - if strings.Contains(string(st.State), "Fail") { - t.Fatal("sector in a failed state", st.State) - } - } - - build.Clock.Sleep(100 * time.Millisecond) - fmt.Printf("WaitSeal: %d %+v\n", len(s), states) - } -} - func TestWindowPost(t *testing.T, b APIBuilder, blocktime time.Duration, nSectors int) { for _, height := range []abi.ChainEpoch{ -1, // before diff --git a/documentation/misc/actors_version_checklist.md b/documentation/misc/actors_version_checklist.md index 6c06441ab..7931acbcc 100644 --- a/documentation/misc/actors_version_checklist.md +++ b/documentation/misc/actors_version_checklist.md @@ -8,7 +8,7 @@ - [ ] Register in `chain/vm/invoker.go` - [ ] Register in `chain/vm/mkactor.go` - [ ] Update `chain/types/state.go` -- [ ] Update `chain/state/statetree.go` +- [ ] Update `chain/state/statetree.go` (New / Load) - [ ] Update `chain/stmgr/forks.go` - [ ] Schedule - [ ] Migration diff --git a/extern/storage-sealing/states_sealing.go b/extern/storage-sealing/states_sealing.go index bec9ad51b..391951dea 100644 --- a/extern/storage-sealing/states_sealing.go +++ b/extern/storage-sealing/states_sealing.go @@ -3,6 +3,7 @@ package sealing import ( "bytes" "context" + "github.com/ipfs/go-cid" "golang.org/x/xerrors" @@ -318,7 +319,7 @@ func (m *Sealing) handlePreCommitting(ctx statemachine.Context, sector SectorInf } params, deposit, tok, err := m.preCommitParams(ctx, sector) - if err != nil { + if params == nil || err != nil { return err } @@ -358,7 +359,7 @@ func (m *Sealing) handleSubmitPreCommitBatch(ctx statemachine.Context, sector Se } params, deposit, _, err := m.preCommitParams(ctx, sector) - if err != nil { + if params == nil || err != nil { return err } diff --git a/storage/wdpost_run_test.go b/storage/wdpost_run_test.go index 2e412880a..5117e718a 100644 --- a/storage/wdpost_run_test.go +++ b/storage/wdpost_run_test.go @@ -145,10 +145,6 @@ func (m mockVerif) VerifyWindowPoSt(ctx context.Context, info proof2.WindowPoStV return true, nil } -func (m mockVerif) AggregateSealProofs(proofType abi.RegisteredSealProof, rap abi.RegisteredAggregationProof, proofs [][]byte) ([]byte, error) { - panic("implement me") -} - func (m mockVerif) VerifyAggregateSeals(aggregate proof5.AggregateSealVerifyProofAndInfos) (bool, error) { panic("implement me") } From f3fda4ae7f53ea44cae3bfb17cd8f5753727e961 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 20 May 2021 18:22:55 +0200 Subject: [PATCH 038/160] Better asserts in TestPledgeBatching --- api/test/pledge.go | 48 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 3 deletions(-) diff --git a/api/test/pledge.go b/api/test/pledge.go index 49df97ea8..202b24515 100644 --- a/api/test/pledge.go +++ b/api/test/pledge.go @@ -146,7 +146,43 @@ func TestPledgeBatching(t *testing.T, b APIBuilder, blocktime time.Duration, nSe } } - pledgeSectors(t, ctx, miner, nSectors, 0, nil) + toCheck := startPledge(t, ctx, miner, nSectors, 0, nil) + + for len(toCheck) > 0 { + states := map[api.SectorState]int{} + + for n := range toCheck { + st, err := miner.SectorsStatus(ctx, n, false) + require.NoError(t, err) + states[st.State]++ + if st.State == api.SectorState(sealing.Proving) { + delete(toCheck, n) + } + if strings.Contains(string(st.State), "Fail") { + t.Fatal("sector in a failed state", st.State) + } + } + if states[api.SectorState(sealing.SubmitPreCommitBatch)] == nSectors || + (states[api.SectorState(sealing.SubmitPreCommitBatch)] > 0 && states[api.SectorState(sealing.PreCommit1)] == 0 && states[api.SectorState(sealing.PreCommit2)] == 0) { + pcb, err := miner.SectorPreCommitFlush(ctx) + require.NoError(t, err) + if pcb != nil { + fmt.Printf("PRECOMMIT BATCH: %s\n", *pcb) + } + } + + if states[api.SectorState(sealing.SubmitCommitAggregate)] == nSectors || + (states[api.SectorState(sealing.SubmitCommitAggregate)] > 0 && states[api.SectorState(sealing.WaitSeed)] == 0 && states[api.SectorState(sealing.Committing)] == 0) { + cb, err := miner.SectorCommitFlush(ctx) + require.NoError(t, err) + if cb != nil { + fmt.Printf("COMMIT BATCH: %s\n", *cb) + } + } + + build.Clock.Sleep(100 * time.Millisecond) + fmt.Printf("WaitSeal: %d %+v\n", len(toCheck), states) + } atomic.StoreInt64(&mine, 0) <-done @@ -204,7 +240,7 @@ func flushSealingBatches(t *testing.T, ctx context.Context, miner TestStorageNod } } -func pledgeSectors(t *testing.T, ctx context.Context, miner TestStorageNode, n, existing int, blockNotif <-chan struct{}) { +func startPledge(t *testing.T, ctx context.Context, miner TestStorageNode, n, existing int, blockNotif <-chan struct{}) map[abi.SectorNumber]struct{} { for i := 0; i < n; i++ { if i%3 == 0 && blockNotif != nil { <-blockNotif @@ -236,6 +272,12 @@ func pledgeSectors(t *testing.T, ctx context.Context, miner TestStorageNode, n, toCheck[number] = struct{}{} } + return toCheck +} + +func pledgeSectors(t *testing.T, ctx context.Context, miner TestStorageNode, n, existing int, blockNotif <-chan struct{}) { + toCheck := startPledge(t, ctx, miner, n, existing, blockNotif) + for len(toCheck) > 0 { flushSealingBatches(t, ctx, miner) @@ -253,6 +295,6 @@ func pledgeSectors(t *testing.T, ctx context.Context, miner TestStorageNode, n, } build.Clock.Sleep(100 * time.Millisecond) - fmt.Printf("WaitSeal: %d %+v\n", len(s), states) + fmt.Printf("WaitSeal: %d %+v\n", len(toCheck), states) } } From 13ff6ed462b7cf6f316f0345dec841215ec2c23b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 20 May 2021 18:38:33 +0200 Subject: [PATCH 039/160] Test pledging before nv13 upgrade --- api/test/pledge.go | 89 ++++++++++++++++++++++++++++++++++++++++++++++ node/node_test.go | 3 ++ 2 files changed, 92 insertions(+) diff --git a/api/test/pledge.go b/api/test/pledge.go index 202b24515..8752ad4ac 100644 --- a/api/test/pledge.go +++ b/api/test/pledge.go @@ -17,8 +17,10 @@ import ( "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/build" + "github.com/filecoin-project/lotus/chain/stmgr" sealing "github.com/filecoin-project/lotus/extern/storage-sealing" bminer "github.com/filecoin-project/lotus/miner" + "github.com/filecoin-project/lotus/node" "github.com/filecoin-project/lotus/node/impl" ) @@ -188,6 +190,93 @@ func TestPledgeBatching(t *testing.T, b APIBuilder, blocktime time.Duration, nSe <-done } +func TestPledgeBeforeNv13(t *testing.T, b APIBuilder, blocktime time.Duration, nSectors int) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + n, sn := b(t, []FullNodeOpts{ + { + Opts: func(nodes []TestNode) node.Option { + return node.Override(new(stmgr.UpgradeSchedule), stmgr.UpgradeSchedule{{ + Network: network.Version9, + Height: 1, + Migration: stmgr.UpgradeActorsV2, + }, { + Network: network.Version10, + Height: 2, + Migration: stmgr.UpgradeActorsV3, + }, { + Network: network.Version12, + Height: 3, + Migration: stmgr.UpgradeActorsV4, + }, { + Network: network.Version13, + Height: 1000000000, + Migration: stmgr.UpgradeActorsV5, + }}) + }, + }, + }, OneMiner) + client := n[0].FullNode.(*impl.FullNodeAPI) + miner := sn[0] + + addrinfo, err := client.NetAddrsListen(ctx) + if err != nil { + t.Fatal(err) + } + + if err := miner.NetConnect(ctx, addrinfo); err != nil { + t.Fatal(err) + } + build.Clock.Sleep(time.Second) + + mine := int64(1) + done := make(chan struct{}) + go func() { + defer close(done) + for atomic.LoadInt64(&mine) != 0 { + build.Clock.Sleep(blocktime) + if err := sn[0].MineOne(ctx, bminer.MineReq{Done: func(bool, abi.ChainEpoch, error) { + + }}); err != nil { + t.Error(err) + } + } + }() + + for { + h, err := client.ChainHead(ctx) + require.NoError(t, err) + if h.Height() > 10 { + break + } + } + + toCheck := startPledge(t, ctx, miner, nSectors, 0, nil) + + for len(toCheck) > 0 { + states := map[api.SectorState]int{} + + for n := range toCheck { + st, err := miner.SectorsStatus(ctx, n, false) + require.NoError(t, err) + states[st.State]++ + if st.State == api.SectorState(sealing.Proving) { + delete(toCheck, n) + } + if strings.Contains(string(st.State), "Fail") { + t.Fatal("sector in a failed state", st.State) + } + } + + build.Clock.Sleep(100 * time.Millisecond) + fmt.Printf("WaitSeal: %d %+v\n", len(toCheck), states) + } + + atomic.StoreInt64(&mine, 0) + <-done +} + func TestPledgeSector(t *testing.T, b APIBuilder, blocktime time.Duration, nSectors int) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() diff --git a/node/node_test.go b/node/node_test.go index 5db7e355f..1cc089b1e 100644 --- a/node/node_test.go +++ b/node/node_test.go @@ -152,6 +152,9 @@ func TestPledgeBatching(t *testing.T) { t.Run("100", func(t *testing.T) { test.TestPledgeBatching(t, builder.MockSbBuilder, 50*time.Millisecond, 100) }) + t.Run("100-before-nv13", func(t *testing.T) { + test.TestPledgeBeforeNv13(t, builder.MockSbBuilder, 50*time.Millisecond, 100) + }) } func TestTapeFix(t *testing.T) { From e3255a06ea2a2d844c0053121c4286a87159f8db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 20 May 2021 18:39:59 +0200 Subject: [PATCH 040/160] sealing: Add missing states to SubmitPreCommitBatch planner --- extern/storage-sealing/fsm.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/extern/storage-sealing/fsm.go b/extern/storage-sealing/fsm.go index d249685b0..594f9f2f5 100644 --- a/extern/storage-sealing/fsm.go +++ b/extern/storage-sealing/fsm.go @@ -72,8 +72,8 @@ var fsmPlanners = map[SectorState]func(events []statemachine.Event, state *Secto ), PreCommitting: planOne( on(SectorPreCommitBatch{}, SubmitPreCommitBatch), - on(SectorSealPreCommit1Failed{}, SealPreCommit1Failed), on(SectorPreCommitted{}, PreCommitWait), + on(SectorSealPreCommit1Failed{}, SealPreCommit1Failed), on(SectorChainPreCommitFailed{}, PreCommitFailed), on(SectorPreCommitLanded{}, WaitSeed), on(SectorDealsExpired{}, DealsExpired), @@ -81,6 +81,11 @@ var fsmPlanners = map[SectorState]func(events []statemachine.Event, state *Secto ), SubmitPreCommitBatch: planOne( on(SectorPreCommitBatchSent{}, PreCommitBatchWait), + on(SectorSealPreCommit1Failed{}, SealPreCommit1Failed), + on(SectorChainPreCommitFailed{}, PreCommitFailed), + on(SectorPreCommitLanded{}, WaitSeed), + on(SectorDealsExpired{}, DealsExpired), + on(SectorInvalidDealIDs{}, RecoverDealIDs), ), PreCommitBatchWait: planOne( on(SectorChainPreCommitFailed{}, PreCommitFailed), @@ -99,8 +104,8 @@ var fsmPlanners = map[SectorState]func(events []statemachine.Event, state *Secto Committing: planCommitting, SubmitCommit: planOne( on(SectorCommitSubmitted{}, CommitWait), - on(SectorCommitFailed{}, CommitFailed), on(SectorSubmitCommitAggregate{}, SubmitCommitAggregate), + on(SectorCommitFailed{}, CommitFailed), ), SubmitCommitAggregate: planOne( on(SectorCommitAggregateSent{}, CommitWait), From f5409845b574eab91b356ac97fda920ffa3d9b98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 25 May 2021 16:07:45 +0200 Subject: [PATCH 041/160] Some review addressing --- api/api_storage.go | 7 +- build/openrpc/miner.json.gz | Bin 7952 -> 7988 bytes chain/vm/gas.go | 4 +- chain/vm/gas_v0.go | 3 +- cmd/lotus-shed/cron-count.go | 99 ------------- cmd/lotus-shed/main.go | 1 - documentation/en/api-methods-miner.md | 3 + extern/sector-storage/mock/mock.go | 2 - .../storageadapter/ondealsectorcommitted.go | 131 ++++++++++-------- 9 files changed, 86 insertions(+), 164 deletions(-) delete mode 100644 cmd/lotus-shed/cron-count.go diff --git a/api/api_storage.go b/api/api_storage.go index f9a5ccdd8..202b40f93 100644 --- a/api/api_storage.go +++ b/api/api_storage.go @@ -85,8 +85,11 @@ type StorageMiner interface { SectorPreCommitFlush(ctx context.Context) (*cid.Cid, error) //perm:admin // SectorPreCommitPending returns a list of pending PreCommit sectors to be sent in the next batch message SectorPreCommitPending(ctx context.Context) ([]abi.SectorID, error) //perm:admin - SectorCommitFlush(ctx context.Context) (*cid.Cid, error) //perm:admin - SectorCommitPending(ctx context.Context) ([]abi.SectorID, error) //perm:admin + // SectorCommitFlush immediately sends a Commit message with sectors aggregated for Commit. + // Returns null if message wasn't sent + SectorCommitFlush(ctx context.Context) (*cid.Cid, error) //perm:admin + // SectorCommitPending returns a list of pending Commit sectors to be sent in the next aggregate message + SectorCommitPending(ctx context.Context) ([]abi.SectorID, error) //perm:admin // WorkerConnect tells the node to connect to workers RPC WorkerConnect(context.Context, string) error //perm:admin retry:true diff --git a/build/openrpc/miner.json.gz b/build/openrpc/miner.json.gz index 75dd3a2a0e7257a3b6de8dd031de46c68a3e691d..1ddc27f12930edce042af42f23c095dc7e9e6964 100644 GIT binary patch delta 3281 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 P00960{(w}jgN^|Jx?OLq delta 3245 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 diff --git a/chain/vm/gas.go b/chain/vm/gas.go index 5ecc42345..67dd7677e 100644 --- a/chain/vm/gas.go +++ b/chain/vm/gas.go @@ -75,7 +75,7 @@ type Pricelist interface { OnHashing(dataSize int) GasCharge OnComputeUnsealedSectorCid(proofType abi.RegisteredSealProof, pieces []abi.PieceInfo) GasCharge OnVerifySeal(info proof5.SealVerifyInfo) GasCharge - OnVerifyAggregateSeals() GasCharge + OnVerifyAggregateSeals(aggregate proof5.AggregateSealVerifyProofAndInfos) GasCharge OnVerifyPost(info proof5.WindowPoStVerifyInfo) GasCharge OnVerifyConsensusFault() GasCharge } @@ -282,7 +282,7 @@ func (ps pricedSyscalls) BatchVerifySeals(inp map[address.Address][]proof5.SealV } func (ps pricedSyscalls) VerifyAggregateSeals(aggregate proof5.AggregateSealVerifyProofAndInfos) error { - ps.chargeGas(ps.pl.OnVerifyAggregateSeals()) + ps.chargeGas(ps.pl.OnVerifyAggregateSeals(aggregate)) defer ps.chargeGas(gasOnActorExec) return ps.under.VerifyAggregateSeals(aggregate) diff --git a/chain/vm/gas_v0.go b/chain/vm/gas_v0.go index d54760b69..c90ebaa73 100644 --- a/chain/vm/gas_v0.go +++ b/chain/vm/gas_v0.go @@ -4,6 +4,7 @@ import ( "fmt" proof2 "github.com/filecoin-project/specs-actors/v2/actors/runtime/proof" + proof5 "github.com/filecoin-project/specs-actors/v5/actors/runtime/proof" "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/big" @@ -187,7 +188,7 @@ func (pl *pricelistV0) OnVerifySeal(info proof2.SealVerifyInfo) GasCharge { } // OnVerifyAggregateSeals -func (pl *pricelistV0) OnVerifyAggregateSeals() GasCharge { +func (pl *pricelistV0) OnVerifyAggregateSeals(aggregate proof5.AggregateSealVerifyProofAndInfos) GasCharge { // TODO: this needs more cost tunning return newGasCharge("OnVerifyAggregateSeals", pl.verifyAggregateSealBase, 0) } diff --git a/cmd/lotus-shed/cron-count.go b/cmd/lotus-shed/cron-count.go deleted file mode 100644 index 622f38791..000000000 --- a/cmd/lotus-shed/cron-count.go +++ /dev/null @@ -1,99 +0,0 @@ -package main - -import ( - "fmt" - - "github.com/filecoin-project/go-address" - "github.com/filecoin-project/lotus/build" - lcli "github.com/filecoin-project/lotus/cli" - "github.com/urfave/cli/v2" - "golang.org/x/xerrors" -) - -var cronWcCmd = &cli.Command{ - Name: "cron-wc", - Description: "cron stats", - Subcommands: []*cli.Command{ - minerDeadlineCronCountCmd, - }, -} - -var minerDeadlineCronCountCmd = &cli.Command{ - Name: "deadline", - Description: "list all addresses of miners with active deadline crons", - Action: func(c *cli.Context) error { - return countDeadlineCrons(c) - }, - Flags: []cli.Flag{ - &cli.StringFlag{ - Name: "tipset", - Usage: "specify tipset state to search on (pass comma separated array of cids)", - }, - }, -} - -func findDeadlineCrons(c *cli.Context) (map[address.Address]struct{}, error) { - api, acloser, err := lcli.GetFullNodeAPI(c) - if err != nil { - return nil, err - } - defer acloser() - ctx := lcli.ReqContext(c) - - ts, err := lcli.LoadTipSet(ctx, c, api) - if err != nil { - return nil, err - } - if ts == nil { - ts, err = api.ChainHead(ctx) - if err != nil { - return nil, err - } - } - - mAddrs, err := api.StateListMiners(ctx, ts.Key()) - if err != nil { - return nil, err - } - activeMiners := make(map[address.Address]struct{}) - for _, mAddr := range mAddrs { - // All miners have active cron before v4. - // v4 upgrade epoch is last epoch running v3 epoch and api.StateReadState reads - // parent state, so v4 state isn't read until upgrade epoch + 2 - if ts.Height() <= build.UpgradeTurboHeight+1 { - activeMiners[mAddr] = struct{}{} - continue - } - st, err := api.StateReadState(ctx, mAddr, ts.Key()) - if err != nil { - return nil, err - } - minerState, ok := st.State.(map[string]interface{}) - if !ok { - return nil, xerrors.Errorf("internal error: failed to cast miner state to expected map type") - } - - activeDlineIface, ok := minerState["DeadlineCronActive"] - if !ok { - return nil, xerrors.Errorf("miner %s had no deadline state, is this a v3 state root?", mAddr) - } - active := activeDlineIface.(bool) - if active { - activeMiners[mAddr] = struct{}{} - } - } - - return activeMiners, nil -} - -func countDeadlineCrons(c *cli.Context) error { - activeMiners, err := findDeadlineCrons(c) - if err != nil { - return err - } - for addr := range activeMiners { - fmt.Printf("%s\n", addr) - } - - return nil -} diff --git a/cmd/lotus-shed/main.go b/cmd/lotus-shed/main.go index 0e0a13a55..ebe4f014a 100644 --- a/cmd/lotus-shed/main.go +++ b/cmd/lotus-shed/main.go @@ -20,7 +20,6 @@ func main() { base32Cmd, base16Cmd, bitFieldCmd, - cronWcCmd, frozenMinersCmd, keyinfoCmd, jwtCmd, diff --git a/documentation/en/api-methods-miner.md b/documentation/en/api-methods-miner.md index 4622d3e53..1e8e7cbc5 100644 --- a/documentation/en/api-methods-miner.md +++ b/documentation/en/api-methods-miner.md @@ -1561,6 +1561,8 @@ Response: `{}` ### SectorCommitFlush +SectorCommitFlush immediately sends a Commit message with sectors aggregated for Commit. +Returns null if message wasn't sent Perms: admin @@ -1570,6 +1572,7 @@ Inputs: `null` Response: `null` ### SectorCommitPending +SectorCommitPending returns a list of pending Commit sectors to be sent in the next aggregate message Perms: admin diff --git a/extern/sector-storage/mock/mock.go b/extern/sector-storage/mock/mock.go index bdd9e14cd..eac75d176 100644 --- a/extern/sector-storage/mock/mock.go +++ b/extern/sector-storage/mock/mock.go @@ -526,7 +526,6 @@ func (m mockVerifProver) VerifyAggregateSeals(aggregate proof5.AggregateSealVeri for _, info := range aggregate.Infos { sis = append(sis, info.Number) } - fmt.Printf("VERSIS %+v\n", sis) return bytes.Equal(aggregate.Proof, out), nil } @@ -543,7 +542,6 @@ func (m mockVerifProver) AggregateSealProofs(aggregateInfo proof5.AggregateSealV for _, info := range aggregateInfo.Infos { sis = append(sis, info.Number) } - fmt.Printf("AGGSIS %+v\n", sis) return out, nil } diff --git a/markets/storageadapter/ondealsectorcommitted.go b/markets/storageadapter/ondealsectorcommitted.go index f9ae201b3..31bc0b8bf 100644 --- a/markets/storageadapter/ondealsectorcommitted.go +++ b/markets/storageadapter/ondealsectorcommitted.go @@ -146,39 +146,13 @@ func (mgr *SectorCommittedManager) OnDealSectorPreCommitted(ctx context.Context, } // Extract the message parameters - switch msg.Method { - case miner.Methods.PreCommitSector: - var params miner.SectorPreCommitInfo - if err := params.UnmarshalCBOR(bytes.NewReader(msg.Params)); err != nil { - return false, xerrors.Errorf("unmarshal pre commit: %w", err) - } + sn, err := dealSectorInPreCommitMsg(msg, res) + if err != nil { + return false, err + } - // Check through the deal IDs associated with this message - for _, did := range params.DealIDs { - if did == res.DealID { - // Found the deal ID in this message. Callback with the sector ID. - cb(params.SectorNumber, false, nil) - return false, nil - } - } - case miner.Methods.PreCommitSectorBatch: - var params miner5.PreCommitSectorBatchParams - if err := params.UnmarshalCBOR(bytes.NewReader(msg.Params)); err != nil { - return false, xerrors.Errorf("unmarshal pre commit: %w", err) - } - - for _, precommit := range params.Sectors { - // Check through the deal IDs associated with this message - for _, did := range precommit.DealIDs { - if did == res.DealID { - // Found the deal ID in this message. Callback with the sector ID. - cb(precommit.SectorNumber, false, nil) - return false, nil - } - } - } - default: - return false, xerrors.Errorf("unexpected method %d", msg.Method) + if sn != nil { + cb(*sn, false, nil) } // Didn't find the deal ID in this message, so keep looking @@ -233,31 +207,7 @@ func (mgr *SectorCommittedManager) OnDealSectorCommitted(ctx context.Context, pr return false, nil } - switch msg.Method { - case miner.Methods.ProveCommitSector: - var params miner.ProveCommitSectorParams - if err := params.UnmarshalCBOR(bytes.NewReader(msg.Params)); err != nil { - return false, xerrors.Errorf("failed to unmarshal prove commit sector params: %w", err) - } - - return params.SectorNumber == sectorNumber, nil - - case miner.Methods.ProveCommitAggregate: - var params miner5.ProveCommitAggregateParams - if err := params.UnmarshalCBOR(bytes.NewReader(msg.Params)); err != nil { - return false, xerrors.Errorf("failed to unmarshal prove commit sector params: %w", err) - } - - set, err := params.SectorNumbers.IsSet(uint64(sectorNumber)) - if err != nil { - return false, xerrors.Errorf("checking if sectorNumber is set in commit aggregate message: %w", err) - } - - return set, nil - - default: - return false, nil - } + return sectorInCommitMsg(msg, sectorNumber) } // The deal must be accepted by the deal proposal start epoch, so timeout @@ -314,6 +264,73 @@ func (mgr *SectorCommittedManager) OnDealSectorCommitted(ctx context.Context, pr return nil } +// dealSectorInPreCommitMsg tries to find a sector containing the specified deal +func dealSectorInPreCommitMsg(msg *types.Message, res sealing.CurrentDealInfo) (*abi.SectorNumber, error) { + switch msg.Method { + case miner.Methods.PreCommitSector: + var params miner.SectorPreCommitInfo + if err := params.UnmarshalCBOR(bytes.NewReader(msg.Params)); err != nil { + return nil, xerrors.Errorf("unmarshal pre commit: %w", err) + } + + // Check through the deal IDs associated with this message + for _, did := range params.DealIDs { + if did == res.DealID { + // Found the deal ID in this message. Callback with the sector ID. + return ¶ms.SectorNumber, nil + } + } + case miner.Methods.PreCommitSectorBatch: + var params miner5.PreCommitSectorBatchParams + if err := params.UnmarshalCBOR(bytes.NewReader(msg.Params)); err != nil { + return nil, xerrors.Errorf("unmarshal pre commit: %w", err) + } + + for _, precommit := range params.Sectors { + // Check through the deal IDs associated with this message + for _, did := range precommit.DealIDs { + if did == res.DealID { + // Found the deal ID in this message. Callback with the sector ID. + return &precommit.SectorNumber, nil + } + } + } + default: + return nil, xerrors.Errorf("unexpected method %d", msg.Method) + } + + return nil, nil +} + +// sectorInCommitMsg checks if the provided message commits specified sector +func sectorInCommitMsg(msg *types.Message, sectorNumber abi.SectorNumber) (bool, error) { + switch msg.Method { + case miner.Methods.ProveCommitSector: + var params miner.ProveCommitSectorParams + if err := params.UnmarshalCBOR(bytes.NewReader(msg.Params)); err != nil { + return false, xerrors.Errorf("failed to unmarshal prove commit sector params: %w", err) + } + + return params.SectorNumber == sectorNumber, nil + + case miner.Methods.ProveCommitAggregate: + var params miner5.ProveCommitAggregateParams + if err := params.UnmarshalCBOR(bytes.NewReader(msg.Params)); err != nil { + return false, xerrors.Errorf("failed to unmarshal prove commit sector params: %w", err) + } + + set, err := params.SectorNumbers.IsSet(uint64(sectorNumber)) + if err != nil { + return false, xerrors.Errorf("checking if sectorNumber is set in commit aggregate message: %w", err) + } + + return set, nil + + default: + return false, nil + } +} + func (mgr *SectorCommittedManager) checkIfDealAlreadyActive(ctx context.Context, ts *types.TipSet, proposal *market.DealProposal, publishCid cid.Cid) (sealing.CurrentDealInfo, bool, error) { res, err := mgr.dealInfo.GetCurrentDealInfo(ctx, ts.Key().Bytes(), proposal, publishCid) if err != nil { From d90ab2b81fea5ba1d5d0e91134d1f843d99f20e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 25 May 2021 16:20:49 +0200 Subject: [PATCH 042/160] config: Document batching fields --- node/config/def.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/node/config/def.go b/node/config/def.go index c0aa82a59..1c82e9b17 100644 --- a/node/config/def.go +++ b/node/config/def.go @@ -84,16 +84,24 @@ type SealingConfig struct { AlwaysKeepUnsealedCopy bool + // enable / disable precommit batching (takes effect after nv13) BatchPreCommits bool + // maximum precommit batch size - batches will be sent immediately above this size MaxPreCommitBatch int MinPreCommitBatch int + // how long to wait before submitting a batch after crossing the minimum batch size PreCommitBatchWait Duration + // time buffer for forceful batch submission before sectors in batch would start expiring PreCommitBatchSlack Duration + // enable / disable commit aggregation (takes effect after nv13) AggregateCommits bool + // maximum batched commit size - batches will be sent immediately above this size MinCommitBatch int MaxCommitBatch int + // how long to wait before submitting a batch after crossing the minimum batch size CommitBatchWait Duration + // time buffer for forceful batch submission before sectors in batch would start expiring CommitBatchSlack Duration TerminateBatchMax uint64 From 04658e1cae27781599592558925fa52a102681f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 25 May 2021 16:26:59 +0200 Subject: [PATCH 043/160] fix lint --- extern/sector-storage/mock/mock.go | 10 ---------- node/config/def.go | 14 +++++++------- 2 files changed, 7 insertions(+), 17 deletions(-) diff --git a/extern/sector-storage/mock/mock.go b/extern/sector-storage/mock/mock.go index eac75d176..a622f8ccb 100644 --- a/extern/sector-storage/mock/mock.go +++ b/extern/sector-storage/mock/mock.go @@ -522,11 +522,6 @@ func (m mockVerifProver) VerifyAggregateSeals(aggregate proof5.AggregateSealVeri } } - var sis []abi.SectorNumber - for _, info := range aggregate.Infos { - sis = append(sis, info.Number) - } - return bytes.Equal(aggregate.Proof, out), nil } @@ -538,11 +533,6 @@ func (m mockVerifProver) AggregateSealProofs(aggregateInfo proof5.AggregateSealV } } - var sis []abi.SectorNumber - for _, info := range aggregateInfo.Infos { - sis = append(sis, info.Number) - } - return out, nil } diff --git a/node/config/def.go b/node/config/def.go index 1c82e9b17..988b0a6c9 100644 --- a/node/config/def.go +++ b/node/config/def.go @@ -85,22 +85,22 @@ type SealingConfig struct { AlwaysKeepUnsealedCopy bool // enable / disable precommit batching (takes effect after nv13) - BatchPreCommits bool + BatchPreCommits bool // maximum precommit batch size - batches will be sent immediately above this size - MaxPreCommitBatch int - MinPreCommitBatch int + MaxPreCommitBatch int + MinPreCommitBatch int // how long to wait before submitting a batch after crossing the minimum batch size - PreCommitBatchWait Duration + PreCommitBatchWait Duration // time buffer for forceful batch submission before sectors in batch would start expiring PreCommitBatchSlack Duration // enable / disable commit aggregation (takes effect after nv13) AggregateCommits bool // maximum batched commit size - batches will be sent immediately above this size - MinCommitBatch int - MaxCommitBatch int + MinCommitBatch int + MaxCommitBatch int // how long to wait before submitting a batch after crossing the minimum batch size - CommitBatchWait Duration + CommitBatchWait Duration // time buffer for forceful batch submission before sectors in batch would start expiring CommitBatchSlack Duration From 7bd0fcbb24294ebccaaa061a001f7ce95519039c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 25 May 2021 16:47:42 +0200 Subject: [PATCH 044/160] sealing: Don't start batch timers with empty batches --- extern/storage-sealing/commit_batch.go | 14 +++++++++----- extern/storage-sealing/precommit_batch.go | 14 +++++++++----- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/extern/storage-sealing/commit_batch.go b/extern/storage-sealing/commit_batch.go index 53f572712..88c8bdf37 100644 --- a/extern/storage-sealing/commit_batch.go +++ b/extern/storage-sealing/commit_batch.go @@ -105,7 +105,7 @@ func (b *CommitBatcher) run() { return case <-b.notify: sendAboveMax = true - case <-time.After(b.batchWait(cfg.CommitBatchWait, cfg.CommitBatchSlack)): + case <-b.batchWait(cfg.CommitBatchWait, cfg.CommitBatchSlack): sendAboveMin = true case fr := <-b.force: // user triggered forceRes = fr @@ -119,12 +119,16 @@ func (b *CommitBatcher) run() { } } -func (b *CommitBatcher) batchWait(maxWait, slack time.Duration) time.Duration { +func (b *CommitBatcher) batchWait(maxWait, slack time.Duration) <-chan time.Time { now := time.Now() b.lk.Lock() defer b.lk.Unlock() + if len(b.todo) == 0 { + return nil + } + var deadline time.Time for sn := range b.todo { sectorDeadline := b.deadlines[sn] @@ -140,12 +144,12 @@ func (b *CommitBatcher) batchWait(maxWait, slack time.Duration) time.Duration { } if deadline.IsZero() { - return maxWait + return time.After(maxWait) } deadline = deadline.Add(-slack) if deadline.Before(now) { - return time.Nanosecond // can't return 0 + return time.After(time.Nanosecond) // can't return 0 } wait := deadline.Sub(now) @@ -153,7 +157,7 @@ func (b *CommitBatcher) batchWait(maxWait, slack time.Duration) time.Duration { wait = maxWait } - return wait + return time.After(wait) } func (b *CommitBatcher) processBatch(notif, after bool) (*cid.Cid, error) { diff --git a/extern/storage-sealing/precommit_batch.go b/extern/storage-sealing/precommit_batch.go index f16e5c5ee..69ba47c60 100644 --- a/extern/storage-sealing/precommit_batch.go +++ b/extern/storage-sealing/precommit_batch.go @@ -95,7 +95,7 @@ func (b *PreCommitBatcher) run() { return case <-b.notify: sendAboveMax = true - case <-time.After(b.batchWait(cfg.PreCommitBatchWait, cfg.PreCommitBatchSlack)): + case <-b.batchWait(cfg.PreCommitBatchWait, cfg.PreCommitBatchSlack): sendAboveMin = true case fr := <-b.force: // user triggered forceRes = fr @@ -109,12 +109,16 @@ func (b *PreCommitBatcher) run() { } } -func (b *PreCommitBatcher) batchWait(maxWait, slack time.Duration) time.Duration { +func (b *PreCommitBatcher) batchWait(maxWait, slack time.Duration) <-chan time.Time { now := time.Now() b.lk.Lock() defer b.lk.Unlock() + if len(b.todo) == 0 { + return nil + } + var deadline time.Time for sn := range b.todo { sectorDeadline := b.deadlines[sn] @@ -130,12 +134,12 @@ func (b *PreCommitBatcher) batchWait(maxWait, slack time.Duration) time.Duration } if deadline.IsZero() { - return maxWait + return time.After(maxWait) } deadline = deadline.Add(-slack) if deadline.Before(now) { - return time.Nanosecond // can't return 0 + return time.After(time.Nanosecond) // can't return 0 } wait := deadline.Sub(now) @@ -143,7 +147,7 @@ func (b *PreCommitBatcher) batchWait(maxWait, slack time.Duration) time.Duration wait = maxWait } - return wait + return time.After(wait) } func (b *PreCommitBatcher) processBatch(notif, after bool) (*cid.Cid, error) { From 4c6c9a0edbd9ac9162e08c24ec1b26c198b82cca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 25 May 2021 21:27:32 +0200 Subject: [PATCH 045/160] Fix windowpost/deadline tests --- api/test/deadlines.go | 2 +- api/test/test.go | 25 +++++++++++++++++++++++++ api/test/window_post.go | 4 ++-- node/node_test.go | 4 ++++ 4 files changed, 32 insertions(+), 3 deletions(-) diff --git a/api/test/deadlines.go b/api/test/deadlines.go index 8060c18f3..68186f0e1 100644 --- a/api/test/deadlines.go +++ b/api/test/deadlines.go @@ -61,7 +61,7 @@ func TestDeadlineToggling(t *testing.T, b APIBuilder, blocktime time.Duration) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - n, sn := b(t, []FullNodeOpts{FullNodeWithLatestActorsAt(upgradeH)}, OneMiner) + n, sn := b(t, []FullNodeOpts{FullNodeWithV4ActorsAt(upgradeH)}, OneMiner) client := n[0].FullNode.(*impl.FullNodeAPI) minerA := sn[0] diff --git a/api/test/test.go b/api/test/test.go index 44530191b..c064ca6fb 100644 --- a/api/test/test.go +++ b/api/test/test.go @@ -169,6 +169,31 @@ var FullNodeWithSDRAt = func(calico, persian abi.ChainEpoch) FullNodeOpts { } } +var FullNodeWithV4ActorsAt = func(upgradeHeight abi.ChainEpoch) FullNodeOpts { + if upgradeHeight == -1 { + upgradeHeight = 3 + } + + return FullNodeOpts{ + Opts: func(nodes []TestNode) node.Option { + return node.Override(new(stmgr.UpgradeSchedule), stmgr.UpgradeSchedule{{ + // prepare for upgrade. + Network: network.Version9, + Height: 1, + Migration: stmgr.UpgradeActorsV2, + }, { + Network: network.Version10, + Height: 2, + Migration: stmgr.UpgradeActorsV3, + }, { + Network: network.Version12, + Height: upgradeHeight, + Migration: stmgr.UpgradeActorsV4, + }}) + }, + } +} + var MineNext = miner.MineReq{ InjectNulls: 0, Done: func(bool, abi.ChainEpoch, error) {}, diff --git a/api/test/window_post.go b/api/test/window_post.go index 128a22641..e6e3ff2a3 100644 --- a/api/test/window_post.go +++ b/api/test/window_post.go @@ -439,7 +439,7 @@ func TestWindowPostDispute(t *testing.T, b APIBuilder, blocktime time.Duration) /// // Then we're going to manually submit bad proofs. n, sn := b(t, []FullNodeOpts{ - FullNodeWithLatestActorsAt(-1), + FullNodeWithV4ActorsAt(-1), }, []StorageMiner{ {Full: 0, Preseal: PresealGenesis}, {Full: 0}, @@ -722,7 +722,7 @@ func TestWindowPostDisputeFails(t *testing.T, b APIBuilder, blocktime time.Durat ctx, cancel := context.WithCancel(context.Background()) defer cancel() - n, sn := b(t, []FullNodeOpts{FullNodeWithLatestActorsAt(-1)}, OneMiner) + n, sn := b(t, []FullNodeOpts{FullNodeWithV4ActorsAt(-1)}, OneMiner) client := n[0].FullNode.(*impl.FullNodeAPI) miner := sn[0] diff --git a/node/node_test.go b/node/node_test.go index 1cc089b1e..5ae15fd8c 100644 --- a/node/node_test.go +++ b/node/node_test.go @@ -173,6 +173,7 @@ func TestWindowedPost(t *testing.T) { } logging.SetLogLevel("miner", "ERROR") + logging.SetLogLevel("gen", "ERROR") logging.SetLogLevel("chainstore", "ERROR") logging.SetLogLevel("chain", "ERROR") logging.SetLogLevel("sub", "ERROR") @@ -221,6 +222,7 @@ func TestWindowPostDispute(t *testing.T) { t.Skip("this takes a few minutes, set LOTUS_TEST_WINDOW_POST=1 to run") } logging.SetLogLevel("miner", "ERROR") + logging.SetLogLevel("gen", "ERROR") logging.SetLogLevel("chainstore", "ERROR") logging.SetLogLevel("chain", "ERROR") logging.SetLogLevel("sub", "ERROR") @@ -234,6 +236,7 @@ func TestWindowPostDisputeFails(t *testing.T) { t.Skip("this takes a few minutes, set LOTUS_TEST_WINDOW_POST=1 to run") } logging.SetLogLevel("miner", "ERROR") + logging.SetLogLevel("gen", "ERROR") logging.SetLogLevel("chainstore", "ERROR") logging.SetLogLevel("chain", "ERROR") logging.SetLogLevel("sub", "ERROR") @@ -247,6 +250,7 @@ func TestDeadlineToggling(t *testing.T) { t.Skip("this takes a few minutes, set LOTUS_TEST_DEADLINE_TOGGLING=1 to run") } logging.SetLogLevel("miner", "ERROR") + logging.SetLogLevel("gen", "ERROR") logging.SetLogLevel("chainstore", "ERROR") logging.SetLogLevel("chain", "ERROR") logging.SetLogLevel("sub", "ERROR") From fc76a09b192b208b927ae4fe0dad9f0963d44d9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 26 May 2021 09:58:57 +0200 Subject: [PATCH 046/160] mock: Use real aggregate lengths --- extern/sector-storage/mock/mock.go | 33 ++++++++++++++++++++++++-- extern/storage-sealing/commit_batch.go | 3 +-- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/extern/sector-storage/mock/mock.go b/extern/sector-storage/mock/mock.go index a622f8ccb..52496f836 100644 --- a/extern/sector-storage/mock/mock.go +++ b/extern/sector-storage/mock/mock.go @@ -512,7 +512,7 @@ func (m mockVerifProver) VerifySeal(svi proof5.SealVerifyInfo) (bool, error) { } func (m mockVerifProver) VerifyAggregateSeals(aggregate proof5.AggregateSealVerifyProofAndInfos) (bool, error) { - out := make([]byte, 200) + out := make([]byte, m.aggLen(len(aggregate.Infos))) for pi, svi := range aggregate.Infos { for i := 0; i < 32; i++ { b := svi.UnsealedCID.Bytes()[i] + svi.SealedCID.Bytes()[31-i] - svi.InteractiveRandomness[i]*svi.Randomness[i] // raw proof byte @@ -526,7 +526,7 @@ func (m mockVerifProver) VerifyAggregateSeals(aggregate proof5.AggregateSealVeri } func (m mockVerifProver) AggregateSealProofs(aggregateInfo proof5.AggregateSealVerifyProofAndInfos, proofs [][]byte) ([]byte, error) { - out := make([]byte, 200) // todo: figure out more real length + out := make([]byte, m.aggLen(len(aggregateInfo.Infos))) // todo: figure out more real length for pi, proof := range proofs { for i := range proof[:32] { out[i] += proof[i] * uint8(pi) @@ -536,6 +536,35 @@ func (m mockVerifProver) AggregateSealProofs(aggregateInfo proof5.AggregateSealV return out, nil } +func (m mockVerifProver) aggLen(nproofs int) int { + switch { + case nproofs <= 8: + return 11220 + case nproofs <= 16: + return 14196 + case nproofs <= 32: + return 17172 + case nproofs <= 64: + return 20148 + case nproofs <= 128: + return 23124 + case nproofs <= 256: + return 26100 + case nproofs <= 512: + return 29076 + case nproofs <= 1024: + return 32052 + case nproofs <= 2048: + return 35028 + case nproofs <= 4096: + return 38004 + case nproofs <= 8192: + return 40980 + default: + panic("too many proofs") + } +} + func (m mockVerifProver) VerifyWinningPoSt(ctx context.Context, info proof5.WinningPoStVerifyInfo) (bool, error) { info.Randomness[31] &= 0x3f return true, nil diff --git a/extern/storage-sealing/commit_batch.go b/extern/storage-sealing/commit_batch.go index 88c8bdf37..845400ccf 100644 --- a/extern/storage-sealing/commit_batch.go +++ b/extern/storage-sealing/commit_batch.go @@ -33,8 +33,7 @@ type CommitBatcherApi interface { } type AggregateInput struct { - spt abi.RegisteredSealProof - // TODO: Something changed in actors, I think this now needs to be AggregateSealVerifyProofAndInfos todo ?? + spt abi.RegisteredSealProof info proof5.AggregateSealVerifyInfo proof []byte } From 21b4741e302e61cf7a994a4c5dc58cd49917fe50 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Wed, 26 May 2021 19:03:46 -0400 Subject: [PATCH 047/160] Fix randomness fetching around null blocks --- chain/gen/gen.go | 40 +++++--- chain/gen/genesis/miners.go | 16 +++- chain/store/store.go | 40 ++++++-- chain/store/store_test.go | 2 +- chain/sync_test.go | 180 +++++++++++++++++++++++++++++------- chain/vm/runtime.go | 22 ++++- chain/vm/vm.go | 6 +- conformance/rand_fixed.go | 12 ++- conformance/rand_record.go | 21 ++++- conformance/rand_replay.go | 33 ++++++- node/impl/full/chain.go | 16 +++- 11 files changed, 314 insertions(+), 74 deletions(-) diff --git a/chain/gen/gen.go b/chain/gen/gen.go index 27feaeaaa..319b7ac29 100644 --- a/chain/gen/gen.go +++ b/chain/gen/gen.go @@ -75,9 +75,10 @@ type ChainGen struct { w *wallet.LocalWallet - eppProvs map[address.Address]WinningPoStProver - Miners []address.Address - receivers []address.Address + eppProvs map[address.Address]WinningPoStProver + Miners []address.Address + receivers []address.Address + // a SecP address banker address.Address bankerNonce uint64 @@ -110,7 +111,7 @@ var DefaultRemainderAccountActor = genesis.Actor{ Meta: remAccMeta.ActorMeta(), } -func NewGeneratorWithSectors(numSectors int) (*ChainGen, error) { +func NewGeneratorWithSectorsAndUpgradeSchedule(numSectors int, us stmgr.UpgradeSchedule) (*ChainGen, error) { j := journal.NilJournal() // TODO: we really shouldn't modify a global variable here. policy.SetSupportedProofTypes(abi.RegisteredSealProof_StackedDrg2KiBV1) @@ -244,7 +245,10 @@ func NewGeneratorWithSectors(numSectors int) (*ChainGen, error) { mgen[genesis2.MinerAddress(uint64(i))] = &wppProvider{} } - sm := stmgr.NewStateManager(cs) + sm, err := stmgr.NewStateManagerWithUpgradeSchedule(cs, us) + if err != nil { + return nil, xerrors.Errorf("initing stmgr: %w", err) + } miners := []address.Address{maddr1, maddr2} @@ -282,6 +286,14 @@ func NewGenerator() (*ChainGen, error) { return NewGeneratorWithSectors(1) } +func NewGeneratorWithSectors(numSectors int) (*ChainGen, error) { + return NewGeneratorWithSectorsAndUpgradeSchedule(numSectors, stmgr.DefaultUpgradeSchedule()) +} + +func NewGeneratorWithUpgradeSchedule(us stmgr.UpgradeSchedule) (*ChainGen, error) { + return NewGeneratorWithSectorsAndUpgradeSchedule(1, us) +} + func (cg *ChainGen) StateManager() *stmgr.StateManager { return cg.sm } @@ -384,7 +396,7 @@ type MinedTipSet struct { } func (cg *ChainGen) NextTipSet() (*MinedTipSet, error) { - mts, err := cg.NextTipSetFromMiners(cg.CurTipset.TipSet(), cg.Miners) + mts, err := cg.NextTipSetFromMiners(cg.CurTipset.TipSet(), cg.Miners, 0) if err != nil { return nil, err } @@ -397,7 +409,7 @@ func (cg *ChainGen) SetWinningPoStProver(m address.Address, wpp WinningPoStProve cg.eppProvs[m] = wpp } -func (cg *ChainGen) NextTipSetFromMiners(base *types.TipSet, miners []address.Address) (*MinedTipSet, error) { +func (cg *ChainGen) NextTipSetFromMiners(base *types.TipSet, miners []address.Address, nulls abi.ChainEpoch) (*MinedTipSet, error) { ms, err := cg.GetMessages(cg) if err != nil { return nil, xerrors.Errorf("get random messages: %w", err) @@ -408,21 +420,23 @@ func (cg *ChainGen) NextTipSetFromMiners(base *types.TipSet, miners []address.Ad msgs[i] = ms } - fts, err := cg.NextTipSetFromMinersWithMessages(base, miners, msgs) + fts, err := cg.NextTipSetFromMinersWithMessagesAndNulls(base, miners, msgs, nulls) if err != nil { return nil, err } + cg.CurTipset = fts + return &MinedTipSet{ TipSet: fts, Messages: ms, }, nil } -func (cg *ChainGen) NextTipSetFromMinersWithMessages(base *types.TipSet, miners []address.Address, msgs [][]*types.SignedMessage) (*store.FullTipSet, error) { +func (cg *ChainGen) NextTipSetFromMinersWithMessagesAndNulls(base *types.TipSet, miners []address.Address, msgs [][]*types.SignedMessage, nulls abi.ChainEpoch) (*store.FullTipSet, error) { var blks []*types.FullBlock - for round := base.Height() + 1; len(blks) == 0; round++ { + for round := base.Height() + nulls + 1; len(blks) == 0; round++ { for mi, m := range miners { bvals, et, ticket, err := cg.nextBlockProof(context.TODO(), base, m, round) if err != nil { @@ -455,6 +469,8 @@ func (cg *ChainGen) NextTipSetFromMinersWithMessages(base *types.TipSet, miners return nil, err } + cg.CurTipset = fts + return fts, nil } @@ -574,7 +590,7 @@ func (mca mca) ChainGetRandomnessFromTickets(ctx context.Context, tsk types.TipS return nil, xerrors.Errorf("loading tipset key: %w", err) } - return mca.sm.ChainStore().GetChainRandomness(ctx, pts.Cids(), personalization, randEpoch, entropy) + return mca.sm.ChainStore().GetChainRandomnessLookingBack(ctx, pts.Cids(), personalization, randEpoch, entropy) } func (mca mca) ChainGetRandomnessFromBeacon(ctx context.Context, tsk types.TipSetKey, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte) (abi.Randomness, error) { @@ -583,7 +599,7 @@ func (mca mca) ChainGetRandomnessFromBeacon(ctx context.Context, tsk types.TipSe return nil, xerrors.Errorf("loading tipset key: %w", err) } - return mca.sm.ChainStore().GetBeaconRandomness(ctx, pts.Cids(), personalization, randEpoch, entropy) + return mca.sm.ChainStore().GetBeaconRandomnessLookingBack(ctx, pts.Cids(), personalization, randEpoch, entropy) } func (mca mca) MinerGetBaseInfo(ctx context.Context, maddr address.Address, epoch abi.ChainEpoch, tsk types.TipSetKey) (*api.MiningBaseInfo, error) { diff --git a/chain/gen/genesis/miners.go b/chain/gen/genesis/miners.go index be33560e5..86fd16812 100644 --- a/chain/gen/genesis/miners.go +++ b/chain/gen/genesis/miners.go @@ -335,13 +335,25 @@ func SetupStorageMiners(ctx context.Context, cs *store.ChainStore, sroot cid.Cid // TODO: copied from actors test harness, deduplicate or remove from here type fakeRand struct{} -func (fr *fakeRand) GetChainRandomness(ctx context.Context, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte) ([]byte, error) { +func (fr *fakeRand) GetChainRandomnessLookingForward(ctx context.Context, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte) ([]byte, error) { out := make([]byte, 32) _, _ = rand.New(rand.NewSource(int64(randEpoch * 1000))).Read(out) //nolint return out, nil } -func (fr *fakeRand) GetBeaconRandomness(ctx context.Context, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte) ([]byte, error) { +func (fr *fakeRand) GetChainRandomnessLookingBack(ctx context.Context, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte) ([]byte, error) { + out := make([]byte, 32) + _, _ = rand.New(rand.NewSource(int64(randEpoch * 1000))).Read(out) //nolint + return out, nil +} + +func (fr *fakeRand) GetBeaconRandomnessLookingForward(ctx context.Context, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte) ([]byte, error) { + out := make([]byte, 32) + _, _ = rand.New(rand.NewSource(int64(randEpoch))).Read(out) //nolint + return out, nil +} + +func (fr *fakeRand) GetBeaconRandomnessLookingBack(ctx context.Context, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte) ([]byte, error) { out := make([]byte, 32) _, _ = rand.New(rand.NewSource(int64(randEpoch))).Read(out) //nolint return out, nil diff --git a/chain/store/store.go b/chain/store/store.go index 7ebe31ec4..dfde93fc7 100644 --- a/chain/store/store.go +++ b/chain/store/store.go @@ -1280,7 +1280,15 @@ func DrawRandomness(rbase []byte, pers crypto.DomainSeparationTag, round abi.Cha return h.Sum(nil), nil } -func (cs *ChainStore) GetBeaconRandomness(ctx context.Context, blks []cid.Cid, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) { +func (cs *ChainStore) GetBeaconRandomnessLookingBack(ctx context.Context, blks []cid.Cid, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) { + return cs.GetBeaconRandomness(ctx, blks, pers, round, entropy, true) +} + +func (cs *ChainStore) GetBeaconRandomnessLookingForward(ctx context.Context, blks []cid.Cid, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) { + return cs.GetBeaconRandomness(ctx, blks, pers, round, entropy, false) +} + +func (cs *ChainStore) GetBeaconRandomness(ctx context.Context, blks []cid.Cid, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte, lookback bool) ([]byte, error) { _, span := trace.StartSpan(ctx, "store.GetBeaconRandomness") defer span.End() span.AddAttributes(trace.Int64Attribute("round", int64(round))) @@ -1299,7 +1307,7 @@ func (cs *ChainStore) GetBeaconRandomness(ctx context.Context, blks []cid.Cid, p searchHeight = 0 } - randTs, err := cs.GetTipsetByHeight(ctx, searchHeight, ts, true) + randTs, err := cs.GetTipsetByHeight(ctx, searchHeight, ts, lookback) if err != nil { return nil, err } @@ -1314,7 +1322,15 @@ func (cs *ChainStore) GetBeaconRandomness(ctx context.Context, blks []cid.Cid, p return DrawRandomness(be.Data, pers, round, entropy) } -func (cs *ChainStore) GetChainRandomness(ctx context.Context, blks []cid.Cid, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) { +func (cs *ChainStore) GetChainRandomnessLookingBack(ctx context.Context, blks []cid.Cid, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) { + return cs.GetChainRandomness(ctx, blks, pers, round, entropy, true) +} + +func (cs *ChainStore) GetChainRandomnessLookingForward(ctx context.Context, blks []cid.Cid, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) { + return cs.GetChainRandomness(ctx, blks, pers, round, entropy, false) +} + +func (cs *ChainStore) GetChainRandomness(ctx context.Context, blks []cid.Cid, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte, lookback bool) ([]byte, error) { _, span := trace.StartSpan(ctx, "store.GetChainRandomness") defer span.End() span.AddAttributes(trace.Int64Attribute("round", int64(round))) @@ -1333,7 +1349,7 @@ func (cs *ChainStore) GetChainRandomness(ctx context.Context, blks []cid.Cid, pe searchHeight = 0 } - randTs, err := cs.GetTipsetByHeight(ctx, searchHeight, ts, true) + randTs, err := cs.GetTipsetByHeight(ctx, searchHeight, ts, lookback) if err != nil { return nil, err } @@ -1608,12 +1624,20 @@ func NewChainRand(cs *ChainStore, blks []cid.Cid) vm.Rand { } } -func (cr *chainRand) GetChainRandomness(ctx context.Context, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) { - return cr.cs.GetChainRandomness(ctx, cr.blks, pers, round, entropy) +func (cr *chainRand) GetChainRandomnessLookingBack(ctx context.Context, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) { + return cr.cs.GetChainRandomnessLookingBack(ctx, cr.blks, pers, round, entropy) } -func (cr *chainRand) GetBeaconRandomness(ctx context.Context, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) { - return cr.cs.GetBeaconRandomness(ctx, cr.blks, pers, round, entropy) +func (cr *chainRand) GetChainRandomnessLookingForward(ctx context.Context, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) { + return cr.cs.GetChainRandomnessLookingForward(ctx, cr.blks, pers, round, entropy) +} + +func (cr *chainRand) GetBeaconRandomnessLookingBack(ctx context.Context, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) { + return cr.cs.GetBeaconRandomnessLookingBack(ctx, cr.blks, pers, round, entropy) +} + +func (cr *chainRand) GetBeaconRandomnessLookingForward(ctx context.Context, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) { + return cr.cs.GetBeaconRandomnessLookingForward(ctx, cr.blks, pers, round, entropy) } func (cs *ChainStore) GetTipSetFromKey(tsk types.TipSetKey) (*types.TipSet, error) { diff --git a/chain/store/store_test.go b/chain/store/store_test.go index 51e2e08d0..62a0430e3 100644 --- a/chain/store/store_test.go +++ b/chain/store/store_test.go @@ -76,7 +76,7 @@ func BenchmarkGetRandomness(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - _, err := cs.GetChainRandomness(context.TODO(), last.Cids(), crypto.DomainSeparationTag_SealRandomness, 500, nil) + _, err := cs.GetChainRandomnessLookingBack(context.TODO(), last.Cids(), crypto.DomainSeparationTag_SealRandomness, 500, nil) if err != nil { b.Fatal(err) } diff --git a/chain/sync_test.go b/chain/sync_test.go index 9570eda32..2e5a5f3cf 100644 --- a/chain/sync_test.go +++ b/chain/sync_test.go @@ -7,6 +7,12 @@ import ( "testing" "time" + "github.com/filecoin-project/go-state-types/crypto" + + "github.com/filecoin-project/go-state-types/network" + + "github.com/filecoin-project/lotus/chain/stmgr" + "github.com/ipfs/go-cid" ds "github.com/ipfs/go-datastore" @@ -101,7 +107,7 @@ func prepSyncTest(t testing.TB, h int) *syncTestUtil { g: g, } - tu.addSourceNode(h) + tu.addSourceNode(stmgr.DefaultUpgradeSchedule(), h) //tu.checkHeight("source", source, h) // separate logs @@ -110,6 +116,53 @@ func prepSyncTest(t testing.TB, h int) *syncTestUtil { return tu } +func prepSyncTestWithV5Height(t testing.TB, h int, v5height abi.ChainEpoch) *syncTestUtil { + logging.SetLogLevel("*", "INFO") + + us := stmgr.UpgradeSchedule{{ + // prepare for upgrade. + Network: network.Version9, + Height: 1, + Migration: stmgr.UpgradeActorsV2, + }, { + Network: network.Version10, + Height: 2, + Migration: stmgr.UpgradeActorsV3, + }, { + Network: network.Version12, + Height: 3, + Migration: stmgr.UpgradeActorsV4, + }, { + Network: network.Version13, + Height: v5height, + Migration: stmgr.UpgradeActorsV5, + }} + + g, err := gen.NewGeneratorWithUpgradeSchedule(us) + + if err != nil { + t.Fatalf("%+v", err) + } + + ctx, cancel := context.WithCancel(context.Background()) + + tu := &syncTestUtil{ + t: t, + ctx: ctx, + cancel: cancel, + + mn: mocknet.New(ctx), + g: g, + } + + tu.addSourceNode(us, h) + //tu.checkHeight("source", source, h) + + // separate logs + fmt.Println("\x1b[31m///////////////////////////////////////////////////\x1b[39b") + return tu +} + func (tu *syncTestUtil) Shutdown() { tu.cancel() } @@ -174,7 +227,7 @@ func (tu *syncTestUtil) pushTsExpectErr(to int, fts *store.FullTipSet, experr bo } } -func (tu *syncTestUtil) mineOnBlock(blk *store.FullTipSet, to int, miners []int, wait, fail bool, msgs [][]*types.SignedMessage) *store.FullTipSet { +func (tu *syncTestUtil) mineOnBlock(blk *store.FullTipSet, to int, miners []int, wait, fail bool, msgs [][]*types.SignedMessage, nulls abi.ChainEpoch) *store.FullTipSet { if miners == nil { for i := range tu.g.Miners { miners = append(miners, i) @@ -191,10 +244,10 @@ func (tu *syncTestUtil) mineOnBlock(blk *store.FullTipSet, to int, miners []int, var nts *store.FullTipSet var err error if msgs != nil { - nts, err = tu.g.NextTipSetFromMinersWithMessages(blk.TipSet(), maddrs, msgs) + nts, err = tu.g.NextTipSetFromMinersWithMessagesAndNulls(blk.TipSet(), maddrs, msgs, 0) require.NoError(tu.t, err) } else { - mt, err := tu.g.NextTipSetFromMiners(blk.TipSet(), maddrs) + mt, err := tu.g.NextTipSetFromMiners(blk.TipSet(), maddrs, nulls) require.NoError(tu.t, err) nts = mt.TipSet } @@ -209,11 +262,11 @@ func (tu *syncTestUtil) mineOnBlock(blk *store.FullTipSet, to int, miners []int, } func (tu *syncTestUtil) mineNewBlock(src int, miners []int) { - mts := tu.mineOnBlock(tu.g.CurTipset, src, miners, true, false, nil) + mts := tu.mineOnBlock(tu.g.CurTipset, src, miners, true, false, nil, 0) tu.g.CurTipset = mts } -func (tu *syncTestUtil) addSourceNode(gen int) { +func (tu *syncTestUtil) addSourceNode(us stmgr.UpgradeSchedule, gen int) { if tu.genesis != nil { tu.t.Fatal("source node already exists") } @@ -229,6 +282,7 @@ func (tu *syncTestUtil) addSourceNode(gen int) { node.Test(), node.Override(new(modules.Genesis), modules.LoadGenesis(genesis)), + node.Override(new(stmgr.UpgradeSchedule), us), ) require.NoError(tu.t, err) tu.t.Cleanup(func() { _ = stop(context.Background()) }) @@ -442,7 +496,7 @@ func TestSyncBadTimestamp(t *testing.T) { fmt.Println("BASE: ", base.Cids()) tu.printHeads() - a1 := tu.mineOnBlock(base, 0, nil, false, true, nil) + a1 := tu.mineOnBlock(base, 0, nil, false, true, nil, 0) tu.g.Timestamper = nil require.NoError(t, tu.g.ResyncBankerNonce(a1.TipSet())) @@ -451,7 +505,7 @@ func TestSyncBadTimestamp(t *testing.T) { fmt.Println("After mine bad block!") tu.printHeads() - a2 := tu.mineOnBlock(base, 0, nil, true, false, nil) + a2 := tu.mineOnBlock(base, 0, nil, true, false, nil, 0) tu.waitUntilSync(0, client) @@ -495,7 +549,7 @@ func TestSyncBadWinningPoSt(t *testing.T) { tu.g.SetWinningPoStProver(tu.g.Miners[1], &badWpp{}) // now ensure that new blocks are not accepted - tu.mineOnBlock(base, client, nil, false, true, nil) + tu.mineOnBlock(base, client, nil, false, true, nil, 0) } func (tu *syncTestUtil) loadChainToNode(to int) { @@ -540,16 +594,16 @@ func TestSyncFork(t *testing.T) { fmt.Println("Mining base: ", base.TipSet().Cids(), base.TipSet().Height()) // The two nodes fork at this point into 'a' and 'b' - a1 := tu.mineOnBlock(base, p1, []int{0}, true, false, nil) - a := tu.mineOnBlock(a1, p1, []int{0}, true, false, nil) - a = tu.mineOnBlock(a, p1, []int{0}, true, false, nil) + a1 := tu.mineOnBlock(base, p1, []int{0}, true, false, nil, 0) + a := tu.mineOnBlock(a1, p1, []int{0}, true, false, nil, 0) + a = tu.mineOnBlock(a, p1, []int{0}, true, false, nil, 0) require.NoError(t, tu.g.ResyncBankerNonce(a1.TipSet())) // chain B will now be heaviest - b := tu.mineOnBlock(base, p2, []int{1}, true, false, nil) - b = tu.mineOnBlock(b, p2, []int{1}, true, false, nil) - b = tu.mineOnBlock(b, p2, []int{1}, true, false, nil) - b = tu.mineOnBlock(b, p2, []int{1}, true, false, nil) + b := tu.mineOnBlock(base, p2, []int{1}, true, false, nil, 0) + b = tu.mineOnBlock(b, p2, []int{1}, true, false, nil, 0) + b = tu.mineOnBlock(b, p2, []int{1}, true, false, nil, 0) + b = tu.mineOnBlock(b, p2, []int{1}, true, false, nil, 0) fmt.Println("A: ", a.Cids(), a.TipSet().Height()) fmt.Println("B: ", b.Cids(), b.TipSet().Height()) @@ -611,13 +665,13 @@ func TestDuplicateNonce(t *testing.T) { msgs[k] = []*types.SignedMessage{makeMsg(tu.g.Miners[k])} } - ts1 := tu.mineOnBlock(base, 0, []int{0, 1}, true, false, msgs) + ts1 := tu.mineOnBlock(base, 0, []int{0, 1}, true, false, msgs, 0) tu.waitUntilSyncTarget(0, ts1.TipSet()) // mine another tipset - ts2 := tu.mineOnBlock(ts1, 0, []int{0, 1}, true, false, make([][]*types.SignedMessage, 2)) + ts2 := tu.mineOnBlock(ts1, 0, []int{0, 1}, true, false, make([][]*types.SignedMessage, 2), 0) tu.waitUntilSyncTarget(0, ts2.TipSet()) var includedMsg cid.Cid @@ -668,11 +722,15 @@ func TestBadNonce(t *testing.T) { base := tu.g.CurTipset + // Get the banker from computed tipset state, not the parent. + st, _, err := tu.g.StateManager().TipSetState(context.TODO(), base.TipSet()) + require.NoError(t, err) + ba, err := tu.g.StateManager().LoadActorRaw(context.TODO(), tu.g.Banker(), st) + require.NoError(t, err) + // Produce a message from the banker with a bad nonce makeBadMsg := func() *types.SignedMessage { - ba, err := tu.nds[0].StateGetActor(context.TODO(), tu.g.Banker(), base.TipSet().Key()) - require.NoError(t, err) msg := types.Message{ To: tu.g.Banker(), From: tu.g.Banker(), @@ -700,7 +758,7 @@ func TestBadNonce(t *testing.T) { msgs := make([][]*types.SignedMessage, 1) msgs[0] = []*types.SignedMessage{makeBadMsg()} - tu.mineOnBlock(base, 0, []int{0}, true, true, msgs) + tu.mineOnBlock(base, 0, []int{0}, true, true, msgs, 0) } func BenchmarkSyncBasic(b *testing.B) { @@ -765,19 +823,19 @@ func TestSyncCheckpointHead(t *testing.T) { fmt.Println("Mining base: ", base.TipSet().Cids(), base.TipSet().Height()) // The two nodes fork at this point into 'a' and 'b' - a1 := tu.mineOnBlock(base, p1, []int{0}, true, false, nil) - a := tu.mineOnBlock(a1, p1, []int{0}, true, false, nil) - a = tu.mineOnBlock(a, p1, []int{0}, true, false, nil) + a1 := tu.mineOnBlock(base, p1, []int{0}, true, false, nil, 0) + a := tu.mineOnBlock(a1, p1, []int{0}, true, false, nil, 0) + a = tu.mineOnBlock(a, p1, []int{0}, true, false, nil, 0) tu.waitUntilSyncTarget(p1, a.TipSet()) tu.checkpointTs(p1, a.TipSet().Key()) require.NoError(t, tu.g.ResyncBankerNonce(a1.TipSet())) // chain B will now be heaviest - b := tu.mineOnBlock(base, p2, []int{1}, true, false, nil) - b = tu.mineOnBlock(b, p2, []int{1}, true, false, nil) - b = tu.mineOnBlock(b, p2, []int{1}, true, false, nil) - b = tu.mineOnBlock(b, p2, []int{1}, true, false, nil) + b := tu.mineOnBlock(base, p2, []int{1}, true, false, nil, 0) + b = tu.mineOnBlock(b, p2, []int{1}, true, false, nil, 0) + b = tu.mineOnBlock(b, p2, []int{1}, true, false, nil, 0) + b = tu.mineOnBlock(b, p2, []int{1}, true, false, nil, 0) fmt.Println("A: ", a.Cids(), a.TipSet().Height()) fmt.Println("B: ", b.Cids(), b.TipSet().Height()) @@ -807,19 +865,19 @@ func TestSyncCheckpointEarlierThanHead(t *testing.T) { fmt.Println("Mining base: ", base.TipSet().Cids(), base.TipSet().Height()) // The two nodes fork at this point into 'a' and 'b' - a1 := tu.mineOnBlock(base, p1, []int{0}, true, false, nil) - a := tu.mineOnBlock(a1, p1, []int{0}, true, false, nil) - a = tu.mineOnBlock(a, p1, []int{0}, true, false, nil) + a1 := tu.mineOnBlock(base, p1, []int{0}, true, false, nil, 0) + a := tu.mineOnBlock(a1, p1, []int{0}, true, false, nil, 0) + a = tu.mineOnBlock(a, p1, []int{0}, true, false, nil, 0) tu.waitUntilSyncTarget(p1, a.TipSet()) tu.checkpointTs(p1, a1.TipSet().Key()) require.NoError(t, tu.g.ResyncBankerNonce(a1.TipSet())) // chain B will now be heaviest - b := tu.mineOnBlock(base, p2, []int{1}, true, false, nil) - b = tu.mineOnBlock(b, p2, []int{1}, true, false, nil) - b = tu.mineOnBlock(b, p2, []int{1}, true, false, nil) - b = tu.mineOnBlock(b, p2, []int{1}, true, false, nil) + b := tu.mineOnBlock(base, p2, []int{1}, true, false, nil, 0) + b = tu.mineOnBlock(b, p2, []int{1}, true, false, nil, 0) + b = tu.mineOnBlock(b, p2, []int{1}, true, false, nil, 0) + b = tu.mineOnBlock(b, p2, []int{1}, true, false, nil, 0) fmt.Println("A: ", a.Cids(), a.TipSet().Height()) fmt.Println("B: ", b.Cids(), b.TipSet().Height()) @@ -833,3 +891,55 @@ func TestSyncCheckpointEarlierThanHead(t *testing.T) { require.Equal(tu.t, p1Head, a.TipSet()) tu.assertBad(p1, b.TipSet()) } + +func TestDrandNull(t *testing.T) { + H := 10 + v5h := abi.ChainEpoch(50) + build.UpgradeHyperdriveHeight = v5h + tu := prepSyncTestWithV5Height(t, H, v5h) + + entropy := []byte{0, 2, 3, 4} + // arbitrarily chosen + pers := crypto.DomainSeparationTag_WinningPoStChallengeSeed + + beforeNull := tu.g.CurTipset + afterNull := tu.mineOnBlock(beforeNull, 0, nil, false, false, nil, 2) + nullHeight := beforeNull.TipSet().Height() + 1 + if afterNull.TipSet().Height() == nullHeight { + t.Fatal("didn't inject nulls as expected") + } + + rand, err := tu.nds[0].ChainGetRandomnessFromBeacon(tu.ctx, afterNull.TipSet().Key(), pers, nullHeight, entropy) + require.NoError(t, err) + + // calculate the expected randomness based on the beacon BEFORE the null + expectedBE := beforeNull.Blocks[0].Header.BeaconEntries + expectedRand, err := store.DrawRandomness(expectedBE[len(expectedBE)-1].Data, pers, nullHeight, entropy) + require.NoError(t, err) + + require.Equal(t, []byte(rand), expectedRand) + + // zoom zoom to past the v5 upgrade by injecting many many nulls + postUpgrade := tu.mineOnBlock(afterNull, 0, nil, false, false, nil, v5h) + nv, err := tu.nds[0].StateNetworkVersion(tu.ctx, types.EmptyTSK) + require.NoError(t, err) + if nv != network.Version13 { + t.Fatal("expect to be v13 by now") + } + + afterNull = tu.mineOnBlock(postUpgrade, 0, nil, false, false, nil, 2) + nullHeight = postUpgrade.TipSet().Height() + 1 + if afterNull.TipSet().Height() == nullHeight { + t.Fatal("didn't inject nulls as expected") + } + + rand, err = tu.nds[0].ChainGetRandomnessFromBeacon(tu.ctx, afterNull.TipSet().Key(), pers, nullHeight, entropy) + require.NoError(t, err) + + // calculate the expected randomness based on the beacon AFTER the null + expectedBE = afterNull.Blocks[0].Header.BeaconEntries + expectedRand, err = store.DrawRandomness(expectedBE[len(expectedBE)-1].Data, pers, nullHeight, entropy) + require.NoError(t, err) + + require.Equal(t, []byte(rand), expectedRand) +} diff --git a/chain/vm/runtime.go b/chain/vm/runtime.go index a3e2f293f..f89bd9f40 100644 --- a/chain/vm/runtime.go +++ b/chain/vm/runtime.go @@ -208,17 +208,31 @@ func (rt *Runtime) GetActorCodeCID(addr address.Address) (ret cid.Cid, ok bool) } func (rt *Runtime) GetRandomnessFromTickets(personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte) abi.Randomness { - res, err := rt.vm.rand.GetChainRandomness(rt.ctx, personalization, randEpoch, entropy) + var err error + var res []byte + if rt.vm.GetNtwkVersion(rt.ctx, randEpoch) >= network.Version13 { + res, err = rt.vm.rand.GetChainRandomnessLookingForward(rt.ctx, personalization, randEpoch, entropy) + } else { + res, err = rt.vm.rand.GetChainRandomnessLookingBack(rt.ctx, personalization, randEpoch, entropy) + } + if err != nil { - panic(aerrors.Fatalf("could not get randomness: %s", err)) + panic(aerrors.Fatalf("could not get ticket randomness: %s", err)) } return res } func (rt *Runtime) GetRandomnessFromBeacon(personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte) abi.Randomness { - res, err := rt.vm.rand.GetBeaconRandomness(rt.ctx, personalization, randEpoch, entropy) + var err error + var res []byte + if rt.vm.GetNtwkVersion(rt.ctx, randEpoch) >= network.Version13 { + res, err = rt.vm.rand.GetBeaconRandomnessLookingForward(rt.ctx, personalization, randEpoch, entropy) + } else { + res, err = rt.vm.rand.GetBeaconRandomnessLookingBack(rt.ctx, personalization, randEpoch, entropy) + } + if err != nil { - panic(aerrors.Fatalf("could not get randomness: %s", err)) + panic(aerrors.Fatalf("could not get beacon randomness: %s", err)) } return res } diff --git a/chain/vm/vm.go b/chain/vm/vm.go index afc74e744..88ec93a80 100644 --- a/chain/vm/vm.go +++ b/chain/vm/vm.go @@ -255,8 +255,10 @@ func NewVM(ctx context.Context, opts *VMOpts) (*VM, error) { } type Rand interface { - GetChainRandomness(ctx context.Context, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) - GetBeaconRandomness(ctx context.Context, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) + GetChainRandomnessLookingBack(ctx context.Context, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) + GetChainRandomnessLookingForward(ctx context.Context, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) + GetBeaconRandomnessLookingBack(ctx context.Context, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) + GetBeaconRandomnessLookingForward(ctx context.Context, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) } type ApplyRet struct { diff --git a/conformance/rand_fixed.go b/conformance/rand_fixed.go index d356b53d0..f15910e1d 100644 --- a/conformance/rand_fixed.go +++ b/conformance/rand_fixed.go @@ -19,10 +19,18 @@ func NewFixedRand() vm.Rand { return &fixedRand{} } -func (r *fixedRand) GetChainRandomness(_ context.Context, _ crypto.DomainSeparationTag, _ abi.ChainEpoch, _ []byte) ([]byte, error) { +func (r *fixedRand) GetChainRandomnessLookingForward(_ context.Context, _ crypto.DomainSeparationTag, _ abi.ChainEpoch, _ []byte) ([]byte, error) { return []byte("i_am_random_____i_am_random_____"), nil // 32 bytes. } -func (r *fixedRand) GetBeaconRandomness(_ context.Context, _ crypto.DomainSeparationTag, _ abi.ChainEpoch, _ []byte) ([]byte, error) { +func (r *fixedRand) GetChainRandomnessLookingBack(_ context.Context, _ crypto.DomainSeparationTag, _ abi.ChainEpoch, _ []byte) ([]byte, error) { + return []byte("i_am_random_____i_am_random_____"), nil // 32 bytes. +} + +func (r *fixedRand) GetBeaconRandomnessLookingForward(_ context.Context, _ crypto.DomainSeparationTag, _ abi.ChainEpoch, _ []byte) ([]byte, error) { + return []byte("i_am_random_____i_am_random_____"), nil // 32 bytes. +} + +func (r *fixedRand) GetBeaconRandomnessLookingBack(_ context.Context, _ crypto.DomainSeparationTag, _ abi.ChainEpoch, _ []byte) ([]byte, error) { return []byte("i_am_random_____i_am_random_____"), nil // 32 bytes. } diff --git a/conformance/rand_record.go b/conformance/rand_record.go index 6f6d064dc..5b4985fef 100644 --- a/conformance/rand_record.go +++ b/conformance/rand_record.go @@ -45,8 +45,17 @@ func (r *RecordingRand) loadHead() { r.head = head.Key() } -func (r *RecordingRand) GetChainRandomness(ctx context.Context, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) { +func (r *RecordingRand) GetChainRandomnessLookingForward(ctx context.Context, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) { + return r.getChainRandomness(ctx, pers, round, entropy) +} + +func (r *RecordingRand) GetChainRandomnessLookingBack(ctx context.Context, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) { + return r.getChainRandomness(ctx, pers, round, entropy) +} + +func (r *RecordingRand) getChainRandomness(ctx context.Context, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) { r.once.Do(r.loadHead) + // FullNode's ChainGetRandomnessFromTickets handles whether we should be looking forward or back ret, err := r.api.ChainGetRandomnessFromTickets(ctx, r.head, pers, round, entropy) if err != nil { return ret, err @@ -70,7 +79,15 @@ func (r *RecordingRand) GetChainRandomness(ctx context.Context, pers crypto.Doma return ret, err } -func (r *RecordingRand) GetBeaconRandomness(ctx context.Context, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) { +func (r *RecordingRand) GetBeaconRandomnessLookingForward(ctx context.Context, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) { + return r.getBeaconRandomness(ctx, pers, round, entropy) +} + +func (r *RecordingRand) GetBeaconRandomnessLookingBack(ctx context.Context, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) { + return r.getBeaconRandomness(ctx, pers, round, entropy) +} + +func (r *RecordingRand) getBeaconRandomness(ctx context.Context, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) { r.once.Do(r.loadHead) ret, err := r.api.ChainGetRandomnessFromBeacon(ctx, r.head, pers, round, entropy) if err != nil { diff --git a/conformance/rand_replay.go b/conformance/rand_replay.go index 1b73e5a08..faae1d090 100644 --- a/conformance/rand_replay.go +++ b/conformance/rand_replay.go @@ -43,7 +43,15 @@ func (r *ReplayingRand) match(requested schema.RandomnessRule) ([]byte, bool) { return nil, false } -func (r *ReplayingRand) GetChainRandomness(ctx context.Context, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) { +func (r *ReplayingRand) GetChainRandomnessLookingForward(ctx context.Context, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) { + return r.getChainRandomness(ctx, pers, round, entropy, false) +} + +func (r *ReplayingRand) GetChainRandomnessLookingBack(ctx context.Context, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) { + return r.getChainRandomness(ctx, pers, round, entropy, true) +} + +func (r *ReplayingRand) getChainRandomness(ctx context.Context, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte, lookback bool) ([]byte, error) { rule := schema.RandomnessRule{ Kind: schema.RandomnessChain, DomainSeparationTag: int64(pers), @@ -57,10 +65,23 @@ func (r *ReplayingRand) GetChainRandomness(ctx context.Context, pers crypto.Doma } r.reporter.Logf("returning fallback chain randomness: dst=%d, epoch=%d, entropy=%x", pers, round, entropy) - return r.fallback.GetChainRandomness(ctx, pers, round, entropy) + + if lookback { + return r.fallback.GetChainRandomnessLookingBack(ctx, pers, round, entropy) + } + + return r.fallback.GetChainRandomnessLookingForward(ctx, pers, round, entropy) } -func (r *ReplayingRand) GetBeaconRandomness(ctx context.Context, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) { +func (r *ReplayingRand) GetBeaconRandomnessLookingForward(ctx context.Context, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) { + return r.getBeaconRandomness(ctx, pers, round, entropy, false) +} + +func (r *ReplayingRand) GetBeaconRandomnessLookingBack(ctx context.Context, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) { + return r.getBeaconRandomness(ctx, pers, round, entropy, true) +} + +func (r *ReplayingRand) getBeaconRandomness(ctx context.Context, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte, lookback bool) ([]byte, error) { rule := schema.RandomnessRule{ Kind: schema.RandomnessBeacon, DomainSeparationTag: int64(pers), @@ -74,6 +95,10 @@ func (r *ReplayingRand) GetBeaconRandomness(ctx context.Context, pers crypto.Dom } r.reporter.Logf("returning fallback beacon randomness: dst=%d, epoch=%d, entropy=%x", pers, round, entropy) - return r.fallback.GetBeaconRandomness(ctx, pers, round, entropy) + if lookback { + return r.fallback.GetBeaconRandomnessLookingBack(ctx, pers, round, entropy) + } + + return r.fallback.GetBeaconRandomnessLookingForward(ctx, pers, round, entropy) } diff --git a/node/impl/full/chain.go b/node/impl/full/chain.go index c45932195..845c0ce60 100644 --- a/node/impl/full/chain.go +++ b/node/impl/full/chain.go @@ -10,6 +10,8 @@ import ( "strings" "sync" + "github.com/filecoin-project/lotus/build" + "go.uber.org/fx" "golang.org/x/xerrors" @@ -95,7 +97,12 @@ func (a *ChainAPI) ChainGetRandomnessFromTickets(ctx context.Context, tsk types. return nil, xerrors.Errorf("loading tipset key: %w", err) } - return a.Chain.GetChainRandomness(ctx, pts.Cids(), personalization, randEpoch, entropy) + // Doing this here is slightly nicer than doing it in the chainstore directly, but it's still bad for ChainAPI to reason about network upgrades + if randEpoch > build.UpgradeHyperdriveHeight { + return a.Chain.GetChainRandomnessLookingForward(ctx, pts.Cids(), personalization, randEpoch, entropy) + } + + return a.Chain.GetChainRandomnessLookingBack(ctx, pts.Cids(), personalization, randEpoch, entropy) } func (a *ChainAPI) ChainGetRandomnessFromBeacon(ctx context.Context, tsk types.TipSetKey, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte) (abi.Randomness, error) { @@ -104,7 +111,12 @@ func (a *ChainAPI) ChainGetRandomnessFromBeacon(ctx context.Context, tsk types.T return nil, xerrors.Errorf("loading tipset key: %w", err) } - return a.Chain.GetBeaconRandomness(ctx, pts.Cids(), personalization, randEpoch, entropy) + // Doing this here is slightly nicer than doing it in the chainstore directly, but it's still bad for ChainAPI to reason about network upgrades + if randEpoch > build.UpgradeHyperdriveHeight { + return a.Chain.GetBeaconRandomnessLookingForward(ctx, pts.Cids(), personalization, randEpoch, entropy) + } + + return a.Chain.GetBeaconRandomnessLookingBack(ctx, pts.Cids(), personalization, randEpoch, entropy) } func (a *ChainAPI) ChainGetBlock(ctx context.Context, msg cid.Cid) (*types.BlockHeader, error) { From cb59daf3c154f773c14efb5caddfecf7c33d935a Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Thu, 27 May 2021 17:44:41 +0200 Subject: [PATCH 048/160] Introduce gas prices for aggregate verifications Signed-off-by: Jakub Sztandera --- chain/vm/gas.go | 31 ++++++++++++++++++++++++++-- chain/vm/gas_v0.go | 45 ++++++++++++++++++++++++++++++++++++----- chain/vm/gas_v0_test.go | 32 +++++++++++++++++++++++++++++ 3 files changed, 101 insertions(+), 7 deletions(-) create mode 100644 chain/vm/gas_v0_test.go diff --git a/chain/vm/gas.go b/chain/vm/gas.go index 67dd7677e..c860ce9a0 100644 --- a/chain/vm/gas.go +++ b/chain/vm/gas.go @@ -160,8 +160,35 @@ var prices = map[abi.ChainEpoch]Pricelist{ hashingBase: 31355, computeUnsealedSectorCidBase: 98647, - verifySealBase: 2000, // TODO gas , it VerifySeal syscall is not used - verifyAggregateSealBase: 400_000_000, // TODO (~40ms, I think) + verifySealBase: 2000, // TODO gas, it VerifySeal syscall is not used + + verifyAggregateSealPer: map[abi.RegisteredSealProof]int64{ + abi.RegisteredSealProof_StackedDrg32GiBV1_1: 449900, + abi.RegisteredSealProof_StackedDrg64GiBV1_1: 359272, + }, + verifyAggregateSealSteps: map[abi.RegisteredSealProof]stepCost{ + abi.RegisteredSealProof_StackedDrg32GiBV1_1: { + {4, 103994170}, + {7, 112356810}, + {13, 122912610}, + {26, 137559930}, + {52, 162039100}, + {103, 210960780}, + {205, 318351180}, + {410, 528274980}, + }, + abi.RegisteredSealProof_StackedDrg64GiBV1_1: { + {4, 102581240}, + {7, 110803030}, + {13, 120803700}, + {26, 134642130}, + {52, 157357890}, + {103, 203017690}, + {205, 304253590}, + {410, 509880640}, + }, + }, + verifyPostLookup: map[abi.RegisteredPoStProof]scalingCost{ abi.RegisteredPoStProof_StackedDrgWindow512MiBV1: { flat: 117680921, diff --git a/chain/vm/gas_v0.go b/chain/vm/gas_v0.go index c90ebaa73..13c5fdd86 100644 --- a/chain/vm/gas_v0.go +++ b/chain/vm/gas_v0.go @@ -18,6 +18,28 @@ type scalingCost struct { scale int64 } +type stepCost []step + +type step struct { + start int64 + cost int64 +} + +func (sc stepCost) Lookup(x int64) int64 { + i := 0 + for ; i < len(sc); i++ { + if sc[i].start > x { + break + } + } + i-- // look at previous item + if i < 0 { + return 0 + } + + return sc[i].cost +} + type pricelistV0 struct { computeGasMulti int64 storageGasMulti int64 @@ -93,9 +115,12 @@ type pricelistV0 struct { computeUnsealedSectorCidBase int64 verifySealBase int64 verifyAggregateSealBase int64 - verifyPostLookup map[abi.RegisteredPoStProof]scalingCost - verifyPostDiscount bool - verifyConsensusFault int64 + verifyAggregateSealPer map[abi.RegisteredSealProof]int64 + verifyAggregateSealSteps map[abi.RegisteredSealProof]stepCost + + verifyPostLookup map[abi.RegisteredPoStProof]scalingCost + verifyPostDiscount bool + verifyConsensusFault int64 } var _ Pricelist = (*pricelistV0)(nil) @@ -189,8 +214,18 @@ func (pl *pricelistV0) OnVerifySeal(info proof2.SealVerifyInfo) GasCharge { // OnVerifyAggregateSeals func (pl *pricelistV0) OnVerifyAggregateSeals(aggregate proof5.AggregateSealVerifyProofAndInfos) GasCharge { - // TODO: this needs more cost tunning - return newGasCharge("OnVerifyAggregateSeals", pl.verifyAggregateSealBase, 0) + proofType := aggregate.SealProof + perProof, ok := pl.verifyAggregateSealPer[proofType] + if !ok { + perProof = pl.verifyAggregateSealPer[abi.RegisteredSealProof_StackedDrg32GiBV1_1] + } + + step, ok := pl.verifyAggregateSealSteps[proofType] + if !ok { + step = pl.verifyAggregateSealSteps[abi.RegisteredSealProof_StackedDrg32GiBV1_1] + } + num := int64(len(aggregate.Infos)) + return newGasCharge("OnVerifyAggregateSeals", perProof*num+step.Lookup(num), 0) } // OnVerifyPost diff --git a/chain/vm/gas_v0_test.go b/chain/vm/gas_v0_test.go new file mode 100644 index 000000000..447e4f70c --- /dev/null +++ b/chain/vm/gas_v0_test.go @@ -0,0 +1,32 @@ +package vm + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestStepGasCost(t *testing.T) { + s := stepCost{ + {4, 103994170}, + {7, 112356810}, + {13, 122912610}, + {26, 137559930}, + {52, 162039100}, + {103, 210960780}, + {205, 318351180}, + {410, 528274980}, + } + + assert.EqualValues(t, 0, s.Lookup(0)) + assert.EqualValues(t, 0, s.Lookup(3)) + assert.EqualValues(t, 103994170, s.Lookup(4)) + assert.EqualValues(t, 103994170, s.Lookup(6)) + assert.EqualValues(t, 112356810, s.Lookup(7)) + assert.EqualValues(t, 210960780, s.Lookup(103)) + assert.EqualValues(t, 210960780, s.Lookup(204)) + assert.EqualValues(t, 318351180, s.Lookup(205)) + assert.EqualValues(t, 318351180, s.Lookup(409)) + assert.EqualValues(t, 528274980, s.Lookup(410)) + assert.EqualValues(t, 528274980, s.Lookup(10000000000)) +} From 10b931312bce2649227ef11a1ecb5c8a493898f8 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Thu, 27 May 2021 13:42:42 -0400 Subject: [PATCH 049/160] Fix edgecase in tipset skipcache --- chain/store/index.go | 3 +++ chain/sync_test.go | 3 +++ 2 files changed, 6 insertions(+) diff --git a/chain/store/index.go b/chain/store/index.go index a9da994af..324fb7a63 100644 --- a/chain/store/index.go +++ b/chain/store/index.go @@ -107,6 +107,9 @@ func (ci *ChainIndex) fillCache(tsk types.TipSetKey) (*lbEntry, error) { } rheight -= ci.skipLength + if rheight < 0 { + rheight = 0 + } var skipTarget *types.TipSet if parent.Height() < rheight { diff --git a/chain/sync_test.go b/chain/sync_test.go index 2e5a5f3cf..095b224ad 100644 --- a/chain/sync_test.go +++ b/chain/sync_test.go @@ -895,6 +895,7 @@ func TestSyncCheckpointEarlierThanHead(t *testing.T) { func TestDrandNull(t *testing.T) { H := 10 v5h := abi.ChainEpoch(50) + ov5h := build.UpgradeHyperdriveHeight build.UpgradeHyperdriveHeight = v5h tu := prepSyncTestWithV5Height(t, H, v5h) @@ -942,4 +943,6 @@ func TestDrandNull(t *testing.T) { require.NoError(t, err) require.Equal(t, []byte(rand), expectedRand) + build.UpgradeHyperdriveHeight = ov5h + } From fba5c65ffdddc9de8959255f594ce3c2bdae02d9 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Fri, 28 May 2021 12:35:48 -0400 Subject: [PATCH 050/160] Extend the default deal start epoch delay --- node/impl/client/client.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/node/impl/client/client.go b/node/impl/client/client.go index cdef4d02b..c2987ddd2 100644 --- a/node/impl/client/client.go +++ b/node/impl/client/client.go @@ -62,7 +62,8 @@ import ( var DefaultHashFunction = uint64(mh.BLAKE2B_MIN + 31) -const dealStartBufferHours uint64 = 49 +// 8 days ~= SealDuration + PreCommit + MaxProveCommitDuration + 8 hour buffer +const dealStartBufferHours uint64 = 8 * 24 type API struct { fx.In From 8003a8a2d0a236974eaa68c97e9dd966a666d1ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sun, 30 May 2021 17:20:14 +0200 Subject: [PATCH 051/160] events: Fix handling of multiple matched events per epoch --- chain/events/events_called.go | 12 ++++--- chain/events/events_test.go | 59 +++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 5 deletions(-) diff --git a/chain/events/events_called.go b/chain/events/events_called.go index 7f39e9038..e84484480 100644 --- a/chain/events/events_called.go +++ b/chain/events/events_called.go @@ -142,8 +142,10 @@ func (e *hcEvents) processHeadChangeEvent(rev, app []*types.TipSet) error { // Queue up calls until there have been enough blocks to reach // confidence on the message calls - for tid, data := range newCalls { - e.queueForConfidence(tid, data, nil, ts) + for tid, calls := range newCalls { + for _, data := range calls { + e.queueForConfidence(tid, data, nil, ts) + } } for at := e.lastTs.Height(); at <= ts.Height(); at++ { @@ -472,7 +474,7 @@ func newMessageEvents(ctx context.Context, hcAPI headChangeAPI, cs eventAPI) mes } // Check if there are any new actor calls -func (me *messageEvents) checkNewCalls(ts *types.TipSet) (map[triggerID]eventData, error) { +func (me *messageEvents) checkNewCalls(ts *types.TipSet) (map[triggerID][]eventData, error) { pts, err := me.cs.ChainGetTipSet(me.ctx, ts.Parents()) // we actually care about messages in the parent tipset here if err != nil { log.Errorf("getting parent tipset in checkNewCalls: %s", err) @@ -483,7 +485,7 @@ func (me *messageEvents) checkNewCalls(ts *types.TipSet) (map[triggerID]eventDat defer me.lk.RUnlock() // For each message in the tipset - res := make(map[triggerID]eventData) + res := make(map[triggerID][]eventData) me.messagesForTs(pts, func(msg *types.Message) { // TODO: provide receipts @@ -498,7 +500,7 @@ func (me *messageEvents) checkNewCalls(ts *types.TipSet) (map[triggerID]eventDat // If there was a match, include the message in the results for the // trigger if matched { - res[tid] = msg + res[tid] = append(res[tid], msg) } } }) diff --git a/chain/events/events_test.go b/chain/events/events_test.go index 3957f425c..b5cbb0594 100644 --- a/chain/events/events_test.go +++ b/chain/events/events_test.go @@ -1323,3 +1323,62 @@ func TestStateChangedTimeout(t *testing.T) { fcs.advance(0, 5, nil) require.False(t, called) } + +func TestCalledMultiplePerEpoch(t *testing.T) { + fcs := &fakeCS{ + t: t, + h: 1, + + msgs: map[cid.Cid]fakeMsg{}, + blkMsgs: map[cid.Cid]cid.Cid{}, + tsc: newTSCache(2*build.ForkLengthThreshold, nil), + } + require.NoError(t, fcs.tsc.add(fcs.makeTs(t, nil, 1, dummyCid))) + + events := NewEvents(context.Background(), fcs) + + t0123, err := address.NewFromString("t0123") + require.NoError(t, err) + + at := 0 + + err = events.Called(func(ts *types.TipSet) (d bool, m bool, e error) { + return false, true, nil + }, func(msg *types.Message, rec *types.MessageReceipt, ts *types.TipSet, curH abi.ChainEpoch) (bool, error) { + switch at { + case 0: + require.Equal(t, uint64(1), msg.Nonce) + require.Equal(t, abi.ChainEpoch(4), ts.Height()) + case 1: + require.Equal(t, uint64(2), msg.Nonce) + require.Equal(t, abi.ChainEpoch(4), ts.Height()) + default: + t.Fatal("apply should only get called twice, at: ", at) + } + at++ + return true, nil + }, func(_ context.Context, ts *types.TipSet) error { + switch at { + case 2: + require.Equal(t, abi.ChainEpoch(4), ts.Height()) + case 3: + require.Equal(t, abi.ChainEpoch(4), ts.Height()) + default: + t.Fatal("revert should only get called twice, at: ", at) + } + at++ + return nil + }, 3, 20, matchAddrMethod(t0123, 5)) + require.NoError(t, err) + + fcs.advance(0, 10, map[int]cid.Cid{ + 1: fcs.fakeMsgs(fakeMsg{ + bmsgs: []*types.Message{ + {To: t0123, From: t0123, Method: 5, Nonce: 1}, + {To: t0123, From: t0123, Method: 5, Nonce: 2}, + }, + }), + }) + + fcs.advance(9, 1, nil) +} From 7fca1c1ee7a1a0c804b753fb6d39d4514154571b Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Thu, 27 May 2021 12:06:15 -0400 Subject: [PATCH 052/160] Implement FIP-0015 --- api/test/window_post.go | 154 ++++++++++++++++++++++++++++++++++++++++ build/params_mainnet.go | 2 +- chain/vm/vm.go | 39 +++++----- node/node_test.go | 28 ++++++++ 4 files changed, 205 insertions(+), 18 deletions(-) diff --git a/api/test/window_post.go b/api/test/window_post.go index e6e3ff2a3..2d3302d64 100644 --- a/api/test/window_post.go +++ b/api/test/window_post.go @@ -6,6 +6,8 @@ import ( "testing" "time" + "github.com/filecoin-project/go-state-types/big" + "github.com/stretchr/testify/require" "github.com/filecoin-project/go-address" @@ -846,3 +848,155 @@ waitForProof: require.Contains(t, err.Error(), "failed to dispute valid post (RetCode=16)") } } + +func TestWindowPostBaseFeeNoBurn(t *testing.T, b APIBuilder, blocktime time.Duration) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + och := build.UpgradeClausHeight + build.UpgradeClausHeight = 10 + n, sn := b(t, DefaultFullOpts(1), OneMiner) + + client := n[0].FullNode.(*impl.FullNodeAPI) + miner := sn[0] + + { + addrinfo, err := client.NetAddrsListen(ctx) + if err != nil { + t.Fatal(err) + } + + if err := miner.NetConnect(ctx, addrinfo); err != nil { + t.Fatal(err) + } + } + + maddr, err := miner.ActorAddress(ctx) + require.NoError(t, err) + + mi, err := client.StateMinerInfo(ctx, maddr, types.EmptyTSK) + require.NoError(t, err) + + build.Clock.Sleep(time.Second) + + done := make(chan struct{}) + go func() { + defer close(done) + for ctx.Err() == nil { + build.Clock.Sleep(blocktime) + if err := miner.MineOne(ctx, MineNext); err != nil { + if ctx.Err() != nil { + // context was canceled, ignore the error. + return + } + t.Error(err) + } + } + }() + defer func() { + cancel() + <-done + }() + + pledgeSectors(t, ctx, miner, 10, 0, nil) + wact, err := client.StateGetActor(ctx, mi.Worker, types.EmptyTSK) + require.NoError(t, err) + en := wact.Nonce + + // wait for a new message to be sent from worker address, it will be a PoSt + +waitForProof: + for { + wact, err := client.StateGetActor(ctx, mi.Worker, types.EmptyTSK) + require.NoError(t, err) + if wact.Nonce > en { + break waitForProof + } + + build.Clock.Sleep(blocktime) + } + + slm, err := client.StateListMessages(ctx, &api.MessageMatch{To: maddr}, types.EmptyTSK, 0) + require.NoError(t, err) + + pmr, err := client.StateReplay(ctx, types.EmptyTSK, slm[0]) + require.NoError(t, err) + + require.Equal(t, pmr.GasCost.BaseFeeBurn, big.Zero()) + + build.UpgradeClausHeight = och +} + +func TestWindowPostBaseFeeBurn(t *testing.T, b APIBuilder, blocktime time.Duration) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + n, sn := b(t, []FullNodeOpts{FullNodeWithLatestActorsAt(-1)}, OneMiner) + + client := n[0].FullNode.(*impl.FullNodeAPI) + miner := sn[0] + + { + addrinfo, err := client.NetAddrsListen(ctx) + if err != nil { + t.Fatal(err) + } + + if err := miner.NetConnect(ctx, addrinfo); err != nil { + t.Fatal(err) + } + } + + maddr, err := miner.ActorAddress(ctx) + require.NoError(t, err) + + mi, err := client.StateMinerInfo(ctx, maddr, types.EmptyTSK) + require.NoError(t, err) + + build.Clock.Sleep(time.Second) + + done := make(chan struct{}) + go func() { + defer close(done) + for ctx.Err() == nil { + build.Clock.Sleep(blocktime) + if err := miner.MineOne(ctx, MineNext); err != nil { + if ctx.Err() != nil { + // context was canceled, ignore the error. + return + } + t.Error(err) + } + } + }() + defer func() { + cancel() + <-done + }() + + pledgeSectors(t, ctx, miner, 10, 0, nil) + wact, err := client.StateGetActor(ctx, mi.Worker, types.EmptyTSK) + require.NoError(t, err) + en := wact.Nonce + + // wait for a new message to be sent from worker address, it will be a PoSt + +waitForProof: + for { + wact, err := client.StateGetActor(ctx, mi.Worker, types.EmptyTSK) + require.NoError(t, err) + if wact.Nonce > en { + break waitForProof + } + + build.Clock.Sleep(blocktime) + } + + slm, err := client.StateListMessages(ctx, &api.MessageMatch{To: maddr}, types.EmptyTSK, 0) + require.NoError(t, err) + + pmr, err := client.StateReplay(ctx, types.EmptyTSK, slm[0]) + require.NoError(t, err) + + require.NotEqual(t, pmr.GasCost.BaseFeeBurn, big.Zero()) +} diff --git a/build/params_mainnet.go b/build/params_mainnet.go index 5c3171a27..2d708f9e4 100644 --- a/build/params_mainnet.go +++ b/build/params_mainnet.go @@ -51,7 +51,7 @@ const UpgradePersianHeight = UpgradeCalicoHeight + (builtin2.EpochsInHour * 60) const UpgradeOrangeHeight = 336458 // 2020-12-22T02:00:00Z -const UpgradeClausHeight = 343200 +var UpgradeClausHeight = abi.ChainEpoch(343200) // 2021-03-04T00:00:30Z const UpgradeTrustHeight = 550321 diff --git a/chain/vm/vm.go b/chain/vm/vm.go index afc74e744..9874ea7da 100644 --- a/chain/vm/vm.go +++ b/chain/vm/vm.go @@ -566,7 +566,7 @@ func (vm *VM) ApplyMessage(ctx context.Context, cmsg types.ChainMsg) (*ApplyRet, gasUsed = 0 } - burn, err := vm.ShouldBurn(st, msg, errcode) + burn, err := vm.ShouldBurn(ctx, st, msg, errcode) if err != nil { return nil, xerrors.Errorf("deciding whether should burn failed: %w", err) } @@ -609,26 +609,31 @@ func (vm *VM) ApplyMessage(ctx context.Context, cmsg types.ChainMsg) (*ApplyRet, }, nil } -func (vm *VM) ShouldBurn(st *state.StateTree, msg *types.Message, errcode exitcode.ExitCode) (bool, error) { - // Check to see if we should burn funds. We avoid burning on successful - // window post. This won't catch _indirect_ window post calls, but this - // is the best we can get for now. - if vm.blockHeight > build.UpgradeClausHeight && errcode == exitcode.Ok && msg.Method == miner.Methods.SubmitWindowedPoSt { - // Ok, we've checked the _method_, but we still need to check - // the target actor. It would be nice if we could just look at - // the trace, but I'm not sure if that's safe? - if toActor, err := st.GetActor(msg.To); err != nil { - // If the actor wasn't found, we probably deleted it or something. Move on. - if !xerrors.Is(err, types.ErrActorNotFound) { - // Otherwise, this should never fail and something is very wrong. - return false, xerrors.Errorf("failed to lookup target actor: %w", err) +func (vm *VM) ShouldBurn(ctx context.Context, st *state.StateTree, msg *types.Message, errcode exitcode.ExitCode) (bool, error) { + if vm.ntwkVersion(ctx, vm.blockHeight) <= network.Version12 { + // Check to see if we should burn funds. We avoid burning on successful + // window post. This won't catch _indirect_ window post calls, but this + // is the best we can get for now. + if vm.blockHeight > build.UpgradeClausHeight && errcode == exitcode.Ok && msg.Method == miner.Methods.SubmitWindowedPoSt { + // Ok, we've checked the _method_, but we still need to check + // the target actor. It would be nice if we could just look at + // the trace, but I'm not sure if that's safe? + if toActor, err := st.GetActor(msg.To); err != nil { + // If the actor wasn't found, we probably deleted it or something. Move on. + if !xerrors.Is(err, types.ErrActorNotFound) { + // Otherwise, this should never fail and something is very wrong. + return false, xerrors.Errorf("failed to lookup target actor: %w", err) + } + } else if builtin.IsStorageMinerActor(toActor.Code) { + // Ok, this is a storage miner and we've processed a window post. Remove the burn. + return false, nil } - } else if builtin.IsStorageMinerActor(toActor.Code) { - // Ok, this is a storage miner and we've processed a window post. Remove the burn. - return false, nil } + + return true, nil } + // Any "don't burn" rules from Network v13 onwards go here, for now we always return true return true, nil } diff --git a/node/node_test.go b/node/node_test.go index 5ae15fd8c..933a0f614 100644 --- a/node/node_test.go +++ b/node/node_test.go @@ -245,6 +245,34 @@ func TestWindowPostDisputeFails(t *testing.T) { test.TestWindowPostDisputeFails(t, builder.MockSbBuilder, 2*time.Millisecond) } +func TestWindowPostBaseFeeNoBurn(t *testing.T) { + if os.Getenv("LOTUS_TEST_WINDOW_POST") != "1" { + t.Skip("this takes a few minutes, set LOTUS_TEST_WINDOW_POST=1 to run") + } + logging.SetLogLevel("miner", "ERROR") + logging.SetLogLevel("gen", "ERROR") + logging.SetLogLevel("chainstore", "ERROR") + logging.SetLogLevel("chain", "ERROR") + logging.SetLogLevel("sub", "ERROR") + logging.SetLogLevel("storageminer", "ERROR") + + test.TestWindowPostBaseFeeNoBurn(t, builder.MockSbBuilder, 2*time.Millisecond) +} + +func TestWindowPostBaseFeeBurn(t *testing.T) { + if os.Getenv("LOTUS_TEST_WINDOW_POST") != "1" { + t.Skip("this takes a few minutes, set LOTUS_TEST_WINDOW_POST=1 to run") + } + logging.SetLogLevel("miner", "ERROR") + logging.SetLogLevel("gen", "ERROR") + logging.SetLogLevel("chainstore", "ERROR") + logging.SetLogLevel("chain", "ERROR") + logging.SetLogLevel("sub", "ERROR") + logging.SetLogLevel("storageminer", "ERROR") + + test.TestWindowPostBaseFeeBurn(t, builder.MockSbBuilder, 2*time.Millisecond) +} + func TestDeadlineToggling(t *testing.T) { if os.Getenv("LOTUS_TEST_DEADLINE_TOGGLING") != "1" { t.Skip("this takes a few minutes, set LOTUS_TEST_DEADLINE_TOGGLING=1 to run") From 61554cf3e019ae00abb6bf2d92e4beda0a9331d8 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Thu, 27 May 2021 15:54:31 -0400 Subject: [PATCH 053/160] Update to latest actors --- chain/actors/policy/policy.go | 7 +------ chain/actors/policy/policy.go.template | 10 +++++++--- chain/vm/runtime.go | 4 ++++ extern/storage-sealing/precommit_batch.go | 2 +- go.mod | 2 +- go.sum | 4 ++-- 6 files changed, 16 insertions(+), 13 deletions(-) diff --git a/chain/actors/policy/policy.go b/chain/actors/policy/policy.go index 113544c05..4d115f783 100644 --- a/chain/actors/policy/policy.go +++ b/chain/actors/policy/policy.go @@ -60,8 +60,6 @@ func SetSupportedProofTypes(types ...abi.RegisteredSealProof) { miner4.PreCommitSealProofTypesV7 = make(map[abi.RegisteredSealProof]struct{}, len(types)*2) miner4.PreCommitSealProofTypesV8 = make(map[abi.RegisteredSealProof]struct{}, len(types)) - miner5.PreCommitSealProofTypesV0 = make(map[abi.RegisteredSealProof]struct{}, len(types)) - miner5.PreCommitSealProofTypesV7 = make(map[abi.RegisteredSealProof]struct{}, len(types)*2) miner5.PreCommitSealProofTypesV8 = make(map[abi.RegisteredSealProof]struct{}, len(types)) AddSupportedProofTypes(types...) @@ -93,9 +91,6 @@ func AddSupportedProofTypes(types ...abi.RegisteredSealProof) { miner4.PreCommitSealProofTypesV7[t+abi.RegisteredSealProof_StackedDrg2KiBV1_1] = struct{}{} miner4.PreCommitSealProofTypesV8[t+abi.RegisteredSealProof_StackedDrg2KiBV1_1] = struct{}{} - miner5.PreCommitSealProofTypesV0[t] = struct{}{} - miner5.PreCommitSealProofTypesV7[t] = struct{}{} - miner5.PreCommitSealProofTypesV7[t+abi.RegisteredSealProof_StackedDrg2KiBV1_1] = struct{}{} miner5.PreCommitSealProofTypesV8[t+abi.RegisteredSealProof_StackedDrg2KiBV1_1] = struct{}{} } @@ -308,7 +303,7 @@ func GetDefaultAggregationProof() abi.RegisteredAggregationProof { func GetSectorMaxLifetime(proof abi.RegisteredSealProof, nwVer network.Version) abi.ChainEpoch { if nwVer <= network.Version10 { - return builtin5.SealProofPoliciesV0[proof].SectorMaxLifetime + return builtin4.SealProofPoliciesV0[proof].SectorMaxLifetime } return builtin5.SealProofPoliciesV11[proof].SectorMaxLifetime diff --git a/chain/actors/policy/policy.go.template b/chain/actors/policy/policy.go.template index f5055c423..aef1081cb 100644 --- a/chain/actors/policy/policy.go.template +++ b/chain/actors/policy/policy.go.template @@ -31,10 +31,12 @@ func SetSupportedProofTypes(types ...abi.RegisteredSealProof) { {{range .versions}} {{if (eq . 0)}} miner{{.}}.SupportedProofTypes = make(map[abi.RegisteredSealProof]struct{}, len(types)) - {{else}} + {{else if (le . 4)}} miner{{.}}.PreCommitSealProofTypesV0 = make(map[abi.RegisteredSealProof]struct{}, len(types)) miner{{.}}.PreCommitSealProofTypesV7 = make(map[abi.RegisteredSealProof]struct{}, len(types)*2) miner{{.}}.PreCommitSealProofTypesV8 = make(map[abi.RegisteredSealProof]struct{}, len(types)) + {{else}} + miner{{.}}.PreCommitSealProofTypesV8 = make(map[abi.RegisteredSealProof]struct{}, len(types)) {{end}} {{end}} @@ -53,11 +55,13 @@ func AddSupportedProofTypes(types ...abi.RegisteredSealProof) { {{range .versions}} {{if (eq . 0)}} miner{{.}}.SupportedProofTypes[t] = struct{}{} - {{else}} + {{else if (le . 4)}} miner{{.}}.PreCommitSealProofTypesV0[t] = struct{}{} miner{{.}}.PreCommitSealProofTypesV7[t] = struct{}{} miner{{.}}.PreCommitSealProofTypesV7[t+abi.RegisteredSealProof_StackedDrg2KiBV1_1] = struct{}{} miner{{.}}.PreCommitSealProofTypesV8[t+abi.RegisteredSealProof_StackedDrg2KiBV1_1] = struct{}{} + {{else}} + miner{{.}}.PreCommitSealProofTypesV8[t+abi.RegisteredSealProof_StackedDrg2KiBV1_1] = struct{}{} {{end}} {{end}} } @@ -203,7 +207,7 @@ func GetDefaultAggregationProof() abi.RegisteredAggregationProof { func GetSectorMaxLifetime(proof abi.RegisteredSealProof, nwVer network.Version) abi.ChainEpoch { if nwVer <= network.Version10 { - return builtin{{.latestVersion}}.SealProofPoliciesV0[proof].SectorMaxLifetime + return builtin4.SealProofPoliciesV0[proof].SectorMaxLifetime } return builtin{{.latestVersion}}.SealProofPoliciesV11[proof].SectorMaxLifetime diff --git a/chain/vm/runtime.go b/chain/vm/runtime.go index f89bd9f40..00c04ceb8 100644 --- a/chain/vm/runtime.go +++ b/chain/vm/runtime.go @@ -81,6 +81,10 @@ type Runtime struct { lastGasCharge *types.GasTrace } +func (rt *Runtime) BaseFee() abi.TokenAmount { + return rt.vm.baseFee +} + func (rt *Runtime) NetworkVersion() network.Version { return rt.vm.GetNtwkVersion(rt.ctx, rt.CurrEpoch()) } diff --git a/extern/storage-sealing/precommit_batch.go b/extern/storage-sealing/precommit_batch.go index 69ba47c60..bce8e21d5 100644 --- a/extern/storage-sealing/precommit_batch.go +++ b/extern/storage-sealing/precommit_batch.go @@ -181,7 +181,7 @@ func (b *PreCommitBatcher) processBatch(notif, after bool) (*cid.Cid, error) { break } - params.Sectors = append(params.Sectors, p.pci) + params.Sectors = append(params.Sectors, *p.pci) deposit = big.Add(deposit, p.deposit) } diff --git a/go.mod b/go.mod index 98e9d3691..9a2dd0f19 100644 --- a/go.mod +++ b/go.mod @@ -47,7 +47,7 @@ require ( github.com/filecoin-project/specs-actors/v2 v2.3.5-0.20210114162132-5b58b773f4fb github.com/filecoin-project/specs-actors/v3 v3.1.0 github.com/filecoin-project/specs-actors/v4 v4.0.0 - github.com/filecoin-project/specs-actors/v5 v5.0.0-20210517165532-c7cff61d07fb + github.com/filecoin-project/specs-actors/v5 v5.0.0-20210528202914-a9f9f95f5e93 github.com/filecoin-project/specs-storage v0.1.1-0.20201105051918-5188d9774506 github.com/filecoin-project/test-vectors/schema v0.0.5 github.com/gbrlsnchs/jwt/v3 v3.0.0-beta.1 diff --git a/go.sum b/go.sum index 3ef546ba4..34d21605f 100644 --- a/go.sum +++ b/go.sum @@ -316,8 +316,8 @@ github.com/filecoin-project/specs-actors/v3 v3.1.0/go.mod h1:mpynccOLlIRy0QnR008 github.com/filecoin-project/specs-actors/v4 v4.0.0 h1:vMALksY5G3J5rj3q9rbcyB+f4Tk1xrLqSgdB3jOok4s= github.com/filecoin-project/specs-actors/v4 v4.0.0/go.mod h1:TkHXf/l7Wyw4ZejyXIPS2rK8bBO0rdwhTZyQQgaglng= github.com/filecoin-project/specs-actors/v5 v5.0.0-20210512015452-4fe3889fff57/go.mod h1:283yBMMUSDB2abcjP/hhrwTkhb9h3sfM6KGrep/ZlBI= -github.com/filecoin-project/specs-actors/v5 v5.0.0-20210517165532-c7cff61d07fb h1:818gGdeEC+7aHGl2X7ptdtYuqoEgRsY3jwz+DvUYUFk= -github.com/filecoin-project/specs-actors/v5 v5.0.0-20210517165532-c7cff61d07fb/go.mod h1:283yBMMUSDB2abcjP/hhrwTkhb9h3sfM6KGrep/ZlBI= +github.com/filecoin-project/specs-actors/v5 v5.0.0-20210528202914-a9f9f95f5e93 h1:PZ5pLy4dZVgL+fXgvSVtPOYhfEYUzEYYVEz7IfG8e5U= +github.com/filecoin-project/specs-actors/v5 v5.0.0-20210528202914-a9f9f95f5e93/go.mod h1:kSDmoQuO8jlhMVzKNoesbhka1e6gHKcLQjKm9mE9Qhw= github.com/filecoin-project/specs-storage v0.1.1-0.20201105051918-5188d9774506 h1:Ur/l2+6qN+lQiqjozWWc5p9UDaAMDZKTlDS98oRnlIw= github.com/filecoin-project/specs-storage v0.1.1-0.20201105051918-5188d9774506/go.mod h1:nJRRM7Aa9XVvygr3W9k6xGF46RWzr2zxF/iGoAIfA/g= github.com/filecoin-project/test-vectors/schema v0.0.5 h1:w3zHQhzM4pYxJDl21avXjOKBLF8egrvwUwjpT8TquDg= From 92f544d96c8b58ec082364cbaf5de2ade09cb219 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Wed, 28 Apr 2021 19:24:09 -0400 Subject: [PATCH 054/160] Add verifreg utils to CLI --- cli/cmd.go | 1 + cli/filplus.go | 277 ++++ cmd/lotus-shed/verifreg.go | 30 +- documentation/en/cli-lotus.md | 2762 +++++++++++++++++++++++++++++++++ 4 files changed, 3060 insertions(+), 10 deletions(-) create mode 100644 cli/filplus.go create mode 100644 documentation/en/cli-lotus.md diff --git a/cli/cmd.go b/cli/cmd.go index efbb3b990..bd7588206 100644 --- a/cli/cmd.go +++ b/cli/cmd.go @@ -70,6 +70,7 @@ var Commands = []*cli.Command{ WithCategory("basic", walletCmd), WithCategory("basic", clientCmd), WithCategory("basic", multisigCmd), + WithCategory("basic", filplusCmd), WithCategory("basic", paychCmd), WithCategory("developer", AuthCmd), WithCategory("developer", MpoolCmd), diff --git a/cli/filplus.go b/cli/filplus.go new file mode 100644 index 000000000..b98ac4ab8 --- /dev/null +++ b/cli/filplus.go @@ -0,0 +1,277 @@ +package cli + +import ( + "context" + "fmt" + + "github.com/filecoin-project/lotus/api" + + verifreg4 "github.com/filecoin-project/specs-actors/v4/actors/builtin/verifreg" + + "github.com/filecoin-project/go-state-types/big" + + "github.com/urfave/cli/v2" + "golang.org/x/xerrors" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + + "github.com/filecoin-project/lotus/blockstore" + "github.com/filecoin-project/lotus/build" + "github.com/filecoin-project/lotus/chain/actors" + "github.com/filecoin-project/lotus/chain/actors/adt" + "github.com/filecoin-project/lotus/chain/actors/builtin/verifreg" + "github.com/filecoin-project/lotus/chain/types" + cbor "github.com/ipfs/go-ipld-cbor" +) + +var filplusCmd = &cli.Command{ + Name: "filplus", + Usage: "Interact with the verified registry actor used by Filplus", + Flags: []cli.Flag{}, + Subcommands: []*cli.Command{ + filplusVerifyClientCmd, + filplusListNotariesCmd, + filplusListClientsCmd, + filplusCheckClientCmd, + filplusCheckNotaryCmd, + }, +} + +var filplusVerifyClientCmd = &cli.Command{ + Name: "grant-datacap", + Usage: "give allowance to the specified verified client address", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "from", + Usage: "specify your notary address to send the message from", + Required: true, + }, + }, + Action: func(cctx *cli.Context) error { + froms := cctx.String("from") + if froms == "" { + return fmt.Errorf("must specify from address with --from") + } + + fromk, err := address.NewFromString(froms) + if err != nil { + return err + } + + if cctx.Args().Len() != 2 { + return fmt.Errorf("must specify two arguments: address and allowance") + } + + target, err := address.NewFromString(cctx.Args().Get(0)) + if err != nil { + return err + } + + allowance, err := types.BigFromString(cctx.Args().Get(1)) + if err != nil { + return err + } + + api, closer, err := GetFullNodeAPI(cctx) + if err != nil { + return err + } + defer closer() + ctx := ReqContext(cctx) + + found, dcap, err := checkNotary(ctx, api, fromk) + if err != nil { + return err + } + + if !found { + return xerrors.New("sender address must be a notary") + } + + if dcap.Cmp(allowance.Int) < 0 { + return xerrors.Errorf("cannot allot more allowance than notary data cap: %s < %s", dcap, allowance) + } + + // TODO: This should be abstracted over actor versions + params, err := actors.SerializeParams(&verifreg4.AddVerifiedClientParams{Address: target, Allowance: allowance}) + if err != nil { + return err + } + + msg := &types.Message{ + To: verifreg.Address, + From: fromk, + Method: verifreg.Methods.AddVerifiedClient, + Params: params, + } + + smsg, err := api.MpoolPushMessage(ctx, msg, nil) + if err != nil { + return err + } + + fmt.Printf("message sent, now waiting on cid: %s\n", smsg.Cid()) + + mwait, err := api.StateWaitMsg(ctx, smsg.Cid(), build.MessageConfidence) + if err != nil { + return err + } + + if mwait.Receipt.ExitCode != 0 { + return fmt.Errorf("failed to add verified client: %d", mwait.Receipt.ExitCode) + } + + return nil + }, +} + +var filplusListNotariesCmd = &cli.Command{ + Name: "list-notaries", + Usage: "list all notaries", + Action: func(cctx *cli.Context) error { + api, closer, err := GetFullNodeAPI(cctx) + if err != nil { + return err + } + defer closer() + ctx := ReqContext(cctx) + + act, err := api.StateGetActor(ctx, verifreg.Address, types.EmptyTSK) + if err != nil { + return err + } + + apibs := blockstore.NewAPIBlockstore(api) + store := adt.WrapStore(ctx, cbor.NewCborStore(apibs)) + + st, err := verifreg.Load(store, act) + if err != nil { + return err + } + return st.ForEachVerifier(func(addr address.Address, dcap abi.StoragePower) error { + _, err := fmt.Printf("%s: %s\n", addr, dcap) + return err + }) + }, +} + +var filplusListClientsCmd = &cli.Command{ + Name: "list-clients", + Usage: "list all verified clients", + Action: func(cctx *cli.Context) error { + api, closer, err := GetFullNodeAPI(cctx) + if err != nil { + return err + } + defer closer() + ctx := ReqContext(cctx) + + act, err := api.StateGetActor(ctx, verifreg.Address, types.EmptyTSK) + if err != nil { + return err + } + + apibs := blockstore.NewAPIBlockstore(api) + store := adt.WrapStore(ctx, cbor.NewCborStore(apibs)) + + st, err := verifreg.Load(store, act) + if err != nil { + return err + } + return st.ForEachClient(func(addr address.Address, dcap abi.StoragePower) error { + _, err := fmt.Printf("%s: %s\n", addr, dcap) + return err + }) + }, +} + +var filplusCheckClientCmd = &cli.Command{ + Name: "check-client-datacap", + Usage: "check verified client remaining bytes", + Action: func(cctx *cli.Context) error { + if !cctx.Args().Present() { + return fmt.Errorf("must specify client address to check") + } + + caddr, err := address.NewFromString(cctx.Args().First()) + if err != nil { + return err + } + + api, closer, err := GetFullNodeAPI(cctx) + if err != nil { + return err + } + defer closer() + ctx := ReqContext(cctx) + + dcap, err := api.StateVerifiedClientStatus(ctx, caddr, types.EmptyTSK) + if err != nil { + return err + } + if dcap == nil { + return xerrors.Errorf("client %s is not a verified client", err) + } + + fmt.Println(*dcap) + + return nil + }, +} + +var filplusCheckNotaryCmd = &cli.Command{ + Name: "check-notaries-datacap", + Usage: "check notaries remaining bytes", + Action: func(cctx *cli.Context) error { + if !cctx.Args().Present() { + return fmt.Errorf("must specify notary address to check") + } + + vaddr, err := address.NewFromString(cctx.Args().First()) + if err != nil { + return err + } + + api, closer, err := GetFullNodeAPI(cctx) + if err != nil { + return err + } + defer closer() + ctx := ReqContext(cctx) + + found, dcap, err := checkNotary(ctx, api, vaddr) + if err != nil { + return err + } + if !found { + return fmt.Errorf("not found") + } + + fmt.Println(dcap) + + return nil + }, +} + +func checkNotary(ctx context.Context, api api.FullNode, vaddr address.Address) (bool, abi.StoragePower, error) { + vid, err := api.StateLookupID(ctx, vaddr, types.EmptyTSK) + if err != nil { + return false, big.Zero(), err + } + + act, err := api.StateGetActor(ctx, verifreg.Address, types.EmptyTSK) + if err != nil { + return false, big.Zero(), err + } + + apibs := blockstore.NewAPIBlockstore(api) + store := adt.WrapStore(ctx, cbor.NewCborStore(apibs)) + + st, err := verifreg.Load(store, act) + if err != nil { + return false, big.Zero(), err + } + + return st.VerifierDataCap(vid) +} diff --git a/cmd/lotus-shed/verifreg.go b/cmd/lotus-shed/verifreg.go index 426827ad2..988de5d53 100644 --- a/cmd/lotus-shed/verifreg.go +++ b/cmd/lotus-shed/verifreg.go @@ -102,8 +102,9 @@ var verifRegAddVerifierCmd = &cli.Command{ } var verifRegVerifyClientCmd = &cli.Command{ - Name: "verify-client", - Usage: "make a given account a verified client", + Name: "verify-client", + Usage: "make a given account a verified client", + Hidden: true, Flags: []cli.Flag{ &cli.StringFlag{ Name: "from", @@ -111,6 +112,7 @@ var verifRegVerifyClientCmd = &cli.Command{ }, }, Action: func(cctx *cli.Context) error { + fmt.Println("DEPRECATED: This behavior is being moved to `lotus verifreg`") froms := cctx.String("from") if froms == "" { return fmt.Errorf("must specify from address with --from") @@ -175,9 +177,11 @@ var verifRegVerifyClientCmd = &cli.Command{ } var verifRegListVerifiersCmd = &cli.Command{ - Name: "list-verifiers", - Usage: "list all verifiers", + Name: "list-verifiers", + Usage: "list all verifiers", + Hidden: true, Action: func(cctx *cli.Context) error { + fmt.Println("DEPRECATED: This behavior is being moved to `lotus verifreg`") api, closer, err := lcli.GetFullNodeAPI(cctx) if err != nil { return err @@ -205,9 +209,11 @@ var verifRegListVerifiersCmd = &cli.Command{ } var verifRegListClientsCmd = &cli.Command{ - Name: "list-clients", - Usage: "list all verified clients", + Name: "list-clients", + Usage: "list all verified clients", + Hidden: true, Action: func(cctx *cli.Context) error { + fmt.Println("DEPRECATED: This behavior is being moved to `lotus verifreg`") api, closer, err := lcli.GetFullNodeAPI(cctx) if err != nil { return err @@ -235,9 +241,11 @@ var verifRegListClientsCmd = &cli.Command{ } var verifRegCheckClientCmd = &cli.Command{ - Name: "check-client", - Usage: "check verified client remaining bytes", + Name: "check-client", + Usage: "check verified client remaining bytes", + Hidden: true, Action: func(cctx *cli.Context) error { + fmt.Println("DEPRECATED: This behavior is being moved to `lotus verifreg`") if !cctx.Args().Present() { return fmt.Errorf("must specify client address to check") } @@ -269,9 +277,11 @@ var verifRegCheckClientCmd = &cli.Command{ } var verifRegCheckVerifierCmd = &cli.Command{ - Name: "check-verifier", - Usage: "check verifiers remaining bytes", + Name: "check-verifier", + Usage: "check verifiers remaining bytes", + Hidden: true, Action: func(cctx *cli.Context) error { + fmt.Println("DEPRECATED: This behavior is being moved to `lotus verifreg`") if !cctx.Args().Present() { return fmt.Errorf("must specify verifier address to check") } diff --git a/documentation/en/cli-lotus.md b/documentation/en/cli-lotus.md new file mode 100644 index 000000000..82c8dfad3 --- /dev/null +++ b/documentation/en/cli-lotus.md @@ -0,0 +1,2762 @@ +# lotus +``` +NAME: + lotus - Filecoin decentralized storage network client + +USAGE: + lotus [global options] command [command options] [arguments...] + +VERSION: + 1.11.0-dev + +COMMANDS: + daemon Start a lotus daemon process + backup Create node metadata backup + version Print version + help, h Shows a list of commands or help for one command + BASIC: + send Send funds between accounts + wallet Manage wallet + client Make deals, store data, retrieve data + msig Interact with a multisig wallet + verifreg Interact with the verified registry actor + paych Manage payment channels + DEVELOPER: + auth Manage RPC permissions + mpool Manage message pool + state Interact with and query filecoin chain state + chain Interact with filecoin blockchain + log Manage logging + wait-api Wait for lotus api to come online + fetch-params Fetch proving parameters + NETWORK: + net Manage P2P Network + sync Inspect or interact with the chain syncer + +GLOBAL OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) +``` + +## lotus daemon +``` +NAME: + lotus daemon - Start a lotus daemon process + +USAGE: + lotus daemon command [command options] [arguments...] + +COMMANDS: + stop Stop a running lotus daemon + help, h Shows a list of commands or help for one command + +OPTIONS: + --api value (default: "1234") + --genesis value genesis file to use for first node run + --bootstrap (default: true) + --import-chain value on first run, load chain from given file or url and validate + --import-snapshot value import chain state from a given chain export file or url + --halt-after-import halt the process after importing chain from file (default: false) + --pprof value specify name of file for writing cpu profile to + --profile value specify type of node + --manage-fdlimit manage open file limit (default: true) + --config value specify path of config file to use + --api-max-req-size value maximum API request size accepted by the JSON RPC server (default: 0) + --restore value restore from backup file + --restore-config value config file to use when restoring from backup + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +### lotus daemon stop +``` +NAME: + lotus daemon stop - Stop a running lotus daemon + +USAGE: + lotus daemon stop [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +## lotus backup +``` +NAME: + lotus backup - Create node metadata backup + +USAGE: + lotus backup [command options] [backup file path] + +DESCRIPTION: + The backup command writes a copy of node metadata under the specified path + +Online backups: +For security reasons, the daemon must be have LOTUS_BACKUP_BASE_PATH env var set +to a path where backup files are supposed to be saved, and the path specified in +this command must be within this base path + +OPTIONS: + --offline create backup without the node running (default: false) + --help, -h show help (default: false) + +``` + +## lotus version +``` +NAME: + lotus version - Print version + +USAGE: + lotus version [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +## lotus send +``` +NAME: + lotus send - Send funds between accounts + +USAGE: + lotus send [command options] [targetAddress] [amount] + +CATEGORY: + BASIC + +OPTIONS: + --from value optionally specify the account to send funds from + --gas-premium value specify gas price to use in AttoFIL (default: "0") + --gas-feecap value specify gas fee cap to use in AttoFIL (default: "0") + --gas-limit value specify gas limit (default: 0) + --nonce value specify the nonce to use (default: 0) + --method value specify method to invoke (default: 0) + --params-json value specify invocation parameters in json + --params-hex value specify invocation parameters in hex + --force must be specified for the action to take effect if maybe SysErrInsufficientFunds etc (default: false) + --help, -h show help (default: false) + +``` + +## lotus wallet +``` +NAME: + lotus wallet - Manage wallet + +USAGE: + lotus wallet command [command options] [arguments...] + +COMMANDS: + new Generate a new key of the given type + list List wallet address + balance Get account balance + export export keys + import import keys + default Get default wallet address + set-default Set default wallet address + sign sign a message + verify verify the signature of a message + delete Delete an account from the wallet + market Interact with market balances + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +### lotus wallet new +``` +NAME: + lotus wallet new - Generate a new key of the given type + +USAGE: + lotus wallet new [command options] [bls|secp256k1 (default secp256k1)] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus wallet list +``` +NAME: + lotus wallet list - List wallet address + +USAGE: + lotus wallet list [command options] [arguments...] + +OPTIONS: + --addr-only, -a Only print addresses (default: false) + --id, -i Output ID addresses (default: false) + --market, -m Output market balances (default: false) + --help, -h show help (default: false) + +``` + +### lotus wallet balance +``` +NAME: + lotus wallet balance - Get account balance + +USAGE: + lotus wallet balance [command options] [address] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus wallet export +``` +NAME: + lotus wallet export - export keys + +USAGE: + lotus wallet export [command options] [address] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus wallet import +``` +NAME: + lotus wallet import - import keys + +USAGE: + lotus wallet import [command options] [ (optional, will read from stdin if omitted)] + +OPTIONS: + --format value specify input format for key (default: "hex-lotus") + --as-default import the given key as your new default key (default: false) + --help, -h show help (default: false) + +``` + +### lotus wallet default +``` +NAME: + lotus wallet default - Get default wallet address + +USAGE: + lotus wallet default [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus wallet set-default +``` +NAME: + lotus wallet set-default - Set default wallet address + +USAGE: + lotus wallet set-default [command options] [address] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus wallet sign +``` +NAME: + lotus wallet sign - sign a message + +USAGE: + lotus wallet sign [command options] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus wallet verify +``` +NAME: + lotus wallet verify - verify the signature of a message + +USAGE: + lotus wallet verify [command options] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus wallet delete +``` +NAME: + lotus wallet delete - Delete an account from the wallet + +USAGE: + lotus wallet delete [command options]

+ +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus wallet market +``` +NAME: + lotus wallet market - Interact with market balances + +USAGE: + lotus wallet market command [command options] [arguments...] + +COMMANDS: + withdraw Withdraw funds from the Storage Market Actor + add Add funds to the Storage Market Actor + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +#### lotus wallet market withdraw +``` +NAME: + lotus wallet market withdraw - Withdraw funds from the Storage Market Actor + +USAGE: + lotus wallet market withdraw [command options] [amount (FIL) optional, otherwise will withdraw max available] + +OPTIONS: + --wallet value, -w value Specify address to withdraw funds to, otherwise it will use the default wallet address + --address value, -a value Market address to withdraw from (account or miner actor address, defaults to --wallet address) + --help, -h show help (default: false) + +``` + +#### lotus wallet market add +``` +NAME: + lotus wallet market add - Add funds to the Storage Market Actor + +USAGE: + lotus wallet market add [command options] + +OPTIONS: + --from value, -f value Specify address to move funds from, otherwise it will use the default wallet address + --address value, -a value Market address to move funds to (account or miner actor address, defaults to --from address) + --help, -h show help (default: false) + +``` + +## lotus client +``` +NAME: + lotus client - Make deals, store data, retrieve data + +USAGE: + lotus client command [command options] [arguments...] + +COMMANDS: + help, h Shows a list of commands or help for one command + DATA: + import Import data + drop Remove import + local List locally imported data + stat Print information about a locally stored file (piece size, etc) + RETRIEVAL: + find Find data in the network + retrieve Retrieve data from network + cancel-retrieval Cancel a retrieval deal by deal ID; this also cancels the associated transfer + STORAGE: + deal Initialize storage deal with a miner + query-ask Find a miners ask + list-deals List storage market deals + get-deal Print detailed deal information + list-asks List asks for top miners + deal-stats Print statistics about local storage deals + inspect-deal Inspect detailed information about deal's lifecycle and the various stages it goes through + UTIL: + commP Calculate the piece-cid (commP) of a CAR file + generate-car Generate a car file from input + balances Print storage market client balances + list-transfers List ongoing data transfers for deals + restart-transfer Force restart a stalled data transfer + cancel-transfer Force cancel a data transfer + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +### lotus client import +``` +NAME: + lotus client import - Import data + +USAGE: + lotus client import [command options] [inputPath] + +CATEGORY: + DATA + +OPTIONS: + --car import from a car file instead of a regular file (default: false) + --quiet, -q Output root CID only (default: false) + --help, -h show help (default: false) + +``` + +### lotus client drop +``` +NAME: + lotus client drop - Remove import + +USAGE: + lotus client drop [command options] [import ID...] + +CATEGORY: + DATA + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus client local +``` +NAME: + lotus client local - List locally imported data + +USAGE: + lotus client local [command options] [arguments...] + +CATEGORY: + DATA + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus client stat +``` +NAME: + lotus client stat - Print information about a locally stored file (piece size, etc) + +USAGE: + lotus client stat [command options] + +CATEGORY: + DATA + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus client find +``` +NAME: + lotus client find - Find data in the network + +USAGE: + lotus client find [command options] [dataCid] + +CATEGORY: + RETRIEVAL + +OPTIONS: + --pieceCid value require data to be retrieved from a specific Piece CID + --help, -h show help (default: false) + +``` + +### lotus client retrieve +``` +NAME: + lotus client retrieve - Retrieve data from network + +USAGE: + lotus client retrieve [command options] [dataCid outputPath] + +CATEGORY: + RETRIEVAL + +OPTIONS: + --from value address to send transactions from + --car export to a car file instead of a regular file (default: false) + --miner value miner address for retrieval, if not present it'll use local discovery + --maxPrice value maximum price the client is willing to consider (default: 0.01 FIL) + --pieceCid value require data to be retrieved from a specific Piece CID + --allow-local (default: false) + --help, -h show help (default: false) + +``` + +### lotus client cancel-retrieval +``` +NAME: + lotus client cancel-retrieval - Cancel a retrieval deal by deal ID; this also cancels the associated transfer + +USAGE: + lotus client cancel-retrieval [command options] [arguments...] + +CATEGORY: + RETRIEVAL + +OPTIONS: + --deal-id value specify retrieval deal by deal ID (default: 0) + --help, -h show help (default: false) + +``` + +### lotus client deal +``` +NAME: + lotus client deal - Initialize storage deal with a miner + +USAGE: + lotus client deal [command options] [dataCid miner price duration] + +CATEGORY: + STORAGE + +DESCRIPTION: + Make a deal with a miner. +dataCid comes from running 'lotus client import'. +miner is the address of the miner you wish to make a deal with. +price is measured in FIL/GB/Epoch. Miners usually don't accept a bid +lower than their advertised ask. You can check a miners listed price +with 'lotus client query-ask '. +duration is how long the miner should store the data for, in blocks. +The minimum value is 518400 (6 months). + +OPTIONS: + --manual-piece-cid value manually specify piece commitment for data (dataCid must be to a car file) + --manual-piece-size value if manually specifying piece cid, used to specify size (dataCid must be to a car file) (default: 0) + --from value specify address to fund the deal with + --start-epoch value specify the epoch that the deal should start at (default: -1) + --fast-retrieval indicates that data should be available for fast retrieval (default: true) + --verified-deal indicate that the deal counts towards verified client total (default: true if client is verified, false otherwise) + --provider-collateral value specify the requested provider collateral the miner should put up + --help, -h show help (default: false) + +``` + +### lotus client query-ask +``` +NAME: + lotus client query-ask - Find a miners ask + +USAGE: + lotus client query-ask [command options] [minerAddress] + +CATEGORY: + STORAGE + +OPTIONS: + --peerid value specify peer ID of node to make query against + --size value data size in bytes (default: 0) + --duration value deal duration (default: 0) + --help, -h show help (default: false) + +``` + +### lotus client list-deals +``` +NAME: + lotus client list-deals - List storage market deals + +USAGE: + lotus client list-deals [command options] [arguments...] + +CATEGORY: + STORAGE + +OPTIONS: + --verbose, -v print verbose deal details (default: false) + --color use color in display output (default: true) + --show-failed show failed/failing deals (default: false) + --watch watch deal updates in real-time, rather than a one time list (default: false) + --help, -h show help (default: false) + +``` + +### lotus client get-deal +``` +NAME: + lotus client get-deal - Print detailed deal information + +USAGE: + lotus client get-deal [command options] [arguments...] + +CATEGORY: + STORAGE + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus client list-asks +``` +NAME: + lotus client list-asks - List asks for top miners + +USAGE: + lotus client list-asks [command options] [arguments...] + +CATEGORY: + STORAGE + +OPTIONS: + --by-ping sort by ping (default: false) + --output-format value Either 'text' or 'csv' (default: "text") + --help, -h show help (default: false) + +``` + +### lotus client deal-stats +``` +NAME: + lotus client deal-stats - Print statistics about local storage deals + +USAGE: + lotus client deal-stats [command options] [arguments...] + +CATEGORY: + STORAGE + +OPTIONS: + --newer-than value (default: 0s) + --help, -h show help (default: false) + +``` + +### lotus client inspect-deal +``` +NAME: + lotus client inspect-deal - Inspect detailed information about deal's lifecycle and the various stages it goes through + +USAGE: + lotus client inspect-deal [command options] [arguments...] + +CATEGORY: + STORAGE + +OPTIONS: + --deal-id value (default: 0) + --proposal-cid value + --help, -h show help (default: false) + +``` + +### lotus client commP +``` +NAME: + lotus client commP - Calculate the piece-cid (commP) of a CAR file + +USAGE: + lotus client commP [command options] [inputFile] + +CATEGORY: + UTIL + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus client generate-car +``` +NAME: + lotus client generate-car - Generate a car file from input + +USAGE: + lotus client generate-car [command options] [inputPath outputPath] + +CATEGORY: + UTIL + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus client balances +``` +NAME: + lotus client balances - Print storage market client balances + +USAGE: + lotus client balances [command options] [arguments...] + +CATEGORY: + UTIL + +OPTIONS: + --client value specify storage client address + --help, -h show help (default: false) + +``` + +### lotus client list-transfers +``` +NAME: + lotus client list-transfers - List ongoing data transfers for deals + +USAGE: + lotus client list-transfers [command options] [arguments...] + +CATEGORY: + UTIL + +OPTIONS: + --verbose, -v print verbose transfer details (default: false) + --color use color in display output (default: true) + --completed show completed data transfers (default: false) + --watch watch deal updates in real-time, rather than a one time list (default: false) + --show-failed show failed/cancelled transfers (default: false) + --help, -h show help (default: false) + +``` + +### lotus client restart-transfer +``` +NAME: + lotus client restart-transfer - Force restart a stalled data transfer + +USAGE: + lotus client restart-transfer [command options] [arguments...] + +CATEGORY: + UTIL + +OPTIONS: + --peerid value narrow to transfer with specific peer + --initiator specify only transfers where peer is/is not initiator (default: true) + --help, -h show help (default: false) + +``` + +### lotus client cancel-transfer +``` +NAME: + lotus client cancel-transfer - Force cancel a data transfer + +USAGE: + lotus client cancel-transfer [command options] [arguments...] + +CATEGORY: + UTIL + +OPTIONS: + --peerid value narrow to transfer with specific peer + --initiator specify only transfers where peer is/is not initiator (default: true) + --cancel-timeout value time to wait for cancel to be sent to storage provider (default: 5s) + --help, -h show help (default: false) + +``` + +## lotus msig +``` +NAME: + lotus msig - Interact with a multisig wallet + +USAGE: + lotus msig command [command options] [arguments...] + +COMMANDS: + create Create a new multisig wallet + inspect Inspect a multisig wallet + propose Propose a multisig transaction + propose-remove Propose to remove a signer + approve Approve a multisig message + add-propose Propose to add a signer + add-approve Approve a message to add a signer + add-cancel Cancel a message to add a signer + swap-propose Propose to swap signers + swap-approve Approve a message to swap signers + swap-cancel Cancel a message to swap signers + lock-propose Propose to lock up some balance + lock-approve Approve a message to lock up some balance + lock-cancel Cancel a message to lock up some balance + vested Gets the amount vested in an msig between two epochs + propose-threshold Propose setting a different signing threshold on the account + help, h Shows a list of commands or help for one command + +OPTIONS: + --confidence value number of block confirmations to wait for (default: 5) + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +### lotus msig create +``` +NAME: + lotus msig create - Create a new multisig wallet + +USAGE: + lotus msig create [command options] [address1 address2 ...] + +OPTIONS: + --required value number of required approvals (uses number of signers provided if omitted) (default: 0) + --value value initial funds to give to multisig (default: "0") + --duration value length of the period over which funds unlock (default: "0") + --from value account to send the create message from + --help, -h show help (default: false) + +``` + +### lotus msig inspect +``` +NAME: + lotus msig inspect - Inspect a multisig wallet + +USAGE: + lotus msig inspect [command options] [address] + +OPTIONS: + --vesting Include vesting details (default: false) + --decode-params Decode parameters of transaction proposals (default: false) + --help, -h show help (default: false) + +``` + +### lotus msig propose +``` +NAME: + lotus msig propose - Propose a multisig transaction + +USAGE: + lotus msig propose [command options] [multisigAddress destinationAddress value (optional)] + +OPTIONS: + --from value account to send the propose message from + --help, -h show help (default: false) + +``` + +### lotus msig propose-remove +``` +NAME: + lotus msig propose-remove - Propose to remove a signer + +USAGE: + lotus msig propose-remove [command options] [multisigAddress signer] + +OPTIONS: + --decrease-threshold whether the number of required signers should be decreased (default: false) + --from value account to send the propose message from + --help, -h show help (default: false) + +``` + +### lotus msig approve +``` +NAME: + lotus msig approve - Approve a multisig message + +USAGE: + lotus msig approve [command options] [proposerAddress destination value [methodId methodParams]] + +OPTIONS: + --from value account to send the approve message from + --help, -h show help (default: false) + +``` + +### lotus msig add-propose +``` +NAME: + lotus msig add-propose - Propose to add a signer + +USAGE: + lotus msig add-propose [command options] [multisigAddress signer] + +OPTIONS: + --increase-threshold whether the number of required signers should be increased (default: false) + --from value account to send the propose message from + --help, -h show help (default: false) + +``` + +### lotus msig add-approve +``` +NAME: + lotus msig add-approve - Approve a message to add a signer + +USAGE: + lotus msig add-approve [command options] [multisigAddress proposerAddress txId newAddress increaseThreshold] + +OPTIONS: + --from value account to send the approve message from + --help, -h show help (default: false) + +``` + +### lotus msig add-cancel +``` +NAME: + lotus msig add-cancel - Cancel a message to add a signer + +USAGE: + lotus msig add-cancel [command options] [multisigAddress txId newAddress increaseThreshold] + +OPTIONS: + --from value account to send the approve message from + --help, -h show help (default: false) + +``` + +### lotus msig swap-propose +``` +NAME: + lotus msig swap-propose - Propose to swap signers + +USAGE: + lotus msig swap-propose [command options] [multisigAddress oldAddress newAddress] + +OPTIONS: + --from value account to send the approve message from + --help, -h show help (default: false) + +``` + +### lotus msig swap-approve +``` +NAME: + lotus msig swap-approve - Approve a message to swap signers + +USAGE: + lotus msig swap-approve [command options] [multisigAddress proposerAddress txId oldAddress newAddress] + +OPTIONS: + --from value account to send the approve message from + --help, -h show help (default: false) + +``` + +### lotus msig swap-cancel +``` +NAME: + lotus msig swap-cancel - Cancel a message to swap signers + +USAGE: + lotus msig swap-cancel [command options] [multisigAddress txId oldAddress newAddress] + +OPTIONS: + --from value account to send the approve message from + --help, -h show help (default: false) + +``` + +### lotus msig lock-propose +``` +NAME: + lotus msig lock-propose - Propose to lock up some balance + +USAGE: + lotus msig lock-propose [command options] [multisigAddress startEpoch unlockDuration amount] + +OPTIONS: + --from value account to send the propose message from + --help, -h show help (default: false) + +``` + +### lotus msig lock-approve +``` +NAME: + lotus msig lock-approve - Approve a message to lock up some balance + +USAGE: + lotus msig lock-approve [command options] [multisigAddress proposerAddress txId startEpoch unlockDuration amount] + +OPTIONS: + --from value account to send the approve message from + --help, -h show help (default: false) + +``` + +### lotus msig lock-cancel +``` +NAME: + lotus msig lock-cancel - Cancel a message to lock up some balance + +USAGE: + lotus msig lock-cancel [command options] [multisigAddress txId startEpoch unlockDuration amount] + +OPTIONS: + --from value account to send the cancel message from + --help, -h show help (default: false) + +``` + +### lotus msig vested +``` +NAME: + lotus msig vested - Gets the amount vested in an msig between two epochs + +USAGE: + lotus msig vested [command options] [multisigAddress] + +OPTIONS: + --start-epoch value start epoch to measure vesting from (default: 0) + --end-epoch value end epoch to stop measure vesting at (default: -1) + --help, -h show help (default: false) + +``` + +### lotus msig propose-threshold +``` +NAME: + lotus msig propose-threshold - Propose setting a different signing threshold on the account + +USAGE: + lotus msig propose-threshold [command options] + +OPTIONS: + --from value account to send the proposal from + --help, -h show help (default: false) + +``` + +## lotus verifreg +``` +NAME: + lotus verifreg - Interact with the verified registry actor + +USAGE: + lotus verifreg command [command options] [arguments...] + +COMMANDS: + verify-client give allowance to the specified verified client address + list-verifiers list all verifiers + list-clients list all verified clients + check-client check verified client remaining bytes + check-verifier check verifiers remaining bytes + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +### lotus verifreg verify-client +``` +NAME: + lotus verifreg verify-client - give allowance to the specified verified client address + +USAGE: + lotus verifreg verify-client [command options] [arguments...] + +OPTIONS: + --from value specify your verifier address to send the message from + --help, -h show help (default: false) + +``` + +### lotus verifreg list-verifiers +``` +NAME: + lotus verifreg list-verifiers - list all verifiers + +USAGE: + lotus verifreg list-verifiers [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus verifreg list-clients +``` +NAME: + lotus verifreg list-clients - list all verified clients + +USAGE: + lotus verifreg list-clients [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus verifreg check-client +``` +NAME: + lotus verifreg check-client - check verified client remaining bytes + +USAGE: + lotus verifreg check-client [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus verifreg check-verifier +``` +NAME: + lotus verifreg check-verifier - check verifiers remaining bytes + +USAGE: + lotus verifreg check-verifier [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +## lotus paych +``` +NAME: + lotus paych - Manage payment channels + +USAGE: + lotus paych command [command options] [arguments...] + +COMMANDS: + add-funds Add funds to the payment channel between fromAddress and toAddress. Creates the payment channel if it doesn't already exist. + list List all locally registered payment channels + voucher Interact with payment channel vouchers + settle Settle a payment channel + status Show the status of an outbound payment channel + status-by-from-to Show the status of an active outbound payment channel by from/to addresses + collect Collect funds for a payment channel + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +### lotus paych add-funds +``` +NAME: + lotus paych add-funds - Add funds to the payment channel between fromAddress and toAddress. Creates the payment channel if it doesn't already exist. + +USAGE: + lotus paych add-funds [command options] [fromAddress toAddress amount] + +OPTIONS: + --restart-retrievals restart stalled retrieval deals on this payment channel (default: true) + --help, -h show help (default: false) + +``` + +### lotus paych list +``` +NAME: + lotus paych list - List all locally registered payment channels + +USAGE: + lotus paych list [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus paych voucher +``` +NAME: + lotus paych voucher - Interact with payment channel vouchers + +USAGE: + lotus paych voucher command [command options] [arguments...] + +COMMANDS: + create Create a signed payment channel voucher + check Check validity of payment channel voucher + add Add payment channel voucher to local datastore + list List stored vouchers for a given payment channel + best-spendable Print vouchers with highest value that is currently spendable for each lane + submit Submit voucher to chain to update payment channel state + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +#### lotus paych voucher create +``` +NAME: + lotus paych voucher create - Create a signed payment channel voucher + +USAGE: + lotus paych voucher create [command options] [channelAddress amount] + +OPTIONS: + --lane value specify payment channel lane to use (default: 0) + --help, -h show help (default: false) + +``` + +#### lotus paych voucher check +``` +NAME: + lotus paych voucher check - Check validity of payment channel voucher + +USAGE: + lotus paych voucher check [command options] [channelAddress voucher] + +OPTIONS: + --help, -h show help (default: false) + +``` + +#### lotus paych voucher add +``` +NAME: + lotus paych voucher add - Add payment channel voucher to local datastore + +USAGE: + lotus paych voucher add [command options] [channelAddress voucher] + +OPTIONS: + --help, -h show help (default: false) + +``` + +#### lotus paych voucher list +``` +NAME: + lotus paych voucher list - List stored vouchers for a given payment channel + +USAGE: + lotus paych voucher list [command options] [channelAddress] + +OPTIONS: + --export Print voucher as serialized string (default: false) + --help, -h show help (default: false) + +``` + +#### lotus paych voucher best-spendable +``` +NAME: + lotus paych voucher best-spendable - Print vouchers with highest value that is currently spendable for each lane + +USAGE: + lotus paych voucher best-spendable [command options] [channelAddress] + +OPTIONS: + --export Print voucher as serialized string (default: false) + --help, -h show help (default: false) + +``` + +#### lotus paych voucher submit +``` +NAME: + lotus paych voucher submit - Submit voucher to chain to update payment channel state + +USAGE: + lotus paych voucher submit [command options] [channelAddress voucher] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus paych settle +``` +NAME: + lotus paych settle - Settle a payment channel + +USAGE: + lotus paych settle [command options] [channelAddress] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus paych status +``` +NAME: + lotus paych status - Show the status of an outbound payment channel + +USAGE: + lotus paych status [command options] [channelAddress] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus paych status-by-from-to +``` +NAME: + lotus paych status-by-from-to - Show the status of an active outbound payment channel by from/to addresses + +USAGE: + lotus paych status-by-from-to [command options] [fromAddress toAddress] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus paych collect +``` +NAME: + lotus paych collect - Collect funds for a payment channel + +USAGE: + lotus paych collect [command options] [channelAddress] + +OPTIONS: + --help, -h show help (default: false) + +``` + +## lotus auth +``` +NAME: + lotus auth - Manage RPC permissions + +USAGE: + lotus auth command [command options] [arguments...] + +COMMANDS: + create-token Create token + api-info Get token with API info required to connect to this node + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +### lotus auth create-token +``` +NAME: + lotus auth create-token - Create token + +USAGE: + lotus auth create-token [command options] [arguments...] + +OPTIONS: + --perm value permission to assign to the token, one of: read, write, sign, admin + --help, -h show help (default: false) + +``` + +### lotus auth api-info +``` +NAME: + lotus auth api-info - Get token with API info required to connect to this node + +USAGE: + lotus auth api-info [command options] [arguments...] + +OPTIONS: + --perm value permission to assign to the token, one of: read, write, sign, admin + --help, -h show help (default: false) + +``` + +## lotus mpool +``` +NAME: + lotus mpool - Manage message pool + +USAGE: + lotus mpool command [command options] [arguments...] + +COMMANDS: + pending Get pending messages + sub Subscribe to mpool changes + stat print mempool stats + replace replace a message in the mempool + find find a message in the mempool + config get or set current mpool configuration + gas-perf Check gas performance of messages in mempool + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +### lotus mpool pending +``` +NAME: + lotus mpool pending - Get pending messages + +USAGE: + lotus mpool pending [command options] [arguments...] + +OPTIONS: + --local print pending messages for addresses in local wallet only (default: false) + --cids only print cids of messages in output (default: false) + --to value return messages to a given address + --from value return messages from a given address + --help, -h show help (default: false) + +``` + +### lotus mpool sub +``` +NAME: + lotus mpool sub - Subscribe to mpool changes + +USAGE: + lotus mpool sub [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus mpool stat +``` +NAME: + lotus mpool stat - print mempool stats + +USAGE: + lotus mpool stat [command options] [arguments...] + +OPTIONS: + --local print stats for addresses in local wallet only (default: false) + --basefee-lookback value number of blocks to look back for minimum basefee (default: 60) + --help, -h show help (default: false) + +``` + +### lotus mpool replace +``` +NAME: + lotus mpool replace - replace a message in the mempool + +USAGE: + lotus mpool replace [command options] | + +OPTIONS: + --gas-feecap value gas feecap for new message (burn and pay to miner, attoFIL/GasUnit) + --gas-premium value gas price for new message (pay to miner, attoFIL/GasUnit) + --gas-limit value gas limit for new message (GasUnit) (default: 0) + --auto automatically reprice the specified message (default: false) + --max-fee value Spend up to X attoFIL for this message (applicable for auto mode) + --help, -h show help (default: false) + +``` + +### lotus mpool find +``` +NAME: + lotus mpool find - find a message in the mempool + +USAGE: + lotus mpool find [command options] [arguments...] + +OPTIONS: + --from value search for messages with given 'from' address + --to value search for messages with given 'to' address + --method value search for messages with given method (default: 0) + --help, -h show help (default: false) + +``` + +### lotus mpool config +``` +NAME: + lotus mpool config - get or set current mpool configuration + +USAGE: + lotus mpool config [command options] [new-config] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus mpool gas-perf +``` +NAME: + lotus mpool gas-perf - Check gas performance of messages in mempool + +USAGE: + lotus mpool gas-perf [command options] [arguments...] + +OPTIONS: + --all print gas performance for all mempool messages (default only prints for local) (default: false) + --help, -h show help (default: false) + +``` + +## lotus state +``` +NAME: + lotus state - Interact with and query filecoin chain state + +USAGE: + lotus state command [command options] [arguments...] + +COMMANDS: + power Query network or miner power + sectors Query the sector set of a miner + active-sectors Query the active sector set of a miner + list-actors list all actors in the network + list-miners list all miners in the network + circulating-supply Get the exact current circulating supply of Filecoin + sector Get miner sector info + get-actor Print actor information + lookup Find corresponding ID address + replay Replay a particular message + sector-size Look up miners sector size + read-state View a json representation of an actors state + list-messages list messages on chain matching given criteria + compute-state Perform state computations + call Invoke a method on an actor locally + get-deal View on-chain deal info + wait-msg Wait for a message to appear on chain + search-msg Search to see whether a message has appeared on chain + miner-info Retrieve miner information + market Inspect the storage market actor + exec-trace Get the execution trace of a given message + network-version Returns the network version + miner-proving-deadline Retrieve information about a given miner's proving deadline + help, h Shows a list of commands or help for one command + +OPTIONS: + --tipset value specify tipset to call method on (pass comma separated array of cids) + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +### lotus state power +``` +NAME: + lotus state power - Query network or miner power + +USAGE: + lotus state power [command options] [ (optional)] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus state sectors +``` +NAME: + lotus state sectors - Query the sector set of a miner + +USAGE: + lotus state sectors [command options] [minerAddress] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus state active-sectors +``` +NAME: + lotus state active-sectors - Query the active sector set of a miner + +USAGE: + lotus state active-sectors [command options] [minerAddress] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus state list-actors +``` +NAME: + lotus state list-actors - list all actors in the network + +USAGE: + lotus state list-actors [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus state list-miners +``` +NAME: + lotus state list-miners - list all miners in the network + +USAGE: + lotus state list-miners [command options] [arguments...] + +OPTIONS: + --sort-by value criteria to sort miners by (none, num-deals) + --help, -h show help (default: false) + +``` + +### lotus state circulating-supply +``` +NAME: + lotus state circulating-supply - Get the exact current circulating supply of Filecoin + +USAGE: + lotus state circulating-supply [command options] [arguments...] + +OPTIONS: + --vm-supply calculates the approximation of the circulating supply used internally by the VM (instead of the exact amount) (default: false) + --help, -h show help (default: false) + +``` + +### lotus state sector +``` +NAME: + lotus state sector - Get miner sector info + +USAGE: + lotus state sector [command options] [minerAddress] [sectorNumber] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus state get-actor +``` +NAME: + lotus state get-actor - Print actor information + +USAGE: + lotus state get-actor [command options] [actorrAddress] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus state lookup +``` +NAME: + lotus state lookup - Find corresponding ID address + +USAGE: + lotus state lookup [command options] [address] + +OPTIONS: + --reverse, -r Perform reverse lookup (default: false) + --help, -h show help (default: false) + +``` + +### lotus state replay +``` +NAME: + lotus state replay - Replay a particular message + +USAGE: + lotus state replay [command options] + +OPTIONS: + --show-trace print out full execution trace for given message (default: false) + --detailed-gas print out detailed gas costs for given message (default: false) + --help, -h show help (default: false) + +``` + +### lotus state sector-size +``` +NAME: + lotus state sector-size - Look up miners sector size + +USAGE: + lotus state sector-size [command options] [minerAddress] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus state read-state +``` +NAME: + lotus state read-state - View a json representation of an actors state + +USAGE: + lotus state read-state [command options] [actorAddress] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus state list-messages +``` +NAME: + lotus state list-messages - list messages on chain matching given criteria + +USAGE: + lotus state list-messages [command options] [arguments...] + +OPTIONS: + --to value return messages to a given address + --from value return messages from a given address + --toheight value don't look before given block height (default: 0) + --cids print message CIDs instead of messages (default: false) + --help, -h show help (default: false) + +``` + +### lotus state compute-state +``` +NAME: + lotus state compute-state - Perform state computations + +USAGE: + lotus state compute-state [command options] [arguments...] + +OPTIONS: + --vm-height value set the height that the vm will see (default: 0) + --apply-mpool-messages apply messages from the mempool to the computed state (default: false) + --show-trace print out full execution trace for given tipset (default: false) + --html generate html report (default: false) + --json generate json output (default: false) + --compute-state-output value a json file containing pre-existing compute-state output, to generate html reports without rerunning state changes + --no-timing don't show timing information in html traces (default: false) + --help, -h show help (default: false) + +``` + +### lotus state call +``` +NAME: + lotus state call - Invoke a method on an actor locally + +USAGE: + lotus state call [command options] [toAddress methodId (optional)] + +OPTIONS: + --from value (default: "f00") + --value value specify value field for invocation (default: "0") + --ret value specify how to parse output (auto, raw, addr, big) (default: "auto") + --help, -h show help (default: false) + +``` + +### lotus state get-deal +``` +NAME: + lotus state get-deal - View on-chain deal info + +USAGE: + lotus state get-deal [command options] [dealId] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus state wait-msg +``` +NAME: + lotus state wait-msg - Wait for a message to appear on chain + +USAGE: + lotus state wait-msg [command options] [messageCid] + +OPTIONS: + --timeout value (default: "10m") + --help, -h show help (default: false) + +``` + +### lotus state search-msg +``` +NAME: + lotus state search-msg - Search to see whether a message has appeared on chain + +USAGE: + lotus state search-msg [command options] [messageCid] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus state miner-info +``` +NAME: + lotus state miner-info - Retrieve miner information + +USAGE: + lotus state miner-info [command options] [minerAddress] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus state market +``` +NAME: + lotus state market - Inspect the storage market actor + +USAGE: + lotus state market command [command options] [arguments...] + +COMMANDS: + balance Get the market balance (locked and escrowed) for a given account + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +#### lotus state market balance +``` +NAME: + lotus state market balance - Get the market balance (locked and escrowed) for a given account + +USAGE: + lotus state market balance [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus state exec-trace +``` +NAME: + lotus state exec-trace - Get the execution trace of a given message + +USAGE: + lotus state exec-trace [command options] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus state network-version +``` +NAME: + lotus state network-version - Returns the network version + +USAGE: + lotus state network-version [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus state miner-proving-deadline +``` +NAME: + lotus state miner-proving-deadline - Retrieve information about a given miner's proving deadline + +USAGE: + lotus state miner-proving-deadline [command options] [minerAddress] + +OPTIONS: + --help, -h show help (default: false) + +``` + +## lotus chain +``` +NAME: + lotus chain - Interact with filecoin blockchain + +USAGE: + lotus chain command [command options] [arguments...] + +COMMANDS: + head Print chain head + getblock Get a block and print its details + read-obj Read the raw bytes of an object + delete-obj Delete an object from the chain blockstore + stat-obj Collect size and ipld link counts for objs + getmessage Get and print a message by its cid + sethead manually set the local nodes head tipset (Caution: normally only used for recovery) + list, love View a segment of the chain + get Get chain DAG node by path + bisect bisect chain for an event + export export chain to a car file + slash-consensus Report consensus fault + gas-price Estimate gas prices + inspect-usage Inspect block space usage of a given tipset + decode decode various types + encode encode various types + disputer interact with the window post disputer + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +### lotus chain head +``` +NAME: + lotus chain head - Print chain head + +USAGE: + lotus chain head [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus chain getblock +``` +NAME: + lotus chain getblock - Get a block and print its details + +USAGE: + lotus chain getblock [command options] [blockCid] + +OPTIONS: + --raw print just the raw block header (default: false) + --help, -h show help (default: false) + +``` + +### lotus chain read-obj +``` +NAME: + lotus chain read-obj - Read the raw bytes of an object + +USAGE: + lotus chain read-obj [command options] [objectCid] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus chain delete-obj +``` +NAME: + lotus chain delete-obj - Delete an object from the chain blockstore + +USAGE: + lotus chain delete-obj [command options] [objectCid] + +DESCRIPTION: + WARNING: Removing wrong objects from the chain blockstore may lead to sync issues + +OPTIONS: + --really-do-it (default: false) + --help, -h show help (default: false) + +``` + +### lotus chain stat-obj +``` +NAME: + lotus chain stat-obj - Collect size and ipld link counts for objs + +USAGE: + lotus chain stat-obj [command options] [cid] + +DESCRIPTION: + Collect object size and ipld link count for an object. + + When a base is provided it will be walked first, and all links visisted + will be ignored when the passed in object is walked. + + +OPTIONS: + --base value ignore links found in this obj + --help, -h show help (default: false) + +``` + +### lotus chain getmessage +``` +NAME: + lotus chain getmessage - Get and print a message by its cid + +USAGE: + lotus chain getmessage [command options] [messageCid] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus chain sethead +``` +NAME: + lotus chain sethead - manually set the local nodes head tipset (Caution: normally only used for recovery) + +USAGE: + lotus chain sethead [command options] [tipsetkey] + +OPTIONS: + --genesis reset head to genesis (default: false) + --epoch value reset head to given epoch (default: 0) + --help, -h show help (default: false) + +``` + +#### lotus chain list, love +``` +``` + +### lotus chain get +``` +NAME: + lotus chain get - Get chain DAG node by path + +USAGE: + lotus chain get [command options] [path] + +DESCRIPTION: + Get ipld node under a specified path: + + lotus chain get /ipfs/[cid]/some/path + + Path prefixes: + - /ipfs/[cid], /ipld/[cid] - traverse IPLD path + - /pstate - traverse from head.ParentStateRoot + + Note: + You can use special path elements to traverse through some data structures: + - /ipfs/[cid]/@H:elem - get 'elem' from hamt + - /ipfs/[cid]/@Hi:123 - get varint elem 123 from hamt + - /ipfs/[cid]/@Hu:123 - get uvarint elem 123 from hamt + - /ipfs/[cid]/@Ha:t01 - get element under Addr(t01).Bytes + - /ipfs/[cid]/@A:10 - get 10th amt element + - .../@Ha:t01/@state - get pretty map-based actor state + + List of --as-type types: + - raw + - block + - message + - smessage, signedmessage + - actor + - amt + - hamt-epoch + - hamt-address + - cronevent + - account-state + + +OPTIONS: + --as-type value specify type to interpret output as + --verbose (default: false) + --tipset value specify tipset for /pstate (pass comma separated array of cids) + --help, -h show help (default: false) + +``` + +### lotus chain bisect +``` +NAME: + lotus chain bisect - bisect chain for an event + +USAGE: + lotus chain bisect [command options] [minHeight maxHeight path shellCommand ] + +DESCRIPTION: + Bisect the chain state tree: + + lotus chain bisect [min height] [max height] '1/2/3/state/path' 'shell command' 'args' + + Returns the first tipset in which condition is true + v + [start] FFFFFFFTTT [end] + + Example: find height at which deal ID 100 000 appeared + - lotus chain bisect 1 32000 '@Ha:t03/1' jq -e '.[2] > 100000' + + For special path elements see 'chain get' help + + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus chain export +``` +NAME: + lotus chain export - export chain to a car file + +USAGE: + lotus chain export [command options] [outputPath] + +OPTIONS: + --tipset value + --recent-stateroots value specify the number of recent state roots to include in the export (default: 0) + --skip-old-msgs (default: false) + --help, -h show help (default: false) + +``` + +### lotus chain slash-consensus +``` +NAME: + lotus chain slash-consensus - Report consensus fault + +USAGE: + lotus chain slash-consensus [command options] [blockCid1 blockCid2] + +OPTIONS: + --from value optionally specify the account to report consensus from + --extra value Extra block cid + --help, -h show help (default: false) + +``` + +### lotus chain gas-price +``` +NAME: + lotus chain gas-price - Estimate gas prices + +USAGE: + lotus chain gas-price [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus chain inspect-usage +``` +NAME: + lotus chain inspect-usage - Inspect block space usage of a given tipset + +USAGE: + lotus chain inspect-usage [command options] [arguments...] + +OPTIONS: + --tipset value specify tipset to view block space usage of (default: "@head") + --length value length of chain to inspect block space usage for (default: 1) + --num-results value number of results to print per category (default: 10) + --help, -h show help (default: false) + +``` + +### lotus chain decode +``` +NAME: + lotus chain decode - decode various types + +USAGE: + lotus chain decode command [command options] [arguments...] + +COMMANDS: + params Decode message params + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +#### lotus chain decode params +``` +NAME: + lotus chain decode params - Decode message params + +USAGE: + lotus chain decode params [command options] [toAddr method params] + +OPTIONS: + --tipset value + --encoding value specify input encoding to parse (default: "base64") + --help, -h show help (default: false) + +``` + +### lotus chain encode +``` +NAME: + lotus chain encode - encode various types + +USAGE: + lotus chain encode command [command options] [arguments...] + +COMMANDS: + params Encodes the given JSON params + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +#### lotus chain encode params +``` +NAME: + lotus chain encode params - Encodes the given JSON params + +USAGE: + lotus chain encode params [command options] [toAddr method params] + +OPTIONS: + --tipset value + --encoding value specify input encoding to parse (default: "base64") + --help, -h show help (default: false) + +``` + +### lotus chain disputer +``` +NAME: + lotus chain disputer - interact with the window post disputer + +USAGE: + lotus chain disputer command [command options] [arguments...] + +COMMANDS: + start Start the window post disputer + dispute Send a specific DisputeWindowedPoSt message + help, h Shows a list of commands or help for one command + +OPTIONS: + --max-fee value Spend up to X FIL per DisputeWindowedPoSt message + --from value optionally specify the account to send messages from + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +#### lotus chain disputer start +``` +NAME: + lotus chain disputer start - Start the window post disputer + +USAGE: + lotus chain disputer start [command options] [minerAddress] + +OPTIONS: + --start-epoch value only start disputing PoSts after this epoch (default: 0) + --help, -h show help (default: false) + +``` + +#### lotus chain disputer dispute +``` +NAME: + lotus chain disputer dispute - Send a specific DisputeWindowedPoSt message + +USAGE: + lotus chain disputer dispute [command options] [minerAddress index postIndex] + +OPTIONS: + --help, -h show help (default: false) + +``` + +## lotus log +``` +NAME: + lotus log - Manage logging + +USAGE: + lotus log command [command options] [arguments...] + +COMMANDS: + list List log systems + set-level Set log level + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +### lotus log list +``` +NAME: + lotus log list - List log systems + +USAGE: + lotus log list [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus log set-level +``` +NAME: + lotus log set-level - Set log level + +USAGE: + lotus log set-level [command options] [level] + +DESCRIPTION: + Set the log level for logging systems: + + The system flag can be specified multiple times. + + eg) log set-level --system chain --system chainxchg debug + + Available Levels: + debug + info + warn + error + + Environment Variables: + GOLOG_LOG_LEVEL - Default log level for all log systems + GOLOG_LOG_FMT - Change output log format (json, nocolor) + GOLOG_FILE - Write logs to file + GOLOG_OUTPUT - Specify whether to output to file, stderr, stdout or a combination, i.e. file+stderr + + +OPTIONS: + --system value limit to log system + --help, -h show help (default: false) + +``` + +## lotus wait-api +``` +NAME: + lotus wait-api - Wait for lotus api to come online + +USAGE: + lotus wait-api [command options] [arguments...] + +CATEGORY: + DEVELOPER + +OPTIONS: + --help, -h show help (default: false) + +``` + +## lotus fetch-params +``` +NAME: + lotus fetch-params - Fetch proving parameters + +USAGE: + lotus fetch-params [command options] [sectorSize] + +CATEGORY: + DEVELOPER + +OPTIONS: + --help, -h show help (default: false) + +``` + +## lotus net +``` +NAME: + lotus net - Manage P2P Network + +USAGE: + lotus net command [command options] [arguments...] + +COMMANDS: + peers Print peers + connect Connect to a peer + listen List listen addresses + id Get node identity + findpeer Find the addresses of a given peerID + scores Print peers' pubsub scores + reachability Print information about reachability from the internet + bandwidth Print bandwidth usage information + block Manage network connection gating rules + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +### lotus net peers +``` +NAME: + lotus net peers - Print peers + +USAGE: + lotus net peers [command options] [arguments...] + +OPTIONS: + --agent, -a Print agent name (default: false) + --extended, -x Print extended peer information in json (default: false) + --help, -h show help (default: false) + +``` + +### lotus net connect +``` +NAME: + lotus net connect - Connect to a peer + +USAGE: + lotus net connect [command options] [peerMultiaddr|minerActorAddress] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus net listen +``` +NAME: + lotus net listen - List listen addresses + +USAGE: + lotus net listen [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus net id +``` +NAME: + lotus net id - Get node identity + +USAGE: + lotus net id [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus net findpeer +``` +NAME: + lotus net findpeer - Find the addresses of a given peerID + +USAGE: + lotus net findpeer [command options] [peerId] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus net scores +``` +NAME: + lotus net scores - Print peers' pubsub scores + +USAGE: + lotus net scores [command options] [arguments...] + +OPTIONS: + --extended, -x print extended peer scores in json (default: false) + --help, -h show help (default: false) + +``` + +### lotus net reachability +``` +NAME: + lotus net reachability - Print information about reachability from the internet + +USAGE: + lotus net reachability [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus net bandwidth +``` +NAME: + lotus net bandwidth - Print bandwidth usage information + +USAGE: + lotus net bandwidth [command options] [arguments...] + +OPTIONS: + --by-peer list bandwidth usage by peer (default: false) + --by-protocol list bandwidth usage by protocol (default: false) + --help, -h show help (default: false) + +``` + +### lotus net block +``` +NAME: + lotus net block - Manage network connection gating rules + +USAGE: + lotus net block command [command options] [arguments...] + +COMMANDS: + add Add connection gating rules + remove Remove connection gating rules + list list connection gating rules + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +#### lotus net block add +``` +NAME: + lotus net block add - Add connection gating rules + +USAGE: + lotus net block add command [command options] [arguments...] + +COMMANDS: + peer Block a peer + ip Block an IP address + subnet Block an IP subnet + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +##### lotus net block add peer +``` +NAME: + lotus net block add peer - Block a peer + +USAGE: + lotus net block add peer [command options] ... + +OPTIONS: + --help, -h show help (default: false) + +``` + +##### lotus net block add ip +``` +NAME: + lotus net block add ip - Block an IP address + +USAGE: + lotus net block add ip [command options] ... + +OPTIONS: + --help, -h show help (default: false) + +``` + +##### lotus net block add subnet +``` +NAME: + lotus net block add subnet - Block an IP subnet + +USAGE: + lotus net block add subnet [command options] ... + +OPTIONS: + --help, -h show help (default: false) + +``` + +#### lotus net block remove +``` +NAME: + lotus net block remove - Remove connection gating rules + +USAGE: + lotus net block remove command [command options] [arguments...] + +COMMANDS: + peer Unblock a peer + ip Unblock an IP address + subnet Unblock an IP subnet + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +##### lotus net block remove peer +``` +NAME: + lotus net block remove peer - Unblock a peer + +USAGE: + lotus net block remove peer [command options] ... + +OPTIONS: + --help, -h show help (default: false) + +``` + +##### lotus net block remove ip +``` +NAME: + lotus net block remove ip - Unblock an IP address + +USAGE: + lotus net block remove ip [command options] ... + +OPTIONS: + --help, -h show help (default: false) + +``` + +##### lotus net block remove subnet +``` +NAME: + lotus net block remove subnet - Unblock an IP subnet + +USAGE: + lotus net block remove subnet [command options] ... + +OPTIONS: + --help, -h show help (default: false) + +``` + +#### lotus net block list +``` +NAME: + lotus net block list - list connection gating rules + +USAGE: + lotus net block list [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +## lotus sync +``` +NAME: + lotus sync - Inspect or interact with the chain syncer + +USAGE: + lotus sync command [command options] [arguments...] + +COMMANDS: + status check sync status + wait Wait for sync to be complete + mark-bad Mark the given block as bad, will prevent syncing to a chain that contains it + unmark-bad Unmark the given block as bad, makes it possible to sync to a chain containing it + check-bad check if the given block was marked bad, and for what reason + checkpoint mark a certain tipset as checkpointed; the node will never fork away from this tipset + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help (default: false) + --version, -v print the version (default: false) + +``` + +### lotus sync status +``` +NAME: + lotus sync status - check sync status + +USAGE: + lotus sync status [command options] [arguments...] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus sync wait +``` +NAME: + lotus sync wait - Wait for sync to be complete + +USAGE: + lotus sync wait [command options] [arguments...] + +OPTIONS: + --watch don't exit after node is synced (default: false) + --help, -h show help (default: false) + +``` + +### lotus sync mark-bad +``` +NAME: + lotus sync mark-bad - Mark the given block as bad, will prevent syncing to a chain that contains it + +USAGE: + lotus sync mark-bad [command options] [blockCid] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus sync unmark-bad +``` +NAME: + lotus sync unmark-bad - Unmark the given block as bad, makes it possible to sync to a chain containing it + +USAGE: + lotus sync unmark-bad [command options] [blockCid] + +OPTIONS: + --all drop the entire bad block cache (default: false) + --help, -h show help (default: false) + +``` + +### lotus sync check-bad +``` +NAME: + lotus sync check-bad - check if the given block was marked bad, and for what reason + +USAGE: + lotus sync check-bad [command options] [blockCid] + +OPTIONS: + --help, -h show help (default: false) + +``` + +### lotus sync checkpoint +``` +NAME: + lotus sync checkpoint - mark a certain tipset as checkpointed; the node will never fork away from this tipset + +USAGE: + lotus sync checkpoint [command options] [tipsetKey] + +OPTIONS: + --epoch value checkpoint the tipset at the given epoch (default: 0) + --help, -h show help (default: false) + +``` From 8d991283f41b60cb1fdf4c73e46e6ad8a11f8532 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Thu, 6 May 2021 23:51:42 -0400 Subject: [PATCH 055/160] Resolve to ID addresses when handling message selection --- chain/messagepool/messagepool.go | 71 +++++++++-- chain/messagepool/provider.go | 2 + chain/messagepool/selection.go | 10 +- chain/store/store.go | 28 ++++- chain/sync.go | 22 +++- chain/sync_test.go | 111 +++++++++++++++++- .../misc/actors_version_checklist.md | 2 +- 7 files changed, 220 insertions(+), 26 deletions(-) diff --git a/chain/messagepool/messagepool.go b/chain/messagepool/messagepool.go index c2566ae24..299634c6f 100644 --- a/chain/messagepool/messagepool.go +++ b/chain/messagepool/messagepool.go @@ -126,8 +126,10 @@ type MessagePool struct { republished map[cid.Cid]struct{} + // only pubkey addresses localAddrs map[address.Address]struct{} + // only pubkey addresses pending map[address.Address]*msgSet curTsLk sync.Mutex // DO NOT LOCK INSIDE lk @@ -443,7 +445,14 @@ func (mp *MessagePool) runLoop() { } func (mp *MessagePool) addLocal(m *types.SignedMessage) error { - mp.localAddrs[m.Message.From] = struct{}{} + // TODO: Is context.TODO() safe here? Idk how Go works. + sk, err := mp.api.StateAccountKey(context.TODO(), m.Message.From, mp.curTs) + if err != nil { + log.Debugf("mpooladdlocal failed to resolve sender: %s", err) + return err + } + + mp.localAddrs[sk] = struct{}{} msgb, err := m.Serialize() if err != nil { @@ -475,7 +484,7 @@ func (mp *MessagePool) verifyMsgBeforeAdd(m *types.SignedMessage, curTs *types.T return false, xerrors.Errorf("message will not be included in a block: %w", err) } - // this checks if the GasFeeCap is suffisciently high for inclusion in the next 20 blocks + // this checks if the GasFeeCap is sufficiently high for inclusion in the next 20 blocks // if the GasFeeCap is too low, we soft reject the message (Ignore in pubsub) and rely // on republish to push it through later, if the baseFee has fallen. // this is a defensive check that stops minimum baseFee spam attacks from overloading validation @@ -645,7 +654,14 @@ func (mp *MessagePool) checkBalance(m *types.SignedMessage, curTs *types.TipSet) // add Value for soft failure check //requiredFunds = types.BigAdd(requiredFunds, m.Message.Value) - mset, ok := mp.pending[m.Message.From] + // TODO: Is context.TODO() safe here? Idk how Go works. + sk, err := mp.api.StateAccountKey(context.TODO(), m.Message.From, mp.curTs) + if err != nil { + log.Debugf("mpoolcheckbalance failed to resolve sender: %s", err) + return err + } + + mset, ok := mp.pending[sk] if ok { requiredFunds = types.BigAdd(requiredFunds, mset.getRequiredFunds(m.Message.Nonce)) } @@ -752,15 +768,22 @@ func (mp *MessagePool) addLocked(m *types.SignedMessage, strict, untrusted bool) return err } - mset, ok := mp.pending[m.Message.From] + // TODO: Is context.TODO() safe here? Idk how Go works. + sk, err := mp.api.StateAccountKey(context.TODO(), m.Message.From, mp.curTs) + if err != nil { + log.Debugf("mpooladd failed to resolve sender: %s", err) + return err + } + + mset, ok := mp.pending[sk] if !ok { - nonce, err := mp.getStateNonce(m.Message.From, mp.curTs) + nonce, err := mp.getStateNonce(sk, mp.curTs) if err != nil { return xerrors.Errorf("failed to get initial actor nonce: %w", err) } mset = newMsgSet(nonce) - mp.pending[m.Message.From] = mset + mp.pending[sk] = mset } incr, err := mset.add(m, mp, strict, untrusted) @@ -811,7 +834,14 @@ func (mp *MessagePool) getNonceLocked(addr address.Address, curTs *types.TipSet) return 0, err } - mset, ok := mp.pending[addr] + // TODO: Is context.TODO() safe here? Idk how Go works. + sk, err := mp.api.StateAccountKey(context.TODO(), addr, mp.curTs) + if err != nil { + log.Debugf("mpoolgetnonce failed to resolve sender: %s", err) + return 0, err + } + + mset, ok := mp.pending[sk] if ok { if stateNonce > mset.nextNonce { log.Errorf("state nonce was larger than mset.nextNonce (%d > %d)", stateNonce, mset.nextNonce) @@ -891,7 +921,14 @@ func (mp *MessagePool) Remove(from address.Address, nonce uint64, applied bool) } func (mp *MessagePool) remove(from address.Address, nonce uint64, applied bool) { - mset, ok := mp.pending[from] + // TODO: Is context.TODO() safe here? Idk how Go works. + sk, err := mp.api.StateAccountKey(context.TODO(), from, mp.curTs) + if err != nil { + log.Debugf("mpoolremove failed to resolve sender: %s", err) + return + } + + mset, ok := mp.pending[sk] if !ok { return } @@ -949,7 +986,14 @@ func (mp *MessagePool) PendingFor(a address.Address) ([]*types.SignedMessage, *t } func (mp *MessagePool) pendingFor(a address.Address) []*types.SignedMessage { - mset := mp.pending[a] + // TODO: Is context.TODO() safe here? Idk how Go works. + sk, err := mp.api.StateAccountKey(context.TODO(), a, mp.curTs) + if err != nil { + log.Debugf("mpoolpendingfor failed to resolve sender: %s", err) + return nil + } + + mset := mp.pending[sk] if mset == nil || len(mset.msgs) == 0 { return nil } @@ -1303,7 +1347,14 @@ func (mp *MessagePool) loadLocal() error { log.Errorf("adding local message: %+v", err) } - mp.localAddrs[sm.Message.From] = struct{}{} + // TODO: Is context.TODO() safe here? Idk how Go works. + sk, err := mp.api.StateAccountKey(context.TODO(), sm.Message.From, mp.curTs) + if err != nil { + log.Debugf("mpoolloadLocal failed to resolve sender: %s", err) + return err + } + + mp.localAddrs[sk] = struct{}{} } return nil diff --git a/chain/messagepool/provider.go b/chain/messagepool/provider.go index 5a6c751bc..75a2efe91 100644 --- a/chain/messagepool/provider.go +++ b/chain/messagepool/provider.go @@ -37,6 +37,8 @@ type mpoolProvider struct { ps *pubsub.PubSub } +var _ Provider = (*mpoolProvider)(nil) + func NewProvider(sm *stmgr.StateManager, ps *pubsub.PubSub) Provider { return &mpoolProvider{sm: sm, ps: ps} } diff --git a/chain/messagepool/selection.go b/chain/messagepool/selection.go index 6c9d506ef..af450645f 100644 --- a/chain/messagepool/selection.go +++ b/chain/messagepool/selection.go @@ -543,10 +543,16 @@ func (mp *MessagePool) selectPriorityMessages(pending map[address.Address]map[ui var chains []*msgChain priority := mpCfg.PriorityAddrs for _, actor := range priority { - mset, ok := pending[actor] + pk, err := mp.api.StateAccountKey(context.TODO(), actor, mp.curTs) + if err != nil { + log.Debugf("mpooladdlocal failed to resolve sender: %s", err) + return nil, gasLimit + } + + mset, ok := pending[pk] if ok { // remove actor from pending set as we are already processed these messages - delete(pending, actor) + delete(pending, pk) // create chains for the priority actor next := mp.createMessageChains(actor, mset, baseFee, ts) chains = append(chains, next...) diff --git a/chain/store/store.go b/chain/store/store.go index dfde93fc7..5414b12fe 100644 --- a/chain/store/store.go +++ b/chain/store/store.go @@ -12,6 +12,8 @@ import ( "strings" "sync" + "github.com/filecoin-project/lotus/chain/state" + "golang.org/x/sync/errgroup" "github.com/filecoin-project/go-state-types/crypto" @@ -1008,17 +1010,33 @@ type BlockMessages struct { func (cs *ChainStore) BlockMsgsForTipset(ts *types.TipSet) ([]BlockMessages, error) { applied := make(map[address.Address]uint64) + cst := cbor.NewCborStore(cs.stateBlockstore) + st, err := state.LoadStateTree(cst, ts.Blocks()[0].ParentStateRoot) + if err != nil { + return nil, xerrors.Errorf("failed to load state tree") + } + selectMsg := func(m *types.Message) (bool, error) { - // The first match for a sender is guaranteed to have correct nonce -- the block isn't valid otherwise - if _, ok := applied[m.From]; !ok { - applied[m.From] = m.Nonce + var sender address.Address + if ts.Height() >= build.UpgradeHyperdriveHeight { + sender, err = st.LookupID(m.From) + if err != nil { + return false, err + } + } else { + sender = m.From } - if applied[m.From] != m.Nonce { + // The first match for a sender is guaranteed to have correct nonce -- the block isn't valid otherwise + if _, ok := applied[sender]; !ok { + applied[sender] = m.Nonce + } + + if applied[sender] != m.Nonce { return false, nil } - applied[m.From]++ + applied[sender]++ return true, nil } diff --git a/chain/sync.go b/chain/sync.go index 66c9c18bd..d908f3fd8 100644 --- a/chain/sync.go +++ b/chain/sync.go @@ -1084,9 +1084,19 @@ func (syncer *Syncer) checkBlockMessages(ctx context.Context, b *types.FullBlock // Phase 2: (Partial) semantic validation: // the sender exists and is an account actor, and the nonces make sense - if _, ok := nonces[m.From]; !ok { + var sender address.Address + if syncer.sm.GetNtwkVersion(ctx, b.Header.Height) >= network.Version13 { + sender, err = st.LookupID(m.From) + if err != nil { + return err + } + } else { + sender = m.From + } + + if _, ok := nonces[sender]; !ok { // `GetActor` does not validate that this is an account actor. - act, err := st.GetActor(m.From) + act, err := st.GetActor(sender) if err != nil { return xerrors.Errorf("failed to get actor: %w", err) } @@ -1094,13 +1104,13 @@ func (syncer *Syncer) checkBlockMessages(ctx context.Context, b *types.FullBlock if !builtin.IsAccountActor(act.Code) { return xerrors.New("Sender must be an account actor") } - nonces[m.From] = act.Nonce + nonces[sender] = act.Nonce } - if nonces[m.From] != m.Nonce { - return xerrors.Errorf("wrong nonce (exp: %d, got: %d)", nonces[m.From], m.Nonce) + if nonces[sender] != m.Nonce { + return xerrors.Errorf("wrong nonce (exp: %d, got: %d)", nonces[sender], m.Nonce) } - nonces[m.From]++ + nonces[sender]++ return nil } diff --git a/chain/sync_test.go b/chain/sync_test.go index 095b224ad..4aa3f6fdf 100644 --- a/chain/sync_test.go +++ b/chain/sync_test.go @@ -10,7 +10,6 @@ import ( "github.com/filecoin-project/go-state-types/crypto" "github.com/filecoin-project/go-state-types/network" - "github.com/filecoin-project/lotus/chain/stmgr" "github.com/ipfs/go-cid" @@ -108,6 +107,7 @@ func prepSyncTest(t testing.TB, h int) *syncTestUtil { } tu.addSourceNode(stmgr.DefaultUpgradeSchedule(), h) + //tu.checkHeight("source", source, h) // separate logs @@ -730,7 +730,6 @@ func TestBadNonce(t *testing.T) { // Produce a message from the banker with a bad nonce makeBadMsg := func() *types.SignedMessage { - msg := types.Message{ To: tu.g.Banker(), From: tu.g.Banker(), @@ -761,6 +760,114 @@ func TestBadNonce(t *testing.T) { tu.mineOnBlock(base, 0, []int{0}, true, true, msgs, 0) } +// This test introduces a block that has 2 messages, with the same sender, and same nonce. +// One of the messages uses the sender's robust address, the other uses the ID address. +// Such a block is invalid and should not sync. +func TestMismatchedNoncesRobustID(t *testing.T) { + v5h := abi.ChainEpoch(4) + tu := prepSyncTestWithV5Height(t, int(v5h+5), v5h) + + base := tu.g.CurTipset + + // Get the banker from computed tipset state, not the parent. + st, _, err := tu.g.StateManager().TipSetState(context.TODO(), base.TipSet()) + require.NoError(t, err) + ba, err := tu.g.StateManager().LoadActorRaw(context.TODO(), tu.g.Banker(), st) + require.NoError(t, err) + + // Produce a message from the banker + makeMsg := func(id bool) *types.SignedMessage { + sender := tu.g.Banker() + if id { + s, err := tu.nds[0].StateLookupID(context.TODO(), sender, base.TipSet().Key()) + require.NoError(t, err) + sender = s + } + + msg := types.Message{ + To: tu.g.Banker(), + From: sender, + + Nonce: ba.Nonce, + + Value: types.NewInt(1), + + Method: 0, + + GasLimit: 100_000_000, + GasFeeCap: types.NewInt(0), + GasPremium: types.NewInt(0), + } + + sig, err := tu.g.Wallet().WalletSign(context.TODO(), tu.g.Banker(), msg.Cid().Bytes(), api.MsgMeta{}) + require.NoError(t, err) + + return &types.SignedMessage{ + Message: msg, + Signature: *sig, + } + } + + msgs := make([][]*types.SignedMessage, 1) + msgs[0] = []*types.SignedMessage{makeMsg(false), makeMsg(true)} + + tu.mineOnBlock(base, 0, []int{0}, true, true, msgs, 0) +} + +// This test introduces a block that has 2 messages, with the same sender, and nonces N and N+1 (so both can be included in a block) +// One of the messages uses the sender's robust address, the other uses the ID address. +// Such a block is valid and should sync. +func TestMatchedNoncesRobustID(t *testing.T) { + v5h := abi.ChainEpoch(4) + tu := prepSyncTestWithV5Height(t, int(v5h+5), v5h) + + base := tu.g.CurTipset + + // Get the banker from computed tipset state, not the parent. + st, _, err := tu.g.StateManager().TipSetState(context.TODO(), base.TipSet()) + require.NoError(t, err) + ba, err := tu.g.StateManager().LoadActorRaw(context.TODO(), tu.g.Banker(), st) + require.NoError(t, err) + + // Produce a message from the banker with specified nonce + makeMsg := func(n uint64, id bool) *types.SignedMessage { + sender := tu.g.Banker() + if id { + s, err := tu.nds[0].StateLookupID(context.TODO(), sender, base.TipSet().Key()) + require.NoError(t, err) + sender = s + } + + msg := types.Message{ + To: tu.g.Banker(), + From: sender, + + Nonce: n, + + Value: types.NewInt(1), + + Method: 0, + + GasLimit: 100_000_000, + GasFeeCap: types.NewInt(0), + GasPremium: types.NewInt(0), + } + + sig, err := tu.g.Wallet().WalletSign(context.TODO(), tu.g.Banker(), msg.Cid().Bytes(), api.MsgMeta{}) + require.NoError(t, err) + + return &types.SignedMessage{ + Message: msg, + Signature: *sig, + } + } + + msgs := make([][]*types.SignedMessage, 1) + msgs[0] = []*types.SignedMessage{makeMsg(ba.Nonce, false), makeMsg(ba.Nonce+1, true)} + + tu.mineOnBlock(base, 0, []int{0}, true, false, msgs, 0) +} + func BenchmarkSyncBasic(b *testing.B) { for i := 0; i < b.N; i++ { runSyncBenchLength(b, 100) diff --git a/documentation/misc/actors_version_checklist.md b/documentation/misc/actors_version_checklist.md index 7931acbcc..769e9da42 100644 --- a/documentation/misc/actors_version_checklist.md +++ b/documentation/misc/actors_version_checklist.md @@ -12,6 +12,6 @@ - [ ] Update `chain/stmgr/forks.go` - [ ] Schedule - [ ] Migration -- [ ] Update upgrade schedule in `api/test/test.go` +- [ ] Update upgrade schedule in `api/test/test.go` and `chain/sync_test.go` - [ ] Update `NewestNetworkVersion` in `build/params_shared_vals.go` - [ ] Register in init in `chain/stmgr/utils.go` From 1f03a618f9abfcb107575e929d4a467f40391d5f Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Tue, 18 May 2021 14:56:42 -0400 Subject: [PATCH 056/160] Plumb contexts through --- chain/messagepool/messagepool.go | 103 ++++++++++------------ chain/messagepool/messagepool_test.go | 46 +++++----- chain/messagepool/pruning.go | 2 +- chain/messagepool/repub_test.go | 4 +- chain/messagepool/selection_test.go | 4 +- chain/messagesigner/messagesigner.go | 8 +- chain/messagesigner/messagesigner_test.go | 4 +- chain/sub/incoming.go | 2 +- node/impl/full/gas.go | 2 +- node/impl/full/mpool.go | 12 +-- node/modules/chain.go | 3 +- node/modules/mpoolnonceapi.go | 2 +- 12 files changed, 94 insertions(+), 98 deletions(-) diff --git a/chain/messagepool/messagepool.go b/chain/messagepool/messagepool.go index 299634c6f..0c8569a1f 100644 --- a/chain/messagepool/messagepool.go +++ b/chain/messagepool/messagepool.go @@ -331,7 +331,7 @@ func (ms *msgSet) getRequiredFunds(nonce uint64) types.BigInt { return types.BigInt{Int: requiredFunds} } -func New(api Provider, ds dtypes.MetadataDS, netName dtypes.NetworkName, j journal.Journal) (*MessagePool, error) { +func New(ctx context.Context, api Provider, ds dtypes.MetadataDS, netName dtypes.NetworkName, j journal.Journal) (*MessagePool, error) { cache, _ := lru.New2Q(build.BlsSignatureCacheSize) verifcache, _ := lru.New2Q(build.VerifSigCacheSize) @@ -375,7 +375,7 @@ func New(api Provider, ds dtypes.MetadataDS, netName dtypes.NetworkName, j journ // load the current tipset and subscribe to head changes _before_ loading local messages mp.curTs = api.SubscribeHeadChanges(func(rev, app []*types.TipSet) error { - err := mp.HeadChange(rev, app) + err := mp.HeadChange(ctx, rev, app) if err != nil { log.Errorf("mpool head notif handler error: %+v", err) } @@ -386,7 +386,7 @@ func New(api Provider, ds dtypes.MetadataDS, netName dtypes.NetworkName, j journ mp.lk.Lock() go func() { - err := mp.loadLocal() + err := mp.loadLocal(ctx) mp.lk.Unlock() mp.curTsLk.Unlock() @@ -444,9 +444,8 @@ func (mp *MessagePool) runLoop() { } } -func (mp *MessagePool) addLocal(m *types.SignedMessage) error { - // TODO: Is context.TODO() safe here? Idk how Go works. - sk, err := mp.api.StateAccountKey(context.TODO(), m.Message.From, mp.curTs) +func (mp *MessagePool) addLocal(ctx context.Context, m *types.SignedMessage) error { + sk, err := mp.api.StateAccountKey(ctx, m.Message.From, mp.curTs) if err != nil { log.Debugf("mpooladdlocal failed to resolve sender: %s", err) return err @@ -519,7 +518,7 @@ func (mp *MessagePool) verifyMsgBeforeAdd(m *types.SignedMessage, curTs *types.T return publish, nil } -func (mp *MessagePool) Push(m *types.SignedMessage) (cid.Cid, error) { +func (mp *MessagePool) Push(ctx context.Context, m *types.SignedMessage) (cid.Cid, error) { err := mp.checkMessage(m) if err != nil { return cid.Undef, err @@ -532,7 +531,7 @@ func (mp *MessagePool) Push(m *types.SignedMessage) (cid.Cid, error) { }() mp.curTsLk.Lock() - publish, err := mp.addTs(m, mp.curTs, true, false) + publish, err := mp.addTs(ctx, m, mp.curTs, true, false) if err != nil { mp.curTsLk.Unlock() return cid.Undef, err @@ -585,7 +584,7 @@ func (mp *MessagePool) checkMessage(m *types.SignedMessage) error { return nil } -func (mp *MessagePool) Add(m *types.SignedMessage) error { +func (mp *MessagePool) Add(ctx context.Context, m *types.SignedMessage) error { err := mp.checkMessage(m) if err != nil { return err @@ -600,7 +599,7 @@ func (mp *MessagePool) Add(m *types.SignedMessage) error { mp.curTsLk.Lock() defer mp.curTsLk.Unlock() - _, err = mp.addTs(m, mp.curTs, false, false) + _, err = mp.addTs(ctx, m, mp.curTs, false, false) return err } @@ -640,7 +639,7 @@ func (mp *MessagePool) VerifyMsgSig(m *types.SignedMessage) error { return nil } -func (mp *MessagePool) checkBalance(m *types.SignedMessage, curTs *types.TipSet) error { +func (mp *MessagePool) checkBalance(ctx context.Context, m *types.SignedMessage, curTs *types.TipSet) error { balance, err := mp.getStateBalance(m.Message.From, curTs) if err != nil { return xerrors.Errorf("failed to check sender balance: %s: %w", err, ErrSoftValidationFailure) @@ -654,8 +653,7 @@ func (mp *MessagePool) checkBalance(m *types.SignedMessage, curTs *types.TipSet) // add Value for soft failure check //requiredFunds = types.BigAdd(requiredFunds, m.Message.Value) - // TODO: Is context.TODO() safe here? Idk how Go works. - sk, err := mp.api.StateAccountKey(context.TODO(), m.Message.From, mp.curTs) + sk, err := mp.api.StateAccountKey(ctx, m.Message.From, mp.curTs) if err != nil { log.Debugf("mpoolcheckbalance failed to resolve sender: %s", err) return err @@ -675,7 +673,7 @@ func (mp *MessagePool) checkBalance(m *types.SignedMessage, curTs *types.TipSet) return nil } -func (mp *MessagePool) addTs(m *types.SignedMessage, curTs *types.TipSet, local, untrusted bool) (bool, error) { +func (mp *MessagePool) addTs(ctx context.Context, m *types.SignedMessage, curTs *types.TipSet, local, untrusted bool) (bool, error) { snonce, err := mp.getStateNonce(m.Message.From, curTs) if err != nil { return false, xerrors.Errorf("failed to look up actor state nonce: %s: %w", err, ErrSoftValidationFailure) @@ -693,17 +691,17 @@ func (mp *MessagePool) addTs(m *types.SignedMessage, curTs *types.TipSet, local, return false, err } - if err := mp.checkBalance(m, curTs); err != nil { + if err := mp.checkBalance(ctx, m, curTs); err != nil { return false, err } - err = mp.addLocked(m, !local, untrusted) + err = mp.addLocked(ctx, m, !local, untrusted) if err != nil { return false, err } if local { - err = mp.addLocal(m) + err = mp.addLocal(ctx, m) if err != nil { return false, xerrors.Errorf("error persisting local message: %w", err) } @@ -712,7 +710,7 @@ func (mp *MessagePool) addTs(m *types.SignedMessage, curTs *types.TipSet, local, return publish, nil } -func (mp *MessagePool) addLoaded(m *types.SignedMessage) error { +func (mp *MessagePool) addLoaded(ctx context.Context, m *types.SignedMessage) error { err := mp.checkMessage(m) if err != nil { return err @@ -738,21 +736,21 @@ func (mp *MessagePool) addLoaded(m *types.SignedMessage) error { return err } - if err := mp.checkBalance(m, curTs); err != nil { + if err := mp.checkBalance(ctx, m, curTs); err != nil { return err } - return mp.addLocked(m, false, false) + return mp.addLocked(ctx, m, false, false) } -func (mp *MessagePool) addSkipChecks(m *types.SignedMessage) error { +func (mp *MessagePool) addSkipChecks(ctx context.Context, m *types.SignedMessage) error { mp.lk.Lock() defer mp.lk.Unlock() - return mp.addLocked(m, false, false) + return mp.addLocked(ctx, m, false, false) } -func (mp *MessagePool) addLocked(m *types.SignedMessage, strict, untrusted bool) error { +func (mp *MessagePool) addLocked(ctx context.Context, m *types.SignedMessage, strict, untrusted bool) error { log.Debugf("mpooladd: %s %d", m.Message.From, m.Message.Nonce) if m.Signature.Type == crypto.SigTypeBLS { mp.blsSigCache.Add(m.Cid(), m.Signature) @@ -768,8 +766,7 @@ func (mp *MessagePool) addLocked(m *types.SignedMessage, strict, untrusted bool) return err } - // TODO: Is context.TODO() safe here? Idk how Go works. - sk, err := mp.api.StateAccountKey(context.TODO(), m.Message.From, mp.curTs) + sk, err := mp.api.StateAccountKey(ctx, m.Message.From, mp.curTs) if err != nil { log.Debugf("mpooladd failed to resolve sender: %s", err) return err @@ -818,24 +815,23 @@ func (mp *MessagePool) addLocked(m *types.SignedMessage, strict, untrusted bool) return nil } -func (mp *MessagePool) GetNonce(addr address.Address) (uint64, error) { +func (mp *MessagePool) GetNonce(ctx context.Context, addr address.Address) (uint64, error) { mp.curTsLk.Lock() defer mp.curTsLk.Unlock() mp.lk.Lock() defer mp.lk.Unlock() - return mp.getNonceLocked(addr, mp.curTs) + return mp.getNonceLocked(ctx, addr, mp.curTs) } -func (mp *MessagePool) getNonceLocked(addr address.Address, curTs *types.TipSet) (uint64, error) { +func (mp *MessagePool) getNonceLocked(ctx context.Context, addr address.Address, curTs *types.TipSet) (uint64, error) { stateNonce, err := mp.getStateNonce(addr, curTs) // sanity check if err != nil { return 0, err } - // TODO: Is context.TODO() safe here? Idk how Go works. - sk, err := mp.api.StateAccountKey(context.TODO(), addr, mp.curTs) + sk, err := mp.api.StateAccountKey(ctx, addr, mp.curTs) if err != nil { log.Debugf("mpoolgetnonce failed to resolve sender: %s", err) return 0, err @@ -878,7 +874,7 @@ func (mp *MessagePool) getStateBalance(addr address.Address, ts *types.TipSet) ( // - strict checks are enabled // - extra strict add checks are used when adding the messages to the msgSet // that means: no nonce gaps, at most 10 pending messages for the actor -func (mp *MessagePool) PushUntrusted(m *types.SignedMessage) (cid.Cid, error) { +func (mp *MessagePool) PushUntrusted(ctx context.Context, m *types.SignedMessage) (cid.Cid, error) { err := mp.checkMessage(m) if err != nil { return cid.Undef, err @@ -891,7 +887,7 @@ func (mp *MessagePool) PushUntrusted(m *types.SignedMessage) (cid.Cid, error) { }() mp.curTsLk.Lock() - publish, err := mp.addTs(m, mp.curTs, true, true) + publish, err := mp.addTs(ctx, m, mp.curTs, true, true) if err != nil { mp.curTsLk.Unlock() return cid.Undef, err @@ -913,16 +909,15 @@ func (mp *MessagePool) PushUntrusted(m *types.SignedMessage) (cid.Cid, error) { return m.Cid(), nil } -func (mp *MessagePool) Remove(from address.Address, nonce uint64, applied bool) { +func (mp *MessagePool) Remove(ctx context.Context, from address.Address, nonce uint64, applied bool) { mp.lk.Lock() defer mp.lk.Unlock() - mp.remove(from, nonce, applied) + mp.remove(ctx, from, nonce, applied) } -func (mp *MessagePool) remove(from address.Address, nonce uint64, applied bool) { - // TODO: Is context.TODO() safe here? Idk how Go works. - sk, err := mp.api.StateAccountKey(context.TODO(), from, mp.curTs) +func (mp *MessagePool) remove(ctx context.Context, from address.Address, nonce uint64, applied bool) { + sk, err := mp.api.StateAccountKey(ctx, from, mp.curTs) if err != nil { log.Debugf("mpoolremove failed to resolve sender: %s", err) return @@ -957,37 +952,36 @@ func (mp *MessagePool) remove(from address.Address, nonce uint64, applied bool) } } -func (mp *MessagePool) Pending() ([]*types.SignedMessage, *types.TipSet) { +func (mp *MessagePool) Pending(ctx context.Context) ([]*types.SignedMessage, *types.TipSet) { mp.curTsLk.Lock() defer mp.curTsLk.Unlock() mp.lk.Lock() defer mp.lk.Unlock() - return mp.allPending() + return mp.allPending(ctx) } -func (mp *MessagePool) allPending() ([]*types.SignedMessage, *types.TipSet) { +func (mp *MessagePool) allPending(ctx context.Context) ([]*types.SignedMessage, *types.TipSet) { out := make([]*types.SignedMessage, 0) for a := range mp.pending { - out = append(out, mp.pendingFor(a)...) + out = append(out, mp.pendingFor(ctx, a)...) } return out, mp.curTs } -func (mp *MessagePool) PendingFor(a address.Address) ([]*types.SignedMessage, *types.TipSet) { +func (mp *MessagePool) PendingFor(ctx context.Context, a address.Address) ([]*types.SignedMessage, *types.TipSet) { mp.curTsLk.Lock() defer mp.curTsLk.Unlock() mp.lk.Lock() defer mp.lk.Unlock() - return mp.pendingFor(a), mp.curTs + return mp.pendingFor(ctx, a), mp.curTs } -func (mp *MessagePool) pendingFor(a address.Address) []*types.SignedMessage { - // TODO: Is context.TODO() safe here? Idk how Go works. - sk, err := mp.api.StateAccountKey(context.TODO(), a, mp.curTs) +func (mp *MessagePool) pendingFor(ctx context.Context, a address.Address) []*types.SignedMessage { + sk, err := mp.api.StateAccountKey(ctx, a, mp.curTs) if err != nil { log.Debugf("mpoolpendingfor failed to resolve sender: %s", err) return nil @@ -1011,7 +1005,7 @@ func (mp *MessagePool) pendingFor(a address.Address) []*types.SignedMessage { return set } -func (mp *MessagePool) HeadChange(revert []*types.TipSet, apply []*types.TipSet) error { +func (mp *MessagePool) HeadChange(ctx context.Context, revert []*types.TipSet, apply []*types.TipSet) error { mp.curTsLk.Lock() defer mp.curTsLk.Unlock() @@ -1028,7 +1022,7 @@ func (mp *MessagePool) HeadChange(revert []*types.TipSet, apply []*types.TipSet) rm := func(from address.Address, nonce uint64) { s, ok := rmsgs[from] if !ok { - mp.Remove(from, nonce, true) + mp.Remove(ctx, from, nonce, true) return } @@ -1037,7 +1031,7 @@ func (mp *MessagePool) HeadChange(revert []*types.TipSet, apply []*types.TipSet) return } - mp.Remove(from, nonce, true) + mp.Remove(ctx, from, nonce, true) } maybeRepub := func(cid cid.Cid) { @@ -1108,7 +1102,7 @@ func (mp *MessagePool) HeadChange(revert []*types.TipSet, apply []*types.TipSet) for _, s := range rmsgs { for _, msg := range s { - if err := mp.addSkipChecks(msg); err != nil { + if err := mp.addSkipChecks(ctx, msg); err != nil { log.Errorf("Failed to readd message from reorg to mpool: %s", err) } } @@ -1116,7 +1110,7 @@ func (mp *MessagePool) HeadChange(revert []*types.TipSet, apply []*types.TipSet) if len(revert) > 0 && futureDebug { mp.lk.Lock() - msgs, ts := mp.allPending() + msgs, ts := mp.allPending(ctx) mp.lk.Unlock() buckets := map[address.Address]*statBucket{} @@ -1323,7 +1317,7 @@ func (mp *MessagePool) Updates(ctx context.Context) (<-chan api.MpoolUpdate, err return out, nil } -func (mp *MessagePool) loadLocal() error { +func (mp *MessagePool) loadLocal(ctx context.Context) error { res, err := mp.localMsgs.Query(query.Query{}) if err != nil { return xerrors.Errorf("query local messages: %w", err) @@ -1339,7 +1333,7 @@ func (mp *MessagePool) loadLocal() error { return xerrors.Errorf("unmarshaling local message: %w", err) } - if err := mp.addLoaded(&sm); err != nil { + if err := mp.addLoaded(ctx, &sm); err != nil { if xerrors.Is(err, ErrNonceTooLow) { continue // todo: drop the message from local cache (if above certain confidence threshold) } @@ -1347,8 +1341,7 @@ func (mp *MessagePool) loadLocal() error { log.Errorf("adding local message: %+v", err) } - // TODO: Is context.TODO() safe here? Idk how Go works. - sk, err := mp.api.StateAccountKey(context.TODO(), sm.Message.From, mp.curTs) + sk, err := mp.api.StateAccountKey(ctx, sm.Message.From, mp.curTs) if err != nil { log.Debugf("mpoolloadLocal failed to resolve sender: %s", err) return err diff --git a/chain/messagepool/messagepool_test.go b/chain/messagepool/messagepool_test.go index e31df936c..3e5bad81f 100644 --- a/chain/messagepool/messagepool_test.go +++ b/chain/messagepool/messagepool_test.go @@ -199,7 +199,7 @@ func (tma *testMpoolAPI) ChainComputeBaseFee(ctx context.Context, ts *types.TipS func assertNonce(t *testing.T, mp *MessagePool, addr address.Address, val uint64) { t.Helper() - n, err := mp.GetNonce(addr) + n, err := mp.GetNonce(context.TODO(), addr) if err != nil { t.Fatal(err) } @@ -211,7 +211,7 @@ func assertNonce(t *testing.T, mp *MessagePool, addr address.Address, val uint64 func mustAdd(t *testing.T, mp *MessagePool, msg *types.SignedMessage) { t.Helper() - if err := mp.Add(msg); err != nil { + if err := mp.Add(context.TODO(), msg); err != nil { t.Fatal(err) } } @@ -226,7 +226,7 @@ func TestMessagePool(t *testing.T) { ds := datastore.NewMapDatastore() - mp, err := New(tma, ds, "mptest", nil) + mp, err := New(context.TODO(), tma, ds, "mptest", nil) if err != nil { t.Fatal(err) } @@ -267,7 +267,7 @@ func TestMessagePoolMessagesInEachBlock(t *testing.T) { ds := datastore.NewMapDatastore() - mp, err := New(tma, ds, "mptest", nil) + mp, err := New(context.TODO(), tma, ds, "mptest", nil) if err != nil { t.Fatal(err) } @@ -293,7 +293,7 @@ func TestMessagePoolMessagesInEachBlock(t *testing.T) { tma.applyBlock(t, a) tsa := mock.TipSet(a) - _, _ = mp.Pending() + _, _ = mp.Pending(context.TODO()) selm, _ := mp.SelectMessages(tsa, 1) if len(selm) == 0 { @@ -316,7 +316,7 @@ func TestRevertMessages(t *testing.T) { ds := datastore.NewMapDatastore() - mp, err := New(tma, ds, "mptest", nil) + mp, err := New(context.TODO(), tma, ds, "mptest", nil) if err != nil { t.Fatal(err) } @@ -355,7 +355,7 @@ func TestRevertMessages(t *testing.T) { assertNonce(t, mp, sender, 4) - p, _ := mp.Pending() + p, _ := mp.Pending(context.TODO()) fmt.Printf("%+v\n", p) if len(p) != 3 { t.Fatal("expected three messages in mempool") @@ -379,7 +379,7 @@ func TestPruningSimple(t *testing.T) { ds := datastore.NewMapDatastore() - mp, err := New(tma, ds, "mptest", nil) + mp, err := New(context.TODO(), tma, ds, "mptest", nil) if err != nil { t.Fatal(err) } @@ -396,14 +396,14 @@ func TestPruningSimple(t *testing.T) { for i := 0; i < 5; i++ { smsg := mock.MkMessage(sender, target, uint64(i), w) - if err := mp.Add(smsg); err != nil { + if err := mp.Add(context.TODO(), smsg); err != nil { t.Fatal(err) } } for i := 10; i < 50; i++ { smsg := mock.MkMessage(sender, target, uint64(i), w) - if err := mp.Add(smsg); err != nil { + if err := mp.Add(context.TODO(), smsg); err != nil { t.Fatal(err) } } @@ -413,7 +413,7 @@ func TestPruningSimple(t *testing.T) { mp.Prune() - msgs, _ := mp.Pending() + msgs, _ := mp.Pending(context.TODO()) if len(msgs) != 5 { t.Fatal("expected only 5 messages in pool, got: ", len(msgs)) } @@ -423,7 +423,7 @@ func TestLoadLocal(t *testing.T) { tma := newTestMpoolAPI() ds := datastore.NewMapDatastore() - mp, err := New(tma, ds, "mptest", nil) + mp, err := New(context.TODO(), tma, ds, "mptest", nil) if err != nil { t.Fatal(err) } @@ -455,7 +455,7 @@ func TestLoadLocal(t *testing.T) { msgs := make(map[cid.Cid]struct{}) for i := 0; i < 10; i++ { m := makeTestMessage(w1, a1, a2, uint64(i), gasLimit, uint64(i+1)) - cid, err := mp.Push(m) + cid, err := mp.Push(context.TODO(), m) if err != nil { t.Fatal(err) } @@ -466,12 +466,12 @@ func TestLoadLocal(t *testing.T) { t.Fatal(err) } - mp, err = New(tma, ds, "mptest", nil) + mp, err = New(context.TODO(), tma, ds, "mptest", nil) if err != nil { t.Fatal(err) } - pmsgs, _ := mp.Pending() + pmsgs, _ := mp.Pending(context.TODO()) if len(msgs) != len(pmsgs) { t.Fatalf("expected %d messages, but got %d", len(msgs), len(pmsgs)) } @@ -495,7 +495,7 @@ func TestClearAll(t *testing.T) { tma := newTestMpoolAPI() ds := datastore.NewMapDatastore() - mp, err := New(tma, ds, "mptest", nil) + mp, err := New(context.TODO(), tma, ds, "mptest", nil) if err != nil { t.Fatal(err) } @@ -526,7 +526,7 @@ func TestClearAll(t *testing.T) { gasLimit := gasguess.Costs[gasguess.CostKey{Code: builtin2.StorageMarketActorCodeID, M: 2}] for i := 0; i < 10; i++ { m := makeTestMessage(w1, a1, a2, uint64(i), gasLimit, uint64(i+1)) - _, err := mp.Push(m) + _, err := mp.Push(context.TODO(), m) if err != nil { t.Fatal(err) } @@ -539,7 +539,7 @@ func TestClearAll(t *testing.T) { mp.Clear(true) - pending, _ := mp.Pending() + pending, _ := mp.Pending(context.TODO()) if len(pending) > 0 { t.Fatalf("cleared the mpool, but got %d pending messages", len(pending)) } @@ -549,7 +549,7 @@ func TestClearNonLocal(t *testing.T) { tma := newTestMpoolAPI() ds := datastore.NewMapDatastore() - mp, err := New(tma, ds, "mptest", nil) + mp, err := New(context.TODO(), tma, ds, "mptest", nil) if err != nil { t.Fatal(err) } @@ -581,7 +581,7 @@ func TestClearNonLocal(t *testing.T) { gasLimit := gasguess.Costs[gasguess.CostKey{Code: builtin2.StorageMarketActorCodeID, M: 2}] for i := 0; i < 10; i++ { m := makeTestMessage(w1, a1, a2, uint64(i), gasLimit, uint64(i+1)) - _, err := mp.Push(m) + _, err := mp.Push(context.TODO(), m) if err != nil { t.Fatal(err) } @@ -594,7 +594,7 @@ func TestClearNonLocal(t *testing.T) { mp.Clear(false) - pending, _ := mp.Pending() + pending, _ := mp.Pending(context.TODO()) if len(pending) != 10 { t.Fatalf("expected 10 pending messages, but got %d instead", len(pending)) } @@ -610,7 +610,7 @@ func TestUpdates(t *testing.T) { tma := newTestMpoolAPI() ds := datastore.NewMapDatastore() - mp, err := New(tma, ds, "mptest", nil) + mp, err := New(context.TODO(), tma, ds, "mptest", nil) if err != nil { t.Fatal(err) } @@ -651,7 +651,7 @@ func TestUpdates(t *testing.T) { for i := 0; i < 10; i++ { m := makeTestMessage(w1, a1, a2, uint64(i), gasLimit, uint64(i+1)) - _, err := mp.Push(m) + _, err := mp.Push(context.TODO(), m) if err != nil { t.Fatal(err) } diff --git a/chain/messagepool/pruning.go b/chain/messagepool/pruning.go index dc1c69417..ad8f38c50 100644 --- a/chain/messagepool/pruning.go +++ b/chain/messagepool/pruning.go @@ -108,7 +108,7 @@ keepLoop: // and remove all messages that are still in pruneMsgs after processing the chains log.Infof("Pruning %d messages", len(pruneMsgs)) for _, m := range pruneMsgs { - mp.remove(m.Message.From, m.Message.Nonce, false) + mp.remove(ctx, m.Message.From, m.Message.Nonce, false) } return nil diff --git a/chain/messagepool/repub_test.go b/chain/messagepool/repub_test.go index 8da64f974..70e457aaa 100644 --- a/chain/messagepool/repub_test.go +++ b/chain/messagepool/repub_test.go @@ -24,7 +24,7 @@ func TestRepubMessages(t *testing.T) { tma := newTestMpoolAPI() ds := datastore.NewMapDatastore() - mp, err := New(tma, ds, "mptest", nil) + mp, err := New(context.TODO(), tma, ds, "mptest", nil) if err != nil { t.Fatal(err) } @@ -56,7 +56,7 @@ func TestRepubMessages(t *testing.T) { for i := 0; i < 10; i++ { m := makeTestMessage(w1, a1, a2, uint64(i), gasLimit, uint64(i+1)) - _, err := mp.Push(m) + _, err := mp.Push(context.TODO(), m) if err != nil { t.Fatal(err) } diff --git a/chain/messagepool/selection_test.go b/chain/messagepool/selection_test.go index e32d897c4..f254c6706 100644 --- a/chain/messagepool/selection_test.go +++ b/chain/messagepool/selection_test.go @@ -60,7 +60,7 @@ func makeTestMessage(w *wallet.LocalWallet, from, to address.Address, nonce uint func makeTestMpool() (*MessagePool, *testMpoolAPI) { tma := newTestMpoolAPI() ds := datastore.NewMapDatastore() - mp, err := New(tma, ds, "test", nil) + mp, err := New(context.TODO(), tma, ds, "test", nil) if err != nil { panic(err) } @@ -464,7 +464,7 @@ func TestBasicMessageSelection(t *testing.T) { tma.applyBlock(t, block2) // we should have no pending messages in the mpool - pend, _ := mp.Pending() + pend, _ := mp.Pending(context.TODO()) if len(pend) != 0 { t.Fatalf("expected no pending messages, but got %d", len(pend)) } diff --git a/chain/messagesigner/messagesigner.go b/chain/messagesigner/messagesigner.go index ce9d01b3a..c64d00003 100644 --- a/chain/messagesigner/messagesigner.go +++ b/chain/messagesigner/messagesigner.go @@ -23,7 +23,7 @@ const dsKeyActorNonce = "ActorNextNonce" var log = logging.Logger("messagesigner") type MpoolNonceAPI interface { - GetNonce(address.Address) (uint64, error) + GetNonce(context.Context, address.Address) (uint64, error) } // MessageSigner keeps track of nonces per address, and increments the nonce @@ -51,7 +51,7 @@ func (ms *MessageSigner) SignMessage(ctx context.Context, msg *types.Message, cb defer ms.lk.Unlock() // Get the next message nonce - nonce, err := ms.nextNonce(msg.From) + nonce, err := ms.nextNonce(ctx, msg.From) if err != nil { return nil, xerrors.Errorf("failed to create nonce: %w", err) } @@ -92,12 +92,12 @@ func (ms *MessageSigner) SignMessage(ctx context.Context, msg *types.Message, cb // nextNonce gets the next nonce for the given address. // If there is no nonce in the datastore, gets the nonce from the message pool. -func (ms *MessageSigner) nextNonce(addr address.Address) (uint64, error) { +func (ms *MessageSigner) nextNonce(ctx context.Context, addr address.Address) (uint64, error) { // Nonces used to be created by the mempool and we need to support nodes // that have mempool nonces, so first check the mempool for a nonce for // this address. Note that the mempool returns the actor state's nonce // by default. - nonce, err := ms.mpool.GetNonce(addr) + nonce, err := ms.mpool.GetNonce(ctx, addr) if err != nil { return 0, xerrors.Errorf("failed to get nonce from mempool: %w", err) } diff --git a/chain/messagesigner/messagesigner_test.go b/chain/messagesigner/messagesigner_test.go index 5eebd36da..8206b11c0 100644 --- a/chain/messagesigner/messagesigner_test.go +++ b/chain/messagesigner/messagesigner_test.go @@ -24,6 +24,8 @@ type mockMpool struct { nonces map[address.Address]uint64 } +var _ MpoolNonceAPI = (*mockMpool)(nil) + func newMockMpool() *mockMpool { return &mockMpool{nonces: make(map[address.Address]uint64)} } @@ -35,7 +37,7 @@ func (mp *mockMpool) setNonce(addr address.Address, nonce uint64) { mp.nonces[addr] = nonce } -func (mp *mockMpool) GetNonce(addr address.Address) (uint64, error) { +func (mp *mockMpool) GetNonce(ctx context.Context, addr address.Address) (uint64, error) { mp.lk.RLock() defer mp.lk.RUnlock() diff --git a/chain/sub/incoming.go b/chain/sub/incoming.go index d1c6414a1..e262fe271 100644 --- a/chain/sub/incoming.go +++ b/chain/sub/incoming.go @@ -516,7 +516,7 @@ func (mv *MessageValidator) Validate(ctx context.Context, pid peer.ID, msg *pubs return pubsub.ValidationReject } - if err := mv.mpool.Add(m); err != nil { + if err := mv.mpool.Add(ctx, m); err != nil { log.Debugf("failed to add message from network to message pool (From: %s, To: %s, Nonce: %d, Value: %s): %s", m.Message.From, m.Message.To, m.Message.Nonce, types.FIL(m.Message.Value), err) ctx, _ = tag.New( ctx, diff --git a/node/impl/full/gas.go b/node/impl/full/gas.go index 3d9889c10..acd2eccfe 100644 --- a/node/impl/full/gas.go +++ b/node/impl/full/gas.go @@ -265,7 +265,7 @@ func gasEstimateGasLimit( return -1, xerrors.Errorf("getting key address: %w", err) } - pending, ts := mpool.PendingFor(fromA) + pending, ts := mpool.PendingFor(ctx, fromA) priorMsgs := make([]types.ChainMsg, 0, len(pending)) for _, m := range pending { if m.Message.Nonce == msg.Nonce { diff --git a/node/impl/full/mpool.go b/node/impl/full/mpool.go index b1e9f94f9..9aa5371e9 100644 --- a/node/impl/full/mpool.go +++ b/node/impl/full/mpool.go @@ -66,7 +66,7 @@ func (a *MpoolAPI) MpoolPending(ctx context.Context, tsk types.TipSetKey) ([]*ty if err != nil { return nil, xerrors.Errorf("loading tipset %s: %w", tsk, err) } - pending, mpts := a.Mpool.Pending() + pending, mpts := a.Mpool.Pending(ctx) haveCids := map[cid.Cid]struct{}{} for _, m := range pending { @@ -125,11 +125,11 @@ func (a *MpoolAPI) MpoolClear(ctx context.Context, local bool) error { } func (m *MpoolModule) MpoolPush(ctx context.Context, smsg *types.SignedMessage) (cid.Cid, error) { - return m.Mpool.Push(smsg) + return m.Mpool.Push(ctx, smsg) } func (a *MpoolAPI) MpoolPushUntrusted(ctx context.Context, smsg *types.SignedMessage) (cid.Cid, error) { - return a.Mpool.PushUntrusted(smsg) + return a.Mpool.PushUntrusted(ctx, smsg) } func (a *MpoolAPI) MpoolPushMessage(ctx context.Context, msg *types.Message, spec *api.MessageSendSpec) (*types.SignedMessage, error) { @@ -190,7 +190,7 @@ func (a *MpoolAPI) MpoolPushMessage(ctx context.Context, msg *types.Message, spe func (a *MpoolAPI) MpoolBatchPush(ctx context.Context, smsgs []*types.SignedMessage) ([]cid.Cid, error) { var messageCids []cid.Cid for _, smsg := range smsgs { - smsgCid, err := a.Mpool.Push(smsg) + smsgCid, err := a.Mpool.Push(ctx, smsg) if err != nil { return messageCids, err } @@ -202,7 +202,7 @@ func (a *MpoolAPI) MpoolBatchPush(ctx context.Context, smsgs []*types.SignedMess func (a *MpoolAPI) MpoolBatchPushUntrusted(ctx context.Context, smsgs []*types.SignedMessage) ([]cid.Cid, error) { var messageCids []cid.Cid for _, smsg := range smsgs { - smsgCid, err := a.Mpool.PushUntrusted(smsg) + smsgCid, err := a.Mpool.PushUntrusted(ctx, smsg) if err != nil { return messageCids, err } @@ -224,7 +224,7 @@ func (a *MpoolAPI) MpoolBatchPushMessage(ctx context.Context, msgs []*types.Mess } func (a *MpoolAPI) MpoolGetNonce(ctx context.Context, addr address.Address) (uint64, error) { - return a.Mpool.GetNonce(addr) + return a.Mpool.GetNonce(ctx, addr) } func (a *MpoolAPI) MpoolSub(ctx context.Context) (<-chan api.MpoolUpdate, error) { diff --git a/node/modules/chain.go b/node/modules/chain.go index ffdf3aa3a..b0f0543c6 100644 --- a/node/modules/chain.go +++ b/node/modules/chain.go @@ -61,7 +61,8 @@ func ChainBlockService(bs dtypes.ExposedBlockstore, rem dtypes.ChainBitswap) dty func MessagePool(lc fx.Lifecycle, sm *stmgr.StateManager, ps *pubsub.PubSub, ds dtypes.MetadataDS, nn dtypes.NetworkName, j journal.Journal) (*messagepool.MessagePool, error) { mpp := messagepool.NewProvider(sm, ps) - mp, err := messagepool.New(mpp, ds, nn, j) + // TODO: I still don't know how go works -- should this context be part of the builder? + mp, err := messagepool.New(context.TODO(), mpp, ds, nn, j) if err != nil { return nil, xerrors.Errorf("constructing mpool: %w", err) } diff --git a/node/modules/mpoolnonceapi.go b/node/modules/mpoolnonceapi.go index efcb14037..3d670611b 100644 --- a/node/modules/mpoolnonceapi.go +++ b/node/modules/mpoolnonceapi.go @@ -23,7 +23,7 @@ type MpoolNonceAPI struct { } // GetNonce gets the nonce from current chain head. -func (a *MpoolNonceAPI) GetNonce(addr address.Address) (uint64, error) { +func (a *MpoolNonceAPI) GetNonce(ctx context.Context, addr address.Address) (uint64, error) { ts := a.StateAPI.Chain.GetHeaviestTipSet() // make sure we have a key address so we can compare with messages From ed93d0725ffc17ab06cf690d32683b8f8753df7a Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Fri, 28 May 2021 20:35:50 -0400 Subject: [PATCH 057/160] Protect mp.localAddrs and mp.pending behind helper functions --- chain/messagepool/messagepool.go | 240 +++++++++++++++++++------- chain/messagepool/messagepool_test.go | 4 +- chain/messagepool/pruning.go | 4 +- chain/messagepool/repub.go | 18 +- chain/messagepool/selection.go | 5 +- node/impl/full/mpool.go | 2 +- 6 files changed, 192 insertions(+), 81 deletions(-) diff --git a/chain/messagepool/messagepool.go b/chain/messagepool/messagepool.go index 0c8569a1f..12e8f24c2 100644 --- a/chain/messagepool/messagepool.go +++ b/chain/messagepool/messagepool.go @@ -126,12 +126,14 @@ type MessagePool struct { republished map[cid.Cid]struct{} - // only pubkey addresses + // do NOT access this map directly, use isLocal, setLocal, and forEachLocal respectively localAddrs map[address.Address]struct{} - // only pubkey addresses + // do NOT access this map directly, use getPendingMset, setPendingMset, deletePendingMset, forEachPending, and clearPending respectively pending map[address.Address]*msgSet + keyCache map[address.Address]address.Address + curTsLk sync.Mutex // DO NOT LOCK INSIDE lk curTs *types.TipSet @@ -331,6 +333,20 @@ func (ms *msgSet) getRequiredFunds(nonce uint64) types.BigInt { return types.BigInt{Int: requiredFunds} } +func (ms *msgSet) toSlice() []*types.SignedMessage { + set := make([]*types.SignedMessage, 0, len(ms.msgs)) + + for _, m := range ms.msgs { + set = append(set, m) + } + + sort.Slice(set, func(i, j int) bool { + return set[i].Message.Nonce < set[j].Message.Nonce + }) + + return set +} + func New(ctx context.Context, api Provider, ds dtypes.MetadataDS, netName dtypes.NetworkName, j journal.Journal) (*MessagePool, error) { cache, _ := lru.New2Q(build.BlsSignatureCacheSize) verifcache, _ := lru.New2Q(build.VerifSigCacheSize) @@ -352,6 +368,7 @@ func New(ctx context.Context, api Provider, ds dtypes.MetadataDS, netName dtypes repubTrigger: make(chan struct{}, 1), localAddrs: make(map[address.Address]struct{}), pending: make(map[address.Address]*msgSet), + keyCache: make(map[address.Address]address.Address), minGasPrice: types.NewInt(0), pruneTrigger: make(chan struct{}, 1), pruneCooldown: make(chan struct{}, 1), @@ -397,12 +414,106 @@ func New(ctx context.Context, api Provider, ds dtypes.MetadataDS, netName dtypes log.Info("mpool ready") - mp.runLoop() + mp.runLoop(context.Background()) }() return mp, nil } +func (mp *MessagePool) resolveToKey(ctx context.Context, addr address.Address) (address.Address, error) { + // check the cache + a, f := mp.keyCache[addr] + if f { + return a, nil + } + + // resolve the address + ka, err := mp.api.StateAccountKey(ctx, addr, mp.curTs) + if err != nil { + return address.Undef, err + } + + // place both entries in the cache (may both be key addresses, which is fine) + mp.keyCache[addr] = ka + mp.keyCache[ka] = ka + + return ka, nil +} + +func (mp *MessagePool) getPendingMset(ctx context.Context, addr address.Address) (*msgSet, bool, error) { + ra, err := mp.resolveToKey(ctx, addr) + if err != nil { + return nil, false, err + } + + ms, f := mp.pending[ra] + + return ms, f, nil +} + +func (mp *MessagePool) setPendingMset(ctx context.Context, addr address.Address, ms *msgSet) error { + ra, err := mp.resolveToKey(ctx, addr) + if err != nil { + return err + } + + mp.pending[ra] = ms + + return nil +} + +// This method isn't strictly necessary, since it doesn't resolve any addresses, but it's safer to have +func (mp *MessagePool) forEachPending(f func(address.Address, *msgSet)) { + for la, ms := range mp.pending { + f(la, ms) + } +} + +func (mp *MessagePool) deletePendingMset(ctx context.Context, addr address.Address) error { + ra, err := mp.resolveToKey(ctx, addr) + if err != nil { + return err + } + + delete(mp.pending, ra) + + return nil +} + +// This method isn't strictly necessary, since it doesn't resolve any addresses, but it's safer to have +func (mp *MessagePool) clearPending() { + mp.pending = make(map[address.Address]*msgSet) +} + +func (mp *MessagePool) isLocal(ctx context.Context, addr address.Address) (bool, error) { + ra, err := mp.resolveToKey(ctx, addr) + if err != nil { + return false, err + } + + _, f := mp.localAddrs[ra] + + return f, nil +} + +func (mp *MessagePool) setLocal(ctx context.Context, addr address.Address) error { + ra, err := mp.resolveToKey(ctx, addr) + if err != nil { + return err + } + + mp.localAddrs[ra] = struct{}{} + + return nil +} + +// This method isn't strictly necessary, since it doesn't resolve any addresses, but it's safer to have +func (mp *MessagePool) forEachLocal(ctx context.Context, f func(context.Context, address.Address)) { + for la := range mp.localAddrs { + f(ctx, la) + } +} + func (mp *MessagePool) Close() error { close(mp.closer) return nil @@ -420,15 +531,15 @@ func (mp *MessagePool) Prune() { mp.pruneTrigger <- struct{}{} } -func (mp *MessagePool) runLoop() { +func (mp *MessagePool) runLoop(ctx context.Context) { for { select { case <-mp.repubTk.C: - if err := mp.republishPendingMessages(); err != nil { + if err := mp.republishPendingMessages(ctx); err != nil { log.Errorf("error while republishing messages: %s", err) } case <-mp.repubTrigger: - if err := mp.republishPendingMessages(); err != nil { + if err := mp.republishPendingMessages(ctx); err != nil { log.Errorf("error while republishing messages: %s", err) } @@ -445,14 +556,10 @@ func (mp *MessagePool) runLoop() { } func (mp *MessagePool) addLocal(ctx context.Context, m *types.SignedMessage) error { - sk, err := mp.api.StateAccountKey(ctx, m.Message.From, mp.curTs) - if err != nil { - log.Debugf("mpooladdlocal failed to resolve sender: %s", err) + if err := mp.setLocal(ctx, m.Message.From); err != nil { return err } - mp.localAddrs[sk] = struct{}{} - msgb, err := m.Serialize() if err != nil { return xerrors.Errorf("error serializing message: %w", err) @@ -653,13 +760,12 @@ func (mp *MessagePool) checkBalance(ctx context.Context, m *types.SignedMessage, // add Value for soft failure check //requiredFunds = types.BigAdd(requiredFunds, m.Message.Value) - sk, err := mp.api.StateAccountKey(ctx, m.Message.From, mp.curTs) + mset, ok, err := mp.getPendingMset(ctx, m.Message.From) if err != nil { - log.Debugf("mpoolcheckbalance failed to resolve sender: %s", err) + log.Debugf("mpoolcheckbalance failed to get pending mset: %s", err) return err } - mset, ok := mp.pending[sk] if ok { requiredFunds = types.BigAdd(requiredFunds, mset.getRequiredFunds(m.Message.Nonce)) } @@ -766,21 +872,22 @@ func (mp *MessagePool) addLocked(ctx context.Context, m *types.SignedMessage, st return err } - sk, err := mp.api.StateAccountKey(ctx, m.Message.From, mp.curTs) + mset, ok, err := mp.getPendingMset(ctx, m.Message.From) if err != nil { - log.Debugf("mpooladd failed to resolve sender: %s", err) + log.Debug(err) return err } - mset, ok := mp.pending[sk] if !ok { - nonce, err := mp.getStateNonce(sk, mp.curTs) + nonce, err := mp.getStateNonce(m.Message.From, mp.curTs) if err != nil { return xerrors.Errorf("failed to get initial actor nonce: %w", err) } mset = newMsgSet(nonce) - mp.pending[sk] = mset + if err = mp.setPendingMset(ctx, m.Message.From, mset); err != nil { + return xerrors.Errorf("failed to set pending mset: %w", err) + } } incr, err := mset.add(m, mp, strict, untrusted) @@ -831,13 +938,12 @@ func (mp *MessagePool) getNonceLocked(ctx context.Context, addr address.Address, return 0, err } - sk, err := mp.api.StateAccountKey(ctx, addr, mp.curTs) + mset, ok, err := mp.getPendingMset(ctx, addr) if err != nil { - log.Debugf("mpoolgetnonce failed to resolve sender: %s", err) + log.Debugf("mpoolgetnonce failed to get mset: %s", err) return 0, err } - mset, ok := mp.pending[sk] if ok { if stateNonce > mset.nextNonce { log.Errorf("state nonce was larger than mset.nextNonce (%d > %d)", stateNonce, mset.nextNonce) @@ -917,13 +1023,12 @@ func (mp *MessagePool) Remove(ctx context.Context, from address.Address, nonce u } func (mp *MessagePool) remove(ctx context.Context, from address.Address, nonce uint64, applied bool) { - sk, err := mp.api.StateAccountKey(ctx, from, mp.curTs) + mset, ok, err := mp.getPendingMset(ctx, from) if err != nil { - log.Debugf("mpoolremove failed to resolve sender: %s", err) + log.Debugf("mpoolremove failed to get mset: %s", err) return } - mset, ok := mp.pending[sk] if !ok { return } @@ -948,7 +1053,10 @@ func (mp *MessagePool) remove(ctx context.Context, from address.Address, nonce u mset.rm(nonce, applied) if len(mset.msgs) == 0 { - delete(mp.pending, from) + if err = mp.deletePendingMset(ctx, from); err != nil { + log.Debugf("mpoolremove failed to delete mset: %s", err) + return + } } } @@ -964,9 +1072,10 @@ func (mp *MessagePool) Pending(ctx context.Context) ([]*types.SignedMessage, *ty func (mp *MessagePool) allPending(ctx context.Context) ([]*types.SignedMessage, *types.TipSet) { out := make([]*types.SignedMessage, 0) - for a := range mp.pending { - out = append(out, mp.pendingFor(ctx, a)...) - } + + mp.forEachPending(func(a address.Address, mset *msgSet) { + out = append(out, mset.toSlice()...) + }) return out, mp.curTs } @@ -981,28 +1090,17 @@ func (mp *MessagePool) PendingFor(ctx context.Context, a address.Address) ([]*ty } func (mp *MessagePool) pendingFor(ctx context.Context, a address.Address) []*types.SignedMessage { - sk, err := mp.api.StateAccountKey(ctx, a, mp.curTs) + mset, ok, err := mp.getPendingMset(ctx, a) if err != nil { - log.Debugf("mpoolpendingfor failed to resolve sender: %s", err) + log.Debugf("mpoolpendingfor failed to get mset: %s", err) return nil } - mset := mp.pending[sk] - if mset == nil || len(mset.msgs) == 0 { + if mset == nil || !ok || len(mset.msgs) == 0 { return nil } - set := make([]*types.SignedMessage, 0, len(mset.msgs)) - - for _, m := range mset.msgs { - set = append(set, m) - } - - sort.Slice(set, func(i, j int) bool { - return set[i].Message.Nonce < set[j].Message.Nonce - }) - - return set + return mset.toSlice() } func (mp *MessagePool) HeadChange(ctx context.Context, revert []*types.TipSet, apply []*types.TipSet) error { @@ -1341,53 +1439,61 @@ func (mp *MessagePool) loadLocal(ctx context.Context) error { log.Errorf("adding local message: %+v", err) } - sk, err := mp.api.StateAccountKey(ctx, sm.Message.From, mp.curTs) - if err != nil { - log.Debugf("mpoolloadLocal failed to resolve sender: %s", err) + if err = mp.setLocal(ctx, sm.Message.From); err != nil { + log.Debugf("mpoolloadLocal errored: %s", err) return err } - - mp.localAddrs[sk] = struct{}{} } return nil } -func (mp *MessagePool) Clear(local bool) { +func (mp *MessagePool) Clear(ctx context.Context, local bool) { mp.lk.Lock() defer mp.lk.Unlock() // remove everything if local is true, including removing local messages from // the datastore if local { - for a := range mp.localAddrs { - mset, ok := mp.pending[a] - if !ok { - continue + mp.forEachLocal(ctx, func(ctx context.Context, la address.Address) { + mset, ok, err := mp.getPendingMset(ctx, la) + if err != nil { + log.Warnf("errored while getting pending mset: %w", err) + return } - for _, m := range mset.msgs { - err := mp.localMsgs.Delete(datastore.NewKey(string(m.Cid().Bytes()))) - if err != nil { - log.Warnf("error deleting local message: %s", err) + if ok { + for _, m := range mset.msgs { + err := mp.localMsgs.Delete(datastore.NewKey(string(m.Cid().Bytes()))) + if err != nil { + log.Warnf("error deleting local message: %s", err) + } } } - } + }) - mp.pending = make(map[address.Address]*msgSet) + mp.clearPending() mp.republished = nil return } - // remove everything except the local messages - for a := range mp.pending { - _, isLocal := mp.localAddrs[a] - if isLocal { - continue + mp.forEachPending(func(a address.Address, ms *msgSet) { + isLocal, err := mp.isLocal(ctx, a) + if err != nil { + log.Warnf("errored while determining isLocal: %w", err) + return } - delete(mp.pending, a) - } + + if isLocal { + return + } + + if err = mp.deletePendingMset(ctx, a); err != nil { + log.Warnf("errored while deleting mset: %w", err) + return + } + }) } func getBaseFeeLowerBound(baseFee, factor types.BigInt) types.BigInt { diff --git a/chain/messagepool/messagepool_test.go b/chain/messagepool/messagepool_test.go index 3e5bad81f..aa3331c11 100644 --- a/chain/messagepool/messagepool_test.go +++ b/chain/messagepool/messagepool_test.go @@ -537,7 +537,7 @@ func TestClearAll(t *testing.T) { mustAdd(t, mp, m) } - mp.Clear(true) + mp.Clear(context.Background(), true) pending, _ := mp.Pending(context.TODO()) if len(pending) > 0 { @@ -592,7 +592,7 @@ func TestClearNonLocal(t *testing.T) { mustAdd(t, mp, m) } - mp.Clear(false) + mp.Clear(context.Background(), false) pending, _ := mp.Pending(context.TODO()) if len(pending) != 10 { diff --git a/chain/messagepool/pruning.go b/chain/messagepool/pruning.go index ad8f38c50..6802e23f3 100644 --- a/chain/messagepool/pruning.go +++ b/chain/messagepool/pruning.go @@ -61,9 +61,9 @@ func (mp *MessagePool) pruneMessages(ctx context.Context, ts *types.TipSet) erro } // we also never prune locally published messages - for actor := range mp.localAddrs { + mp.forEachLocal(ctx, func(ctx context.Context, actor address.Address) { protected[actor] = struct{}{} - } + }) // Collect all messages to track which ones to remove and create chains for block inclusion pruneMsgs := make(map[cid.Cid]*types.SignedMessage, mp.currentSize) diff --git a/chain/messagepool/repub.go b/chain/messagepool/repub.go index 5fa68aa53..4323bdee1 100644 --- a/chain/messagepool/repub.go +++ b/chain/messagepool/repub.go @@ -18,7 +18,7 @@ const repubMsgLimit = 30 var RepublishBatchDelay = 100 * time.Millisecond -func (mp *MessagePool) republishPendingMessages() error { +func (mp *MessagePool) republishPendingMessages(ctx context.Context) error { mp.curTsLk.Lock() ts := mp.curTs @@ -32,13 +32,18 @@ func (mp *MessagePool) republishPendingMessages() error { pending := make(map[address.Address]map[uint64]*types.SignedMessage) mp.lk.Lock() mp.republished = nil // clear this to avoid races triggering an early republish - for actor := range mp.localAddrs { - mset, ok := mp.pending[actor] + mp.forEachLocal(ctx, func(ctx context.Context, actor address.Address) { + mset, ok, err := mp.getPendingMset(ctx, actor) + if err != nil { + log.Debugf("failed to get mset: %w", err) + return + } + if !ok { - continue + return } if len(mset.msgs) == 0 { - continue + return } // we need to copy this while holding the lock to avoid races with concurrent modification pend := make(map[uint64]*types.SignedMessage, len(mset.msgs)) @@ -46,7 +51,8 @@ func (mp *MessagePool) republishPendingMessages() error { pend[nonce] = m } pending[actor] = pend - } + }) + mp.lk.Unlock() mp.curTsLk.Unlock() diff --git a/chain/messagepool/selection.go b/chain/messagepool/selection.go index af450645f..dfed2b6b5 100644 --- a/chain/messagepool/selection.go +++ b/chain/messagepool/selection.go @@ -654,8 +654,7 @@ func (mp *MessagePool) getPendingMessages(curTs, ts *types.TipSet) (map[address. inSync = true } - // first add our current pending messages - for a, mset := range mp.pending { + mp.forEachPending(func(a address.Address, mset *msgSet) { if inSync { // no need to copy the map result[a] = mset.msgs @@ -668,7 +667,7 @@ func (mp *MessagePool) getPendingMessages(curTs, ts *types.TipSet) (map[address. result[a] = msetCopy } - } + }) // we are in sync, that's the happy path if inSync { diff --git a/node/impl/full/mpool.go b/node/impl/full/mpool.go index 9aa5371e9..e91fc8b9e 100644 --- a/node/impl/full/mpool.go +++ b/node/impl/full/mpool.go @@ -120,7 +120,7 @@ func (a *MpoolAPI) MpoolPending(ctx context.Context, tsk types.TipSetKey) ([]*ty } func (a *MpoolAPI) MpoolClear(ctx context.Context, local bool) error { - a.Mpool.Clear(local) + a.Mpool.Clear(ctx, local) return nil } From 183c12db2549af28ba83e4c89209d70a533dbe74 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Sun, 30 May 2021 14:54:09 -0400 Subject: [PATCH 058/160] Make mempool reject ID addresses that are not reorg-stable --- chain/messagepool/provider.go | 2 +- chain/stmgr/stmgr.go | 40 +++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/chain/messagepool/provider.go b/chain/messagepool/provider.go index 75a2efe91..b90a8af7d 100644 --- a/chain/messagepool/provider.go +++ b/chain/messagepool/provider.go @@ -75,7 +75,7 @@ func (mpp *mpoolProvider) GetActorAfter(addr address.Address, ts *types.TipSet) } func (mpp *mpoolProvider) StateAccountKey(ctx context.Context, addr address.Address, ts *types.TipSet) (address.Address, error) { - return mpp.sm.ResolveToKeyAddress(ctx, addr, ts) + return mpp.sm.ResolveToKeyAddressReorgProof(ctx, addr, ts) } func (mpp *mpoolProvider) MessagesForBlock(h *types.BlockHeader) ([]*types.Message, []*types.SignedMessage, error) { diff --git a/chain/stmgr/stmgr.go b/chain/stmgr/stmgr.go index 93832f185..fb1fb80f0 100644 --- a/chain/stmgr/stmgr.go +++ b/chain/stmgr/stmgr.go @@ -6,6 +6,8 @@ import ( "fmt" "sync" + "github.com/filecoin-project/lotus/chain/actors/policy" + "github.com/ipfs/go-cid" cbor "github.com/ipfs/go-ipld-cbor" logging "github.com/ipfs/go-log/v2" @@ -542,6 +544,44 @@ func (sm *StateManager) ResolveToKeyAddress(ctx context.Context, addr address.Ad return vm.ResolveToKeyAddr(tree, cst, addr) } +// ResolveToKeyAddressReorgProof is similar to stmgr.ResolveToKeyAddress but fails if the ID address being resolved isn't reorg-stable yet. +// It should not be used for consensus-critical subsystems. +func (sm *StateManager) ResolveToKeyAddressReorgProof(ctx context.Context, addr address.Address, ts *types.TipSet) (address.Address, error) { + switch addr.Protocol() { + case address.BLS, address.SECP256K1: + return addr, nil + case address.Actor: + return address.Undef, xerrors.New("cannot resolve actor address to key address") + default: + } + + if ts == nil { + ts = sm.cs.GetHeaviestTipSet() + } + + if ts.Height() > policy.ChainFinality { + var err error + ts, err = sm.ChainStore().GetTipsetByHeight(ctx, ts.Height()-policy.ChainFinality, ts, true) + if err != nil { + return address.Undef, xerrors.Errorf("failed to load lookback tipset: %w", err) + } + } + + cst := cbor.NewCborStore(sm.cs.StateBlockstore()) + + tree, err := state.LoadStateTree(cst, ts.ParentState()) + if err != nil { + return address.Undef, xerrors.Errorf("failed to load parent state tree: %w", err) + } + + resolved, err := vm.ResolveToKeyAddr(tree, cst, addr) + if err == nil { + return resolved, nil + } + + return address.Undef, xerrors.New("ID address not found in lookback state") +} + func (sm *StateManager) GetBlsPublicKey(ctx context.Context, addr address.Address, ts *types.TipSet) (pubk []byte, err error) { kaddr, err := sm.ResolveToKeyAddress(ctx, addr, ts) if err != nil { From 9ceee6028bb7f0958db5730d048f4710732be9e3 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Sun, 30 May 2021 15:20:47 -0400 Subject: [PATCH 059/160] More tweaking of context handling in the messagepool --- chain/messagepool/messagepool.go | 7 ++++-- chain/messagepool/messagepool_test.go | 20 +++++++-------- chain/messagepool/pruning.go | 7 +++++- chain/messagepool/repub_test.go | 2 +- chain/messagepool/selection.go | 18 +++++++------- chain/messagepool/selection_test.go | 36 +++++++++++++-------------- node/impl/full/mpool.go | 2 +- node/modules/chain.go | 3 +-- node/modules/mpoolnonceapi.go | 4 +-- 9 files changed, 53 insertions(+), 46 deletions(-) diff --git a/chain/messagepool/messagepool.go b/chain/messagepool/messagepool.go index 12e8f24c2..405e22320 100644 --- a/chain/messagepool/messagepool.go +++ b/chain/messagepool/messagepool.go @@ -347,7 +347,7 @@ func (ms *msgSet) toSlice() []*types.SignedMessage { return set } -func New(ctx context.Context, api Provider, ds dtypes.MetadataDS, netName dtypes.NetworkName, j journal.Journal) (*MessagePool, error) { +func New(api Provider, ds dtypes.MetadataDS, netName dtypes.NetworkName, j journal.Journal) (*MessagePool, error) { cache, _ := lru.New2Q(build.BlsSignatureCacheSize) verifcache, _ := lru.New2Q(build.VerifSigCacheSize) @@ -390,6 +390,8 @@ func New(ctx context.Context, api Provider, ds dtypes.MetadataDS, netName dtypes // enable initial prunes mp.pruneCooldown <- struct{}{} + ctx, cancel := context.WithCancel(context.Background()) + // load the current tipset and subscribe to head changes _before_ loading local messages mp.curTs = api.SubscribeHeadChanges(func(rev, app []*types.TipSet) error { err := mp.HeadChange(ctx, rev, app) @@ -403,6 +405,7 @@ func New(ctx context.Context, api Provider, ds dtypes.MetadataDS, netName dtypes mp.lk.Lock() go func() { + defer cancel() err := mp.loadLocal(ctx) mp.lk.Unlock() @@ -414,7 +417,7 @@ func New(ctx context.Context, api Provider, ds dtypes.MetadataDS, netName dtypes log.Info("mpool ready") - mp.runLoop(context.Background()) + mp.runLoop(ctx) }() return mp, nil diff --git a/chain/messagepool/messagepool_test.go b/chain/messagepool/messagepool_test.go index aa3331c11..307ec20a0 100644 --- a/chain/messagepool/messagepool_test.go +++ b/chain/messagepool/messagepool_test.go @@ -226,7 +226,7 @@ func TestMessagePool(t *testing.T) { ds := datastore.NewMapDatastore() - mp, err := New(context.TODO(), tma, ds, "mptest", nil) + mp, err := New(tma, ds, "mptest", nil) if err != nil { t.Fatal(err) } @@ -267,7 +267,7 @@ func TestMessagePoolMessagesInEachBlock(t *testing.T) { ds := datastore.NewMapDatastore() - mp, err := New(context.TODO(), tma, ds, "mptest", nil) + mp, err := New(tma, ds, "mptest", nil) if err != nil { t.Fatal(err) } @@ -295,7 +295,7 @@ func TestMessagePoolMessagesInEachBlock(t *testing.T) { _, _ = mp.Pending(context.TODO()) - selm, _ := mp.SelectMessages(tsa, 1) + selm, _ := mp.SelectMessages(context.Background(), tsa, 1) if len(selm) == 0 { t.Fatal("should have returned the rest of the messages") } @@ -316,7 +316,7 @@ func TestRevertMessages(t *testing.T) { ds := datastore.NewMapDatastore() - mp, err := New(context.TODO(), tma, ds, "mptest", nil) + mp, err := New(tma, ds, "mptest", nil) if err != nil { t.Fatal(err) } @@ -379,7 +379,7 @@ func TestPruningSimple(t *testing.T) { ds := datastore.NewMapDatastore() - mp, err := New(context.TODO(), tma, ds, "mptest", nil) + mp, err := New(tma, ds, "mptest", nil) if err != nil { t.Fatal(err) } @@ -423,7 +423,7 @@ func TestLoadLocal(t *testing.T) { tma := newTestMpoolAPI() ds := datastore.NewMapDatastore() - mp, err := New(context.TODO(), tma, ds, "mptest", nil) + mp, err := New(tma, ds, "mptest", nil) if err != nil { t.Fatal(err) } @@ -466,7 +466,7 @@ func TestLoadLocal(t *testing.T) { t.Fatal(err) } - mp, err = New(context.TODO(), tma, ds, "mptest", nil) + mp, err = New(tma, ds, "mptest", nil) if err != nil { t.Fatal(err) } @@ -495,7 +495,7 @@ func TestClearAll(t *testing.T) { tma := newTestMpoolAPI() ds := datastore.NewMapDatastore() - mp, err := New(context.TODO(), tma, ds, "mptest", nil) + mp, err := New(tma, ds, "mptest", nil) if err != nil { t.Fatal(err) } @@ -549,7 +549,7 @@ func TestClearNonLocal(t *testing.T) { tma := newTestMpoolAPI() ds := datastore.NewMapDatastore() - mp, err := New(context.TODO(), tma, ds, "mptest", nil) + mp, err := New(tma, ds, "mptest", nil) if err != nil { t.Fatal(err) } @@ -610,7 +610,7 @@ func TestUpdates(t *testing.T) { tma := newTestMpoolAPI() ds := datastore.NewMapDatastore() - mp, err := New(context.TODO(), tma, ds, "mptest", nil) + mp, err := New(tma, ds, "mptest", nil) if err != nil { t.Fatal(err) } diff --git a/chain/messagepool/pruning.go b/chain/messagepool/pruning.go index 6802e23f3..829258d25 100644 --- a/chain/messagepool/pruning.go +++ b/chain/messagepool/pruning.go @@ -57,7 +57,12 @@ func (mp *MessagePool) pruneMessages(ctx context.Context, ts *types.TipSet) erro mpCfg := mp.getConfig() // we never prune priority addresses for _, actor := range mpCfg.PriorityAddrs { - protected[actor] = struct{}{} + pk, err := mp.api.StateAccountKey(ctx, actor, mp.curTs) + if err != nil { + log.Debugf("pruneMessages failed to resolve priority address: %s", err) + } + + protected[pk] = struct{}{} } // we also never prune locally published messages diff --git a/chain/messagepool/repub_test.go b/chain/messagepool/repub_test.go index 70e457aaa..580231f7a 100644 --- a/chain/messagepool/repub_test.go +++ b/chain/messagepool/repub_test.go @@ -24,7 +24,7 @@ func TestRepubMessages(t *testing.T) { tma := newTestMpoolAPI() ds := datastore.NewMapDatastore() - mp, err := New(context.TODO(), tma, ds, "mptest", nil) + mp, err := New(tma, ds, "mptest", nil) if err != nil { t.Fatal(err) } diff --git a/chain/messagepool/selection.go b/chain/messagepool/selection.go index dfed2b6b5..9379cb892 100644 --- a/chain/messagepool/selection.go +++ b/chain/messagepool/selection.go @@ -40,7 +40,7 @@ type msgChain struct { prev *msgChain } -func (mp *MessagePool) SelectMessages(ts *types.TipSet, tq float64) (msgs []*types.SignedMessage, err error) { +func (mp *MessagePool) SelectMessages(ctx context.Context, ts *types.TipSet, tq float64) (msgs []*types.SignedMessage, err error) { mp.curTsLk.Lock() defer mp.curTsLk.Unlock() @@ -51,9 +51,9 @@ func (mp *MessagePool) SelectMessages(ts *types.TipSet, tq float64) (msgs []*typ // than any other block, then we don't bother with optimal selection because the // first block will always have higher effective performance if tq > 0.84 { - msgs, err = mp.selectMessagesGreedy(mp.curTs, ts) + msgs, err = mp.selectMessagesGreedy(ctx, mp.curTs, ts) } else { - msgs, err = mp.selectMessagesOptimal(mp.curTs, ts, tq) + msgs, err = mp.selectMessagesOptimal(ctx, mp.curTs, ts, tq) } if err != nil { @@ -67,7 +67,7 @@ func (mp *MessagePool) SelectMessages(ts *types.TipSet, tq float64) (msgs []*typ return msgs, nil } -func (mp *MessagePool) selectMessagesOptimal(curTs, ts *types.TipSet, tq float64) ([]*types.SignedMessage, error) { +func (mp *MessagePool) selectMessagesOptimal(ctx context.Context, curTs, ts *types.TipSet, tq float64) ([]*types.SignedMessage, error) { start := time.Now() baseFee, err := mp.api.ChainComputeBaseFee(context.TODO(), ts) @@ -93,7 +93,7 @@ func (mp *MessagePool) selectMessagesOptimal(curTs, ts *types.TipSet, tq float64 // 0b. Select all priority messages that fit in the block minGas := int64(gasguess.MinGas) - result, gasLimit := mp.selectPriorityMessages(pending, baseFee, ts) + result, gasLimit := mp.selectPriorityMessages(ctx, pending, baseFee, ts) // have we filled the block? if gasLimit < minGas { @@ -391,7 +391,7 @@ tailLoop: return result, nil } -func (mp *MessagePool) selectMessagesGreedy(curTs, ts *types.TipSet) ([]*types.SignedMessage, error) { +func (mp *MessagePool) selectMessagesGreedy(ctx context.Context, curTs, ts *types.TipSet) ([]*types.SignedMessage, error) { start := time.Now() baseFee, err := mp.api.ChainComputeBaseFee(context.TODO(), ts) @@ -417,7 +417,7 @@ func (mp *MessagePool) selectMessagesGreedy(curTs, ts *types.TipSet) ([]*types.S // 0b. Select all priority messages that fit in the block minGas := int64(gasguess.MinGas) - result, gasLimit := mp.selectPriorityMessages(pending, baseFee, ts) + result, gasLimit := mp.selectPriorityMessages(ctx, pending, baseFee, ts) // have we filled the block? if gasLimit < minGas { @@ -527,7 +527,7 @@ tailLoop: return result, nil } -func (mp *MessagePool) selectPriorityMessages(pending map[address.Address]map[uint64]*types.SignedMessage, baseFee types.BigInt, ts *types.TipSet) ([]*types.SignedMessage, int64) { +func (mp *MessagePool) selectPriorityMessages(ctx context.Context, pending map[address.Address]map[uint64]*types.SignedMessage, baseFee types.BigInt, ts *types.TipSet) ([]*types.SignedMessage, int64) { start := time.Now() defer func() { if dt := time.Since(start); dt > time.Millisecond { @@ -543,7 +543,7 @@ func (mp *MessagePool) selectPriorityMessages(pending map[address.Address]map[ui var chains []*msgChain priority := mpCfg.PriorityAddrs for _, actor := range priority { - pk, err := mp.api.StateAccountKey(context.TODO(), actor, mp.curTs) + pk, err := mp.api.StateAccountKey(ctx, actor, mp.curTs) if err != nil { log.Debugf("mpooladdlocal failed to resolve sender: %s", err) return nil, gasLimit diff --git a/chain/messagepool/selection_test.go b/chain/messagepool/selection_test.go index f254c6706..463473229 100644 --- a/chain/messagepool/selection_test.go +++ b/chain/messagepool/selection_test.go @@ -60,7 +60,7 @@ func makeTestMessage(w *wallet.LocalWallet, from, to address.Address, nonce uint func makeTestMpool() (*MessagePool, *testMpoolAPI) { tma := newTestMpoolAPI() ds := datastore.NewMapDatastore() - mp, err := New(context.TODO(), tma, ds, "test", nil) + mp, err := New(tma, ds, "test", nil) if err != nil { panic(err) } @@ -427,7 +427,7 @@ func TestBasicMessageSelection(t *testing.T) { mustAdd(t, mp, m) } - msgs, err := mp.SelectMessages(ts, 1.0) + msgs, err := mp.SelectMessages(context.Background(), ts, 1.0) if err != nil { t.Fatal(err) } @@ -495,7 +495,7 @@ func TestBasicMessageSelection(t *testing.T) { tma.setStateNonce(a1, 10) tma.setStateNonce(a2, 10) - msgs, err = mp.SelectMessages(ts3, 1.0) + msgs, err = mp.SelectMessages(context.Background(), ts3, 1.0) if err != nil { t.Fatal(err) } @@ -569,7 +569,7 @@ func TestMessageSelectionTrimming(t *testing.T) { mustAdd(t, mp, m) } - msgs, err := mp.SelectMessages(ts, 1.0) + msgs, err := mp.SelectMessages(context.Background(), ts, 1.0) if err != nil { t.Fatal(err) } @@ -633,7 +633,7 @@ func TestPriorityMessageSelection(t *testing.T) { mustAdd(t, mp, m) } - msgs, err := mp.SelectMessages(ts, 1.0) + msgs, err := mp.SelectMessages(context.Background(), ts, 1.0) if err != nil { t.Fatal(err) } @@ -712,7 +712,7 @@ func TestPriorityMessageSelection2(t *testing.T) { mustAdd(t, mp, m) } - msgs, err := mp.SelectMessages(ts, 1.0) + msgs, err := mp.SelectMessages(context.Background(), ts, 1.0) if err != nil { t.Fatal(err) } @@ -782,7 +782,7 @@ func TestPriorityMessageSelection3(t *testing.T) { } // test greedy selection - msgs, err := mp.SelectMessages(ts, 1.0) + msgs, err := mp.SelectMessages(context.Background(), ts, 1.0) if err != nil { t.Fatal(err) } @@ -805,7 +805,7 @@ func TestPriorityMessageSelection3(t *testing.T) { } // test optimal selection - msgs, err = mp.SelectMessages(ts, 0.1) + msgs, err = mp.SelectMessages(context.Background(), ts, 0.1) if err != nil { t.Fatal(err) } @@ -872,7 +872,7 @@ func TestOptimalMessageSelection1(t *testing.T) { mustAdd(t, mp, m) } - msgs, err := mp.SelectMessages(ts, 0.25) + msgs, err := mp.SelectMessages(context.Background(), ts, 0.25) if err != nil { t.Fatal(err) } @@ -941,7 +941,7 @@ func TestOptimalMessageSelection2(t *testing.T) { mustAdd(t, mp, m) } - msgs, err := mp.SelectMessages(ts, 0.1) + msgs, err := mp.SelectMessages(context.Background(), ts, 0.1) if err != nil { t.Fatal(err) } @@ -1020,7 +1020,7 @@ func TestOptimalMessageSelection3(t *testing.T) { } } - msgs, err := mp.SelectMessages(ts, 0.1) + msgs, err := mp.SelectMessages(context.Background(), ts, 0.1) if err != nil { t.Fatal(err) } @@ -1108,7 +1108,7 @@ func testCompetitiveMessageSelection(t *testing.T, rng *rand.Rand, getPremium fu logging.SetLogLevel("messagepool", "error") // 1. greedy selection - greedyMsgs, err := mp.selectMessagesGreedy(ts, ts) + greedyMsgs, err := mp.selectMessagesGreedy(context.Background(), ts, ts) if err != nil { t.Fatal(err) } @@ -1137,7 +1137,7 @@ func testCompetitiveMessageSelection(t *testing.T, rng *rand.Rand, getPremium fu var bestMsgs []*types.SignedMessage for j := 0; j < nMiners; j++ { tq := rng.Float64() - msgs, err := mp.SelectMessages(ts, tq) + msgs, err := mp.SelectMessages(context.Background(), ts, tq) if err != nil { t.Fatal(err) } @@ -1396,7 +1396,7 @@ readLoop: minGasLimit := int64(0.9 * float64(build.BlockGasLimit)) // greedy first - selected, err := mp.SelectMessages(ts, 1.0) + selected, err := mp.SelectMessages(context.Background(), ts, 1.0) if err != nil { t.Fatal(err) } @@ -1410,7 +1410,7 @@ readLoop: } // high quality ticket - selected, err = mp.SelectMessages(ts, .8) + selected, err = mp.SelectMessages(context.Background(), ts, .8) if err != nil { t.Fatal(err) } @@ -1424,7 +1424,7 @@ readLoop: } // mid quality ticket - selected, err = mp.SelectMessages(ts, .4) + selected, err = mp.SelectMessages(context.Background(), ts, .4) if err != nil { t.Fatal(err) } @@ -1438,7 +1438,7 @@ readLoop: } // low quality ticket - selected, err = mp.SelectMessages(ts, .1) + selected, err = mp.SelectMessages(context.Background(), ts, .1) if err != nil { t.Fatal(err) } @@ -1452,7 +1452,7 @@ readLoop: } // very low quality ticket - selected, err = mp.SelectMessages(ts, .01) + selected, err = mp.SelectMessages(context.Background(), ts, .01) if err != nil { t.Fatal(err) } diff --git a/node/impl/full/mpool.go b/node/impl/full/mpool.go index e91fc8b9e..d2552e1d5 100644 --- a/node/impl/full/mpool.go +++ b/node/impl/full/mpool.go @@ -58,7 +58,7 @@ func (a *MpoolAPI) MpoolSelect(ctx context.Context, tsk types.TipSetKey, ticketQ return nil, xerrors.Errorf("loading tipset %s: %w", tsk, err) } - return a.Mpool.SelectMessages(ts, ticketQuality) + return a.Mpool.SelectMessages(ctx, ts, ticketQuality) } func (a *MpoolAPI) MpoolPending(ctx context.Context, tsk types.TipSetKey) ([]*types.SignedMessage, error) { diff --git a/node/modules/chain.go b/node/modules/chain.go index b0f0543c6..ffdf3aa3a 100644 --- a/node/modules/chain.go +++ b/node/modules/chain.go @@ -61,8 +61,7 @@ func ChainBlockService(bs dtypes.ExposedBlockstore, rem dtypes.ChainBitswap) dty func MessagePool(lc fx.Lifecycle, sm *stmgr.StateManager, ps *pubsub.PubSub, ds dtypes.MetadataDS, nn dtypes.NetworkName, j journal.Journal) (*messagepool.MessagePool, error) { mpp := messagepool.NewProvider(sm, ps) - // TODO: I still don't know how go works -- should this context be part of the builder? - mp, err := messagepool.New(context.TODO(), mpp, ds, nn, j) + mp, err := messagepool.New(mpp, ds, nn, j) if err != nil { return nil, xerrors.Errorf("constructing mpool: %w", err) } diff --git a/node/modules/mpoolnonceapi.go b/node/modules/mpoolnonceapi.go index 3d670611b..6d775f010 100644 --- a/node/modules/mpoolnonceapi.go +++ b/node/modules/mpoolnonceapi.go @@ -27,14 +27,14 @@ func (a *MpoolNonceAPI) GetNonce(ctx context.Context, addr address.Address) (uin ts := a.StateAPI.Chain.GetHeaviestTipSet() // make sure we have a key address so we can compare with messages - keyAddr, err := a.StateAPI.StateManager.ResolveToKeyAddress(context.TODO(), addr, ts) + keyAddr, err := a.StateAPI.StateManager.ResolveToKeyAddress(ctx, addr, ts) if err != nil { return 0, err } // Load the last nonce from the state, if it exists. highestNonce := uint64(0) - if baseActor, err := a.StateAPI.StateManager.LoadActorRaw(context.TODO(), addr, ts.ParentState()); err != nil { + if baseActor, err := a.StateAPI.StateManager.LoadActorRaw(ctx, addr, ts.ParentState()); err != nil { if !xerrors.Is(err, types.ErrActorNotFound) { return 0, err } From 621e4eab0dc42354f772225657a9b7ca02dd88b3 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Mon, 31 May 2021 18:12:42 -0400 Subject: [PATCH 060/160] Address review --- chain/messagepool/messagepool.go | 5 ++-- chain/messagepool/messagepool_test.go | 2 +- chain/messagepool/provider.go | 6 ++--- chain/messagepool/pruning.go | 2 +- chain/messagepool/selection.go | 2 +- chain/stmgr/stmgr.go | 33 +++++++++++++++++++++------ 6 files changed, 35 insertions(+), 15 deletions(-) diff --git a/chain/messagepool/messagepool.go b/chain/messagepool/messagepool.go index 405e22320..94d7c68ef 100644 --- a/chain/messagepool/messagepool.go +++ b/chain/messagepool/messagepool.go @@ -390,7 +390,7 @@ func New(api Provider, ds dtypes.MetadataDS, netName dtypes.NetworkName, j journ // enable initial prunes mp.pruneCooldown <- struct{}{} - ctx, cancel := context.WithCancel(context.Background()) + ctx, cancel := context.WithCancel(context.TODO()) // load the current tipset and subscribe to head changes _before_ loading local messages mp.curTs = api.SubscribeHeadChanges(func(rev, app []*types.TipSet) error { @@ -431,7 +431,7 @@ func (mp *MessagePool) resolveToKey(ctx context.Context, addr address.Address) ( } // resolve the address - ka, err := mp.api.StateAccountKey(ctx, addr, mp.curTs) + ka, err := mp.api.StateAccountKeyAtFinality(ctx, addr, mp.curTs) if err != nil { return address.Undef, err } @@ -875,6 +875,7 @@ func (mp *MessagePool) addLocked(ctx context.Context, m *types.SignedMessage, st return err } + // Note: If performance becomes an issue, making this getOrCreatePendingMset will save some work mset, ok, err := mp.getPendingMset(ctx, m.Message.From) if err != nil { log.Debug(err) diff --git a/chain/messagepool/messagepool_test.go b/chain/messagepool/messagepool_test.go index 307ec20a0..1210dd2aa 100644 --- a/chain/messagepool/messagepool_test.go +++ b/chain/messagepool/messagepool_test.go @@ -150,7 +150,7 @@ func (tma *testMpoolAPI) GetActorAfter(addr address.Address, ts *types.TipSet) ( }, nil } -func (tma *testMpoolAPI) StateAccountKey(ctx context.Context, addr address.Address, ts *types.TipSet) (address.Address, error) { +func (tma *testMpoolAPI) StateAccountKeyAtFinality(ctx context.Context, addr address.Address, ts *types.TipSet) (address.Address, error) { if addr.Protocol() != address.BLS && addr.Protocol() != address.SECP256K1 { return address.Undef, fmt.Errorf("given address was not a key addr") } diff --git a/chain/messagepool/provider.go b/chain/messagepool/provider.go index b90a8af7d..b2cc26e58 100644 --- a/chain/messagepool/provider.go +++ b/chain/messagepool/provider.go @@ -25,7 +25,7 @@ type Provider interface { PutMessage(m types.ChainMsg) (cid.Cid, error) PubSubPublish(string, []byte) error GetActorAfter(address.Address, *types.TipSet) (*types.Actor, error) - StateAccountKey(context.Context, address.Address, *types.TipSet) (address.Address, error) + StateAccountKeyAtFinality(context.Context, address.Address, *types.TipSet) (address.Address, error) MessagesForBlock(*types.BlockHeader) ([]*types.Message, []*types.SignedMessage, error) MessagesForTipset(*types.TipSet) ([]types.ChainMsg, error) LoadTipSet(tsk types.TipSetKey) (*types.TipSet, error) @@ -74,8 +74,8 @@ func (mpp *mpoolProvider) GetActorAfter(addr address.Address, ts *types.TipSet) return st.GetActor(addr) } -func (mpp *mpoolProvider) StateAccountKey(ctx context.Context, addr address.Address, ts *types.TipSet) (address.Address, error) { - return mpp.sm.ResolveToKeyAddressReorgProof(ctx, addr, ts) +func (mpp *mpoolProvider) StateAccountKeyAtFinality(ctx context.Context, addr address.Address, ts *types.TipSet) (address.Address, error) { + return mpp.sm.ResolveToKeyAddressAtFinality(ctx, addr, ts) } func (mpp *mpoolProvider) MessagesForBlock(h *types.BlockHeader) ([]*types.Message, []*types.SignedMessage, error) { diff --git a/chain/messagepool/pruning.go b/chain/messagepool/pruning.go index 829258d25..c10239b8e 100644 --- a/chain/messagepool/pruning.go +++ b/chain/messagepool/pruning.go @@ -57,7 +57,7 @@ func (mp *MessagePool) pruneMessages(ctx context.Context, ts *types.TipSet) erro mpCfg := mp.getConfig() // we never prune priority addresses for _, actor := range mpCfg.PriorityAddrs { - pk, err := mp.api.StateAccountKey(ctx, actor, mp.curTs) + pk, err := mp.resolveToKey(ctx, actor) if err != nil { log.Debugf("pruneMessages failed to resolve priority address: %s", err) } diff --git a/chain/messagepool/selection.go b/chain/messagepool/selection.go index 9379cb892..3684a7b80 100644 --- a/chain/messagepool/selection.go +++ b/chain/messagepool/selection.go @@ -543,7 +543,7 @@ func (mp *MessagePool) selectPriorityMessages(ctx context.Context, pending map[a var chains []*msgChain priority := mpCfg.PriorityAddrs for _, actor := range priority { - pk, err := mp.api.StateAccountKey(ctx, actor, mp.curTs) + pk, err := mp.resolveToKey(ctx, actor) if err != nil { log.Debugf("mpooladdlocal failed to resolve sender: %s", err) return nil, gasLimit diff --git a/chain/stmgr/stmgr.go b/chain/stmgr/stmgr.go index fb1fb80f0..049ac3153 100644 --- a/chain/stmgr/stmgr.go +++ b/chain/stmgr/stmgr.go @@ -89,6 +89,7 @@ type StateManager struct { expensiveUpgrades map[abi.ChainEpoch]struct{} stCache map[string][]cid.Cid + tCache treeCache compWait map[string]chan struct{} stlk sync.Mutex genesisMsigLk sync.Mutex @@ -101,6 +102,12 @@ type StateManager struct { genesisMarketFunds abi.TokenAmount } +// Caches a single state tree +type treeCache struct { + root cid.Cid + tree *state.StateTree +} + func NewStateManager(cs *store.ChainStore) *StateManager { sm, err := NewStateManagerWithUpgradeSchedule(cs, DefaultUpgradeSchedule()) if err != nil { @@ -153,7 +160,11 @@ func NewStateManagerWithUpgradeSchedule(cs *store.ChainStore, us UpgradeSchedule newVM: vm.NewVM, cs: cs, stCache: make(map[string][]cid.Cid), - compWait: make(map[string]chan struct{}), + tCache: treeCache{ + root: cid.Undef, + tree: nil, + }, + compWait: make(map[string]chan struct{}), }, nil } @@ -544,9 +555,9 @@ func (sm *StateManager) ResolveToKeyAddress(ctx context.Context, addr address.Ad return vm.ResolveToKeyAddr(tree, cst, addr) } -// ResolveToKeyAddressReorgProof is similar to stmgr.ResolveToKeyAddress but fails if the ID address being resolved isn't reorg-stable yet. +// ResolveToKeyAddressAtFinality is similar to stmgr.ResolveToKeyAddress but fails if the ID address being resolved isn't reorg-stable yet. // It should not be used for consensus-critical subsystems. -func (sm *StateManager) ResolveToKeyAddressReorgProof(ctx context.Context, addr address.Address, ts *types.TipSet) (address.Address, error) { +func (sm *StateManager) ResolveToKeyAddressAtFinality(ctx context.Context, addr address.Address, ts *types.TipSet) (address.Address, error) { switch addr.Protocol() { case address.BLS, address.SECP256K1: return addr, nil @@ -559,8 +570,8 @@ func (sm *StateManager) ResolveToKeyAddressReorgProof(ctx context.Context, addr ts = sm.cs.GetHeaviestTipSet() } + var err error if ts.Height() > policy.ChainFinality { - var err error ts, err = sm.ChainStore().GetTipsetByHeight(ctx, ts.Height()-policy.ChainFinality, ts, true) if err != nil { return address.Undef, xerrors.Errorf("failed to load lookback tipset: %w", err) @@ -568,10 +579,18 @@ func (sm *StateManager) ResolveToKeyAddressReorgProof(ctx context.Context, addr } cst := cbor.NewCborStore(sm.cs.StateBlockstore()) + tree := sm.tCache.tree - tree, err := state.LoadStateTree(cst, ts.ParentState()) - if err != nil { - return address.Undef, xerrors.Errorf("failed to load parent state tree: %w", err) + if tree == nil || sm.tCache.root != ts.ParentState() { + tree, err = state.LoadStateTree(cst, ts.ParentState()) + if err != nil { + return address.Undef, xerrors.Errorf("failed to load parent state tree: %w", err) + } + + sm.tCache = treeCache{ + root: ts.ParentState(), + tree: tree, + } } resolved, err := vm.ResolveToKeyAddr(tree, cst, addr) From 2ab24b358d5c63e6af958c7eb922126caa4cd7e7 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Mon, 31 May 2021 18:50:29 -0400 Subject: [PATCH 061/160] Fix supported proof type manipulations for v5 actors --- chain/actors/policy/policy.go | 7 +++++++ chain/actors/policy/policy.go.template | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/chain/actors/policy/policy.go b/chain/actors/policy/policy.go index 4d115f783..bb35025ec 100644 --- a/chain/actors/policy/policy.go +++ b/chain/actors/policy/policy.go @@ -92,6 +92,13 @@ func AddSupportedProofTypes(types ...abi.RegisteredSealProof) { miner4.PreCommitSealProofTypesV8[t+abi.RegisteredSealProof_StackedDrg2KiBV1_1] = struct{}{} miner5.PreCommitSealProofTypesV8[t+abi.RegisteredSealProof_StackedDrg2KiBV1_1] = struct{}{} + wpp, err := t.RegisteredWindowPoStProof() + if err != nil { + // Fine to panic, this is a test-only method + panic(err) + } + + miner5.WindowPoStProofTypes[wpp] = struct{}{} } } diff --git a/chain/actors/policy/policy.go.template b/chain/actors/policy/policy.go.template index aef1081cb..cd2eca4ea 100644 --- a/chain/actors/policy/policy.go.template +++ b/chain/actors/policy/policy.go.template @@ -62,6 +62,13 @@ func AddSupportedProofTypes(types ...abi.RegisteredSealProof) { miner{{.}}.PreCommitSealProofTypesV8[t+abi.RegisteredSealProof_StackedDrg2KiBV1_1] = struct{}{} {{else}} miner{{.}}.PreCommitSealProofTypesV8[t+abi.RegisteredSealProof_StackedDrg2KiBV1_1] = struct{}{} + wpp, err := t.RegisteredWindowPoStProof() + if err != nil { + // Fine to panic, this is a test-only method + panic(err) + } + + miner{{.}}.WindowPoStProofTypes[wpp] = struct{}{} {{end}} {{end}} } From 85873f6057bf64c95742ddd2bd8cd1a7c60035fd Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Mon, 31 May 2021 19:28:49 -0400 Subject: [PATCH 062/160] Wdpost run should respect AddressedPartitionsMax --- storage/wdpost_run.go | 14 ++++++++++++-- storage/wdpost_run_test.go | 20 +++++++++++++------- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/storage/wdpost_run.go b/storage/wdpost_run.go index fd7c4f184..ca149aec3 100644 --- a/storage/wdpost_run.go +++ b/storage/wdpost_run.go @@ -497,9 +497,14 @@ func (s *WindowPoStScheduler) runPost(ctx context.Context, di dline.Info, ts *ty return nil, xerrors.Errorf("getting partitions: %w", err) } + nv, err := s.api.StateNetworkVersion(ctx, ts.Key()) + if err != nil { + return nil, xerrors.Errorf("getting network version: %w", err) + } + // Split partitions into batches, so as not to exceed the number of sectors // allowed in a single message - partitionBatches, err := s.batchPartitions(partitions) + partitionBatches, err := s.batchPartitions(partitions, nv) if err != nil { return nil, err } @@ -679,7 +684,7 @@ func (s *WindowPoStScheduler) runPost(ctx context.Context, di dline.Info, ts *ty return posts, nil } -func (s *WindowPoStScheduler) batchPartitions(partitions []api.Partition) ([][]api.Partition, error) { +func (s *WindowPoStScheduler) batchPartitions(partitions []api.Partition, nv network.Version) ([][]api.Partition, error) { // We don't want to exceed the number of sectors allowed in a message. // So given the number of sectors in a partition, work out the number of // partitions that can be in a message without exceeding sectors per @@ -695,6 +700,11 @@ func (s *WindowPoStScheduler) batchPartitions(partitions []api.Partition) ([][]a return nil, xerrors.Errorf("getting sectors per partition: %w", err) } + // Also respect the AddressedPartitionsMax (which is the same as DeclarationsMax (which is all really just MaxPartitionsPerDeadline)) + if partitionsPerMsg > policy.GetDeclarationsMax(nv) { + partitionsPerMsg = policy.GetDeclarationsMax(nv) + } + // The number of messages will be: // ceiling(number of partitions / partitions per message) batchCount := len(partitions) / partitionsPerMsg diff --git a/storage/wdpost_run_test.go b/storage/wdpost_run_test.go index 5117e718a..f80f6bee2 100644 --- a/storage/wdpost_run_test.go +++ b/storage/wdpost_run_test.go @@ -5,6 +5,9 @@ import ( "context" "testing" + builtin5 "github.com/filecoin-project/specs-actors/v5/actors/builtin" + miner5 "github.com/filecoin-project/specs-actors/v5/actors/builtin/miner" + "github.com/stretchr/testify/require" "golang.org/x/xerrors" @@ -177,13 +180,16 @@ func TestWDPostDoPost(t *testing.T) { mockStgMinerAPI := newMockStorageMinerAPI() // Get the number of sectors allowed in a partition for this proof type - sectorsPerPartition, err := builtin2.PoStProofWindowPoStPartitionSectors(proofType) + sectorsPerPartition, err := builtin5.PoStProofWindowPoStPartitionSectors(proofType) require.NoError(t, err) // Work out the number of partitions that can be included in a message // without exceeding the message sector limit require.NoError(t, err) - partitionsPerMsg := int(miner2.AddressedSectorsMax / sectorsPerPartition) + partitionsPerMsg := int(miner5.AddressedSectorsMax / sectorsPerPartition) + if partitionsPerMsg > miner5.AddressedPartitionsMax { + partitionsPerMsg = miner5.AddressedPartitionsMax + } // Enough partitions to fill expectedMsgCount-1 messages partitionCount := (expectedMsgCount - 1) * partitionsPerMsg @@ -219,11 +225,11 @@ func TestWDPostDoPost(t *testing.T) { } di := &dline.Info{ - WPoStPeriodDeadlines: miner2.WPoStPeriodDeadlines, - WPoStProvingPeriod: miner2.WPoStProvingPeriod, - WPoStChallengeWindow: miner2.WPoStChallengeWindow, - WPoStChallengeLookback: miner2.WPoStChallengeLookback, - FaultDeclarationCutoff: miner2.FaultDeclarationCutoff, + WPoStPeriodDeadlines: miner5.WPoStPeriodDeadlines, + WPoStProvingPeriod: miner5.WPoStProvingPeriod, + WPoStChallengeWindow: miner5.WPoStChallengeWindow, + WPoStChallengeLookback: miner5.WPoStChallengeLookback, + FaultDeclarationCutoff: miner5.FaultDeclarationCutoff, } ts := mockTipSet(t) From 9fcb564bef2aa6d2814d340bf14ac8686b78e7e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 1 Jun 2021 11:56:19 +0200 Subject: [PATCH 063/160] Make commit batcher more robust --- api/api_storage.go | 3 +- api/apistruct/struct.go | 5 +- api/test/pledge.go | 4 +- extern/storage-sealing/commit_batch.go | 201 +++++++++++++++---- extern/storage-sealing/sealiface/batching.go | 16 ++ extern/storage-sealing/sealing.go | 3 +- extern/storage-sealing/states_sealing.go | 16 +- node/impl/storminer.go | 3 +- storage/sealing.go | 3 +- 9 files changed, 201 insertions(+), 53 deletions(-) create mode 100644 extern/storage-sealing/sealiface/batching.go diff --git a/api/api_storage.go b/api/api_storage.go index 202b40f93..c2f3a3d57 100644 --- a/api/api_storage.go +++ b/api/api_storage.go @@ -24,6 +24,7 @@ import ( "github.com/filecoin-project/lotus/extern/sector-storage/fsutil" "github.com/filecoin-project/lotus/extern/sector-storage/stores" "github.com/filecoin-project/lotus/extern/sector-storage/storiface" + "github.com/filecoin-project/lotus/extern/storage-sealing/sealiface" ) // StorageMiner is a low-level interface to the Filecoin network storage miner node @@ -87,7 +88,7 @@ type StorageMiner interface { SectorPreCommitPending(ctx context.Context) ([]abi.SectorID, error) //perm:admin // SectorCommitFlush immediately sends a Commit message with sectors aggregated for Commit. // Returns null if message wasn't sent - SectorCommitFlush(ctx context.Context) (*cid.Cid, error) //perm:admin + SectorCommitFlush(ctx context.Context) ([]sealiface.CommitBatchRes, error) //perm:admin // SectorCommitPending returns a list of pending Commit sectors to be sent in the next aggregate message SectorCommitPending(ctx context.Context) ([]abi.SectorID, error) //perm:admin diff --git a/api/apistruct/struct.go b/api/apistruct/struct.go index 13375cf72..d70c6aa0d 100644 --- a/api/apistruct/struct.go +++ b/api/apistruct/struct.go @@ -28,6 +28,7 @@ import ( "github.com/filecoin-project/lotus/extern/sector-storage/sealtasks" "github.com/filecoin-project/lotus/extern/sector-storage/stores" "github.com/filecoin-project/lotus/extern/sector-storage/storiface" + "github.com/filecoin-project/lotus/extern/storage-sealing/sealiface" marketevents "github.com/filecoin-project/lotus/markets/loggers" "github.com/filecoin-project/lotus/node/modules/dtypes" "github.com/filecoin-project/specs-storage/storage" @@ -639,7 +640,7 @@ type StorageMinerStruct struct { SealingSchedDiag func(p0 context.Context, p1 bool) (interface{}, error) `perm:"admin"` - SectorCommitFlush func(p0 context.Context) (*cid.Cid, error) `perm:"admin"` + SectorCommitFlush func(p0 context.Context) ([]sealiface.CommitBatchRes, error) `perm:"admin"` SectorCommitPending func(p0 context.Context) ([]abi.SectorID, error) `perm:"admin"` @@ -1931,7 +1932,7 @@ func (s *StorageMinerStruct) SealingSchedDiag(p0 context.Context, p1 bool) (inte return s.Internal.SealingSchedDiag(p0, p1) } -func (s *StorageMinerStruct) SectorCommitFlush(p0 context.Context) (*cid.Cid, error) { +func (s *StorageMinerStruct) SectorCommitFlush(p0 context.Context) ([]sealiface.CommitBatchRes, error) { return s.Internal.SectorCommitFlush(p0) } diff --git a/api/test/pledge.go b/api/test/pledge.go index 8752ad4ac..b4bf88b59 100644 --- a/api/test/pledge.go +++ b/api/test/pledge.go @@ -178,7 +178,7 @@ func TestPledgeBatching(t *testing.T, b APIBuilder, blocktime time.Duration, nSe cb, err := miner.SectorCommitFlush(ctx) require.NoError(t, err) if cb != nil { - fmt.Printf("COMMIT BATCH: %s\n", *cb) + fmt.Printf("COMMIT BATCH: %+v\n", cb) } } @@ -325,7 +325,7 @@ func flushSealingBatches(t *testing.T, ctx context.Context, miner TestStorageNod cb, err := miner.SectorCommitFlush(ctx) require.NoError(t, err) if cb != nil { - fmt.Printf("COMMIT BATCH: %s\n", *cb) + fmt.Printf("COMMIT BATCH: %+v\n", cb) } } diff --git a/extern/storage-sealing/commit_batch.go b/extern/storage-sealing/commit_batch.go index 845400ccf..c01067872 100644 --- a/extern/storage-sealing/commit_batch.go +++ b/extern/storage-sealing/commit_batch.go @@ -22,6 +22,7 @@ import ( "github.com/filecoin-project/lotus/chain/actors/builtin/miner" "github.com/filecoin-project/lotus/chain/actors/policy" "github.com/filecoin-project/lotus/extern/sector-storage/ffiwrapper" + "github.com/filecoin-project/lotus/extern/storage-sealing/sealiface" ) const arp = abi.RegisteredAggregationProof_SnarkPackV1 @@ -30,6 +31,9 @@ type CommitBatcherApi interface { SendMsg(ctx context.Context, from, to address.Address, method abi.MethodNum, value, maxFee abi.TokenAmount, params []byte) (cid.Cid, error) StateMinerInfo(context.Context, address.Address, TipSetToken) (miner.MinerInfo, error) ChainHead(ctx context.Context) (TipSetToken, abi.ChainEpoch, error) + + StateSectorPreCommitInfo(ctx context.Context, maddr address.Address, sectorNumber abi.SectorNumber, tok TipSetToken) (*miner.SectorPreCommitOnChainInfo, error) + StateMinerInitialPledgeCollateral(context.Context, address.Address, miner.SectorPreCommitInfo, TipSetToken) (big.Int, error) } type AggregateInput struct { @@ -49,10 +53,10 @@ type CommitBatcher struct { deadlines map[abi.SectorNumber]time.Time todo map[abi.SectorNumber]AggregateInput - waiting map[abi.SectorNumber][]chan cid.Cid + waiting map[abi.SectorNumber][]chan sealiface.CommitBatchRes notify, stop, stopped chan struct{} - force chan chan *cid.Cid + force chan chan []sealiface.CommitBatchRes lk sync.Mutex } @@ -68,10 +72,10 @@ func NewCommitBatcher(mctx context.Context, maddr address.Address, api CommitBat deadlines: map[abi.SectorNumber]time.Time{}, todo: map[abi.SectorNumber]AggregateInput{}, - waiting: map[abi.SectorNumber][]chan cid.Cid{}, + waiting: map[abi.SectorNumber][]chan sealiface.CommitBatchRes{}, notify: make(chan struct{}, 1), - force: make(chan chan *cid.Cid), + force: make(chan chan []sealiface.CommitBatchRes), stop: make(chan struct{}), stopped: make(chan struct{}), } @@ -82,8 +86,8 @@ func NewCommitBatcher(mctx context.Context, maddr address.Address, api CommitBat } func (b *CommitBatcher) run() { - var forceRes chan *cid.Cid - var lastMsg *cid.Cid + var forceRes chan []sealiface.CommitBatchRes + var lastMsg []sealiface.CommitBatchRes cfg, err := b.getConfig() if err != nil { @@ -111,7 +115,7 @@ func (b *CommitBatcher) run() { } var err error - lastMsg, err = b.processBatch(sendAboveMax, sendAboveMin) + lastMsg, err = b.maybeStartBatch(sendAboveMax, sendAboveMin) if err != nil { log.Warnw("CommitBatcher processBatch error", "error", err) } @@ -159,12 +163,9 @@ func (b *CommitBatcher) batchWait(maxWait, slack time.Duration) <-chan time.Time return time.After(wait) } -func (b *CommitBatcher) processBatch(notif, after bool) (*cid.Cid, error) { +func (b *CommitBatcher) maybeStartBatch(notif, after bool) ([]sealiface.CommitBatchRes, error) { b.lk.Lock() defer b.lk.Unlock() - params := miner5.ProveCommitAggregateParams{ - SectorNumbers: bitfield.New(), - } total := len(b.todo) if total == 0 { @@ -184,6 +185,45 @@ func (b *CommitBatcher) processBatch(notif, after bool) (*cid.Cid, error) { return nil, nil } + var res []sealiface.CommitBatchRes + + if total < cfg.MinCommitBatch || total < miner5.PreCommitSectorBatchMaxSize { + res, err = b.processIndividually() + } else { + res, err = b.processBatch(cfg) + } + if err != nil && len(res) == 0 { + return nil, err + } + + for _, r := range res { + if err != nil { + r.Error = err.Error() + } + + for _, sn := range r.Sectors { + for _, ch := range b.waiting[sn] { + ch <- r // buffered + } + + delete(b.waiting, sn) + delete(b.todo, sn) + delete(b.deadlines, sn) + } + } + + return res, nil +} + +func (b *CommitBatcher) processBatch(cfg sealiface.Config) ([]sealiface.CommitBatchRes, error) { + total := len(b.todo) + + var res sealiface.CommitBatchRes + + params := miner5.ProveCommitAggregateParams{ + SectorNumbers: bitfield.New(), + } + proofs := make([][]byte, 0, total) infos := make([]proof5.AggregateSealVerifyInfo, 0, total) @@ -202,12 +242,13 @@ func (b *CommitBatcher) processBatch(notif, after bool) (*cid.Cid, error) { }) for _, info := range infos { + res.Sectors = append(res.Sectors, info.Number) proofs = append(proofs, b.todo[info.Number].proof) } mid, err := address.IDFromAddress(b.maddr) if err != nil { - return nil, xerrors.Errorf("getting miner id: %w", err) + return []sealiface.CommitBatchRes{res}, xerrors.Errorf("getting miner id: %w", err) } params.AggregateProof, err = b.prover.AggregateSealProofs(proof5.AggregateSealVerifyProofAndInfos{ @@ -217,55 +258,107 @@ func (b *CommitBatcher) processBatch(notif, after bool) (*cid.Cid, error) { Infos: infos, }, proofs) if err != nil { - return nil, xerrors.Errorf("aggregating proofs: %w", err) + return []sealiface.CommitBatchRes{res}, xerrors.Errorf("aggregating proofs: %w", err) } enc := new(bytes.Buffer) if err := params.MarshalCBOR(enc); err != nil { - return nil, xerrors.Errorf("couldn't serialize ProveCommitAggregateParams: %w", err) + return []sealiface.CommitBatchRes{res}, xerrors.Errorf("couldn't serialize ProveCommitAggregateParams: %w", err) } + mi, err := b.api.StateMinerInfo(b.mctx, b.maddr, nil) + if err != nil { + return []sealiface.CommitBatchRes{res}, xerrors.Errorf("couldn't get miner info: %w", err) + } + + from, _, err := b.addrSel(b.mctx, mi, api.CommitAddr, b.feeCfg.MaxCommitGasFee, b.feeCfg.MaxCommitGasFee) + if err != nil { + return []sealiface.CommitBatchRes{res}, xerrors.Errorf("no good address found: %w", err) + } + + // todo: collateral + + mcid, err := b.api.SendMsg(b.mctx, from, b.maddr, miner.Methods.ProveCommitAggregate, big.Zero(), b.feeCfg.MaxCommitGasFee, enc.Bytes()) + if err != nil { + return []sealiface.CommitBatchRes{res}, xerrors.Errorf("sending message failed: %w", err) + } + + res.Msg = &mcid + + log.Infow("Sent ProveCommitAggregate message", "cid", mcid, "from", from, "todo", total, "sectors", len(infos)) + + return []sealiface.CommitBatchRes{res}, nil +} + +func (b *CommitBatcher) processIndividually() ([]sealiface.CommitBatchRes, error) { mi, err := b.api.StateMinerInfo(b.mctx, b.maddr, nil) if err != nil { return nil, xerrors.Errorf("couldn't get miner info: %w", err) } - from, _, err := b.addrSel(b.mctx, mi, api.CommitAddr, b.feeCfg.MaxCommitGasFee, b.feeCfg.MaxCommitGasFee) + tok, _, err := b.api.ChainHead(b.mctx) if err != nil { - return nil, xerrors.Errorf("no good address found: %w", err) + return nil, err } - mcid, err := b.api.SendMsg(b.mctx, from, b.maddr, miner.Methods.ProveCommitAggregate, big.Zero(), b.feeCfg.MaxCommitGasFee, enc.Bytes()) - if err != nil { - return nil, xerrors.Errorf("sending message failed: %w", err) - } + var res []sealiface.CommitBatchRes - log.Infow("Sent ProveCommitAggregate message", "cid", mcid, "from", from, "todo", total, "sectors", len(infos)) - - err = params.SectorNumbers.ForEach(func(us uint64) error { - sn := abi.SectorNumber(us) - - for _, ch := range b.waiting[sn] { - ch <- mcid // buffered + for sn, info := range b.todo { + r := sealiface.CommitBatchRes{ + Sectors: []abi.SectorNumber{sn}, } - delete(b.waiting, sn) - delete(b.todo, sn) - delete(b.deadlines, sn) - return nil - }) - if err != nil { - return nil, xerrors.Errorf("done sectors foreach: %w", err) + + mcid, err := b.processSingle(mi, sn, info, tok) + if err != nil { + log.Errorf("process single error: %+v", err) // todo: return to user + r.FailedSectors[sn] = err.Error() + } else { + r.Msg = &mcid + } + + res = append(res, r) } - return &mcid, nil + return res, nil +} + +func (b *CommitBatcher) processSingle(mi miner.MinerInfo, sn abi.SectorNumber, info AggregateInput, tok TipSetToken) (cid.Cid, error) { + enc := new(bytes.Buffer) + params := &miner.ProveCommitSectorParams{ + SectorNumber: sn, + Proof: info.proof, + } + + if err := params.MarshalCBOR(enc); err != nil { + return cid.Undef, xerrors.Errorf("marshaling commit params: %w", err) + } + + collateral, err := b.getSectorCollateral(sn, tok) + if err != nil { + return cid.Undef, err + } + + goodFunds := big.Add(collateral, b.feeCfg.MaxCommitGasFee) + + from, _, err := b.addrSel(b.mctx, mi, api.CommitAddr, goodFunds, collateral) + if err != nil { + return cid.Undef, xerrors.Errorf("no good address to send commit message from: %w", err) + } + + mcid, err := b.api.SendMsg(b.mctx, from, b.maddr, miner.Methods.ProveCommitSector, collateral, b.feeCfg.MaxCommitGasFee, enc.Bytes()) + if err != nil { + return cid.Undef, xerrors.Errorf("pushing message to mpool: %w", err) + } + + return mcid, nil } // register commit, wait for batch message, return message CID -func (b *CommitBatcher) AddCommit(ctx context.Context, s SectorInfo, in AggregateInput) (mcid cid.Cid, err error) { +func (b *CommitBatcher) AddCommit(ctx context.Context, s SectorInfo, in AggregateInput) (res sealiface.CommitBatchRes, err error) { _, curEpoch, err := b.api.ChainHead(b.mctx) if err != nil { log.Errorf("getting chain head: %s", err) - return cid.Undef, nil + return sealiface.CommitBatchRes{}, nil } sn := s.SectorNumber @@ -274,7 +367,7 @@ func (b *CommitBatcher) AddCommit(ctx context.Context, s SectorInfo, in Aggregat b.deadlines[sn] = getSectorDeadline(curEpoch, s) b.todo[sn] = in - sent := make(chan cid.Cid, 1) + sent := make(chan sealiface.CommitBatchRes, 1) b.waiting[sn] = append(b.waiting[sn], sent) select { @@ -284,15 +377,15 @@ func (b *CommitBatcher) AddCommit(ctx context.Context, s SectorInfo, in Aggregat b.lk.Unlock() select { - case c := <-sent: - return c, nil + case r := <-sent: + return r, nil case <-ctx.Done(): - return cid.Undef, ctx.Err() + return sealiface.CommitBatchRes{}, ctx.Err() } } -func (b *CommitBatcher) Flush(ctx context.Context) (*cid.Cid, error) { - resCh := make(chan *cid.Cid, 1) +func (b *CommitBatcher) Flush(ctx context.Context) ([]sealiface.CommitBatchRes, error) { + resCh := make(chan []sealiface.CommitBatchRes, 1) select { case b.force <- resCh: select { @@ -364,3 +457,25 @@ func getSectorDeadline(curEpoch abi.ChainEpoch, si SectorInfo) time.Time { return time.Now().Add(time.Duration(deadlineEpoch-curEpoch) * time.Duration(build.BlockDelaySecs) * time.Second) } + +func (b *CommitBatcher) getSectorCollateral(sn abi.SectorNumber, tok TipSetToken) (abi.TokenAmount, error) { + pci, err := b.api.StateSectorPreCommitInfo(b.mctx, b.maddr, sn, tok) + if err != nil { + return big.Zero(), xerrors.Errorf("getting precommit info: %w", err) + } + if pci == nil { + return big.Zero(), xerrors.Errorf("precommit info not found on chain") + } + + collateral, err := b.api.StateMinerInitialPledgeCollateral(b.mctx, b.maddr, pci.Info, tok) + if err != nil { + return big.Zero(), xerrors.Errorf("getting initial pledge collateral: %w", err) + } + + collateral = big.Sub(collateral, pci.PreCommitDeposit) + if collateral.LessThan(big.Zero()) { + collateral = big.Zero() + } + + return collateral, nil +} diff --git a/extern/storage-sealing/sealiface/batching.go b/extern/storage-sealing/sealiface/batching.go new file mode 100644 index 000000000..e7c2cadbb --- /dev/null +++ b/extern/storage-sealing/sealiface/batching.go @@ -0,0 +1,16 @@ +package sealiface + +import ( + "github.com/ipfs/go-cid" + + "github.com/filecoin-project/go-state-types/abi" +) + +type CommitBatchRes struct { + Sectors []abi.SectorNumber + + FailedSectors map[abi.SectorNumber]string + + Msg *cid.Cid + Error string // if set, means that all sectors are failed, implies Msg==nil +} diff --git a/extern/storage-sealing/sealing.go b/extern/storage-sealing/sealing.go index fc452cc6f..61360dc12 100644 --- a/extern/storage-sealing/sealing.go +++ b/extern/storage-sealing/sealing.go @@ -27,6 +27,7 @@ import ( "github.com/filecoin-project/lotus/chain/types" sectorstorage "github.com/filecoin-project/lotus/extern/sector-storage" "github.com/filecoin-project/lotus/extern/sector-storage/ffiwrapper" + "github.com/filecoin-project/lotus/extern/storage-sealing/sealiface" ) const SectorStorePrefix = "/sectors" @@ -214,7 +215,7 @@ func (m *Sealing) SectorPreCommitPending(ctx context.Context) ([]abi.SectorID, e return m.precommiter.Pending(ctx) } -func (m *Sealing) CommitFlush(ctx context.Context) (*cid.Cid, error) { +func (m *Sealing) CommitFlush(ctx context.Context) ([]sealiface.CommitBatchRes, error) { return m.commiter.Flush(ctx) } diff --git a/extern/storage-sealing/states_sealing.go b/extern/storage-sealing/states_sealing.go index 391951dea..6f4c57bfd 100644 --- a/extern/storage-sealing/states_sealing.go +++ b/extern/storage-sealing/states_sealing.go @@ -581,7 +581,7 @@ func (m *Sealing) handleSubmitCommitAggregate(ctx statemachine.Context, sector S return ctx.Send(SectorCommitFailed{xerrors.Errorf("sector had nil commR or commD")}) } - mcid, err := m.commiter.AddCommit(ctx.Context(), sector, AggregateInput{ + res, err := m.commiter.AddCommit(ctx.Context(), sector, AggregateInput{ info: proof.AggregateSealVerifyInfo{ Number: sector.SectorNumber, Randomness: sector.TicketValue, @@ -596,7 +596,19 @@ func (m *Sealing) handleSubmitCommitAggregate(ctx statemachine.Context, sector S return ctx.Send(SectorCommitFailed{xerrors.Errorf("queuing commit for aggregation failed: %w", err)}) } - return ctx.Send(SectorCommitAggregateSent{mcid}) + if res.Error != "" { + return ctx.Send(SectorCommitFailed{xerrors.Errorf("aggregate error: %s", res.Error)}) + } + + if e, found := res.FailedSectors[sector.SectorNumber]; found { + return ctx.Send(SectorCommitFailed{xerrors.Errorf("sector failed in aggregate processing: %s", e)}) + } + + if res.Msg == nil { + return ctx.Send(SectorCommitFailed{xerrors.Errorf("aggregate message was nil")}) + } + + return ctx.Send(SectorCommitAggregateSent{*res.Msg}) } func (m *Sealing) handleCommitWait(ctx statemachine.Context, sector SectorInfo) error { diff --git a/node/impl/storminer.go b/node/impl/storminer.go index 8660f1efb..9b6f65207 100644 --- a/node/impl/storminer.go +++ b/node/impl/storminer.go @@ -32,6 +32,7 @@ import ( "github.com/filecoin-project/lotus/extern/sector-storage/stores" "github.com/filecoin-project/lotus/extern/sector-storage/storiface" sealing "github.com/filecoin-project/lotus/extern/storage-sealing" + "github.com/filecoin-project/lotus/extern/storage-sealing/sealiface" "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/api/apistruct" @@ -386,7 +387,7 @@ func (sm *StorageMinerAPI) SectorMarkForUpgrade(ctx context.Context, id abi.Sect return sm.Miner.MarkForUpgrade(id) } -func (sm *StorageMinerAPI) SectorCommitFlush(ctx context.Context) (*cid.Cid, error) { +func (sm *StorageMinerAPI) SectorCommitFlush(ctx context.Context) ([]sealiface.CommitBatchRes, error) { return sm.Miner.CommitFlush(ctx) } diff --git a/storage/sealing.go b/storage/sealing.go index cd215f238..bd8241197 100644 --- a/storage/sealing.go +++ b/storage/sealing.go @@ -11,6 +11,7 @@ import ( "github.com/filecoin-project/specs-storage/storage" sealing "github.com/filecoin-project/lotus/extern/storage-sealing" + "github.com/filecoin-project/lotus/extern/storage-sealing/sealiface" ) // TODO: refactor this to be direct somehow @@ -67,7 +68,7 @@ func (m *Miner) SectorPreCommitPending(ctx context.Context) ([]abi.SectorID, err return m.sealing.SectorPreCommitPending(ctx) } -func (m *Miner) CommitFlush(ctx context.Context) (*cid.Cid, error) { +func (m *Miner) CommitFlush(ctx context.Context) ([]sealiface.CommitBatchRes, error) { return m.sealing.CommitFlush(ctx) } From 084b0e7f60fce9d3ae19bec00b6e3bd20f374138 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 1 Jun 2021 12:02:34 +0200 Subject: [PATCH 064/160] Handle collateral when submitting aggregated commits --- build/openrpc/full.json.gz | Bin 22809 -> 22810 bytes build/openrpc/miner.json.gz | Bin 7988 -> 8040 bytes build/openrpc/worker.json.gz | Bin 2577 -> 2578 bytes extern/storage-sealing/commit_batch.go | 24 +++++++++++++++++++----- 4 files changed, 19 insertions(+), 5 deletions(-) diff --git a/build/openrpc/full.json.gz b/build/openrpc/full.json.gz index a81cb15650063bac74bd3fb4aa2cd03f77604826..47e55e0c58fcc473991a1e90044d1f138815ffe7 100644 GIT binary patch literal 22810 zcmb4~b8m0o*R|VzYTLF?ZQHhO+dj43Q`@#}+qT`F`aZw!^9r8*$671d$xe1Mvogmu z#w3V>1p4p!x%%>S-fTN6AM+~ZyrC$Yz!9XvC$avrnl>Xb6xQsh}r5i9b2zD3f?nluq zU}!J^sKr%~0~+$F1sX3RRR3i>j_>VpmY;hY9)5f}ycTao z$?FxyC2V!>3r36x{Q}~QkD?GhV|+Tyq#P28iwZoGr?LwiNSBII@zJv?+vR3wA+lzc&FUcSV|MVar;~KR0^tlfE_GTkhW?_VnZa_R;BnFd11SezSpcL=?*h z|7|7X)v#PC5f{8&ZG6?Ea#S4Ifx1yqLcd&NdDM)~>W_0vi)b3XKA z&45+#=3wt1{yOlM$sqF4x#;Wpw=lRQ&eB`xh3KgZ*@>E8|ysf*jvvv2}&92@#%;YxLex?Q>6;>=!vdQ@zPvW&|+T-41;3OkF zzzuQR5k*aLCuM{unAcGdXuWaRk>lkC2dH;$;O`~m!G8SrvChTou2p_gjcBPSj-#_s zr8lBVjoGq&njuWwe4Hb~E9aJu6W2i+oBOi{cMfbY3s6qv>Ya%%4G8(+3wgvthXWcy znpb6nIGM`ac>z?4xum};EcqqZkW4P2@j4BmlpH@;abS7NuWWs@NIvv-x3_px^C7$^ zXPJXPOLxxzOi%}Ueu`$eoPj3{3FTlxijd0VJ`|=kySsMgrnZC-&_Bttiy2yCdVd+F zsnMs*_Pw1z^K_q)8#Zn3$ZYau0s03Sc8_S9N_Q*?N{yP6%88)5JUa=r+$L0f=1vw3 zV?}tJvn+LO(wa{eGpfgEzHV-uY)X%hcIKl3V$tP$N)1L!k%3c?zFjHhixOrst|pY+ zVys43PRP6lS>Qn)h*bO~pi+vqmGbXLfa5bD9azyoAYe>FQA!{AsC=2F&;a9Hq8+gG zpMij|+jt5Sr7_?_lqCwsI4SX%0G#(6Qr?<8^%VtU&;RjAk_s5 z210R36vZwR?3su!cotzFqN5@Zp*%j46mAn(Pc)LEFplu9c!=vq2(o;{oG@-(o9vy? zOLfsT)`{`nJeCozgJiqj2*IJkKwT8b(Mtp-XNR~PC0jyPe_U%R#NNx7D1uJ*PS1}A zh}SK@Uk48`0l&^S1a5YaeD@-X!^=H*R_@M@o(~6vdTIC0{+lI&jt@{+p1ZG?kE5F- zY%D!L{tuBB&l@D4PZ!!V)AWuXDqWE^fxaIXC$Hl4mY&Y5oc+fUitzo&^qukwSR~b0 z?SmpACZvM}A|XWvGKMG(1^;XX7MsenLY|^1tFR^#3hQ9PDDqyp$Q!=XXXodj@zq6d zrc}f^{08G$V}RTrl1}Hb8APs7zsia)44Bo%MoDe35`pVv%d-)@wAj9LcopoK(qAO<2S0IpeeUga+N6U!XO5c>r|s4K&M(*1k0sfXZlameT^WyM_nO|}7@7`lJTTez;Bi#;ta zxR(oJH*-OaH^dAZaamQA-Ab!+sB@aD)BbYSZe?Wd9o0NjGdiKy52dr7NQB;{0h7wS za!GT|N0X_+WIlgjTr@ND_jo2$5;J!L!|+>clw@iASg$LI=;f*ybd4HnZMK)&c+JI< z2^H5LEy`l*P0)po);@2+`J6QbE6xRMR%0QpM&j(6_2{vZ4ra0j#gz0h@cCdOIOIre zA**H44xVzbR^#NRd|yJIskR#d1x9d6!CDSqQ79#PphPSAh9^E^>j!lFQZyNwn%XU- zBf{8vYsqi$q|b#UC85vC30_(pj_y$%+R*ywp9msh!7nlVXw&Uwww!@qbv`=6e0o7O zkB(@p^4ehE0*~}}?vJym#TaEryg@hjn2F>9T5fb5$g;l*ZhUYTT?8#ix!~te!9Ll~ zK6Y@!WodY0F$p2t$Iw*VLfTX~ULy!+BEm39lj9WUz$Vb7bCLn;zPUip1f}=6dq3|Y z71h9dVPOaNKcaDO^ZB$s9!nH^KYHC>&zF_zKczGm{%_MUc5g6nyz2hoe;+HczhV4b z{x-lIiFfUViI?{x==*&*n4si#|1ID}=p!+e`r(dfi{$nlKW9+JCz6c9Op4 z^=n^Wws<>wM}NpWzv*9YZ%6x=u;dj_M_a=fDan#rtSM|coxB}Ck;t78nfwnp< zfqm=ISBPW${n)!~?awMq+xzX2=3+n5N)3*C&p2OsR+5vO!b&{nFowEoX@CY3-{c8eibykZS)4>l*uv`}p z5X^Z?%*64I!7u^I)DT&WiH^TQfyjyQfICKM9hK`>t#e{fBG$})XDlONBq7WzTd@@2 z2bnNK3S?7V@&?&W9>(Q$R#X;rRCh4PE z=WYIBKc66%`xDH`{X8-5O#-z)DT_FSE(I`5(5WW8r z;AI{nqY&ZU+_H6LRbOUoaVlo>t`b83`!6EsTOZAViDbL}t-Gom-2HhlMq|BpJ9L2w z7t6i52!cpqPUYUhC$9+F4f3PZ;l#@R#Pe(g#)NxmfS^O?BkZ$OuljikPc$TdXpXZC zMbhO71dsMEtyUzhmvJ5>9gCiGS)*wnfS2Nlv=og(lUJo>|41c@N_Ra$FIR$Q{xfw{ zQ?i>?TB3d8Nj!4?#^&~h-Q?tY!IsCq=i57lCWDV=NN@-0=#DKQ1WwwZ{``EFT}2dJ zXN+jow54DGgF?yb840@OXxH07puANgP-WY7!?!(0F0Br z2!`)#HsSI3`)gj){*{-z__X!4fBE<+?&o;$MQe*o{a~~{L91I+rhG@$vRWD98T=lo zkm^Yek#rzF&_%{25}eJ^p_mALb1=UsCwyqHK91jJQEQxv+z+c9YrG` zEGJ+xff#$!r_#?yg6PjKJyXA%Q?Kxf02(Ye^F~LzMtj(+(7nG>$F%WC$lYgxX zu|Yc4+N^6m=?+GhCX-X>k6?nd$Vi{f^H640jErrsQjEztJdvg7StrBL*_`=OlmC*50eq~^5^G>ED51A>c*Bs7vwB>0@TV}4x zO+69g!U9WVoIFVXp`&G`1=-S~W)l}zmM2BZ9p_T*=0Ew&GCCP!C3xGB6`6mjKmWva ze%vC0J;n_ZK~F(yRy2a~!DJkaGny;e4;FOADuWU_DD)kRx?bhmjjS_Z;RAkA3iho= z=BBM28<0R6RC8Ufn)k50W4}fMa45_xh+7;>`T(QNut5ub2I}cL5NnnR1^K2u&YIWQ zSSr(gefb--)7(qM0#pgdK7aJ%DvkNg*V=_M(HrgSK}fxp$OJrFsPK2ikV+u zillx6WI)rC`qP>Kt}YR)qH~Q0!(!nWDSX^1+`K<4;c5^|rLbN!)A37(@z!x$9OGg+ zcoMoqaZH@z{^KH7x{MW6ZeRtnN_SA2I(K=&x7lOk`)sn$hpFn9p+ySS>3FWEEQLWE z+qZ6ZdonvxFmT_q!dJ1$8>kQ68^J_Lq8btCPQOx|bS;*JEzULksp_e~;Wl6Y(6QX7A}c*|0#{Ow|_ zni8KIhAOZtN{V(rSL_&k%e!}M(ldBv*f(aTn+PZ@bF?3L(_EIk4ANXwHclvZZMpY`-*!5)5ge3yoDeqbEtz~}n`L8- z|Ln0-5u}%-MOe12=P%o=79G?6U61zl_qrtsBVh+7d2y(EaWFA++sCgg9FuNyGTAp@ zlubrq|MP+;2`1agdU~(rVksoggxMaUH5spwe1Dt_!X>yQW*2zVI`>cs@a?`oX z1?rIjLbkI1ZL)mxCpUdU=iDi-JM1cvtD5(+@PY=~FWugG9x5WT1od|#W8$h4vU}`d z6iPiAui{+hCFrDDeMfzyD$Y+`B&x~@L~7W%(ZOQgu<4JADPYse#7Pm&ZtkFo>|doO zfT|)Iydfx?Eh`vQWEn1p>EHt7OcT&XMSSJlR-`t?x(1F%VxBHXdlRa>(kh;Zktm#% zanyY&MGr%bUMG&4ANKT&MiwU)TK;y=$$8qS9UG_8mznZ2d#S8Q<7pxvA2wid*Nr^7 zK<1qezkYe>q{0`sel7It(D1hneARRv4ZiPd#=Oq1d}a%6$nf!!Gg!yn7%>ouiIB0kjidpq#6>e zwAk8irY>sB*>iR`+iGvN{BL=Athco<0aDJG|ABlkbImY31W`HQ&^1e)yR;Nv);Mq013#^vPi(j0l})nYvFUBe$$s4Z%D8g#1{S{TJ8@@r*FzcWKOH-3A8&OaV_*_P~+`M{h9RLf%=Fgkhnw` z^i8>b*wqZIZwVJWR3;LdyrFEUIA}ZJxyCWv^t-3zTsv3Q8>vtxhT!Rj;E_^oeFdDV zh!rc@wA}=S4Yv|YaV}I%D#E=N*lpD81MDaIe~={#nR}C4X*+t@;%OHD{R3c|uuN?= zB2oAUt6VW=>J@v%Vz67=2#nWSY-7d*EZT-*eYurK{4Pt2zZ-`oU%4r{(V-)PdWS?5 z>o}DrmYoT9AlKR}q1Bt(~by@iQw7rlQxj@S=7Paj64-8xkAk4y|aO z%JO{v;Vk*pwgQy49FFkR;=mc411!+XzVTwMii@ps#yGKty;)2++p5IY zb9%H-G%)gSRy&A^Nalf`%+O#NN8bc=HA)bvrSlnLA7ulx^mv$tX+nunQDrw!a`x|B zr^#ZW6slVpDa-}Vwe@Q^JF2i9=;uFHlF*UUrTOR4;JYeflJXY-DFwBk_WpNyU&KuM z`B{a`6>u5~N1KTZYw!K`_I8hj6@%LLyQ-ZFchDR5)K+Y{R7Y8}v(j z^cE#*c}93%gm$PF!+=B%tE$>DVu$2b{ai{KMp#4x9+#T{9O8Lwxo5D@g2w6R(K>M! z&H{UDI4NGL`PSJctkqgByF}KZ;+kVOFDSiF>`vB<^w@=NS}s#Z%{}Q{y>kj8XAMbS zP@U$$vMT_bXP!*YBdE}UAJS7P{*H9(WtONY+hD`JEs6TM@r7r6AtAc(({ylvHH4il zA7^m%sFNyguIlC8agQwky&$4>wfB$i&qQ^?_}&6eA=T70o!|L$%>kRYg0-6CE1NAz zr{`o36_;Hc;7S^lYX4)1`3U1HWk#*x4V8V<7$++@5+f;up!W-HZE&SsI*Ejb|q(2Jl*4c)*92@T1` z$l&@qz1_bk-~Qm?;s4y9zyI4`yN3 z#myn{22V;G=Y+_@F&ZaYo~C)rchdq%ik}s`JBR%c?)m3F|6Tt7HRhTgVP!V3QE-XB zwSdlZVWSBF!XW{GujRncDNxJ-@99|7yY6DkHXrmk$@fz|j{5%{-U#`o9W@8xqhsYWVT@SZ5K zMFWIe?&N$dg4y-6=dbPVpV#=Sna~-2d-BEY`;eXc^}g=j;Mb2T#}%vRWm#b5i%xBo zz=Ga|slp~||Ves@m13xL;3urHD^n`z`zC5#oDq3E9 zPr3O=t53@evX?*v?5i%&X1BVpProVol(G~_Q*++;k6j?p-~UgIFRr{>>?t)YGV%*f zVd4d#3##o*shD3QU%!}cLJv${z=quyK^#5@G>FMt#8v%DKv|w7j6kG}Tnw~`!a647 zW--LUX+(-M2#q@c?0TGA4gNT7;`f^POt5Fa3bAjHL zuo{;GgPW%h?we}LmM5$(rLqzkF`z0;CDnR-bo5jl6KF-9n?ar7GdulKS{4LGl5?=% z1rd;1Pf1FJg0pLFr{b9a)Tj(7f$c>6XJXG-fr8 z>6&6gu(RiAUHrlqW2{?qiqqomtgT#+#akW^OIyg^(pPl#`oD~NPr`c!Xs)EdjW}Jl zNzMf8A7uM|dIHIb7$=jZEs?ItJPgnD^q3-t^LX_oqr=K+&;pcASrbKpTsU;Mt6yBz z(s^8;XEr4Mn)-B2;JjvVAATQ=6bs%I$PsKAV{Ut?Ro zO?H&Keb6O?lMJa!0CD6K?FYb{gbohlX5=gTxv(w_%9u72)IDyy`3imjhTk zR8$T6I)%7X(_RegZoWfO$bQPSQ?SD;k5lNaz&tkZ$7wK6f}E#>n3Urnl#UP?YJEWE3rP6mxU+VncSopkf{c~f`g4r zQdujRx*?WtR3K`7S3I8B^0{FW-1M>cZ_%z$;nKNuHRy)d^1?H5f{ue!@j1G;5W^L% zxDSvGm)LZWMIy1*V%%qr6OWiU$v3o1BL1)xPh^6_%%WBNXH_U}E-n*Z{=MirAf{D6 zMo;sy4=UIpDRb6MJAz~qrNeg`BCOdAPE#HLWaXv&NHf|a zjHM<*iLd2A&$rQF?)#sUQ@Pmj=VURN1MOIs4^c2bzcZzGIFH&Cp|ZBNloS_D<^vHRNwgiN7%mqIG72NuygvwNgR9!kzV zS#pL}!#nK|Jho9F?zl5oYw#V*vKXi3}73o_Um zL(p|%fZbwWnn#KD;FM*E>$YVYaQZTAz$azEtc63G z-;ywVsbE2Jem@@cP(yLDNJTXCdv9y+>-+X*_wMV?C%p&NA9CW1Dah*M!Y{9B!E&Lq zr9l#&yE!blrPG#)+&V*Iqwyk@Bkb_B4yw?Zo5&MS2x#v;Nf{R}<;=a2zj;lYio6-9 z)0oAP2V=Gg3-s7!1wWM=zo{Aw8WUm4)LoMXN|(5Uwy^ZWY_@REOe@s;yJ~lvu=2wG zHIiCo9#DEdUuG9IX}6^(4@xDJnBlzv1P%45xaqjS62EaX^CHUXJAjOi^{t11UBzI1 zb$YcHTUmXz7u}T?-6I`JhrouhHUPWnDk)j7zvz{%O$W7 z70{TA(ajfX=uT^*L>r`wbcgcO91cubVC-0vR>z)B68K3p{OJ5GS<6lGZn8im2M4X zkpLF0f)0rN`vdzoJRAj^aoQyrSkcWCSIH-M-Vh0gq}iQ#vIZ`G$))l}ozpPFl~?>T zJ&}lBuZnS_#_>6kE>miX&^?pZJOp~NdgW@jO(W~#77P^r`7{7tf1j2sUe5hNF{QqPSyYCFY!$c!_~cP|RhCD=>n{PRJM~BASfmMBvlZzA=5GMxj|&(&rh!Z2dmY18Cn?GQNfuPm#qrN?FUE zdoUXo>T;uWWS3dsgx6UaY-!#03k~$_>NEyS za$h=*;tm5?G~}+zKEV)gb@#HW)!OC;jpskAwVKZxC`OU4YN;>qRjxgiXl}{mYN*U< zs^``3>3$W}mad@(e8H${oIwW276+%Ma}85-Ov00}wqvR|PR|sA$y}3*3*UXeBCHQ@o*Xc^xg0q=`G0>@m3OqrNPUPphnk_#@UC*^aY1ra{KwqKeffPgp&hi^REC9iU+aV-Wp9-;F!8U8sk|*O@_)(x15Z>S z>c@{0cV~012)_Jzi|99`85fh}hH-XwZy#i}`e{7gh4#`oG)|9$vZFT`Uy*(yooGWW z%DQj%GQ}a+5EhY^V_lqx;q^4&L6g z2@70Es!%F}~aw zBmxrGXW49J#^kf~Gium6MmSy!*m%Sr5Kt^9RqjmhO50$j_1fF&PLrkK&Y(-4(c0W4 zi`^+svjc2WrGRVA8J z`6t7vp_K567dX~zJ;IcN#*o4l-CR`P{&w7ZjeJWfKVq37O^UV3NsDu!5N4owyPg5GAmcaAkZMjM*qUFgFHck27np+) zaq%WU7Naj0T1N5@`KstogTTPh;!CLLozA1X?ppd8uq|EZ8dT|@a{7Ld%CFy&v7>;N zvS)4#OwYHB%?i1|%&UXUR~AGw@@Nfk7POlWfk@;8?;hvmIpWxB_up!jMnJl$3*TVR zMpzYn-Dwp-{4P+P!9tJFB7IjmX$u!wMcU+bZJjTzvyTd2GUAT9&Y^DE+^)Iz<%LH) zx{mg&utIBzVqByD`(C{?Xd2KOJBpa3oXchG?2tykgt#gSu=86MR|Sjxq}Zte37^h8 zC9;FuHNr?9q7mIM$Awh>!+UOD7^hJqAi;C71R~dB#jX?)_|;ZqcjDHJL_|ick%x|EMry zrzYa+*q-e_HIqQ3JWEfH){#@8lwR~4^NH)A%S<&pJlFCgb?15OU#M4CgNE3NaeANT(SV z;!lNppD3b;3au2R5(>g7CPO?4ZROTS zNo+CsCS4VNJIq4XU3KMpLS9;waWeiB2pQ(Koh9$q5k55C9w08fi6s^=G_vHZ>^RQDB^=1rmYl~Ih>sRyz&GPkYkt%&b$ZFOQR)Xa zGD6ol^?c5-+Kug=z61L{IYckL`~x*En;Pkxis$7#EL8DbAVX15X0##CI2>`*Lk2AR z7$`)J+t3oqt+V2b$5#4!?vOo(l9wK9!JTqqnCz#8*!0ykL`Wn3PWQ_ByP!rZtnA_r=M?`ykdD=FIUN1Y3?2s4%F3Yj)-q4MIA& zj5N`Y>j*TC%i+S5kf|fu7xnnd&X!u%Q|s*6a?4LQq<&bhMs%e^fSCtx@Q6o18;s0{ zR;CNKZA&5VrDW#FDYh-W0S9#3)4Bn#RYA0uHT|E|K`m>+5|w+s+iRN^%FTuJl^Wbe zT^s9Y%wR2rbOkSwp3(y0P;3kV8T&hAJJW4vf8MBv5AQZ#;-wTJvEtp^U9IfqFVJYs zfsxm~T_b(*i-y}_m`{tNAe3!Pq+z+cyZJJ48#OV+D>q(Eo~mOsnIY%|q9 zb9OldF|n&v)yoWM_rBRw3XkV%YkJGRn8F5IEzJNe?!7~R-#y
e$!;kf}SU`^?6$zKT)9Vihk!x!~u?Y%fKItQ~7OKE=DVCS-N z^vI$q>=t4kNY(=W|#4F_!eYtVD1+3ta% zD5J(lThBfsIvf_2xYV{*%jwKBzy8H%tb>?f1{(qvLF8aK==Pfa-+=(Zp+=URB>QCF z{K!wkdFXG=3_n9mDW7odkjOG7-Go`HXyh+6OqP)##nP9=&S8J_AI(qm``lrFdE}oI z!%T#H^&z%*!wFRpcZ#$1P{K5>+0yV22*?LASDl846VAIso(V;>fH8IA#hVzNX|aiF z3!WDzVOjbQR|Pcw@xIrT8!EERmMz)ht~{u`=)Kdx;Z)YOhRyRB%f7I=$BhskDOJ1k zrCn4OEf?$gYSGqNq-{Gd+!}XRxwb!AExn7`R()G|ua~V(ixr%GOJAC^I;5;?XwVH^ z1)a_K*XBAZ%~i_{YLi;IEY{mkeQ4gB;IR+g4h;R1j_S)p!R_>{?PzHP3C~&$YBbi( z4SRz@2rzsr=sp^?#bJ+H4X*>&-FG8(?FZljal*0ic;a_g{0fgAh3)Ut*ZHk$w?vA~ zo}>##yU(#Y?GFOYWeaKV`+bA(ZNiT}ObbXk#31x?2cRXh%*_^d&Cz0=1tja!?96ws zYv~Xs{=YNo$dGWxEOyef-j}e{=jv*Ai<-4tOvL&gd@~X2 zWwO)b1!n;viRx!N7?*HQ)_kG33ZqZ}hC!cbs5{DT9D!4K!h14KrP2CXM18!B$*GOt zb~Xt~cQDH@x?}AHdgR-0CTofJ9jp7mbKg17qnAo+_A6=Z`(=3S(@yP4@CN0&-DjGhdJ8qAE1K!;CTM{4m6 z%Zq3sa>jX#{vPykrl0%m5H{cVjN(@pXWvUh_SOgds04I9^p zT|!$E+>Xz%SV6tHfcSCi4u?~M+$|%23H=4GjXO7c;#Vrd9&UrYOw|*&Xjd#%5}b&R zmFXp-J89|Z|FYzB3D{{K4~~&R$|LunkzYw(L`0|m!h?s8PlejJvfpsnp`=!CsZM9* zwP!Xo-WX0o^Cz}IXyZ-V13uDO&{>#|0EQI(-RrCnV3pI)X}8bZ4FThH#=KAfgqI#F zceiFRT)yB!D3taR;7#wyV;Jud1w0{V!mVT+AyFZXfdM8jAi)AXKMxxWT?cimHrYEx2-0pdwwYEQ_+NykIn3rYZld zQ2WUCNLj7rG?UGGQw*dDoBSsJ;e4vr;?-OI&>He#3X(CMB|Fb>rtA>=W5M!{+^{H3 zv00jzyLyLbOB0OwAB}iPbuFsIst%Mhf7Q!-S<5{W5;L|)om%pdNSBmo-t;)6fQx$@ z87!q0(2{CogMh!VM5g3>%oW$T4X_+N9EQG5ANNOxz-KX*XYJI!Tw(4B2_RefoPczO z*u0M){S4K9uZAxf`4%qQ2Hgx+=^-6Cb7*H>%_$Mz=~j2Q*Buv=90d2@7mKfuH40QY z3+DKO2onU^wx&^!L>Qy%reNb@{L;3ZAbQ$Qft+1OeGu? z@L6-gxJin?o_uSIVQlg(wbj0z+H0%z_U+>j>ip}cB zipAP#yuPsQ1lUSN9tu%_e{U({X7a;s?)!xM@{=M65f;o@uSrrI`;eOovrvn(at#6m z5RrjI0W7#NrZKbX2ILk=C8mA?rE8+}n3LS*3zC}6>mSBz&ro8Led*;?CTyz=q>yjQ0D5Es z;Bg(kTI2DO!6_H)(EV2Ul!}Dn*`%WYvMP zC`=ShE)+RPc0D!LeRfd@r8DKa<_4=Ad!;lp0Vjw`yW?*xSe*u@_8v z@s>I+$$JOVOi^600HT>8q)2ut9{d9k2xJBUXn+GWW)386f|Hhvl_7`@4=(YWBi#nU&1=#dz^i1(u{W{b|QoKV|D$Tcw^G3ZhhX{OJ=4DdiutzbXO2+7E zu2Z;w6mTz-YRs{B4s8SD?Yxaw9628l{&Ow1A%VN~9pS|s8TNOsm^FX7X#|q4> zP<(R`;lJH$U-K8w$^-)3pB@%$V#Q%+- zNw;=)pkG#*tzj{+Bw=I^h@lfpfzHAl65|NWw!?HG4 zNu4>&^m0jGl11Vdj2rTG5)tk6EK6Ub)&OIhFfW_&5L0B%Jd;0ZuBx_S%`$gO%)enn zDR*rE^I-zg&OBlQDi+@j9Zz5YF%A=o@1ivGXMy7J@cLZv$dtMTDKUYokj*^)+-vNb z`j-`5w#2K#*Sc2TzY<8YaQJ=GM-#xDlLktZR!nnA!!Wm>^YTm^T}IPJbYiN zka@di-Vp}KZX&_ z>~*%7)McFnGh4t-&?mA|?=wszv=1X`b!?ns_#}IsR>q}_@NhU+UEi1=% zDif9l8P+Y=4PdAD&b64@p#V(Xl3a59PhCfUmS7+211uOxOO)av&q#^6bo)4-eeao^ zuX%eYIx2wP-!fQDK3wa43CP}kob5?*c0Q5H-Kxt^sC4{j%(+$D5f<&W>N4Px$zYsz zLzzaoNP3Q7()0dV9~D2C7MwTZKy;N2QF9L>$x2-Lj3#~egy@?yrOc?oXV!0r3fqRQ z3L8S_F`)~tVp_$_-5i#ZG%nYp@_~OY)&(~4Q_!18`YbBCxsyOk*KGpWF3ZVG*e=g! z^Hl9Gs)E%S+Jt!;9$JVsGRRYlw1h%s+08{-9{>!5dKuJO3kbfFHF6m!a~UE7X(~b{ z*9J;*bHsla7r$>SOr=j~Gnnt-Yx#Xm$)agFogl2!9Gb&6+x$ePSyazNc(ryN1br@} z8LS-T5udasQp&-nz$R57cTK_~*su{d%O=5)%_~bIP!>AnM%6QpX!E| z5=+(Y^BOyg=t*7ji!@cvw%I|7aD8sW5ei_v9_*uWRsP&oSBvX7Jzg4^d)uq;$fdZ? z9vM7e;d)~t<*Y=AzaA{i((eO#4vX0G!UJlU#iWZ(Tb-r0t^nPwwScQUYt*x}L^TZi zy>kpJh4xYYj*IvRs0*U7#k`^q>9M~QyJ(f!IH3^h9A9Un&;Xs%)q25ZiyNE|Z1j>+ zOPF!$3=f>M6h-}Pv^iLX_O!Tyg<9ptZ7OBYvJgu4m(i<{r#gcH#tVBTsT^lWwJcR;UYo zgcCoe!t5HzF!T|XR1pPST|8@ZJ95#Z(u{4%;=L^+QA`6TlwWexuS2GH%dc<+CEbM+ zs#u+n9(5}#(S#Q3U*fPO<8B3JKm()={8tfd4=&ir6`p*-@^Wt9BXX&z70n&yn%2DM z>-}2&oT{Z*>qDZ*{tOIDiP=G&4Hv~ly(}%z9eb7WOUe{9(#~IMlr6Bt#MQnAWHvq_ zhTOn#$0)9|;#x1&q_E>`uw*kHqEN8Qoh;8pxC3X55WIeb8Hrj|T>pqJ9Npn~C(Y7U zZtqi!T;6I|Watm?QLNSwCr9_IW0J!=2DPL7=8YG%0tw!q2a_|RRy2HNZq@2$O>;|+ z1v^9a3>1^@w~yOqInBg;+5ZN!CppbiSetRv)a7IE&ITC9EtWB(HN9)B#D=dI1*q-M z5Cw*3{0$RQweP*M$;jffA^*n%z5sw9J8m67dSr2uz=a=6QZ<8Dzx8C&sPtm15%H!Dad<_8GQLOAaBQl_fELxzIN9>=CIh8wUWgB~>$lkZNGO6BQN0DoTw*E+;)t;F zt75*f^Y;d>ab4WpXOL%%KXZrWpuqjQ`d;5E@-@V=Y zeecchnF=uAa?K)poQ%7BObJ7Tg-k+_QxEzwpq=t4Xy#B*(Ub{xE>5j!_S1s)Ij_P` z&ta<3R$0mNZKt+L<79ej6E=1X&^xNcts%VyOy<09XEdyzY(=ArEvKyHoITrc%I_B1 z-ZMs=lpo15D5SK-Ccq?ZV7v~{F`Y~>SAe86?N~cKe)T{NW31J+l8gB-%K%>%N@p93 zbYaX#j!GUDDgKr0_Ds=FN3nu)L$}NpS)`p zH`ai;>Ap>^6Ds~|)-_K&(PmU@ps);~%`M96V^n-^m@GT0aPYlz17}Bbj`VMNo$%iw z^e~?vx_>24Wte&ny%zP}Lw1FUek4A>p?4A7l6hGZB z*-W+g{?0J#v*}fBF#5| z956aT0129h?)GQ(L<7s{naY`w%sbDPi`Kzomo=wGhc!C~6=w?@R#Y zaTo-M@VqCZ*t;@tO8o4CxaeMiv5!7VaNe;;8A^VQq30noN}fp7@o~uMa2P-PBK^o@ zC&@)S+4L9fVJ41{Dv+%dx3B!sDqdbvdN+X1U3L&B$nl_pWts6bb$m=d(a70R&5u#h z%vzY#$V{$rg_4Od3lt!CfJsrcLyYcGrMH@qKVij6H?JFQA>*2b9cx}QyQ810Bjf(g zLPy5P+OyQn+oX}7b}1|~1XJ9RR5G|67Q}WjdqK?N341|oZHV0NLcb%WeK_ONL;I zB~!IB`X_ae1yqbsBPOXpRhrB)#AT9(NSnoM20=@%%rXe3_IP;$!E6B&CDkRKY8#>Q zfmDAhr$q60Lk&*Uh_S+CZdUfvu+z{K%F7rD>X#!%b`X0&|!frMC# zk!{dp8}!%)J+?uQe2a3AH=&>h$YoP31E2%Ubu?Rr^*>2jcmI$;38Ep zD+7uL+E_qT8TB`Hksv5+P8{R{P8&gmGU8nP!opOR7BK)4a-ln^eW?%4W^BwlLz$kc z&nRGJ_tKi|P2GlhQc39J%zEhtn(W)8&jq<8v*oipSnt}Yeh1S?nPY#I`U(ri8fgH^ zwnv#FprYKvsIqtVTI;gcn^5f68JlhpbXB^gRx~26zJd#XhdLD#H)C<>t_?Jvaq(Qd zp6G@j6uk@CS(JIM0udXUCM`>XR>+kCpx$Gu_gF@^NkqVq$54N0>k_9?%5N#gZTx z0Lc1;g^q(58-|`Yj|Uo0bQjFV*cr=8V#R=m?+}2%rQyIsub7XBEBEDL`L=_pmEvPj z5f6tb5QOyj;zTU{TuAEYkg2q_Ozwt0Ns2K%B2ZwME*OMdH{*GL@-Yh3Iw6h%rWR0i z|ANslaL{iqVjL5xHS6{Xp~OL5Re^N@;?CAfD?>dn_NWDHnkp|Aib`2;wk>gF1gSs_ zi4zI|L*DR}Dl-t9!qIJLOe+X(+}akn;EF~D6Xb%y96%5Y{tyTx;D8tEAVi>BZtc+H z*b#4E`Op#6Eb2u})=N!}Wz60&WaF)@%(j~4Z>u1xM1?6@R1OQX1&l&KNRqZXEO^C6 zqOnM*me%rHnvQOL7SKs11U8izP!#+uvtuiWdJfqoo?tF77oS6RhR`welb6>4n&5Dv zA6!Xp**VzhcCIy1==_Ix+yCtyjUDnoWAyKT{Y&89vg^r%f$yTxG}y!Y4rOS@$8$D% zeSdfF|2-tTv!V0Po5^%{JfU}dHgNCn;Qc<6$nc7dZXCHtr}vmEP`cgi+(?#IK7ez` zKFMI!?|#T1$CSwit`sF+-8#e_$>}+Q44ol#q{bzle4L`-lyR)m{ij~2TWE+t>3fho zy2a7t-=JYgqLyWx3iTB##>c5-Q<#$504i*6w}knYI2HJ=m@fV#j4VuVw&VZQ1{%X) zl!S=yg#!VH^zViZHoemxHZgnYft17(FYcJ1Fo^V2nudhWjzpRd%9?gYW`k2VoEiwH z-Hu0{w!;-NU+HkqLgK_Tntyp|c=5k1q)p!Z%M9m*o0c=Y#x*rvkg4eir)EPmnG{Vd zn=GUxo=sIKDe~E(swTrGbCagaO?!$+xJf^nXBb`l)9j#Sat6V>ntq99V~wjtJhR>A zO1M#j{gf~+!{z;CoP8r=gRLgM>;n^amk`0ip(?AGf?kajmMIw3;@Ctp)$|m^*sz(x zGYjP!cN?R=#wkZiux0bmn+jw$t(U^1%Liez#Mlz!RT5(@X*WE@n@z+w)WY(*&Lh2t zVBm}eb!lZ}iK|!HS#ma&D>k~M^e*&`EGp^N7kJ6UMFEq4>D0`LVp&^Dw7NPSj|R&u zqz=Nqy6q#1X#>P;i~Kw4Q$>?leBU$mq22?X(!#L)taExwY^ z!7mPWe^Dy#ZmQ86JqNN;v6fJk?-Qj0jC|!# zBqT=<3gg{+G?PeiN-8ZP@~f>)WZi5cR=vND>izg!RfKm=y*5(quUe<9X;pjerYd-X z93`eHo7~FQV^!9+Zs)!BU2hYk>3!_?z8Ffimh4h1v5}xKyH%R$M5A4`{KbP!fT4A_ z3}W~uCCIkHK=Q3D0HtvN5P1Pg1{_-wrzZUx_yFZ=NtD3A06KRd^aXzL#0wQyk!1jE z7Ta1lYvHVgvlh-idpNr=U{FHnAMTEY0BUWoSy()7@wmm~7LPv)9R9=?EW(${awa(DBe}(sB+B|J47_4_%Lp091AY`bVqXSOC2=TaLpP=NOWQLa2SyuOqSY2J< zt);?leJ{^*mrjGX@~n5c{C4T+T@lAEro2l>T#~mR%0e;87rF3L@>xkRt<3XWgsjeX zr%l*P8-lE|2^)fJy+e>~0)DK!@}JMLxCDZX8+m99c}bn7T8H%58bQdoA{b)gvN)64 zZ+`qjT1K)U@>!>wwOH(VVzKC0)&sFrDH9Zy&DNVq(Azu_Z-eZ$jq=WJDC2lr@5x-2 zPP#qwNH_EV45Ty%b~|N6eyGj(NxE`IF%iEp(@a?AVwuZkGnYk2)-2C`bHQI0J+Nu- zb7}9pop?@9kR&o%R}eog4%q80 zAly19>CackI8a&A$*33lzBiBHU^}E+(_o`m*jt5WjL>U+_X{rk)l^Q>#u61(@Lu2l zjy!ywq$Xgps(2Gkc3BwhtvG(wtmnrA+YI8TLL)H*1Y6&aiidN&4qe7!aQPyrj-u`gN+~ zHV7o?pegOn_uZm?^eru_>Pc!*Gn#J8GX&45bL5LS?Xiau;o?!)GV@qjOUs5t7qYXq zH_;gi%(zDRI1Db_l-0~=JY2{jaA0zEMX`YBYx*Siy}%y8l9vSXas#0N5gyE=D=Be9 z91c7bHPtQN994((=QAJl**Xi7T*30cw68A>&_w8oKVy` zC+OWDtN=#^KOI2@EwEEyQIkbYYfKF-YFa5Vd{<|1{{k^@9XL!^Hi^&>V#43-w~Ne> zbYno{bm4RyhU%aTGV?T{+R45PFjr|=7zhc+o}ygQm3J5fc#2pmf&wHi7$FryAe*Jj zp=5|%L>vTM?Rq2WKS@D|uMS!%7}n@~Cnnn-k|)pr0eyLgYwQUYsICt@1)CBHLz-gF~vI0&ooA zVVpl*z{Uv-1S5t*l5nFG<2pieH?nf7l;Z({jFQzEAuXO+v&09wf&ARYj}5GZnbfAO zvDRACiPzgzo>b9k^C>39q|nBcLRL5SmXf4mA*G1gG@uo-sH6)lq7+Qf2Pb}s+%A|P zNW@A@*;0@TN`B!0O;I3@(H(#@I0wUkP5|cC?f++Z0(=O);;k6{FhyfyRPK2jay+a2 zm*&K|fT*%QYjwh&0<u#{JWhmG`K@S_OA9a zDb`ba;kgF&dA(Dkcj$$K3Fb$F^S2{jN&PC|@~Z;Xvdk458334UqxB#rRTemqfB^!b z;M$x5CSL&v2%33w-~>oo5tVL2*`&7mgdjc>0tFujG#rhUF^W)kbs^+)6JSW#-XrL$ zr~@)NcjqATkM4GEv2&*m*XNrv9Xz3^?-fMFLyj0k9AM!KCdV`+T!u4QQ27W!hmupm z19@T;&rBSUj1D&{A*uWgrF?Ztan-P&ZR@c04c!ez< zUG@$~qX3Nr!#nuixs~K+x7#^^99FE$EuJ98;lx+5TjaLt;gMVr8%ba0IcnO5X!C7$WL$@5&NMqf%@U%Sra{_h(#3kcry-gg>m@{^gP6mpa+T5+`ycvZ5RsY zC3vAN7I<_)_@d*R@1pwTW=dvE&;$KU+nzJ=ZK7zgk(qjopc}`T9j}y|uU*)hvLlpU zn+jSKzqD~zs!m&lO;l~s6EuXO$EzZKnH{=pBjpYr4I|N2G+cMuvV}_n&n1GFs|rWA z6KAr8<^4Wv1hv&}-u((SLbT!@YT=5Mu58RGY9+l~i;MVzDhOQpj|wfWH>h;;ZMz|# zJiULe63u>vCPVF((|ex=&1IQ%Wpg;BJvjbAbCbkTxjq){dsQ$)nf(AyAxGd2$&1CQ zqp$>Je)0z)y9!lqHpy!fFPmGPK*eJ+t&N;^A;XpVPBe3MNH?~u)wjZyu~}U0b%h2) zos##kEe?ARO=Ex>>MPRCE;`)rA*;|%WMv%uypOh33sjzpTD8E%WC<>)Dlc%`)a10S ztP=OJ=%A>AAV3b10bwABxoxDhcD&G)Z&K`0-zb%p8IYM2b+nOHpCij&Z0zixPYr6- z7MGZ{r3IpDxr*ZJw^j#VH&}P%MG@d(#+i(TlTt~brMO|)Ml5_qVV=rLEZ&Sl-1^py z{6fAOc|xVOWQuDkH;awfrEhBW6Ccp59`YPX=HY@|_&{0T_6!3m9_z-PdpKc2a-7@{l`1L7((O zU$V#{4FCxzD8LT%qUt-~Rl4>rcm<9wdjeJ_J7vWNFFo02G+S`MRhoG%V_ET$tyQ-4 zVm)%plbbZKO4VYf)kViPDXcF%ml&Gl|74;BU6PU&<`i7JJ!!R#$pNU!e6KeT9+9?N zACE|r93)@A4dz_1uwR{E?sK};OJWC_FfrXY*J`AFF212H;{ugtNZ0cdC z4Zx=s#cYrT$!?W=udpeKa%xElb5XVK?pVXD-H&?TWwW?(Rg&W5*haUy+tVc7LKD)Y zRUo=%Viyt3Ay+>(r&7rXOo>$#SvF6zqVTMiYL%(1lD%R=2ei|ux*2*))Ax?g0y;_m z{y>R?RKd?OAeXxBdk&e5AuTT%pF?(r&@uFrm)8NB;BcZJT**+(odb0*SS~?X>`I-# zJ?HH<@Le>T277qlp$yIVc+N(z@9*yYzlUUZHgx`ZGnwv=C-jcb2JZbGyx(V?A5D}0 z%CByE$0_yQEvoAIGYkzGPiWJgqPaKeM=!R~Z5_RiZHYf^{v~?oJrJ;A<^>pd_>wIxg|k3 z(Xzlk_j<$I0J05Xg(8KG+lzINOWqxr(*HqV&91EQ_CqhcB6;J%2kwsxLkILajDi?~ zfM$`v84uBg9peRl7{&8_`ryWuE{&V^@a*=I&)YoUlY&RDPz5MQBbrzmr0)?1iJFQ6 zARcW4oDl&g+`VW(8UxG+nu-Gu41$2bcqW2-_g)SH4+PlS#|Ien^I*j%1Omq|=fM{| znx{Ab*b%_))As`_TD=X9>o3ol2pc!Xn9&JNGGu-~akg0>s9u0NK4oD=cnD?XS4`Vx zB+h_Epk#jJ=LSmyhDabh!<88X7$>l@wz0PHzI;6zcz)>_nfCfD-8DR`g4)eQ|01>?R`Dv#ou_ zL$vyNz0f;||4q;H{k_!XW+y%Sw5ZUx>o*Ha>uc!2bm&Dh!S!aH_PUjhk6p$Sl)}%% zL5lrIi#lH5kJ-7JI4mhG8wE|Y{W%{UELQ_oT>APs&2{oQzmq+JoP4i#BSn+N%O<)g zmmQnEra(a~lI6R1ayv&cUI+?#hJA@SR(xf(M-O z5(2^9j}Y_?^X@yE*$;zoe_)3asdY*xAT{JJ6aqN=gtm|I@=IJ{=}V7$G7wr<@}&dG zWd^;9#TGI7HEVb6-0vubMO9N?V4 zl19$ozTVV!eme{_i7|H-cp-oafO4q7HBlkS&*ZOzE&aGYaTzS<5kBB!7_bTDsNsH< zB4ZOs@d?d~UE{)*ztWrGkCi-;5AqlgJQ@#_1?*jc}R_FP@-D`kJMMh$Sm%UERET_Pm{f14TVVR0V=^5-9s*7N6RR72U0z6ac z4!CN;@&eU7Tj)zQD(UnR&OzpqE?Tl#iAW+AMvHYNu7p(&nO(=u;9@uznQ?@fx>Z2o zPJ`5T7thVsjcH!`>e}9DibFv9+ZI=a-fBR=H~?>ZbkUT6!HAIX2QL9cEf7n0D4w|&e4#11>8d8h$9D^l z#z#K!%TegE7UE4p{FJt})Dx_21* z`0E#Fcz1sB29f2}1yVY?x@w9i)Bkx`S^2$qNNM*1cokR~c{tdZ*eGac_j7;ex_&S( zdbfYrT$O71@=<6-p!s?CwK#ttZ*%f-b#U?K!N+5tWVMCOd+eQ;9`I2CPe0D*CyF7c z!$HCmp(^8@qsUlQo{=w9IAIY|LC9he66jajrx^L5I`+pl0uHX;_Q!^j#Dw!~Bu^GS zyG7KcG%gq431U!2dG;Pz6X;cOj21oPpllF-m(s9Gn-`CGylII1B+C`$-t3e19XhZq z5c!^+MS)fne`WJ2-SB+?@W$m#%G*_GTV8ucUbzA(S|TT);bL9FymMM%gMu*Z@a7@C zT$dod7`4k1TSxqTSO#8F(+ag$sy*x8#wy1=z9hT6rk}8gH*W?#sjC^#jN*>rV&_P; zN^2`Ca%{yulchaAt3WO0qDxxZ+p@5c!nV_N@#M4;MoPU8{s|>V%m?jTQX#v4e_ft1 z5H5^NlYy`+51Kvk>0=_I{aQ`be_@q{m|Xv>a}(39NW?t)bzv9mZ7cxHm|W2=VqxcG zPUnjO(D;Hss;Nc6Nr}bc#WU1Y)Hw6X4*kQ-YtrSeMwGn+DG>v&x2Lxq?iVxux-s3}VeI;Kj)M0g*|MB_-Ta{*)y(*^k;yA|n!Bl_G zSEboms8EhvRb+65GG>b9J` zCYVED1U(ul@Pr6^K1dIeGfIg#7eF~B&y3PndUeItJCJI94RP=5m^?81FmKU^)wCnk zoFu3NUDX@?^}c&&aFgxXx&aK_!v`g>&?RtzqH1dgx$`_^j0rYzKb$ zM?|^`>-2~c`&{@5Y^AscadPzhRd7v|DI64@6`z2BAH4jDy#2p0VG5eS_%E-=hrf|{ zk%8XIZohhbeZSov&ljhvwLh~;OurG6l8X;$n4mrH(08M%++S!1dcVeaWwCAqFK`UL zx4mC4qYLD|`sY;;qX}omy+yDvu5Utg4V{jUmu_T)(C@y#935nxhrGJCS8tq7uA*PU zErZtBJG-)L)vWEp-M&5CQT!CyP1daDk@gK-BxMKs#fWf|-H3vo@*Z!{fh`hir-Sw< z==8J%_vcCYGyk^iMW{Ai}CwWaV})3^*YMilha#EWRCb4X|d} zt`i*UrctM*R^n9X71wwlRXW^SwI0O--KILX#K?y6guT0g0{eXXD$biusxbNVn}$Qx zHv33FSNn46Lf(7XqpU1N0yJa41D47J?z~#gbVCb#vd`Ky=QEsnw-PH^z@?PGybh8 zZR91)>|dZF!o}X~6JA{ykfa?ux}cqCpw#i|+c!j40STv|CBSiQNiwD$dL8YaTuXRF zKm0#*1|QdcL#geCi8l}Ai%5e^GKx21mx}n+(k+XwajvlE6_+!wffx#yY6xRR{p@@a zslYoR8G; z_o8#UE6VbVDvRl&bF<^pIzvg18$vyEIs+d~otXu)CZ;{+3hKIqXRasGB@NMuCw8P) zU(O3Gr*~wEb6Zfu`g@gShg&xqWgA1l2@h1l;YhkNZs12@sbyj=Sysbr>IInEfYIj` zNO8KEOJJ&EPNy~{4b2yJJ}UuD!|X?c$VsDpf`#M|CVoWyNHL}K)kmIKdq}J|E4WJj zOIaCrqGJuSOpSfD(h0}Oeg%x5EXk*Jw$`FDYV6SWBcuDd4K*mdvYhpE1L*^hcqc$m zQ=l(weu9|h(i>_0b_#nByz~3HyyiGtvVvptvJ&(tb{P*o$r)mkmE+uy>pqCA2tatT zBESqn2e9c02Qfh1_BhCDaO87VZ7!6J8u-+L))Ns|Tbx*c_fWsq298;VgvEo~*D0xu z(%hMxWF3vCm_qxjq|RX@oVuuW491#utX!95;BH0?L5!T4?Ko*^E^yBALqfFeTJ1AUxDi|4|hz&W+HXw#A4Jn; ztt_psnhcLN=PWs_y1&&k$x~S<2l;ytP;S%s`-MCtU>>+uO1 zfa3xi}O_H8#-|v}%~l z<>?jRj)3d#@UL)~}p|y_{_A z8$P44=jYGdp1wYxpT80EuGBV(l=p{gV%a-X`AW8zO)Kev9^oK^XQ*JJ5Gki+Fy#}? zN}`o&T4qq|Fo;+USL^bBDwM8_d0Z%#CC(85lcLIppvT+%XeFTuM0xT+&hDwBf0TTG z0^@U6C#Pn=wczBlAJnaB+x^hc-OuEx4O15rtqpo`)D5J^wcuuBh@l&SKZ9vdhdvWn zDMe=PcgbDC$1u{ebKJ6<XN#e!FBQ zq!=jJc&XIeFdkZ`Ptbionf}$Ywq7wolnqlq-f1LjZRp@Gw-z;(-h4E((qfV{z=*gP z70|&U(RP*C5EDri6~qfZ;CAg;F2msHA5sms>X@%?>xix$m>q8G{EdOn^u~^cZzM8G zpE-G>I*KSA&7$mviGAeQAq4ObO_M`gw)gvBHyw0x!|uuh@t-62(cwr9Y{vJCzNpI0 zeP2kk+>(T~%E~FvSQ}{vg2P@-sGsQ5yQOc}Tf2-a$T#e9RK5mMSDN)D~Y=42w| zBbm6x$5S#U?(}P>}{&6e0?iH#lq#K4CRq1B*Nx_*;p^c=T@ftKc64 z1&93Z7LFFVXxk)mZIEE)YS@1BI`g*wQ@mq9ZQog*9+$P|QV`R1RUs6`VR%oP(u1h- zd0Jk#vr#3zYc>OgwA$vKs^+xk(<4dt>a}WT!{fg5!$c7NURM>9wlm%U{p8;n^Yq=l%93it3>$H zgS|}sLQ){Dcqgu|^^n(n+r2iv&msAon38%KQY2rUh2wq7R2s0cbNx!QpWTs+=}GY0 z=r!%bdw5Wlw+Cv#F%>F9<%WDNX4Mf?-+R-&v%P-v3X8EIzfrfrpP8AdS-bRG!^--) z^!u}^xW13<64q+SH5iH{1_AX{nuS0`hpa>+Nd_0?bBlN%huGbKlNo2Y{w{hOI)p>f zNU`ME4Ng>(K#j}Sv$f22m|i}IUsOh(`WuX!EM%?JpKf;+`=BC9 zXt`D+J?Vbei2M2T#v_!&=L03OwXb%wKQ?`}#l0dFk7jo~HaOdoS%z=Zbj_Ipwc%np zdl-GT5R!95=YZ9mLQ-FLFo^-`6j4?^rWElsY2_s?y{74$irUzLI_QqNfP2nz@g#~s zdN6$>c_%n_9g(TF>>?b_-HarYNgqG0g5zhWDO zm%h1uVHwvG{GI)o!*gYHITiV0xtMzBjnX#jbUf}6DL?6D`&1ZNK6%K{% zXK-NqijQ6Nr?&HW=<}c><}F_JyQ4(6Y0(up*RR%Z;-Q%ejRay%_@biR67Iyr$Jse-^gLm_xcLkh=| zWY2P1E-B|KoFm#XxY3U54-h57X+HMv;;_4#O;#woJTAwO8=cf6f=&3a>d?ZZhB+04 zvn-`+x-K`xFuR;;*Tn&P8tLQffx|vtLK^J3C**La4!MLeg|}^4_WY*sm|B9_1)&j_ zPEZsxUtuklb_??R)+sBFzL{1xtZzyGdTXTG6N%eBBU(~FJwtv#?NhC{k6|beWAvM9LkJ_ zSz=Yn5I1kQ+sWAo=r7Fwg)9?@nwgps+fm16Q?ry$tKb_1mC9RD$s)|G3YD1cx6M`a z!Je)|P$nx?P1)yAYU?r&mA7s&N9@r)9;s#$w(|lr1M8$0Zn22QO`0`aJ7WwGzWL|w zI|J(n77n)Y_ZTY`W5xH;52svOa^KQu%D#}HdGpR^3Jd6@DRCmD1;wJ1g^Js))rfTk zEPeuc=HzVf$aLR3#>@8V&RQzjqr}>_n9(7m%TlX%Pxj9xl|14snXyx%Qlefn+JFrO z(&D@<2l`srde*@#ko*fx>`owh*z4)0M}k$2Vv6hw$2tHLvjcGL#|niLO0J|R(C63I z)~}wcN_~3a9@5>!B*Q0*b50|Hwv>fNWY2&kEj>TzH(@2BPpcw<3&{y*ou(U7 z7ppj|!PJ0@@z(|>PwTC9&)ZI@YxN3ZSuyo>c& zoTD6Z9VZ{sG@Y~LOn>EE8)5TYk%F)J1xg%n1nMR`6Ku->L5!e)WWV?PIJChUoE6@oIUszk36} z2X}e3zy6Jthrh1%zTdF;4v`onQ02PLN6r(sKtd{ znfO3l=pYTFTdoE*B6>=hf@99x#roK(+{fU!Q@a*TpQUklj{c=`c}PL9`k#yH7Wt?{ zlj_Gg;k)x!ByRb+*n9UF>7Jow7SN9{3B?)!>ABcZ1mHwL ze&B=0{$E=lY6G5wF-&*eCF*Tn17}R1pTHzJ^Toa@;G%&CievvE)1PGCXJm)QneoX# zAth`XBXOGY)XB{KVFaGtzj1nxy{h0yv0t+w-=Dg_7tfx_>&PL4bE1cuErG7w6Z6?= zQ*Uq2-_zc2t6^5V(Otc}QnsyRiXKEaAwScDM5@(s{Au(z54P zG&~qzS-#VTTNd3foxgT63GphaS%yFw*6HudXwS>Fg#NL56PpVYI-YAk+2@~20Lylg zFCTc6r$Mj=?@kb4-*p9@yet(XWARt>4-z2wo4as5BiGZD)q5Es^oQRcXD3;gQQw}O z^?TQ|o0zvqtI$ohuHM`_Ot@bpS56BPvdNyiV|om_nHBMqCg zwv)on49@I#0UIu1i<_`+qL-MpTGV=7dm-IJlouUPgZ0;MV06Cr2kU;XdVdxI`pP6p zb%bjYH~n)R9r}p(ylyq|n9xf~6h1j?W(8q@ClKwO>KA2|G%n}onF+BsWv{Lotk-nT z!=Jse0!rz&Ru;=HFy?y>;R8hbAscyj)phwBsXr^M*Ba*Ul^x}-pVY}Bk&0Q&~+-I^`qh3mNYL`%W8Sd#~%U1L#B5%MB6cy zzhW8L7=hH?F_mSU0S|Dngotv@_t48RgJzuF%M!6G2zH`K*c|N7x$h#Ig%BV-piFWDHx%otTvjheg;&jf5wi~H8y0lQ?|Qy zJ4be+W1{ZgAC3a#IVu{!D$9sIj$e1sq+rd3nws5Zs)0ecC6cP+M(^#ToVnh!;bapb zd3rmyQ?n7IM$n!l(tMQoCS1UdzsNg^i=u(IoEYkK7Q)ojy{#LTTA)ynXf+~frBj;@ zS4*&pHo;5(bqdA{`y92vr_OWpm-wlQZc=$CM6sfb3FHdZ;8ub{9U+zx80wo56uItP z5=zW))Q*`+>$0o~NtS&FM&kwGjQ$-tHK^*Nsi!DthV2}!ZBw3D$p7$8;$;V+XdJZ# zWE>f|{X9+=Hd4P`gi4Q|0sv9EV*K|z=>EMBdTyudNhlzDN00!PcttrBNI#e8uRkz4c3NwI}K=Vp(l~aoo25 zq8!q@q@aAD<_3N^`o?;w&2$2hYnev@2nK@Y?Fv=8wiE=TN$567VBLiD8|cDZW^lI- zg?amCZ%64TwxuKD7WM$3Y#k8p#Xa*JG(MZLpLximn$mEXQne+I?v*Rm)8bse?0r=1bCS zP<$;${TKJA+Q-$DE5{Ov9@4bl>)+}Rt6~~gf*Q^xU~}Ec8!cM2fLm}#U6Tx@$wLQ^ zWC8xHXeUnr=E(}xg+Clx~5jmol{{`cE|7C|jh$mw* z$yg@`Zn#=tEBXSf93*5FF}WQ|(MH2BzgF3;IRk*xt)#g+1ihrG7GoPUTJ7SXn0511XkqAl8nI?3 zL4#fb9l73fTZ&7r`es{Xx5&wr^Yqfv;>s{PbDr()6g~jpai-@IFh_!%HgSr@`Mm=g z3Bggsba#$(e%-w;^&?5JKB_q+Mfm3#vcW`BTUuLjw~|Z7G1vT(mu{6@_e}vyOqJde z1(=4)Ke<=HCOF4Z?4o*`wAW9OnCat2!b{2eGprp;xcYNg8`5)I;G5=m2a z2CpgNMr7F?LX|ap!vb$=ReEIjPc&F(wxnYFjao`}H5wgyiT5;nafu!fib7XqKYx^G zs%u%+YHec!+jE9uE&KBZl0k&DO3Dpvm2^)fvr7_-3NmM^@^SS?nomWxxpT|`Pav`i zXn-D)*}3T{O%QKv4V=Jp{-fK>(OgJROc)Tncjs#DH*B zbWt)@Y2z5f+0E4XK1T9yca>P&G^PNVBS-q;yq;)a_S8FwP&%u0j<}?qY3qZLZo*G< zFtF#2t2b!Z3eKXF)FQp~DAT`5ghpwZHfD$UgIAY;|5kjqUUZG+xlZv@W1*kGy@@Nm zYGsk;AMYxfOg2q^c)gm0j?`KVIWW5W05Kb7zwn1NC7GoERFoO4OaJbAOlE15T{Y>I zm6s72RaNm*#FM^cU{zTht?E+AwOVPP1=UFH+zyL2p5(^n60~run_uJ1?7~Q~2%s1c z+1t;Sh2p=s!TNs83Ra%E6C-zLG}7&oig4cM6Wegp@NFEqsxhOVJ-v7#Q9CYn zWaG-ZOpu;lj+9%~Zr>dT>sXGO8yfMN*fuOKy;rI)M4QzFCG8g*zK1H}YXpL=X(C){ zfbLJ_Nt>_Uy8X&29JyJL_HT*L}xOWRq8+Q+aTeP3#z+M?8$=RvEk(d zfVp$zcm43<_pt|{F!0f*!B+mCV_#_ zz(KK*-2~+FW@IWcc7spIwCnxKd&tqrc{i^^02c0%3HXRUS(gn|W{>@jbXaXHsam#1 zvc3Q@dNBk-9yv#D&_5+&Tc+l4BAuun&g;fnomu3@u~F;cnpk0p73{;wT8x5|BYwbe z*NWDClcBQd1tW`R_n{%cAVYJq&?us40vSoaQ8rY94Q|RY9BY0UkBwyc&pd&!r|XeM zWl{!YGN@)UdUSRZUF#&w_2LU+O&a%+ zP&3xmXEPq{!<&k!{5(@b6FLJ+d5@FFn;P=B78mbIRp#{t^20}749k*7=}L!G6?Qdx zFKJB!{sE`L6;t|G9;P_&s{81%E=}X+*9H@|+E0kLv>Ht>orUWbwHM>BY(>HCWSzYscauyNG`ov<$~Zj#yzg zWh1_bJ5x+QWI)swwt8LK7Repxp(auYy~b|wtXQ-0Z0vu1jw2xNz<6|8=CDfDtFyj) z)+`R4{J$z=l;D~4WsgR)jHD(KeCwwowg!spS++r&1imwn-!F*7OHxZtp%yv16bfI; z@+GM^n%y862m!Lp75FQx{4wglMaRE35xvr#53%OgAM4d$OjDTZ2HzUghhQg6v zOuw?DPk)cmMdZiOCnz+;s65e@r}%>0-DjhcgjUTKzG-2%#3fwds4=VcJPfh1YRlon zJ$iVlL~Sj;@o>&_s?gL?l#AibCcE$yb5Bi}{s&qkVG{t4#J#c#5(B7^Tx?tJtH;W` zgwY)y3P@rk3}NMUow!tRqx_<->O^_*MN6L6&{DNeF~cUMi}yw8cY&)m#SGLn~!* zZ^Y<~hV+oqHsFi=tm>YEsWHt9S&(DV>`zW2UO=*+Qd!C&U+V|XUA!OLp2lM$T0$1x z;n^A&hsxUQaWZXpYxuiIiFHPOg^FoVdqSLWX5C9t^}p1l2)a;ogvJ85HELets$=8+NDe&=hQL| zG+MZJO8GBvLM9dRCl&A0OGL2Lw|lybk^FR1u0aISus0=81S)Vehe)SGp>uq%_Cx|1 z8tCvoZm4HUfF~wbHL<7;et@XPx4pcqXV4=3s=sJ~`*A&FB~2&oZL-=TC&RM21@Mcm^{lGLzYup#+dUz_4Pamr%e2W?-N_V53e zi?nwWqs!sb4(-#fdFbN?Xu4y>3sHvs#!h_AZc+{^ne&d=6;^y=(xSTTZ&jDEUQH=k zMhFrP@~i|>iNZ_7d_(C0JOW4}LwN8QQ%bxV^35)b@ue+cD%L2j7S%$|EJal&70fO<^Xu>vVJ>@jYCL^bnv2_IQWHw%6Z&@ z8rYMx*Ih&k@z33Ua|+0}+?ZmzBMs3YJRnRoZ9EY2KL}gf!XNRLT3d4~TU#2RRvK=9 zEzR^R6l1NW3!82FmQHReTXwxE-!SLh*IIUw|Ajd2m$GTG?10w5Yi;)9(O>t%XDm9o zbiSNG%1Md4W=lKNiwR_hZTapP!L1Sd`bQ(*i?h{I%B^Tq)h??4M01`GQru@D)-KN1 z(tDHxyF_r{ekvMy9#z}Gq1jzvV8~Mf<%iPqPR%`o&=(CXBmC`yh6EUgc5~uT&eRg^ zlYF4HsiKs2*EoAN-}1>0tv}YW5n3tX5uSOu+iRQV&&7lA{yVsxv^G}Jn2u5kAq`gIIK>IX zrrsC;RQ5KcJ=1M&yWgmT1M9L=;-L^OrsCQAy-{@a<99=|ClzpF(@9g^TlRGKoEao1 za_ERcNQ+7!^|?YQWFz)S0!fBhwPrS1z;ABJQrW@{z|(^UJyWf6Hi?wfe0u=_ zwZ@Lxaq29^$d2sT zB4^MjQvDIalc(yKp_mq3P^n!MN9{feE>anwXr;Qg*`Zb?`1Z;B{F&R}1_z8H#x83* z5KQh((6#e+C_M6ed#^6sBkzLAIuOt@YKNR%A85?Z$0mG~kbBqVSlQ^rz2f6MyiB^^-ks zXv0lN#P?n%X_SHu8&X+PN0%{b{=?W+O;rsgpqbLNa8?p#Bz?ZA)!aVzS+Pe~KSXX4fC+EVeBC+mpXSL!pB;6ZsP* zDbehSUkrj0fh^>WOjO`(ID1Zhi-0o!H+j4VIGr$JU@*t^5xNOQ5sk9V`~q%tyI_R= zE2UIDUtnP(U-YIB$Ztu7?s`Gf&iQqEmpkYS`^*0IN_?-2(((=uYHVeQYoaAA<(iOF z4e9W&)Q@QA6_leYJ5-Qvn)S~;l8<2o9_m4CccERjXi=~kKD1B8!gN@#4XAh%2kWVI zmMvdvS+@DR3~CKv49)wB-D1_3^1ZVh#6TawKSy#TU*aHI`qPp(pRXBegVW(1bLO&r zXxDA+Fu=Uj@R@xh1^jZ;rrWEWsd5ciaz5*RLRj2iBOix?vYq{G#(PjuVOSm0%(3!O zq<+}}UbD8|WS7(%5u(_I?z$|^m3FC+scAoqz+OvY+U%f03C@H4X18Z(+#mX=;lWl* z;!3-<{l^w2)6xb&3zB@U=TtoBo6`c%wn?tHoBIhfR8>sH8ZW$Q%e9e6HJ@va`r@fr zp9#7S+-9ySEy^j%76H&yR2*pWG+m>qPD7g zXFGBGKDXA@yjnT7!^9X}*h|9k!gcJYQe8m%_5uVm!tV<+Fvrpq&GfAdnvbf~0oNn|u{3 zZPfq>G%p8@-Y#?)1PNZwgE43ahVMZVsz_!pj;NlCAvLoZ+|gPt;RbQxgL9%aPn&rC zTX`k!YBIFh|7_cYjB2uQNd@7~i>XGF8P+)h5oZ93R**7-(sD(9BlkJoGnOsXP)X2y z*@T0rb*b?&W%9*>-_TgR@1OU{Y@T%iNWo7`+M$M^#||K@ZC=zE6Qr=SFEBy(?;;Mf z{U;L_Q4i8V*Ntc6u~L?OltbRhVh6>E?}tuGVC{JXfDzL~v`^Rra_07;aZ=$MA*nN%W6E|&j1$}4}Y4>lI+Q1o2v!kJFv`DEn z{h7Ou^;TCHbMnvCsW}=&hn`gb#C#9W%;T(2(StF%8{!4AJhI^<*S{t(bZRG(*WVlD2|d8Fw!l54i%hT%v^J^(h$tFNA-2!S72z5iIuB9pB98+Z@Vpq%X9AgpG2 zS(2zp2Y2Sz1`6!Pei*>E%>n+jfMlG_NIeYUl8jD}=_Csq3hs6knMCPqr!xHJ4ol2Nin}@BIRt+32Jx`@&kKgZ7%Xqv)_Yjj0FuYFy zq4PfGHIRALpPDM0{Um=f3WBaPC=D|l4Nims zk<+4T3+SLZI{H#F$_4+R=+p{!*-P8P&$F6d;R^`6KC?DfRvvxn&T~J3+$}KpT?#mX z96$ogC2b9{fE{TV03F7{ixa|(U)cnpOKicRqbwBy_6aDw^%T^n0ZRm=7wzKj{B;;` zV^T)yl(uo&}szgtJ^ke)t7~0YpUz zFac)9lv9S8yBo+Uo(WC;2cvtK=b>L{n_W>xLzgrR01gcB;j9#A3;+I;M1}qK#446y7h@plgGh$PQyEp31IIA6syNtF}|AFO@kOd#0 z3y2m%xw;%~2;0tI;Chrrh-d=7E&IY&nbG!ZH0l&1B07lHS#!h8%t!=2*bvN{m=`D^ zJx9d-;F~;7NmrJ9Av1y*_&+jP@l`Z{W7K0FPw;J6@*>Oy@~8yU@#^7(ht5&~TwOYkV?*u5iolua};WT(A>l#i!o;^*`L zigRLLu*a|&L}Yl4VT(eB*Y(kjm|%aOUx4O?}5H4X70pr*WitMdTtD2mm#tXU9i$;Oew# zX;D9J;R5K5_x_)53H4ij2zy4nn0&%OAvIYOp{=$)c@j0h5Vo=;G1J34EOf3|dWY*K z&JO_i&xWx#Ffjl=;0K~RMNpto0op|N^pwc>E?|_58<2t@>2;|W+(vf|CkYQh3 z(#Ik?R>;E;R!xK^p=pMMCZE6s!^bPMXaI80fHsg&9hDc_^J_Tn<}lb0Nvcx;nFnu% zX@ONxt8>(i!sL7l3>k|^`BxAZw%*}fx#XU0so+5BA`wPkhY(o3UCmQEi?HI+bNN4ovDqSZeD(Qs&cP5~D`LVvr!FRe%d{Mhdk+|afp_)xx%d`HhgSWLiH0gZh z07^lNgUn@!l{PAINmh%~l{|oVY3FG+d5QC2X`pA@srmTy1x+L8fH}pbpg%)LS)Dee ze$$Em7&-jEe^)KCMoMM2j`qzd+USu{<`XHlMckYAu!=mj4GcPkQ(ckYW{>%7(Sg+1 zaTLfqAc^$of|F$A1#B&_7+~Tu7$L+4_EQxT) z-{hY(M)MJkvf$tENo@P80-SQuLJIKAs<5c)<&gm$UaDit>%J#dj&}&exhKAQeH|Q& zG>}hxx&(A#U7LO6#LiN5hYz?tg9-TvaPhv!@^En`C^|TK*}HqS`FeXbf%7DDP)-4n z@IoHLVFnWzH=s!MU8An`vN^i?t?rkU3(`yq)42cTAd9fkm;jq}`bRxrC9Aspt*7Pq z7l-Q|r-lg;s24t7(3E<8E)&v7Ru{86%gxuZfx^n`CFJ~U5Hc~k2}svB|K8-B!G%5Y z(vFHNha2z8s%7APSRe(hO0PJ&I~_!_Y>NwQ30U*_pdX}hcc-#7NSMjBM0|B)3UgB> zW~;!AEI{W}7bj%ei`9y6>(j@>Q~M7oK_W)U7#1voYt7Lm(K@c~BzhK1 zLgndZsY|x(B5%IR7tFT4ia6{~R)4!s3D~i2k{SC9ima2f% zfcCA4`t7t?k7pM>q3RmUl+{&+q?C-==pP}`y4|O#!Lw(yM&YoJonh5N+$AN7(%B*c z35;Q0-Dhw=X(MgK+qJ2RaY4w9 zL*1RRDC1!C~$#ugw%9Z+0%K@C#A9$phTRRN&X zBrBb;O)+M^fm)n^^{Hn0sz5?MMYe$)uoxaa-Vekh)~Ml*wrbG7(AJM}A&Il)CuE#v z0v(9EosGGm0dq>>dFU?RAg@vXDY4$XT)&}iPB~Q1b;m7 z+*-DNtsusF7RF=Q4&gwjKIpatB*9zQ767XcW6p_`E9d+uu;F*|qkPSmuyh15P5tYx z@Jed1f{pLHMKIHU?;W-ow8*;{?%CnuS`5s*5Kk=|0{FnWPv5Qhtg-2w5=^#@>XOdR zuaepOyKSIY)Pv$UEU@*k@OG}1F&@U zMTTqH&RolSK6F!;_6EI!z=yF%KsX~lS^wrv#nZWJNy^}U&XBe;-ywoW?rszd{{$@6 zvBNdpT3-fjSWsUva&@lR;;A{1vNWzuBR!y-K!2Br7+l8O&{3!x<1^ihoU}fz%o^}e zi>HFjKEM?fbaVtYfLpON>$1T!F|5p#Wl+Q5$)Tk#YPrkOAud*74^)`f=qt3*XQ1Q~ zHVjeNx69UdAoQazYnFIpU0ziam!Jmkikta>+Wxgpb9tRdW4P>ucbASAJ2QtP}f z{?N?Vj*jjB&$o}-T}6JHqZgUafuT~@f65HGn@6?DN9+Goa#n9qxL>p%x=UKRduWi9 zl5QBfyHUD9LK=~nVE_@3?(XhJnjr@15|Bpn@O_@&xjQ%K=KT}iz4uz5wFYWRRp-Ro z#i}=++b&n9vNk-3X$hSstpc;-+5;6a6e1N&q?5M+U)XcM->^FDYiiD=;rIYGDqU~p zQf^vH`lhEWi)PdfN8?=Wo0N`i0s;t|e6V@M%K9ql+EHBgIvnjADt@?qD37BmuGq?! zSp7KWzP|$Zl+R9}8Kq|}s2ddOVRW|WusBFoFE((H(uVp}WKnmA+9k$KZj*s`b~Y1GDF_6ET+&mxYu}8~zL7bXr-@KAuU=KL5}#?a1I-qZJwJS8t3I z*+9(bxJ-?;J2Z>>TtMsz?&b-mR47u%S9ie(Dq5E3{vBbO1v4EgBv*(e6$TZ(%U#if z2EU+(q_Q$JHWQZIM&x>$7H&hmt3tX)TDZR{xPOb}qssCVYFb>MYz^UJXFj5zP+v6X zG5*o!3>H`nZ-sWbRuYOnJHrJvBzf(0?Q=h3=Z$lX-COI1xI;O`s%maKeKF2(?GT|G zN1~hE3}hC(tG6$zKgxKn5mBj*IwKw>nD@RtK7s?HOrF?hBwnPsjDR>Tg5@J>efho@ z@j8!Q-8K>nTkXc4rtxnIw+Xau9wUKM2+o-%5`%vIijYcjNS2&8O3*rFE=DY|<^Mjm z33D;k*cO?`ZGL&x{`2?y&Am#kV3K+A5+R`#5q_|OMWx8d5;8S*2dgG|JY!L;;06G* z=a2}2(Gfhgm~E1VLC`->lk8H|d%gvw zfEjhcr$N7rQHBDn9S)`w=g@{s$bK3eJI^TH^4MgIp8rG&~tNB0cBJWv-_X}7CkP(OQeJY+o5X6HD(vNUmauzxht(k{64 zjkoEB)9X^mbtl>sa~!9a#J3=w6Bu1P0New{h=^|jV=A$~t>zs27Bu2LNK0J{dlh>{ zfmf2nUaK|?1r-xd{S`}X8P|@^8?*#TosisLw>jLuicCM#&(fTLAup=2#-BE+C(sTn zJ*Dqw8lbZmRJfNPpDRXl_*BS_QFZUZ(*@!jiFM!=y;%S$yaEmT}sHk#tkk-U;ezb=nMWi=PFe!_7nTCBd}U z8!K#D&+FO|gu+&pJWyD=M6-*xTGYe$)>E?hVboK6;=6rJwyvS$kaLUPU7p^XmS#T1 zkJ33BtO6SVd!rSaO(|Ni7U4&!kH7SvA9!PB}?=OxE{Ic693Z5Eef^z@Z=(SsdXZrHQr=j3NN~=FwRHUQkItA*o zAv%poB(MiM7uN#=8!-mmQfm+2NShZMAIUMK9I3SYZFbk@#SkNboUF)fLy5Qtj$_^L zq+#u8-)*7$>;N&*#fviYHv97G`(60z0q^zzIkOt=^~{?y$BYIiS3)FaYp3s9)7agG z;&a(gYSlBBEnQ8kHhtQ{+;*miO%=|uheR_sHQk?Lo-z!6d^0{WE#tRTs#J^CD=-41 zs6O@2b=b#vzLO6Ihhyxh{r{zc_9rYcU!UAEXQZ%@1(yC zHB!Y5j+qEzpy@!&7y;{2zfDTufIbTBL`?L#ckFM=x^#WlL@3hqBLta9>OIZ`IEhTsKVjJZR`Itvtv!Yw`jo z5bim`KmXxW(xkf`rMnSE-N7Outb9Mb)*w(|rA-!*Z7}wijF5~GDYFl1@&=haEJ(_4 z$3Ny$iwL>iCU;csws##lkPxI)MTY(t2NC~H;4SI_F&@GBLf2X~+=r)L+lroJYX+TH zNfGZgdo(*;P!NLIs%|{K;6|3*KsIcR$n@nf!je&yUm8%7L0hzhficixCIx%K`VXBm z4;nyWR(4*b!Iz1RzHdW3Lt^Ymgs6WBu=q2BzKjjZ;U)sr!Gnx1MijL_ze`~uLQD#u z;V`vh6^^oB;mOc@vsTcE^O0E~(MD${ViQ{`8T}bwk@5Fu(tFy0Tw~W0R{Z5uck`%U zsq>ttAJi9BWRpM>aEzVQ#Eyxk?A^06+OiK(ywH-V72 zLJ;0>q6-angX`7M`LF+p%x8j3X7K-~%3Lw`WN&t0><}Zh8<;HFHgvvYp06dh1qNkf zJ`sx!4cA)fWTZHw3O4x~_l2lXss!n)lZFxvk`xJNjfMswsnJpt(`{Af=S9ly`HrKm z!X3Y^WOPnQv2Vpfd?)b836y6uVjo@h z1$%Wu4Ge5PVPoDDdLqlQ^gf@iiAWkHXFPLyVEU5SSVeL<;g-d*E6;VKGuFZhxlQ%H zNzB0U$-FDWQ-zr-BdbK0m06(%G-?~zS?ESv6He={>LgqA`(RFsr#Y{?hTj_%Q3QSIKl6h|EZ6z z_e4T1jC|faHBii5yv~g3-^TqaCVu+&?&t6GqVe?mKJPtAe+BUz_gw_U%Uv1r0tv&u zo7umd+v!Bk%)gC?rK0=7m`)bl^Oe|W<7~d~XL8LC{&|?e{&QZH1o|<~7gWk+A7L$- z;w-NAvw}vJWTNfdPG^C=qWSO&_N66Y&TCb#dWY>zdab=t4bI$v>}5g|LRDOZ^B}Kk zcx)jl_qV76a(icXsf;lO9> z;lvlKwm*FbDVwt|fE1sU)0oeL-Mie*ba8Zl!3-EGmb zc0YD-%Nm9~z*p#^X&`Cr4wQC(kn7;8TWT5;YcERJUb55JEAyINhblDXp;^$Ti`2BU zF~rHb4r|n+&E+M42UH9#@iOY z^T~f%C@vQF-&OnA$@y5@)e|uIrN5l9ESqgiq2z=bm?KpD))0A9NJJ?#Bs2y(m7+eR zuSJGhPloFw+lGVF6c^R0Ehw261t{ePcQ{H3XMC!>`|dyRco;@=*uG zUwr;IFRUlxaDjLdajTj!>oVt-rOb zN74M!w^{P{ksDHD@HB~Lbhp5q(k=L}VlZ;^O``2CjH%Er6ez`lmDmWF|F8uMX9bIW zNBg-yBgdafCOW(;y*8&o$)gNXbw_g#MegAG6b9PI=TK+O%qE;TwO8rU`V-B-x`-gg z8=Xn%WMt`dpgE@L4y^A0EC5F`yt5as%iHd|Lv0Bb zj)e#YZRM{Z4=%XL(t|(j6>R{)7j2E|qi@eT!tDamX=2V9kUBe6JbP!85s$^6 zZ1m?;qKkZoY-|Xw1==?T9siIx{LV4Y!kC~e99&#z_}iXOuaT~2FAR#}WF>j*LdMNu zwjs61oe-DZdA4El@a&XbayqIU!`cC~qR(4v%wM@GB3tK+zSk})&&4ifdFSY!J7Tdh z?-u-8Op^gM-+;9Xtbm0vs|~rtWhMDo;zdLzvuND@v5%4&^ol#m95C$^Eai|lfOUH2 zuQNDdz8_KL6Vwq%rcPcI{1Ag;!Y?YxE_r0F33aKFojt^lA0c)Ffp~`YK)0hQ+bvH# zn-$GVM}b}TqmJcV4}97Poo&12dii`W3eHwC!!@@!=oGqZ?)XR1-19 z0terxI?D_{9a-;&Y7e0{4JR)A2msOVn;pFR5p0e7d0I^6P_1FgzvQG2E2mOR?0QEm zNQq!;oC^6RGS0CniedXKCVRV?Egjrj#I$q(&$rat8J8B+JW{d}Q`yr=dO2@=!(AVbPmgW9v~QM^UFP()jW&32Bo(F^kk! z%q4h5f92hRIi(auhWn*H9BX5KkZu$PqWuq}-<3ciSrn99UBZQUqsSCnLc+k4_yfeHrIqu*~t z2A6!NEBu_eUN#@XZ#Y6a?Q+nLp$)uleL!UvdB2y*2p9w8^@$`t0*VP*X_N^asHc7i z!gRhE3|M|$+6F9DH}Gb-TH4VsGFI_@{h~zPJ>X z$+x=nO7ags3-ld6QJeold2NWC$s3$R1-W&Z3h$&A-%f+_VU;sB2kRa|G}?=1;8DG% zXxX8NZC`_UD#xL~&Az;ngskJi3bpnq2B2S2eC$}lTrs=tZX&X-{EZ+l+9|g>g{9fN;H?aaX@ski)2i6B%vA- zxvqdz%ddO#rDYSJ#|j+s6FDC!MQ*mN>|gwxew8%68}8uXN(PmKh|IgaMemAwix{}$ z!vLPWAkOTB*Al=1oN^(nLk{IjU69qYUE0=SbI2dki-j>l_If6M6P0$uia}uTT*;5S zuv1aDS-gWP*u|AtrYT3ct*~9;>W|@4$y_l{NZ4^Z%0i!ECZidHMAEQ_3k6bLDAuQ3 z#2S@<>WM*k*hp|RO8?E9V_{)q=zDXN5gG;|T79h&gPj;YseCr8l>br;bcV~*)LBvCZYG7b zNUj+fht(pl2fuC<^8__2Obm@+N&jWMRetfBM*#KrT)dU=NQzCfYM1^Bk*!E$eq?9K zikEMN?c%9I=UJWES_@F=Q-I61@%p-Bieny*BOg1y4u50($X>EL^{P&(7jn}8dlIK{ zf#D}VJ%z?HUA;rI+B~EoN8JtV&LUXgFiV1ZefN9KEjrV`^_U}t?hiRKRaT1SDntKk zEWFx9k=3D8n~b<+C-QXH*uc_L(Ijr4-ErskYsZnLNpy$d!mn8se69Glx0h~(Z#T^u z>VkzYoy;sH5p=YO>3ll$V9Y$Y);zaIxLr%FL!aXSZDrtpM3XXCn{*+Ea z^)8TxiN(P>yX)?PCaoX3s#FN7Yn_8kEqSA7WjMS?Oslo^sr*_~{o@*C5_eWWN2dU5 z;uc)EQ{UCy$j`1Dv8dm;D7_q>zM~0&j=4`W+vB29*ZnjtZC=}U8uVWjPUhn6IhvPX zrO1*@*D4&v`31-S*aGW_!@`6`ATY@=ST@7aGsAcK$~AsT!gMnwrNW)2*P&%%pFJ2H z*P?vN|GD}6!}$JIYeN8g-_Rq4I^jXb3|P&3L-F(ej~8wL;N|54UDTa12pRBy0JV*l A00000 diff --git a/build/openrpc/miner.json.gz b/build/openrpc/miner.json.gz index 1ddc27f12930edce042af42f23c095dc7e9e6964..dd1b12856db99e2cd59453cf86b081faf57ded17 100644 GIT binary patch delta 7157 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 delta 7112 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 yjg%sUaV}DHh-&0RR8~fK;x7jsXBr8N;>! diff --git a/build/openrpc/worker.json.gz b/build/openrpc/worker.json.gz index 5bb69aa4919c86a4700e9fe7a69472082c5d4cc8..4c2337e31a1d9171d1bde64d53ced59a4e4b1f89 100644 GIT binary patch delta 2368 zcmV-G3BUG{6p|E>903iH9bkWFtRj4O#)`4Hxm8!m5VzNS4sr7IzmgbH+~1%oGExzt)cmCA^!wP z#RZu{>k4ee#U)HEa6b8K?gJjYTijTfI8MkGaYustAQaW@ zOo%JN!hWJ?*>VMfh>4XPGQ)_4Rmi~d^J2x~=DLfxgpa?2t#Eo!6&m=qY~f~MVF7;! zRDgpAN(7)^hA+S-N|%I#TUe{N)y=%E0*Nj4&RF2OHx{M}BB6iCj3C{YnJYvkB4}-p z-%nIq7u!H#sA#IE;RwYH34JPOFMFMKe%}qZ?gG z2CmX=N4%|@s9a9hDwMGS#%4Tg2edNd&hO^man6tBKU~X7M8f=3N_00`2ah~nmnm z<6@uTUyO`ha4NbC5mk=IaD?8BId?Y!`Kp0@+^zu7c}jn)S;!12D%`A``V}XPnx+H7 z4nq!3PV(j84^Z~3q7;NQ)x5@)u{7nnf9({6ZsY3Vh^Sdq+)ot0x5L^0$Q`KRJTBQS z=Q%bm;wy9!i^Owx{?itmr>qZ{Risi0g$Of^nj zM7!?Nug!=C1KNAAiduE60IAQ-V06I2b5luvU&-e z3RC-eGLfLl&25-%sW~w<(?JcuH2`<00C$!Ta2IUCE5e#4$r1ZH=4~QwTX)d{jQ}8t{#XVf4Bq* z;$F**E*Or!Nb}d9;a}R|E-GsHinFJsfJd(WVvV^9jT*+vDBzz_&Dg|bcvv?HsW~ds zMQYkg5|~MEtU_!Ry1jg>@R8rcmGKNJ8ixescJf&@Jl`}AjoZ6d___~n@5@O}H6Cf> zksg04_7tq6jfk4bil`IPGAfDUFO( zQjZjIzMcdDby{D(Z1%1)jpb{JA=D;Y5axey59ziMpP+}3H6#tag{Htj=(iD7bm!<{ z1}v&~&3w}zl8OjbZO2)j`o-hS%wh6LiKUcMFu0SQPMd-4uvDTTH-8DF)-KJMt|&mE zy!BO31tJbi*29(L3j1=4*gxkAE|>$4;Zl3<=CK;FOp@7Oy4)U5!Y|u1w$Wv80<3@i z46vSCHqU6s4mVKtJH2jSMXi6Pd%G3mib9)b@ID1@_FzU-n21E;7{YZAL_-Re z4X%K=L+*K0WrgT@HAzrb^Ztx%68AF9TQZO`KC>ID4zU%zhfIxrbmBJ*B+V8X|Ls~A zL+Z#H-eh_x>-i$xPvceY1$^y;SD6*x13bW6sE99mtl13Jnn~Gth!FNPjFx}>qDcuY zCR6Cb0jB7LCM9{b>i2&L7!ou0c7xH{zF>1r3K;7|pDW^e0q6>q>&Kue9-1(8uk>jjP(4@HULLSF@YH|6(}BX%X~Djj zdgr0Ga}a9N38)YB4%UudS!f(66d#RZ(YBk!c_%Hn4wd1Zp3PaOS9jLwv~YKes&~Ka zim=H28guS$*#DJk>UIJ5kC=%Ss zv8glGk+>bTK^#Ybl;9v5x7I=zN>9Lq3lAJLo+E<(Yrdk=LIe%D12%d&^Dv|K3{mD# zJw)Ay7r2Vr`suMx>^pGcUR3Cc6Xqa-KI&RCB4@3%;B4neLG#Ta&B|1>GL>=(&y$o1 zC{?@KIJoBmRgio9N~rozKtWX$shBQTsGd#WBuVq-D6WCVF1PO_3YelUQZo>MhycXQ mI*mjkU@TnS`AN}xtr?ZGGrU;bEdCb&0RR7WReSRRdjJ4x6^SbV delta 2367 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 diff --git a/extern/storage-sealing/commit_batch.go b/extern/storage-sealing/commit_batch.go index c01067872..c540f6507 100644 --- a/extern/storage-sealing/commit_batch.go +++ b/extern/storage-sealing/commit_batch.go @@ -216,6 +216,11 @@ func (b *CommitBatcher) maybeStartBatch(notif, after bool) ([]sealiface.CommitBa } func (b *CommitBatcher) processBatch(cfg sealiface.Config) ([]sealiface.CommitBatchRes, error) { + tok, _, err := b.api.ChainHead(b.mctx) + if err != nil { + return nil, err + } + total := len(b.todo) var res sealiface.CommitBatchRes @@ -226,6 +231,7 @@ func (b *CommitBatcher) processBatch(cfg sealiface.Config) ([]sealiface.CommitBa proofs := make([][]byte, 0, total) infos := make([]proof5.AggregateSealVerifyInfo, 0, total) + collateral := big.Zero() for id, p := range b.todo { if len(infos) >= cfg.MaxCommitBatch { @@ -233,6 +239,15 @@ func (b *CommitBatcher) processBatch(cfg sealiface.Config) ([]sealiface.CommitBa break } + sc, err := b.getSectorCollateral(id, tok) + if err != nil { + res.FailedSectors[id] = err.Error() + continue + } + + collateral = big.Add(collateral, sc) + + res.Sectors = append(res.Sectors, id) params.SectorNumbers.Set(uint64(id)) infos = append(infos, p.info) } @@ -242,7 +257,6 @@ func (b *CommitBatcher) processBatch(cfg sealiface.Config) ([]sealiface.CommitBa }) for _, info := range infos { - res.Sectors = append(res.Sectors, info.Number) proofs = append(proofs, b.todo[info.Number].proof) } @@ -271,14 +285,14 @@ func (b *CommitBatcher) processBatch(cfg sealiface.Config) ([]sealiface.CommitBa return []sealiface.CommitBatchRes{res}, xerrors.Errorf("couldn't get miner info: %w", err) } - from, _, err := b.addrSel(b.mctx, mi, api.CommitAddr, b.feeCfg.MaxCommitGasFee, b.feeCfg.MaxCommitGasFee) + goodFunds := big.Add(b.feeCfg.MaxCommitGasFee, collateral) + + from, _, err := b.addrSel(b.mctx, mi, api.CommitAddr, goodFunds, collateral) if err != nil { return []sealiface.CommitBatchRes{res}, xerrors.Errorf("no good address found: %w", err) } - // todo: collateral - - mcid, err := b.api.SendMsg(b.mctx, from, b.maddr, miner.Methods.ProveCommitAggregate, big.Zero(), b.feeCfg.MaxCommitGasFee, enc.Bytes()) + mcid, err := b.api.SendMsg(b.mctx, from, b.maddr, miner.Methods.ProveCommitAggregate, collateral, b.feeCfg.MaxCommitGasFee, enc.Bytes()) if err != nil { return []sealiface.CommitBatchRes{res}, xerrors.Errorf("sending message failed: %w", err) } From cd0a1c97fac54b8a93b93d1c5b194d82d8e90753 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 1 Jun 2021 12:25:19 +0200 Subject: [PATCH 065/160] Improve cli for flushing commit batches --- cmd/lotus-storage-miner/sectors.go | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/cmd/lotus-storage-miner/sectors.go b/cmd/lotus-storage-miner/sectors.go index 2bb44b4f4..257fb2713 100644 --- a/cmd/lotus-storage-miner/sectors.go +++ b/cmd/lotus-storage-miner/sectors.go @@ -997,15 +997,30 @@ var sectorsBatchingPendingCommit = &cli.Command{ ctx := lcli.ReqContext(cctx) if cctx.Bool("publish-now") { - cid, err := api.SectorCommitFlush(ctx) + res, err := api.SectorCommitFlush(ctx) if err != nil { return xerrors.Errorf("flush: %w", err) } - if cid == nil { + if res == nil { return xerrors.Errorf("no sectors to publish") } - fmt.Println("sector batch published: ", cid) + for i, re := range res { + fmt.Printf("Batch %d:\n", i) + if re.Error != "" { + fmt.Printf("\tError: %s\n", re.Error) + } else { + fmt.Printf("\tMessage: %s\n", re.Msg) + } + fmt.Printf("\tSectors:\n") + for _, sector := range re.Sectors { + if e, found := re.FailedSectors[sector]; found { + fmt.Printf("\t\t%d\tERROR %s\n", sector, e) + } else { + fmt.Printf("\t\t%d\tOK\n", sector) + } + } + } return nil } From cb4eb487f4e9496765f62be8bf703e3e99661e08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 1 Jun 2021 12:27:22 +0200 Subject: [PATCH 066/160] commit batcher: Fix min aggregate size check --- extern/storage-sealing/commit_batch.go | 2 +- node/config/def.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/extern/storage-sealing/commit_batch.go b/extern/storage-sealing/commit_batch.go index c540f6507..7d128fe76 100644 --- a/extern/storage-sealing/commit_batch.go +++ b/extern/storage-sealing/commit_batch.go @@ -187,7 +187,7 @@ func (b *CommitBatcher) maybeStartBatch(notif, after bool) ([]sealiface.CommitBa var res []sealiface.CommitBatchRes - if total < cfg.MinCommitBatch || total < miner5.PreCommitSectorBatchMaxSize { + if total < cfg.MinCommitBatch || total < miner5.MinAggregatedSectors { res, err = b.processIndividually() } else { res, err = b.processBatch(cfg) diff --git a/node/config/def.go b/node/config/def.go index 988b0a6c9..636265560 100644 --- a/node/config/def.go +++ b/node/config/def.go @@ -271,7 +271,7 @@ func DefaultStorageMiner() *StorageMiner { PreCommitBatchSlack: Duration(3 * time.Hour), AggregateCommits: true, - MinCommitBatch: 1, // we must have at least one proof to aggregate + MinCommitBatch: miner5.MinAggregatedSectors, // we must have at least four proofs to aggregate MaxCommitBatch: miner5.MaxAggregatedSectors, // this is the maximum aggregation per FIP13 CommitBatchWait: Duration(24 * time.Hour), // this can be up to 6 days CommitBatchSlack: Duration(1 * time.Hour), From 5c3ebd424cc706622a8a898694ed7e9b909b2d23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 1 Jun 2021 12:33:24 +0200 Subject: [PATCH 067/160] miner: Improve batching CLI UX --- cmd/lotus-storage-miner/sectors.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/lotus-storage-miner/sectors.go b/cmd/lotus-storage-miner/sectors.go index 257fb2713..c76d4a249 100644 --- a/cmd/lotus-storage-miner/sectors.go +++ b/cmd/lotus-storage-miner/sectors.go @@ -980,7 +980,7 @@ var sectorsBatching = &cli.Command{ } var sectorsBatchingPendingCommit = &cli.Command{ - Name: "pending-commit", + Name: "commit", Usage: "list sectors waiting in commit batch queue", Flags: []cli.Flag{ &cli.BoolFlag{ @@ -1042,7 +1042,7 @@ var sectorsBatchingPendingCommit = &cli.Command{ } var sectorsBatchingPendingPreCommit = &cli.Command{ - Name: "pending-precommit", + Name: "precommit", Usage: "list sectors waiting in precommit batch queue", Flags: []cli.Flag{ &cli.BoolFlag{ From 482e1110c2e94c2dcdd74015d382cad815a7ad94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 1 Jun 2021 14:35:30 +0200 Subject: [PATCH 068/160] precommit batcher: Improve error propagation --- api/api_storage.go | 2 +- api/apistruct/struct.go | 4 +- api/test/pledge.go | 4 +- build/openrpc/miner.json.gz | Bin 8040 -> 8066 bytes cmd/lotus-storage-miner/sectors.go | 17 +++- extern/storage-sealing/precommit_batch.go | 86 ++++++++++++------- extern/storage-sealing/sealiface/batching.go | 7 ++ extern/storage-sealing/sealing.go | 2 +- extern/storage-sealing/states_sealing.go | 16 +++- node/impl/storminer.go | 2 +- storage/sealing.go | 2 +- 11 files changed, 94 insertions(+), 48 deletions(-) diff --git a/api/api_storage.go b/api/api_storage.go index c2f3a3d57..e50fedc19 100644 --- a/api/api_storage.go +++ b/api/api_storage.go @@ -83,7 +83,7 @@ type StorageMiner interface { SectorMarkForUpgrade(ctx context.Context, id abi.SectorNumber) error //perm:admin // SectorPreCommitFlush immediately sends a PreCommit message with sectors batched for PreCommit. // Returns null if message wasn't sent - SectorPreCommitFlush(ctx context.Context) (*cid.Cid, error) //perm:admin + SectorPreCommitFlush(ctx context.Context) ([]sealiface.PreCommitBatchRes, error) //perm:admin // SectorPreCommitPending returns a list of pending PreCommit sectors to be sent in the next batch message SectorPreCommitPending(ctx context.Context) ([]abi.SectorID, error) //perm:admin // SectorCommitFlush immediately sends a Commit message with sectors aggregated for Commit. diff --git a/api/apistruct/struct.go b/api/apistruct/struct.go index d70c6aa0d..acdc0f9b5 100644 --- a/api/apistruct/struct.go +++ b/api/apistruct/struct.go @@ -650,7 +650,7 @@ type StorageMinerStruct struct { SectorMarkForUpgrade func(p0 context.Context, p1 abi.SectorNumber) error `perm:"admin"` - SectorPreCommitFlush func(p0 context.Context) (*cid.Cid, error) `perm:"admin"` + SectorPreCommitFlush func(p0 context.Context) ([]sealiface.PreCommitBatchRes, error) `perm:"admin"` SectorPreCommitPending func(p0 context.Context) ([]abi.SectorID, error) `perm:"admin"` @@ -1952,7 +1952,7 @@ func (s *StorageMinerStruct) SectorMarkForUpgrade(p0 context.Context, p1 abi.Sec return s.Internal.SectorMarkForUpgrade(p0, p1) } -func (s *StorageMinerStruct) SectorPreCommitFlush(p0 context.Context) (*cid.Cid, error) { +func (s *StorageMinerStruct) SectorPreCommitFlush(p0 context.Context) ([]sealiface.PreCommitBatchRes, error) { return s.Internal.SectorPreCommitFlush(p0) } diff --git a/api/test/pledge.go b/api/test/pledge.go index b4bf88b59..08548dc60 100644 --- a/api/test/pledge.go +++ b/api/test/pledge.go @@ -169,7 +169,7 @@ func TestPledgeBatching(t *testing.T, b APIBuilder, blocktime time.Duration, nSe pcb, err := miner.SectorPreCommitFlush(ctx) require.NoError(t, err) if pcb != nil { - fmt.Printf("PRECOMMIT BATCH: %s\n", *pcb) + fmt.Printf("PRECOMMIT BATCH: %+v\n", pcb) } } @@ -319,7 +319,7 @@ func flushSealingBatches(t *testing.T, ctx context.Context, miner TestStorageNod pcb, err := miner.SectorPreCommitFlush(ctx) require.NoError(t, err) if pcb != nil { - fmt.Printf("PRECOMMIT BATCH: %s\n", *pcb) + fmt.Printf("PRECOMMIT BATCH: %+v\n", pcb) } cb, err := miner.SectorCommitFlush(ctx) diff --git a/build/openrpc/miner.json.gz b/build/openrpc/miner.json.gz index dd1b12856db99e2cd59453cf86b081faf57ded17..f60d44df02aadd18fc0904012e557bc37e6711c0 100644 GIT binary patch delta 7407 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 delta 7383 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 JdLTBB0RXE=QaS(t diff --git a/cmd/lotus-storage-miner/sectors.go b/cmd/lotus-storage-miner/sectors.go index c76d4a249..2476c16e8 100644 --- a/cmd/lotus-storage-miner/sectors.go +++ b/cmd/lotus-storage-miner/sectors.go @@ -1059,15 +1059,26 @@ var sectorsBatchingPendingPreCommit = &cli.Command{ ctx := lcli.ReqContext(cctx) if cctx.Bool("publish-now") { - cid, err := api.SectorPreCommitFlush(ctx) + res, err := api.SectorPreCommitFlush(ctx) if err != nil { return xerrors.Errorf("flush: %w", err) } - if cid == nil { + if res == nil { return xerrors.Errorf("no sectors to publish") } - fmt.Println("sector batch published: ", cid) + for i, re := range res { + fmt.Printf("Batch %d:\n", i) + if re.Error != "" { + fmt.Printf("\tError: %s\n", re.Error) + } else { + fmt.Printf("\tMessage: %s\n", re.Msg) + } + fmt.Printf("\tSectors:\n") + for _, sector := range re.Sectors { + fmt.Printf("\t\t%d\tOK\n", sector) + } + } return nil } diff --git a/extern/storage-sealing/precommit_batch.go b/extern/storage-sealing/precommit_batch.go index bce8e21d5..dd674d331 100644 --- a/extern/storage-sealing/precommit_batch.go +++ b/extern/storage-sealing/precommit_batch.go @@ -18,6 +18,7 @@ import ( "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/chain/actors/builtin/miner" + "github.com/filecoin-project/lotus/extern/storage-sealing/sealiface" ) type PreCommitBatcherApi interface { @@ -41,10 +42,10 @@ type PreCommitBatcher struct { deadlines map[abi.SectorNumber]time.Time todo map[abi.SectorNumber]*preCommitEntry - waiting map[abi.SectorNumber][]chan cid.Cid + waiting map[abi.SectorNumber][]chan sealiface.PreCommitBatchRes notify, stop, stopped chan struct{} - force chan chan *cid.Cid + force chan chan []sealiface.PreCommitBatchRes lk sync.Mutex } @@ -59,10 +60,10 @@ func NewPreCommitBatcher(mctx context.Context, maddr address.Address, api PreCom deadlines: map[abi.SectorNumber]time.Time{}, todo: map[abi.SectorNumber]*preCommitEntry{}, - waiting: map[abi.SectorNumber][]chan cid.Cid{}, + waiting: map[abi.SectorNumber][]chan sealiface.PreCommitBatchRes{}, notify: make(chan struct{}, 1), - force: make(chan chan *cid.Cid), + force: make(chan chan []sealiface.PreCommitBatchRes), stop: make(chan struct{}), stopped: make(chan struct{}), } @@ -73,8 +74,8 @@ func NewPreCommitBatcher(mctx context.Context, maddr address.Address, api PreCom } func (b *PreCommitBatcher) run() { - var forceRes chan *cid.Cid - var lastMsg *cid.Cid + var forceRes chan []sealiface.PreCommitBatchRes + var lastRes []sealiface.PreCommitBatchRes cfg, err := b.getConfig() if err != nil { @@ -83,10 +84,10 @@ func (b *PreCommitBatcher) run() { for { if forceRes != nil { - forceRes <- lastMsg + forceRes <- lastRes forceRes = nil } - lastMsg = nil + lastRes = nil var sendAboveMax, sendAboveMin bool select { @@ -102,7 +103,7 @@ func (b *PreCommitBatcher) run() { } var err error - lastMsg, err = b.processBatch(sendAboveMax, sendAboveMin) + lastRes, err = b.maybeStartBatch(sendAboveMax, sendAboveMin) if err != nil { log.Warnw("PreCommitBatcher processBatch error", "error", err) } @@ -150,10 +151,9 @@ func (b *PreCommitBatcher) batchWait(maxWait, slack time.Duration) <-chan time.T return time.After(wait) } -func (b *PreCommitBatcher) processBatch(notif, after bool) (*cid.Cid, error) { +func (b *PreCommitBatcher) maybeStartBatch(notif, after bool) ([]sealiface.PreCommitBatchRes, error) { b.lk.Lock() defer b.lk.Unlock() - params := miner5.PreCommitSectorBatchParams{} total := len(b.todo) if total == 0 { @@ -173,7 +173,35 @@ func (b *PreCommitBatcher) processBatch(notif, after bool) (*cid.Cid, error) { return nil, nil } + // todo support multiple batches + res, err := b.processBatch(cfg) + if err != nil && len(res) == 0 { + return nil, err + } + + for _, r := range res { + if err != nil { + r.Error = err.Error() + } + + for _, sn := range r.Sectors { + for _, ch := range b.waiting[sn] { + ch <- r // buffered + } + + delete(b.waiting, sn) + delete(b.todo, sn) + delete(b.deadlines, sn) + } + } + + return res, nil +} + +func (b *PreCommitBatcher) processBatch(cfg sealiface.Config) ([]sealiface.PreCommitBatchRes, error) { + params := miner5.PreCommitSectorBatchParams{} deposit := big.Zero() + var res sealiface.PreCommitBatchRes for _, p := range b.todo { if len(params.Sectors) >= cfg.MaxPreCommitBatch { @@ -181,54 +209,46 @@ func (b *PreCommitBatcher) processBatch(notif, after bool) (*cid.Cid, error) { break } + res.Sectors = append(res.Sectors, p.pci.SectorNumber) params.Sectors = append(params.Sectors, *p.pci) deposit = big.Add(deposit, p.deposit) } enc := new(bytes.Buffer) if err := params.MarshalCBOR(enc); err != nil { - return nil, xerrors.Errorf("couldn't serialize PreCommitSectorBatchParams: %w", err) + return []sealiface.PreCommitBatchRes{res}, xerrors.Errorf("couldn't serialize PreCommitSectorBatchParams: %w", err) } mi, err := b.api.StateMinerInfo(b.mctx, b.maddr, nil) if err != nil { - return nil, xerrors.Errorf("couldn't get miner info: %w", err) + return []sealiface.PreCommitBatchRes{res}, xerrors.Errorf("couldn't get miner info: %w", err) } goodFunds := big.Add(deposit, b.feeCfg.MaxPreCommitGasFee) from, _, err := b.addrSel(b.mctx, mi, api.PreCommitAddr, goodFunds, deposit) if err != nil { - return nil, xerrors.Errorf("no good address found: %w", err) + return []sealiface.PreCommitBatchRes{res}, xerrors.Errorf("no good address found: %w", err) } mcid, err := b.api.SendMsg(b.mctx, from, b.maddr, miner.Methods.PreCommitSectorBatch, deposit, b.feeCfg.MaxPreCommitGasFee, enc.Bytes()) if err != nil { - return nil, xerrors.Errorf("sending message failed: %w", err) + return []sealiface.PreCommitBatchRes{res}, xerrors.Errorf("sending message failed: %w", err) } - log.Infow("Sent ProveCommitAggregate message", "cid", mcid, "from", from, "sectors", total) + res.Msg = &mcid - for _, sector := range params.Sectors { - sn := sector.SectorNumber + log.Infow("Sent ProveCommitAggregate message", "cid", mcid, "from", from, "sectors", len(b.todo)) - for _, ch := range b.waiting[sn] { - ch <- mcid // buffered - } - delete(b.waiting, sn) - delete(b.todo, sn) - delete(b.deadlines, sn) - } - - return &mcid, nil + return []sealiface.PreCommitBatchRes{res}, nil } // register PreCommit, wait for batch message, return message CID -func (b *PreCommitBatcher) AddPreCommit(ctx context.Context, s SectorInfo, deposit abi.TokenAmount, in *miner0.SectorPreCommitInfo) (mcid cid.Cid, err error) { +func (b *PreCommitBatcher) AddPreCommit(ctx context.Context, s SectorInfo, deposit abi.TokenAmount, in *miner0.SectorPreCommitInfo) (res sealiface.PreCommitBatchRes, err error) { _, curEpoch, err := b.api.ChainHead(b.mctx) if err != nil { log.Errorf("getting chain head: %s", err) - return cid.Undef, nil + return sealiface.PreCommitBatchRes{}, err } sn := s.SectorNumber @@ -240,7 +260,7 @@ func (b *PreCommitBatcher) AddPreCommit(ctx context.Context, s SectorInfo, depos pci: in, } - sent := make(chan cid.Cid, 1) + sent := make(chan sealiface.PreCommitBatchRes, 1) b.waiting[sn] = append(b.waiting[sn], sent) select { @@ -253,12 +273,12 @@ func (b *PreCommitBatcher) AddPreCommit(ctx context.Context, s SectorInfo, depos case c := <-sent: return c, nil case <-ctx.Done(): - return cid.Undef, ctx.Err() + return sealiface.PreCommitBatchRes{}, ctx.Err() } } -func (b *PreCommitBatcher) Flush(ctx context.Context) (*cid.Cid, error) { - resCh := make(chan *cid.Cid, 1) +func (b *PreCommitBatcher) Flush(ctx context.Context) ([]sealiface.PreCommitBatchRes, error) { + resCh := make(chan []sealiface.PreCommitBatchRes, 1) select { case b.force <- resCh: select { diff --git a/extern/storage-sealing/sealiface/batching.go b/extern/storage-sealing/sealiface/batching.go index e7c2cadbb..d0e6d4178 100644 --- a/extern/storage-sealing/sealiface/batching.go +++ b/extern/storage-sealing/sealiface/batching.go @@ -14,3 +14,10 @@ type CommitBatchRes struct { Msg *cid.Cid Error string // if set, means that all sectors are failed, implies Msg==nil } + +type PreCommitBatchRes struct { + Sectors []abi.SectorNumber + + Msg *cid.Cid + Error string // if set, means that all sectors are failed, implies Msg==nil +} diff --git a/extern/storage-sealing/sealing.go b/extern/storage-sealing/sealing.go index 61360dc12..e69ce5be0 100644 --- a/extern/storage-sealing/sealing.go +++ b/extern/storage-sealing/sealing.go @@ -207,7 +207,7 @@ func (m *Sealing) TerminatePending(ctx context.Context) ([]abi.SectorID, error) return m.terminator.Pending(ctx) } -func (m *Sealing) SectorPreCommitFlush(ctx context.Context) (*cid.Cid, error) { +func (m *Sealing) SectorPreCommitFlush(ctx context.Context) ([]sealiface.PreCommitBatchRes, error) { return m.precommiter.Flush(ctx) } diff --git a/extern/storage-sealing/states_sealing.go b/extern/storage-sealing/states_sealing.go index 6f4c57bfd..815ad6ac0 100644 --- a/extern/storage-sealing/states_sealing.go +++ b/extern/storage-sealing/states_sealing.go @@ -355,7 +355,7 @@ func (m *Sealing) handlePreCommitting(ctx statemachine.Context, sector SectorInf func (m *Sealing) handleSubmitPreCommitBatch(ctx statemachine.Context, sector SectorInfo) error { if sector.CommD == nil || sector.CommR == nil { - return ctx.Send(SectorCommitFailed{xerrors.Errorf("sector had nil commR or commD")}) + return ctx.Send(SectorSealPreCommit1Failed{xerrors.Errorf("sector had nil commR or commD")}) } params, deposit, _, err := m.preCommitParams(ctx, sector) @@ -363,12 +363,20 @@ func (m *Sealing) handleSubmitPreCommitBatch(ctx statemachine.Context, sector Se return err } - mcid, err := m.precommiter.AddPreCommit(ctx.Context(), sector, deposit, params) + res, err := m.precommiter.AddPreCommit(ctx.Context(), sector, deposit, params) if err != nil { - return ctx.Send(SectorCommitFailed{xerrors.Errorf("queuing precommit batch failed: %w", err)}) + return ctx.Send(SectorChainPreCommitFailed{xerrors.Errorf("queuing precommit batch failed: %w", err)}) } - return ctx.Send(SectorPreCommitBatchSent{mcid}) + if res.Error != "" { + return ctx.Send(SectorChainPreCommitFailed{xerrors.Errorf("precommit batch error: %s", res.Error)}) + } + + if res.Msg == nil { + return ctx.Send(SectorChainPreCommitFailed{xerrors.Errorf("batch message was nil")}) + } + + return ctx.Send(SectorPreCommitBatchSent{*res.Msg}) } func (m *Sealing) handlePreCommitWait(ctx statemachine.Context, sector SectorInfo) error { diff --git a/node/impl/storminer.go b/node/impl/storminer.go index 9b6f65207..e10925927 100644 --- a/node/impl/storminer.go +++ b/node/impl/storminer.go @@ -375,7 +375,7 @@ func (sm *StorageMinerAPI) SectorTerminatePending(ctx context.Context) ([]abi.Se return sm.Miner.TerminatePending(ctx) } -func (sm *StorageMinerAPI) SectorPreCommitFlush(ctx context.Context) (*cid.Cid, error) { +func (sm *StorageMinerAPI) SectorPreCommitFlush(ctx context.Context) ([]sealiface.PreCommitBatchRes, error) { return sm.Miner.SectorPreCommitFlush(ctx) } diff --git a/storage/sealing.go b/storage/sealing.go index bd8241197..6a1195826 100644 --- a/storage/sealing.go +++ b/storage/sealing.go @@ -60,7 +60,7 @@ func (m *Miner) TerminatePending(ctx context.Context) ([]abi.SectorID, error) { return m.sealing.TerminatePending(ctx) } -func (m *Miner) SectorPreCommitFlush(ctx context.Context) (*cid.Cid, error) { +func (m *Miner) SectorPreCommitFlush(ctx context.Context) ([]sealiface.PreCommitBatchRes, error) { return m.sealing.SectorPreCommitFlush(ctx) } From 49fce48c3e8de610472bb457b3cfa619c02fdfe3 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Tue, 1 Jun 2021 12:43:16 -0400 Subject: [PATCH 069/160] Tweak CallVM to use current epoch, not future epoch --- chain/stmgr/call.go | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/chain/stmgr/call.go b/chain/stmgr/call.go index 961bebd9c..cfbf60a95 100644 --- a/chain/stmgr/call.go +++ b/chain/stmgr/call.go @@ -155,11 +155,6 @@ func (sm *StateManager) CallWithGas(ctx context.Context, msg *types.Message, pri return nil, xerrors.Errorf("computing tipset state: %w", err) } - state, err = sm.handleStateForks(ctx, state, ts.Height(), nil, ts) - if err != nil { - return nil, fmt.Errorf("failed to handle fork: %w", err) - } - r := store.NewChainRand(sm.cs, ts.Cids()) if span.IsRecordingEvents() { @@ -172,7 +167,7 @@ func (sm *StateManager) CallWithGas(ctx context.Context, msg *types.Message, pri vmopt := &vm.VMOpts{ StateBase: state, - Epoch: ts.Height() + 1, + Epoch: ts.Height(), Rand: r, Bstore: sm.cs.StateBlockstore(), Syscalls: sm.cs.VMSys(), From 8072573292dfba1a3eaad77500340eb5ab281488 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 1 Jun 2021 19:11:32 +0200 Subject: [PATCH 070/160] Fix TestWindowPostDispute --- api/test/window_post.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/test/window_post.go b/api/test/window_post.go index 2d3302d64..04056d56e 100644 --- a/api/test/window_post.go +++ b/api/test/window_post.go @@ -543,7 +543,7 @@ func TestWindowPostDispute(t *testing.T, b APIBuilder, blocktime time.Duration) for { di, err = client.StateMinerProvingDeadline(ctx, evilMinerAddr, types.EmptyTSK) require.NoError(t, err) - if di.Index == evilSectorLoc.Deadline { + if di.Index == evilSectorLoc.Deadline && di.CurrentEpoch - di.PeriodStart > 1 { break } build.Clock.Sleep(blocktime) @@ -640,7 +640,7 @@ func TestWindowPostDispute(t *testing.T, b APIBuilder, blocktime time.Duration) for { di, err = client.StateMinerProvingDeadline(ctx, evilMinerAddr, types.EmptyTSK) require.NoError(t, err) - if di.Index == evilSectorLoc.Deadline { + if di.Index == evilSectorLoc.Deadline && di.CurrentEpoch - di.PeriodStart > 1 { break } build.Clock.Sleep(blocktime) From 5c5b8866c7034de72db2e08254c372f9d782acb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 1 Jun 2021 19:14:08 +0200 Subject: [PATCH 071/160] gofmt --- api/test/window_post.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/test/window_post.go b/api/test/window_post.go index 04056d56e..8a6ca8044 100644 --- a/api/test/window_post.go +++ b/api/test/window_post.go @@ -543,7 +543,7 @@ func TestWindowPostDispute(t *testing.T, b APIBuilder, blocktime time.Duration) for { di, err = client.StateMinerProvingDeadline(ctx, evilMinerAddr, types.EmptyTSK) require.NoError(t, err) - if di.Index == evilSectorLoc.Deadline && di.CurrentEpoch - di.PeriodStart > 1 { + if di.Index == evilSectorLoc.Deadline && di.CurrentEpoch-di.PeriodStart > 1 { break } build.Clock.Sleep(blocktime) @@ -640,7 +640,7 @@ func TestWindowPostDispute(t *testing.T, b APIBuilder, blocktime time.Duration) for { di, err = client.StateMinerProvingDeadline(ctx, evilMinerAddr, types.EmptyTSK) require.NoError(t, err) - if di.Index == evilSectorLoc.Deadline && di.CurrentEpoch - di.PeriodStart > 1 { + if di.Index == evilSectorLoc.Deadline && di.CurrentEpoch-di.PeriodStart > 1 { break } build.Clock.Sleep(blocktime) From 66c1554670b10147f512a7c1f3fd2a6a282897ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 1 Jun 2021 19:23:12 +0200 Subject: [PATCH 072/160] vm syscalls: fix typo --- chain/vm/syscalls.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chain/vm/syscalls.go b/chain/vm/syscalls.go index 568197bc8..bb93fce8d 100644 --- a/chain/vm/syscalls.go +++ b/chain/vm/syscalls.go @@ -287,7 +287,7 @@ func (ss *syscallShim) VerifyAggregateSeals(aggregate proof5.AggregateSealVerify return xerrors.Errorf("failed to verify aggregated PoRep: %w", err) } if !ok { - return fmt.Errorf("invalid aggredate proof") + return fmt.Errorf("invalid aggregate proof") } return nil From c8cef1cb7e072677a424faeceef079757656ac5e Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Tue, 1 Jun 2021 13:13:45 -0400 Subject: [PATCH 073/160] Fix chain/gen randomness getting (test only) --- chain/gen/gen.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/chain/gen/gen.go b/chain/gen/gen.go index 319b7ac29..0cbdb2188 100644 --- a/chain/gen/gen.go +++ b/chain/gen/gen.go @@ -590,6 +590,10 @@ func (mca mca) ChainGetRandomnessFromTickets(ctx context.Context, tsk types.TipS return nil, xerrors.Errorf("loading tipset key: %w", err) } + if randEpoch > build.UpgradeHyperdriveHeight { + return mca.sm.ChainStore().GetChainRandomnessLookingForward(ctx, pts.Cids(), personalization, randEpoch, entropy) + } + return mca.sm.ChainStore().GetChainRandomnessLookingBack(ctx, pts.Cids(), personalization, randEpoch, entropy) } @@ -599,6 +603,10 @@ func (mca mca) ChainGetRandomnessFromBeacon(ctx context.Context, tsk types.TipSe return nil, xerrors.Errorf("loading tipset key: %w", err) } + if randEpoch > build.UpgradeHyperdriveHeight { + return mca.sm.ChainStore().GetBeaconRandomnessLookingForward(ctx, pts.Cids(), personalization, randEpoch, entropy) + } + return mca.sm.ChainStore().GetBeaconRandomnessLookingBack(ctx, pts.Cids(), personalization, randEpoch, entropy) } From b6b56320bf23575499a4ffdae16dc88e1fd5535c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 1 Jun 2021 20:27:09 +0200 Subject: [PATCH 074/160] Fix StateComputeDataCommitment --- storage/adapter_storage_miner.go | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/storage/adapter_storage_miner.go b/storage/adapter_storage_miner.go index f4d1e0038..3ebe874a9 100644 --- a/storage/adapter_storage_miner.go +++ b/storage/adapter_storage_miner.go @@ -147,15 +147,7 @@ func (s SealingAPIAdapter) StateComputeDataCommitment(ctx context.Context, maddr return cid.Undef, xerrors.Errorf("failed to unmarshal TipSetToken to TipSetKey: %w", err) } - ts, err := s.delegate.ChainGetTipSet(ctx, tsk) - if err != nil { - return cid.Cid{}, err - } - - // using parent ts because the migration won't be run on the first nv13 - // tipset we apply StateCall to (because we don't run migrations in StateCall - // and just apply to parent state) - nv, err := s.delegate.StateNetworkVersion(ctx, ts.Parents()) + nv, err := s.delegate.StateNetworkVersion(ctx, tsk) if err != nil { return cid.Cid{}, err } From 8b84e499c5265d2a2a0a49c3d9d2eaa0e2371d83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 1 Jun 2021 20:36:20 +0200 Subject: [PATCH 075/160] storagefsm: Handle preCommitParams errors better --- extern/storage-sealing/states_sealing.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/storage-sealing/states_sealing.go b/extern/storage-sealing/states_sealing.go index 815ad6ac0..b6ebe32c5 100644 --- a/extern/storage-sealing/states_sealing.go +++ b/extern/storage-sealing/states_sealing.go @@ -360,7 +360,7 @@ func (m *Sealing) handleSubmitPreCommitBatch(ctx statemachine.Context, sector Se params, deposit, _, err := m.preCommitParams(ctx, sector) if params == nil || err != nil { - return err + return ctx.Send(SectorChainPreCommitFailed{xerrors.Errorf("preCommitParams: %w", err)}) } res, err := m.precommiter.AddPreCommit(ctx.Context(), sector, deposit, params) From 39f2246ae69fac5d8d4209f00323deaf5659daaa Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Tue, 1 Jun 2021 14:06:13 -0400 Subject: [PATCH 076/160] Use build.UpgradeHyperDriveHeight directly when sourcing randomness in VM --- chain/vm/runtime.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chain/vm/runtime.go b/chain/vm/runtime.go index 00c04ceb8..7c40fed62 100644 --- a/chain/vm/runtime.go +++ b/chain/vm/runtime.go @@ -214,7 +214,7 @@ func (rt *Runtime) GetActorCodeCID(addr address.Address) (ret cid.Cid, ok bool) func (rt *Runtime) GetRandomnessFromTickets(personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte) abi.Randomness { var err error var res []byte - if rt.vm.GetNtwkVersion(rt.ctx, randEpoch) >= network.Version13 { + if randEpoch > build.UpgradeHyperdriveHeight { res, err = rt.vm.rand.GetChainRandomnessLookingForward(rt.ctx, personalization, randEpoch, entropy) } else { res, err = rt.vm.rand.GetChainRandomnessLookingBack(rt.ctx, personalization, randEpoch, entropy) From f6963523f8d6f22e90ae1094ebc547b7f568824e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 2 Jun 2021 11:13:24 +0200 Subject: [PATCH 077/160] Use filecoin-ffi master --- extern/filecoin-ffi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/filecoin-ffi b/extern/filecoin-ffi index 58771ba4d..8b97bd823 160000 --- a/extern/filecoin-ffi +++ b/extern/filecoin-ffi @@ -1 +1 @@ -Subproject commit 58771ba4d942badc306925160a945022ad335161 +Subproject commit 8b97bd8230b77bd32f4f27e4766a6d8a03b4e801 From 6854cc59448d9ab9bdb1115bade56f68c0df5824 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Wed, 2 Jun 2021 14:51:49 -0400 Subject: [PATCH 078/160] Update to specs-actors v5-rc-2 --- go.mod | 2 +- go.sum | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 9a2dd0f19..0a4f4b532 100644 --- a/go.mod +++ b/go.mod @@ -47,7 +47,7 @@ require ( github.com/filecoin-project/specs-actors/v2 v2.3.5-0.20210114162132-5b58b773f4fb github.com/filecoin-project/specs-actors/v3 v3.1.0 github.com/filecoin-project/specs-actors/v4 v4.0.0 - github.com/filecoin-project/specs-actors/v5 v5.0.0-20210528202914-a9f9f95f5e93 + github.com/filecoin-project/specs-actors/v5 v5.0.0-20210602024058-0c296bb386bf github.com/filecoin-project/specs-storage v0.1.1-0.20201105051918-5188d9774506 github.com/filecoin-project/test-vectors/schema v0.0.5 github.com/gbrlsnchs/jwt/v3 v3.0.0-beta.1 diff --git a/go.sum b/go.sum index 34d21605f..3791cd134 100644 --- a/go.sum +++ b/go.sum @@ -251,8 +251,9 @@ github.com/filecoin-project/go-address v0.0.5/go.mod h1:jr8JxKsYx+lQlQZmF5i2U0Z+ github.com/filecoin-project/go-amt-ipld/v2 v2.1.0/go.mod h1:nfFPoGyX0CU9SkXX8EoCcSuHN1XcbN0c6KBh7yvP5fs= github.com/filecoin-project/go-amt-ipld/v2 v2.1.1-0.20201006184820-924ee87a1349 h1:pIuR0dnMD0i+as8wNnjjHyQrnhP5O5bmba/lmgQeRgU= github.com/filecoin-project/go-amt-ipld/v2 v2.1.1-0.20201006184820-924ee87a1349/go.mod h1:vgmwKBkx+ca5OIeEvstiQgzAZnb7R6QaqE1oEDSqa6g= -github.com/filecoin-project/go-amt-ipld/v3 v3.0.0 h1:Ou/q82QeHGOhpkedvaxxzpBYuqTxLCcj5OChkDNx4qc= github.com/filecoin-project/go-amt-ipld/v3 v3.0.0/go.mod h1:Qa95YNAbtoVCTSVtX38aAC1ptBnJfPma1R/zZsKmx4o= +github.com/filecoin-project/go-amt-ipld/v3 v3.1.0 h1:ZNJ9tEG5bE72vBWYiuh5bkxJVM3ViHNOmQ7qew9n6RE= +github.com/filecoin-project/go-amt-ipld/v3 v3.1.0/go.mod h1:UjM2QhDFrrjD5s1CdnkJkat4ga+LqZBZgTMniypABRo= github.com/filecoin-project/go-bitfield v0.2.0/go.mod h1:CNl9WG8hgR5mttCnUErjcQjGvuiZjRqK9rHVBsQF4oM= github.com/filecoin-project/go-bitfield v0.2.3/go.mod h1:CNl9WG8hgR5mttCnUErjcQjGvuiZjRqK9rHVBsQF4oM= github.com/filecoin-project/go-bitfield v0.2.4 h1:uZ7MeE+XfM5lqrHJZ93OnhQKc/rveW8p9au0C68JPgk= @@ -279,8 +280,9 @@ github.com/filecoin-project/go-hamt-ipld v0.1.5 h1:uoXrKbCQZ49OHpsTCkrThPNelC4W3 github.com/filecoin-project/go-hamt-ipld v0.1.5/go.mod h1:6Is+ONR5Cd5R6XZoCse1CWaXZc0Hdb/JeX+EQCQzX24= github.com/filecoin-project/go-hamt-ipld/v2 v2.0.0 h1:b3UDemBYN2HNfk3KOXNuxgTTxlWi3xVvbQP0IT38fvM= github.com/filecoin-project/go-hamt-ipld/v2 v2.0.0/go.mod h1:7aWZdaQ1b16BVoQUYR+eEvrDCGJoPLxFpDynFjYfBjI= -github.com/filecoin-project/go-hamt-ipld/v3 v3.0.1 h1:zbzs46G7bOctkZ+JUX3xirrj0RaEsi+27dtlsgrTNBg= github.com/filecoin-project/go-hamt-ipld/v3 v3.0.1/go.mod h1:gXpNmr3oQx8l3o7qkGyDjJjYSRX7hp/FGOStdqrWyDI= +github.com/filecoin-project/go-hamt-ipld/v3 v3.1.0 h1:rVVNq0x6RGQIzCo1iiJlGFm9AGIZzeifggxtKMU7zmI= +github.com/filecoin-project/go-hamt-ipld/v3 v3.1.0/go.mod h1:bxmzgT8tmeVQA1/gvBwFmYdT8SOFUwB3ovSUfG1Ux0g= github.com/filecoin-project/go-jsonrpc v0.1.4-0.20210217175800-45ea43ac2bec h1:rGI5I7fdU4viManxmDdbk5deZO7afe6L1Wc04dAmlOM= github.com/filecoin-project/go-jsonrpc v0.1.4-0.20210217175800-45ea43ac2bec/go.mod h1:XBBpuKIMaXIIzeqzO1iucq4GvbF8CxmXRFoezRh+Cx4= github.com/filecoin-project/go-multistore v0.0.3 h1:vaRBY4YiA2UZFPK57RNuewypB8u0DzzQwqsL0XarpnI= @@ -315,9 +317,8 @@ github.com/filecoin-project/specs-actors/v3 v3.1.0 h1:s4qiPw8pgypqBGAy853u/zdZJ7 github.com/filecoin-project/specs-actors/v3 v3.1.0/go.mod h1:mpynccOLlIRy0QnR008BwYBwT9fen+sPR13MA1VmMww= github.com/filecoin-project/specs-actors/v4 v4.0.0 h1:vMALksY5G3J5rj3q9rbcyB+f4Tk1xrLqSgdB3jOok4s= github.com/filecoin-project/specs-actors/v4 v4.0.0/go.mod h1:TkHXf/l7Wyw4ZejyXIPS2rK8bBO0rdwhTZyQQgaglng= -github.com/filecoin-project/specs-actors/v5 v5.0.0-20210512015452-4fe3889fff57/go.mod h1:283yBMMUSDB2abcjP/hhrwTkhb9h3sfM6KGrep/ZlBI= -github.com/filecoin-project/specs-actors/v5 v5.0.0-20210528202914-a9f9f95f5e93 h1:PZ5pLy4dZVgL+fXgvSVtPOYhfEYUzEYYVEz7IfG8e5U= -github.com/filecoin-project/specs-actors/v5 v5.0.0-20210528202914-a9f9f95f5e93/go.mod h1:kSDmoQuO8jlhMVzKNoesbhka1e6gHKcLQjKm9mE9Qhw= +github.com/filecoin-project/specs-actors/v5 v5.0.0-20210602024058-0c296bb386bf h1:xt9A1omyhSDbQvpVk7Na1J15a/n8y0y4GQDLeiWLpFs= +github.com/filecoin-project/specs-actors/v5 v5.0.0-20210602024058-0c296bb386bf/go.mod h1:b/btpRl84Q9SeDKlyIoORBQwe2OTmq14POrYrVvBWCM= github.com/filecoin-project/specs-storage v0.1.1-0.20201105051918-5188d9774506 h1:Ur/l2+6qN+lQiqjozWWc5p9UDaAMDZKTlDS98oRnlIw= github.com/filecoin-project/specs-storage v0.1.1-0.20201105051918-5188d9774506/go.mod h1:nJRRM7Aa9XVvygr3W9k6xGF46RWzr2zxF/iGoAIfA/g= github.com/filecoin-project/test-vectors/schema v0.0.5 h1:w3zHQhzM4pYxJDl21avXjOKBLF8egrvwUwjpT8TquDg= From 1f10600f13cbce0d068b445eadf30fefa6e098d1 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Wed, 2 Jun 2021 19:04:17 -0400 Subject: [PATCH 079/160] Lotus version 1.10.0-rc1 --- CHANGELOG.md | 16 ++++++++++++++++ build/version.go | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c0cc71d6..f7fdd052e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,21 @@ # Lotus changelog +# 1.10.0-rc1 / 2021-06-02 + +This is the first release candidate for Lotus v1.10.0, an upcoming mandatory release of Lotus that will introduce Filecoin network v13. Included in the new network version are the following FIPs: + +- [FIP-0008](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0008.md): Add miner batched sector pre-commit method +- [FIP-0011](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0011.md): Remove reward auction from reporting consensus faults +- [FIP-0012](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0012.md): DataCap Top up for FIL+ Client Addresses +- [FIP-0013](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0013.md): Add ProveCommitSectorAggregated method to reduce on-chain congestion +- [FIP-0015](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0015.md): Revert FIP-0009(Exempt Window PoSts from BaseFee burn) + +This release candidate does not set the upgrade epochs for any of the networks, including test networks. It is primarily intended for node operators to begin integration work, especially miners wishing to get familiar with the new `ProveCommit` aggregation. + +Note that this release is built on top of Lotus v1.9.0. Enterprising users can use the `master` branch of Lotus to get the latest functionality, including all changes in this release candidate. + +A detailed changelog will be included in a later release candidate. + # 1.9.0 / 2021-05-17 This is an optional Lotus release that introduces various improvements to the sealing, mining, and deal-making processes. diff --git a/build/version.go b/build/version.go index c712bfbef..22c445a45 100644 --- a/build/version.go +++ b/build/version.go @@ -29,7 +29,7 @@ func buildType() string { } // BuildVersion is the local build version, set by build system -const BuildVersion = "1.9.0" +const BuildVersion = "1.10.0-rc1" func UserVersion() string { return BuildVersion + buildType() + CurrentCommit From 4a321c6da2dda20d19f995f3a3c1af15036ea983 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Wed, 2 Jun 2021 19:15:48 -0400 Subject: [PATCH 080/160] Fix nerpa build --- build/openrpc/full.json.gz | Bin 22806 -> 22811 bytes build/openrpc/miner.json.gz | Bin 8062 -> 8066 bytes build/openrpc/worker.json.gz | Bin 2575 -> 2578 bytes build/params_nerpanet.go | 2 +- testplans/lotus-soup/go.mod | 2 +- testplans/lotus-soup/go.sum | 25 +++++++++++++------------ 6 files changed, 15 insertions(+), 14 deletions(-) diff --git a/build/openrpc/full.json.gz b/build/openrpc/full.json.gz index 0963300b5887b349e91beba9e1380fb8d730629b..b99a3c9f54e84458ac8c66345852b6ec53ef944a 100644 GIT binary patch delta 22785 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 CRpuK2 delta 22780 zcmaI7V{o9&^M)JSwr$(ClZ|a_vJ`Hn#0#bKd{&d^}aBx@u;=PF2@T z_jTW$(*&B{1ezEMY&$r32DtO1505Qbt`O?JXr(A+q|ddF<{GB;C`_KB7I;oeK-puLIiuXWX6?N5G1UljLxi= z=yzIagyK*zN5=6yI3yB*#46nGiJNJPJaZ2N2);x(UQ3A+ZBoRs|;LdQ3 z(9lHZDN60Y!4Qbjl16xtcKYCFnlf&BN`sMA`t%NMt%`>uJ346qj~PeM*_YVw2@uFI zOQG>G5OxqzeTc20s2ft&(7dbv!f7jByJKc>wKB^|xGN9ji*O&LGY_07REFacdOS#9 z09P!bh!?gWZRUHG9ty0>Ov4}ZDMy&>PVeQ)yP z@gD-Cr#s*8Kcr(}yXtdoPYs}#q(R4u`v_dy4=RZKSEv1M6cS^Oh`|+%S%fK(ezM|5 zm4zVmu49iWDyl}@iS)e9$AzmmL3LIB`#O+j>vzA!cJ0{vOcy|psfeF}{dq`je)3H4 z-~#Y~fM%=22S!X7RBXb6H@MB=x?MMi>;&LXkm(`iePR*D{|FEn=?Hj$SblTZogZ{U zALoG!2ob{i9y@gWr})*(S^V8UwX@TUZlpya=(?Iug6#6>a{I$Wc{Z+3N&^Q2%>6WWC)0eUrYst&63=lMI5ob!dI8uBki!)&E zWyb0L7vf2G2v8WZyB*tk!2KZD@e+>wyhfP)3e11>e>p2r|1%1iK$(v4{SfcLp4kZ{ z-Pp;yo*E)1^X?w9%dg9%e4n}R`UsAmav*H^? zEq?TcFu-8=jrxX!MetZ@jwLxx{Rfba=Ybh%?z2GtC?|T}53iA6%p#+zJc2{Va7W@B zy2{(?{ZeXoi^=1s@cs64^B`2=V~}LqnyHE)nD1tK=zV}pF~dXK=Ji@N(BKXFfp!4{ zwv36Wzeu$DwA5^e6PcIt^#-{&pXrIjx0&a7()*8e&{U&%j8vLCbrM-;#0QWG>MVU=pCA;5g{K>vVPLK9^><((*lBVBG8RMbvg@tyv%jKVxfw8 zg7SweieWh&q!WwSLl2WVB?jMSy%<31`r1JQ;&SU-~-`~Y2XtZO298dfHXx6 z2`8^mD9TrZ&lckE+zZ8-3uNzm80aZ0oZlo4k(W&MXv{RY(jbnKYC(q=4X1&?Te^t| z^*Q@41Za3<3>b*_JrJp~U&wfC5gUoOf%7gzR=Y`^>LnLSv>!O)5RBdAMCmZgLJXcT zgK`ikKl#M=4<`aG+yH~5U3qwXUeHynG3pQ@phr*;r2U53XZQT^`D+Y)&iDIn@d)7b9R5bdqtk# zpOC)YJE*J7uz7wiu_iV6c)eeoIE%79e%|l$^x32?z=`H|3@>P6Qc(~MNJ{`46bTd$ z0pXoU?V!=+ee>3u{#9ff4i-mOjXN2gS&17jkPf0uIr)-!uyaIAWcK5GOG8eGTnd3 z{`eb9ZTMtdq!%wp+hNfv{>TlwW2r`KxxgM{HLF4Tp7Hm4&-(LeFZJMg(#r#$mlt5Fd8qNVp zO><=c1zbroo2gI{OUT$!i%LtDShlnby_J_QD)IO9HDcGsjOLpd*7rr@q2GM8irq``kduffRFS|*=~w7j-GO9ah%;^#G4%uJDx#p3Og|v7b*lYjKFAu0<&$GQ4_aGry0g zl9MQ7kT8{)HvTdwY%65ut|x`j&h#57@P$eItHY<&?dMW+ZY}n@T56W}(5#@6pPye6 z186nZsS;OuA-Cb9pN9ifrjk}FanTpDG0)tmEhV^?^JtUes)r@GB8d=e5>@`9snswo z9LX|v5SEl8UV-nKjQ)fUj%F2s^%*&%mrHa7P1NvBE_*@MNol|3sWaAf@jr?ufp&>C zkiX~7Jj}>XMcX&hyf#@MI1@ZJWsSA?1e6hQ;m*15jTtWL+wL#|f{qUG?(fjt^Ac(* zy*KD?p|d@M`g4GCnqCpg8&vm%u|y8gg(kcCG;1H!pC<>I-!yuyLlJ$j6;nZx%eO|iB|{ko zB*OcU!UA$%{;&6&1@tAKon`D~1oXujTfuxwJ7#7?iC?|l?Ipd55aVAbSGW0>3IBY0 z+fT0NzXbnruRnP<0)Q}5`HWXIz$e@%7%NJf)ndSJGv(66KuL385{8C6)oUv5Yv}We z&|5CIb~5CGg~RGZ;&K*BGCBjsr|OxZ>sHt_n(a(Mo%xaLm^TMITp9sEi$UHIR>E}q z;;JgxHLwG1)Q@;f&|^%urh4l z^>x51VmZK{Xne-Tfo?w3GN7|-K%jTHg1xIXAD&`i+>(P_RqGQ>x6vuL$V#`+O}9y7 zt%KCMgL5)aXDX}4Zz^j8lxAG0V-Pv2tw*PIE)M8|F-Ag~{SK@u-D+oF$>)Ps;T1Y; z;}ed3lG96|RoN|7Rp{OS$tOl@d|PIJ+jH)!`iu>L@Oh%?kGA)f$p2-fRYg_%pm1Z% zn>!u#@_utH7nK1C4vJ7M+(k1{21&>dF?z8U-id{%Rk&d=`Qdg zwpMy%e{i`h5d7%`vig-4j=}zw!k_CEt-a{u{>;rXfKSWmP)5tAq;SO&O`4=bX!|=LYrGtKd2%-Olemn;fkk8qtAI#==fSHc%k|hei~i%>fVRiQ&!v z-4cA`5TvZot;eqdrPgfwyMS~`Xz>r2(*?y8QA7{t$4VMD@XN_UI$nqO@or^o(5Sc` zRalvib?*1sZ&qSU0@pu09yo#Ecyx*zG=cwk#*ToZkp2Kwz78X(#3G|o(@8Uqy)QP- zq~sq5zG%f?JdR`#OhiZ@=7FyWXUTq2K^q-daQ09bmE8K9X*$e_>BU@ZMnKr0R7tg{D>R4u2eO^>hf6z!Zu5F!{3? zM6FyNJVNPHg_G-h7^mw4EVcA2D6wCNw%K)(8(Kcr(wc^2d9xT*IM>#4GD# zt{GpJKDY4h`n%=4n)y6FxTJN_UAA~0BRs(=ZxJ;wP{%WIUett0U7@RRHW~l;E}HmO-ItQPBGy?BnJ+EK9o_ZQ4Gs5M$PXakHtt7#-bNx2d z(L6W-u&L3?ru0hg;kBd`>B`Rs^+leEhQGv_t8rETt_!Yzge)+heE3rH$?x|kRE|0I0 zI}#d4vB)#n*d;zYv06c1vxP1aPSMyo;x!$h@pV}Uq&xD}i8X+0Me~sWp(Lkc5f@PlTrdwe_Pf5k4yTHaf_!P0DO(X6(*mHT}ZzB$$wRyOMDQrTrqC zbt1~1`hGm79&Wgj6RW}O%+3U81@hr8@~$Qy6gh|UPMzR=m!~qliB?r?q(^c1@A~8W!O(<$Hxf!n?KVI)aeI$0H$|B3 z$Ymve1uo{&bwIltxYfOu?P1ub*;DqHX=8hP+ne&+v%A-yTNcvrL4LrO@UDv2Oe0Ah@htOmPkJg7nGY{KX27 z&+IL$b4$mYMCjgyVzf0c+Zv9-09CJOKag|AAbnkYhd=s*W|jOgI;MhBOzC`pOLbxN z|1gHmf*g_H8@xZNYlzHZ5`^qmc5hT8~ z%)3`P>%-!?^R+@cFoi+Wi1b2TUWKFMwiX`lz78U(+H_ ziK%uD!Kz@KXzP!ps0;>~0y5cZ!ok#!5Nd=D_i>!*VrqG)&fS`twKll+F`2j>OO1s8 z#hEX6YXEi3@jO{M8y)B3w0PVY-9M-r(x6|^*z>j0+eU7}JSNSW>4=-%MHxS`=1ZCk z&&QK#h%=_wf;jUl#6bj^`)9XtuRV__iC2(kZF_*W<5{|={G?Ie zgFIOJlvOGY0#PSgCGzuC*G#E;$ulbKMsyt}OMp^Rm~2pAU&j*600UAt-IT@IoPJ6g zW;VbKliFr~vpnTeo3z`A4CkmPl873D3subqI~cj4J068$FO5UjyNI?^>~tPo5R`SVaq$TCTaZLd*5% zPLMPT#_nfR0_PFq({NV);xqTOJY`?*lwFIh^n$Zv6CfLhlpUjUvR;{$4+dtIO{S?) zH+o?BR*}dueBv}6D#_GpXl*>yg-kY+ifO+ToLl`OSLwZs{L-y<+r9qBVN%!Dx0B8- z8Kg>p=eZqX>zG`in6=VT&V2he}D$xLVcYX?Jn^Cj$B^Uk>=1 z0Axhr%Fk1&xy--@k~k$<`Gj=oiXJC*N@H0E5MF8Y0`>t~m?hsF%`wa6MxCETDek>h zoOtIN><&YV(#se1@5Af%LQqU_8p9`lFiFTaFkZ~ah@?h9wY02(JQNDq9PtPcsiZs| zIiX>?7!FKwgDc3wONP%>JtoX*1#c_0--$DWGmhBJmujOoV zK5E1&!A|M#nXl62jT#BhmINM|}B6AXNW_~keOBB{$} z7b|Xy{i)eJOwJPE*{=Qa>sOkZceKl?_qgIOMDWyMRWpA{te}#fuXhQZD3d^tgCaNR zzbH{F&ehB;7yFW-MS#wkE$Y~v`J zI6B_RH+W*UuZQS1M|Zc|;IB{XJuA+;_!1Z!*=8yd9Mq2etrj?5A*9w=7e3soB|rTe z7T}hcSgjuhp!~V7H^;9A1+5AmND{^&S&}p^%%4wQ6IlSE5%Poo8?RcRfI_U^$a|E1 zUg~nr#)*vUOq#7i=3#__M&@B_zJHoBmC#<`^L=xA`ehGiI%&B{I4+^e|04+PY^AadJ$yx1H_ZDu7K2+F7S`Qt}z=?bCS${VP# zY=Ar3xUL?sKls@z)(s%7(;j$%@$!kE;&2J)g<;W&_GD_dl{sC@e)S;`(AFiKZ!tdm zMt%n!|MwC6uU;Sj%`ahLrfr|dummRaZEg&Ki$exMaN2-$Xhk)EvTf&48hFE~ICRt; zD5f@y##O$WvYE=7^|0`Pc;Df> z-IK#({5y8O|NE&ysa-u=(Imx^jS*Re|NtbPUzeQy82z+3&l4&3_^D9Lyo5aofmZ`A`5J;bK z28KEkeFkVnUdCbH;2H#UK}Sn)t&UN(0be{6$6oLEZnzJ_^l3?V3*P#A zy(;BoRS#bUI=JHYj~aDc^U+i-{76TIwf_1%Fpy@}Lo~=&P!AMiGdv$A9Fj1&a1M}P z%4FfQv^YbwU}ge%;K)0q$O*JRK?%VW2$(T2V2#24QGnZ&p&)-lj91KL)*%tpI$$MK z{FJQI0<`pg?5DCKvQjW2cUjlaJtemvI-Rf>QJ}ZfWp&FN`NKwL>7~@L z`-Q6}UB4F|Z!6SQ)480*d^DO;ah$%UXX+I71^A$Ta7sU78k+oILzybNAQDB6PIH1g zen8}ajnXOCmy5CfFFnOy?vqs+pswEJOa&ASsz7q-YLXbh3Wv_l9a|fke;-!Wc&ySLe3kFzpzm3N;zvdB0(x_dI#wL+Jr&t_F-%4kKgb5_lG7PaJ4D@r@H&OO zN8Ln~`FdkTn#X3}YErP%B~wo5%yzyp9#HCRNtn85n|+jMc(ATpGaJcA#P;=|W0;F} z^&j-RgmPX>QcNi;lf}JHF&Abl0{-Ojng&n!3qj&Lmh&q_KMl)|H0gOO??h zjoCl2zVQ0w$Iqw2^a}vm>G{YrW9w}aE&|L&CM}_kE@?K(O5?!(wy^!$o=e2h@mFkG z;)26OXVeX0R!na>TBX7--GZjZ2THfp$`5Tn2!NfM{q-*b3 z@+HI^^tuCpu26(>)2ln)QxW=uSU)lS|)&5p4J$ zM5=?NxVAyJ`G~|7N}HO`7zb zr|SjuS;6T>uT~yf?kuq`2aEY+-qxj%KG)kxVxSxLwH*3l8=o$-XG$lnx)-nfMEs;e zx*to3TzL@D$`BB*RY=kq%P7<=H0Hj0zj{}1wy(eXUXDEn<>xt2C(Na+8Huk}?U3X! z#VUx1_JSg2tP6#JcFljGi0G*DRApqVB0WortnMB^IA-}(*1(lbix+b?Ah<`N%eLar zCrYhHX*4mU9b-~G=0z~SjFy=ck|1-^^hJ8Bb1;O;wowmfUir)qjzyT&d*P1#zsjdU z1pg}#K zcGJ^?_vP%qy0gvfzMSkn^-z9FCYqT!qSw*V>cbvp_rY5Jef-mW9p^IpP9(b-^_S{; zE(;zN&4EnITFv&R#33fQd8#Z&T)=iTC}oeKV?tYtel|t?K2!g#@wM@&HoaLZw8=&u zslc12*|iPO2W?}dYELy2r->LjR0e_9GD!OaQm7mXO?u$4Kb+eBppBQ|9#LMM(GlN> zBxR6?aNrXg9u%U5v$@p-It9gm?=@*ZP;FUjI~q#(!@v*I{&-FNS4XfEfwoqiY1jXe ztii&CI0oa=CLHJCZ>s@miQKtJoAMHb$R_%O#C-ri7Aa;8r>YlS$HO78v|R6J|0Jw@ z?FH}tSSo_cooU>>M_f&Y{kn!JROhBSFS$mhW2@!&=*YeXz)a#?DGcrYs^(}w$gbjG z!rDhl#JMr?)1@mVp*r?I4Z^gTwiAg3k)?pR%W*$wVd$GADrfs(zQgTTb9aW1hpg56 zJR)!}cv@KUhAYOMXhxUsboG;eRa9n^`upv&ljIyGpIjKyBY72TBpgg z!=-HG9^#mdOH^eshf)iDks?0{!>BEUTp<7iH&F3m$w93GKNNIk@tpD={y~VDGW=5z z(D5?}e!X3E@pH4qV?+fU={bYEwJwi;g%H6AI5$4>`aFA9BbG5=aZUa zO{ey~p@S@Ec7wFbkY<&My9SU*WPiK6#4UOQ>e5E4S8IKyt5@?;)N7lYT0D*G9t?oG zUnLxk#hIP8a!$)*kLnJJHS8EKxTU`)Q6W-9(1n>p(k;T%adj=N#p^c`YlLBomvr*e zpP{*&A-Oyu9GRE7pFouC#!}A_e6|ThsEp$^u3&y|rd3A#JbI2vN(>kAPaYIOKl7$} z7NXoH2;JBJGa4C~N&S`1f|$34I{*SC8F>}oUZe=UkXj!LuB~AfTS3i%#l`$l5PBkD{PA3Pl@klrC`1R*vvr#CY1PooPx2*RG=IW-U(U zrbFrB*;~v|lT7whJt*h~@pmUte?&hvAQG%K_g&cSwmN2&TV$u(=$6}Lm1_b^x{_Cu zbBcKWzsu;b-uogj)$Dx=CWmjW znHAUN)sg*eCby9lQ9I}geuPU$)cc-(fabM1Q%`6Zal!h!yG`vddw+EuuH*abZD=Ru z5IgdD^q>BCB;TkbDbG5e1kC6t?S)0u6~vFHgLvLG#;+pk>Gy0Cc?tl1=Yc&!H3}~o z!GxL6+)|no(9?*`P2c#`6wone@KYxO z{GGNaOPIDX^-p*MzDfb956d_(e(j@L%^5C8!Tujv`IyAQ;lrYm9M+NE8duQa0(HBJ z0!?i0>M`~jio4^$3g~KOQcDmgR)_YkY0wln7&bg41BREFO5vnZ4bD#MKD9$*V0|A0 z4vo;JQv^uz-65$6O^MRUqgE zJvt!3UXT{gjn=kA7l3pjwV05BlheIKUq*jy8*rdQq9Z&H60LH^Ld&Whp%n=Y0|j%# ztL{K}2Y-jy5|8k8Ti5g9DW)JcbEWDWCL2@jSdxB4< z7l=4u(`yqhV3ki%;#W7G>#CQM2KiOQDw-e-MxE=-FPSB>`3kl%Ns6xcwd<Wgs-+5q)*h2{-yNJJHbr(_qhO}_XMPWi6haAS9E@uzpzFCx{8Omd8{VR=CB? zb_nCp%=3Q{@;at#-sWT8#^q+~k}x7jKPbo<-**y8tVH^}J0LC{t=tACN_oXG?*ZJ2 zIrcC>=_1%pLjNK$SNHDa@k`NYn-i%{IAsa*2rxmdRGDCCZS)9#TSMH91;GP3CjVVf zr8Wwc$5wI5>(N}^V?8bVS1GRKn0LQ!(Z{lyS$eK7K>h9lmuQR1)<%3GUGb$eu~?Jx zOW|xtC%4=)v?8*UiG@bi5#A5uunuUULO{U@H{1{3_mGr#OPdH$*;Qo|6D<(gk!Arg zqu5erplvC2Rz2-bCRf9HukTqK3h!HFGQWlsN2$V@h#3Osgk^itAR80Rk+JRO=X5MM zi3Fss$a*X6m&eUZ_}& zm{S$m)t0McSp-3KQeiJdHh}D|Jmm!zpW^?r+GPl*?yWC78uH8}ONQxA6D)dNORUtmtSM2TU*V@gVBK^+dxieu*9!4}PKhfy)qo{yPjH*yAd{x{_V>*hL z8xyl2BzJCcN$}6n7A06^?1Lp9%D?|nJTgqxX3RNR?q5Wx#DmJ zGy?A^7$VuHF-gH`RHZ3>YT0GT2_-b^=9P6NhVVw5QPa$dSippV?pLKTWo6+gt`bYL zg+86y6Y=l4UmQ5rDbEy|%#A$1a3B2}-#ia`NG}rKhh5(g_wO$RHmC=-82ej3T`@mh z`+tAk;?3mU6`iQE-pb2lyZsS^4;Of#^g@-LezI?<1)n$OZQj&KSCN7-g*~r=(XtDW zqWk+}2`Uy$E*A(JwqR72Ek}HCBWoBXN+2a2pK3Gl@VMXFSR@}c5UdHisbaL<-*33^ zK;nDf-Wxt~3p3^4n$Z!V&I_>yTk_vJTxF#cXTUkz3%!^E<--!$8^n(ER(3X7$A%Sb z*nIPh{O!Iq%i%ss)A66bgl5@xC1bSm zRb=yB$K;ezcX$7zlRI#)RP!NdDe=5v)qzggOINJz z$T&Sj2g#Cm?fL@Fw}awtZ0TV*qs>&OtEp4pw6cAM)3FS>X1H0dM@zc@_bDav8n3PQ z;M77?`f04Mb&AVu*}V zJGx!}^p!7ox_AB+!?m0uC(*S1apjQL^Z}l2Fw_Y$edS@G2I!paU2uaIwTxDrkW-_P zx_^w4Nm$CnP{PvV)vjB|6pNbJ3HV7^?`zB+4q5Sz@gTaq*Oy>)27e-m{vdauIv7;uVwgJ3}fJUr5TKP${ ziD21=a?#}&9%w~vD`t%hO`0YWN-5lz!4hh{fphi8KSwfRPO zFkN{>u3*Cr;Gxvcp}Gww$O$kSfhF^`OhBuP<57sm!>6@-kEKIhcva-hg+fraa{KS+ z>gypKu!FWb!nqC5RSn#>>yy42q_u;@2u8wG&8_hGiK=W%2$J(*w7`}`)lOGR$k zIWG}TJSB)YbOMqzy|Y4_%Ugw*I8mpCo8~ysf4+H=GV+QMjt?!Oo$rc|F8%Mt*Fbvg z*X}u0i<%?LNo8VJj|o=M{`jBzT1Pt2O3mrCI!X38v46j;j5}t?5(n?!bqwoi!?BcL z$92yYeWD~5@?=^VwGt#}zqYh@(EzF9zQ+p&87Ck2B%10TQTE>{Xto}9e2y{)wbz5^ zM77s@I_E@McI~gQCV%am+8SC@FoE{BL9IZhiynB6Tpl9wlll|#{okT`M=CbzHKVyM z0a>7BhUlp}IO&kNSkAfwHlP*%@8QiW(9MAj_lyHsnEYiZn|##s=f5BufhSxaN~-En zYgMjh1~tE`fkIZSSiaVbzl{HyUuXA)W4;;w`6juh)-1G*}x_G(6T(74sE&A93>ALGw^L)*Be5rUuB{C8r+p> z*V~R9>5d>3K`bDIkr~=*2h+EdoD{%?Li1Ot4ppmNra{ZEXMK*zss`&-5|1#q>Yl8H4~nZT>e5?vSKdo? zF3O?n*EYGFQ*XjTU=8EDtEq_o{H&U&>-dbUT}^J))u~B?B*ya7zHMyV9r9rm%w0|H z#df?2;|`z3OBu9)1a#*)B2W> zp@}>O2(wR8lHzYMP@P@Ss9F5;t0diOEI2kW2`3!;vV!`@Mg>Sdd_A3{eMw;xKV~=g z1lNhbo_w5Uw@N;IKb@K`ZcM-b1rR9Y-C_I;@bl%kDnyL$(%o-W-;1qAYB@152@Yn* z@#*n)j!EXCi&_Ijq#i~OmnvpDg2Vm4sOq+wDA~c%Z#F|CEzXBS5qyY9QhUT+`m~)2 zjUHGJ1NoG20&$Pc6auBsw*E74fkZJ+B=N=jhrgbW>szi6{b?1I>!O#raH8Ew_DJ%2 z$l!LuYQ=Z+xXgAfN;*H`LD+5nnX*Kn**(v`sr zlhf{^GChOB4(1ZD4}@7bPGJH;K{)<67G!@1EwI!T?;A}gm+;*|G{;W(ZpDk z&GQr?S>5oy8#`ad`L z_5`sN457HNw>M!SEE08+E{AG^;Y9-7;w+I-t~=bFCUiV?EH-%GgAavKDah}y9 zX1L7Z_L!avElv=ekFNh|G%3d2C*h6tr|OAu_hx6(WxF59Nus~0Y1t0#{!fK0dy;)M zdcBAaa$dpzeB}UpX#7&ga7uzYMlM@CwaR6|55hU(3UllL#kaIqB@fvFeMES=sT)(pfT;3KMQP2qevGqVw5ZC&h(vuu6fOzqbg zKD@CeIj;dODSt(~bd#xa2MLm9?~P}igtnz@%h?3V(R7@KSR6aq?mgOVd$PVP?7C+U zIwoCO&hDk|?uF^*G6SB);b4WX1-T^i}d{*;pk+&x%7GoD_ z8h0Sn1!C*_d$Ja8CFRP>r)k+HTk!9 zG*)1Q*JU@9=rA+Zr|8C)(ZCpfTB9fotzi{(yQ|DIvNDI=5~ZlL`#fj;NR#bf!zmFT z0go*1fCeca#C#~|IJ~2&p1&(SOVG143}jNcxe$H{$jCJ`=+ROU(SW|nYkg^BCa7Fb zXWaL<~-&aV%QT{MS-@d2D~gvoVKdz&{!N!MsJ5(L_Z$hA8Q7aI7F zUP1fc!|AJ?T3*z!x`QG@pr+pBvLyb~i`PbAk(z__qM0z)kmtZ`&GQPdxs&I2JMu|1 zQu=yzn3Swl2JwP4M4{75A&$f~CU~1na0m?N5HaRc-J!EG{7A5wWyipUHL7k)omZKP zC3%zvbx(SQkvX=-8;SB1S61=oECOU4`0!xPmBnV!89`ZYE8WHdc^bwsFDc*jmzP~A zYWYAZ z-Tgh331za#S!-wLaydUUy=C@A>ZblCST$zrK1+9|k@#sPm4eVvvsV4PifD8uAu&Wv zA1x|$^gI&YH&b+p#Z_wA2mqXOj8r_Rl!{nwOjU&J59?zS^;QxENOjhHeBKbLtJh!s zf>ef4`mn-D0io|y@lW;IjKUC80v{zx1NZ?1xFo&0C6k8uySXi7qVR>Wa)ZC?pZ!WE42%&r2{(LP`HH5NelPo`4A>`SFKG=$FcN zEo{4=Ixdx2UGTeF_I9W3x)9e#*EMBF3QK|9srSoO7cIoukA-%}XxMQjS%wVHzIF#= z7QMH%*!fStn+?6Nf@?7$8V$Wx;AegIbeIL?V^`yG^q70_K71%?HdNq04Glmt&?&IJ zqYW?!{^vlL<1Z!!T=crGmRq&0IJ?8^WB%I35-bgUS9XBYO}JNeD!?oj*Z5&eJvTv` zac~qbw6hhDA+JC$F z5#nsOPpp@K`*4kR)!prfyOebea8ZvJF)c*aQDV#KTBBjBCJI1`2#2QlLEMpH{|>H0 zC<^KOW-%AYGRcxeXI_FxbmO>7L!3`zZrX;6?(z%@ly1c2{100-x}T+$-_a};A1d6Z zaGDsBg6nhx+P?qUGMk@~$4<2dj`GEZ4W}OeWEn=sVjQ!6%mlV-J~Xzt0z)0LTLz_W z!&6`PaXhjbzMZFC{an2fspl)ZaB3QSkR3QMxLxk1b6s{B$wQPvUzHaldDT>vVnC>tm9;=6NO-o z3zC6QP1Ay5Nl*ax+q|W?i!HR?8a4WO9xD|&OYo&;;3o*bj3LWoVT$WYs!$fOlQJ?H zvwCA(V(b!bD{qLxs<`71Z&kZ9iLQwA*%h!j9K8aG5PElLL(ts29txOF1_K?Vpdh4| zmIf=pM8e=qor$_8!gx2fVeNEes?JNb^yLbN*K>0IF*=Tkwio`(`srQ5PmDu&Jwd3C z2UXqG)yLn>$Ir{x#}z~flW%GojI!VP3>oiT2vLGQGw=+Fr>F0iXCU`pd6gtnjUJ>l?|0K`TZDMqd&kdC6jV-F}S-*b{zpM zva(S@<-e(mOuJrEsiiklXw^+jMwZ>po#4DC6mi5kQg_$_mYfs7ky_8bK$mwmA}g<3WG5T|x1=!(weeipQb-6Y zrGB-QMZq&|3ErZk%t+d-qYOaR`KVhF_5=?S?kA?tHh|!=ikHO_w}qoRbzaE3s=?Z6*!HVw9Ca_srVII<^$KH444vG)WcE) zD$HfTe@& z8J$ow1T8Gv^4AMb$7%@B=oTZsQX|<3lqk=xf)6F#HpX%b)WL(~mCi53(9!~_agSwmcG7G!KdJ%dS zJL1oN(2=XSf2i}d!E#X6__|?wg~!@+fWkju`To^(cxj@*UzQ~1+6ouILEP`92c^Vc z*b;$ajpWRaQmNv5Cvy^Q38a7NQ?PRbJK<*YR{NkLTe&3lTq2tpJnK!!30>@0iSpg> zSXB?gJDW?T7Dom;>uxhQs%-0~`Q(REJm9-0b4f0ycYubUlgS7U{~O}go#Cyitvc%H z@o^26#txU2su?x@v*D)6PUlzvrRM#gvyjf?AAC&9-{{o~cT@I1V~!wJ5OW|b#Hbgg z7CN6~SOwb`nCQ6uEw~Cb)&_ORsXR^8FkM7c4o>-&n}D`f`24)Kh1u~V<5n-NNz8>g zQ^x2X^8`$XOO*Z+p6b>DvwT065uDuBaTC7i`YJMgbguDngub&YtVx2!xfQzuu|*Fy z5>@8JpM0zzTJsy8t&V8Hwz-3Ic2~qn9KQ(C2#tk?$KBfFS%lGnO!SQoUv;_~ItmIb zi9Bh`K7b~DSp_3^EV?+hy`QopeK#BB@9$A2s5@p_6PAL8ZhmCK%`hR}@63hpjE z;&qd@vXXy&`~`y}D} zpH=ksH}lKv4nfoI;x_@W(e(~--jl~pv${`!FR{LX()X*8(4{*++j!X8wyaTRhr5TO zum0~vJ>JFsv2aD3?Cs4R->>Vd?HgaGfLV<|&JB;>{4-tT80?33pYc#hV(*LB^L$W6%7 zGFX+Gg%|Q9DBEWu4fQDbH6SY-9u!%kUgPFsr%|6r*)Z-+;qODF8`C(Xn(8Ep(4gg< z*3GJ^1qZBXvpW**^O+0ndPo*p+(US1^&P~m*mr*-!*uJ*a^zzFNQPD^6(Z4o{gkaE>Kt5H7v0%@{| z^yhvNzKqy)wnA6B*0;5~Wlb8iu|n_61$ z%Gtz9v2*T{@9ZZV#EygcPe&bAX4Xv7;jq=W+asd3oh>cRb}H&e3xuO<<)0d@Q_m^- zgc~AmDpO8&VI}v#Q79wfDUF%ZZMMi12Hk(KJB5t0k?cm;Xswntug2ON9YOG)^P~Z7 z$N}}bLTZ;yBy!y zAZ~A9S2G}yKZ?44}(YS)zSSxT^m^a_yLCnXl_zB@WxwhG8Qb@3 z8q-{Kz(AzMKk_s)q1srO9~AKuuestmoV1D7<$s?cYB;GGU01_|$EmUDdD+FdQ&5r+ zA78m}Jk&wd&6+p1>#bXS74Cnol`ti)I@yN~)UAL?Tr}kg-jpiw4zA5Y6y2sbQyB|W zDQ1|1Nekg+F6y}TX?J|=CS{?@z?hsidV;qN8dqE|lT66&E1Le5ltA4L8>&9VE4=7W z7urGRv9E@-&g=9i#lb_vl7bSQ5Av>j(~ zdX_lVhU$J?7c3?li%r2h99>$z2|kNZ8jH2_kB+pS>nT#Qc8%dlRu3`C2T)qj)gvny zEZyU{+h?QiaCr<`y_aqAX@2nje0!dWWVknBpn3c>2_dDq++xQSj+na!HH!@9^o`h} z#P01Hrp72Fz9r?Gd3;NI`K^ysHw@2?dLOSFzX^|?nN}BbY!MOtGB768Dez^~vZKar z@_VVFH`=n$MZ>xqi~2J8J7C){S)LiSBCWfTYSxT>F6(1s8t)H+mNa;U%S)TQw1<_> zJW*=h8fB`(H#MCh)n;Z$Wv{7Ap@2M5Hx^L~Fc!kl<;|!_nUpRM=FPVZp%qlYteAqN zGV>>iC@k<=3*#8OTBXVgAO?oXaIEpuxyt!Z^%ikbh>LFCiL*y`0(5rHFr{b;4!8Qc zw6H42GUv{w2?p?=`7S;f+JiAmzoFOi^QXMc)Ex8lSE*RIW8cpOL#^Mf$*J7R|8yZQ zn!z9-`RP&o;IFXph;ehoQne0#ss@E^NC^~fFA}r$8Am$=GcM*2fc9z$eeTGZ)pF$f zu={L8ns1p_5OTcb0G2Asu-dO4eRnDSF`N&O#VS;)@*pD`t(Ozu&|pJlo#jy&vKDR| zOjOPbINZskyBk!LC3Ov-QRCzBmsO6`KG}l6;5nM13%yTDs61_Ha}cKk&`6T1+*6x5 zwHFhRRgO>mgX|f6dCT2CKQw9NVFjAM_6eib_Gv(>cG*N1v(EQWrsNId<3NP;R_e=-JU?Xl60elSh zUbD}U_1lK`LMIv*>Dn6vIUMn=wxL%d?#76c2-HnMQO(oR@Kvqc^6|eVb9>$A54~bai&hKW4~NLT25&t%k*HOvLf6^O<%wt z5FJXUu7u(y5X@`vj$3%Pt8mOzkTOEf?_Ze$l|0ICC+F}Tv?tfxov_6e4!*LRj|xv) z4Yh5REjJq}VmXa!t9hk}E%oU$F*Fv!t+LSCs zA*IBU09FZG!-=kTTH&4YeOpzxGvWEZTOsrbFoD86bRuETLx0(W;d2_m;mn_k(TyTA$I0tX-#0IC@zwj~ zgcq#M9Q1AMt-%wXf3*@Q82p6Tj6VH-3MMVUbK>D92K6fPr%{n!VhVvwMFUivmixPM zI@00l#Z^|WFD_JcqSr|S(R$sxnYLPK#?h1GwDae1UC0Fx0kB)De}J988|h}ca+Oij z8yt3`g9^M#d=k&b_r^2_fwiys%%}CdYqj)-eCnYQtgOnImde>$IMiohU-U8p=gEOs zM|rTZtn=nvV;82Oh)_Bc6~PWl(A!_(GEAu0U$wBA(Ux89!x;x+y>5rXWAm-GpIg?w zw{ztMU&j8cSFviGFb7ysFKOGv zZQra9^aZd_t}+zD3?1Az2NfmS2TTH|jvRND>Ke{av*j;Uu;Bhz^1xrQ5_OPsG1uwA z9P_cV<~S*2kD!26l>c;VlN_@&8k}-0{abEXP98&bHF#^1FP7s0i%|Xvf_GD|?2DG5 zlqP#tPx2C$mW6K{`$$t?d6#Fu`zfad#KNbZ;h_{XL5)?&rr`IS9!IPQc;Qg9pL1Cg zfm9X7+@(E|| z)uS!44h3FEBHUZ{!C*_-ffWE_DEfu0!JpQRx#H2f!=n?LDD z=^R8)t)8aWw1wbM66qWDlV8{s)u?xuQ4VlqmY0+=eG3~U&o$!u(Y#3gTU1kACx|aA zd_|9`{zE%Cqp1{5)_<{e;{S`S3n){6crfn2!a%p-EmPxRRWoTH8cVD%H>sUuFZL{O z0wCHdO&wio9Q=6ccWR~9n&iU^#<1DheJ_!>*32Fxm$T5P+Uj&nkFat6AQst&_S>R2 zQhxvXP^N|~EnjQnUBeiP-!OeHWs9YJ>u{Rl$C$R%I)oI}!8?}{?+`=vBZvTn7Dc1^ z5c#{*+(zlRcdPtriPJFVcHBP{t zSN&QQ2uPZ7L<~kKvT915llG`$`#GZn8bMW?6-n=lXR?iiScy(ns9Z7V0fPX(meAUK zqL}FZk1lL3Gc_0n7S==ydcpHwQJn9~nj?A9&FkmqdW`UG__7hsm&jgZC~EK%xd&%Y zUJNaJ-_f28m#6_g#wM>j?O$HY+o~Sx5*~S9*%`+h&rkyY2o(?{<`P)=T6l$>s-B60 ze^puL%U5?No*n#{EMg;3gpC1iqm*{w%XZqH4!u<2Zo#MD`EuE`=iXSX zjV_*Ao6d4oHXUCe!$saj8R@m?yioMd}_i(p$PB#EC3zU|I)S+xf zn9Cd{v-~zagEOAe=K+da4E7T?;fBhwx>lRMg99`tK8lZrzTUZQ?zCT1Y}$vno77&P zM2a~IZktMMB!0cuqKs7O7!1uz99IqcV3oVe0`X{+g!a5&Ac3T4%#%P48G;lu#mO!nnk8WxwS(S(M^H z9pmR>)r|ZahsG84pp~5nFFME?+Y8k2%~SJTJ?aD8e1g~#CR>B9U0mil zA|ojOTr3V9O%5$w+=D(n^upPOvY$o1F571u%LBn_jj-0|#8rR}iEn{JiJD676Ibot z7}$%<%b8eo{XHw(BH)nshizug>t9M-6G5qEWkFXre?7O>57|Ufll1$uZ0m-5Y@L38 zKcW_v)$8ZkFTexPwx$Dkc2)Ns>7K-L;+PA@GP{$k>0vHgbrn2vTgS#TbG!_O7VzYq zEp8sGpz*PqE2CB;dlQ$ib{_R9>A0SNUt7EE9>-H(bQzjtnN+YfK6rMVQ2s7-`k<}< zMkgwB(*0d7vzJm1t6ATNxL46QRB=7283RMc#vr!V_#6b#9x8b${+eToabNG_aN7pv zphac!+8KlGY_+FVBi7p_f^Q%3ox?7R);g7tFcPxA)}5x1up&Bi+EV<|O#wY@)A)>1 z+*!>_(|{J$;T{=fu}kvp$m*}XOkKKtI;7-GF5NglIW-!R*OZ7|{wK=@(;7=&T$4ip z7F~WfmTe6xE$Ed@FzlRoN4mb#zEEhsi~z(P95Yli_qlBhRR(>g*s{oKQD9(oD#nAN zdkPO-z};|k&Ypo6sl8X4RU6?6e?9xB*>ngUI&vR0E|MsdAE-S(llqH79>v$ zp)02TL7Lc>kfK{mPfP+yu6!JuPe(G@r6vfsc0v9Q=IkRyAZQF?E>rXCO7&S$q-NF| z!Dk$ypc3-B*4n0IHVXV_iZdmsRq=}Krse=?v3Zb`*s0pq$8HQ+76ap<)F2bBGkk2@ zNHmw|W0dS}acV4gG=V0^2kBn2HTgXXr~bY0XS8&MuH%dGn?~E6BAjCs1jcSIrk>1X zW?oE5VFzyt`3+sk+2PgVfx+VDkF&cc{Ed`g6{YOeOBo?pj-HB?Cc~a; zzv^!Y#rrHS14x57n_d5pZD})xM-IE6(vDt;W7?z*a`-wPfqPi;WDRc$&3iY0Q!e(s z%S%}+?(jyRn4Wv<+-a|wCD@L@+77__HMF~|Wo`Ayub`U&ug(*Zrc#@`fIlu;Hh!2x z+b;i{UR^-?Np>g0sp^3tYML2ES*lg_{2)pfgsuu(u)MA-@Nome!Thm9F7O9WMrL9p z3!PJDDzeO-Y={Ff%ZYKCLBe${_IQk}jMffmh2t~lWueITTr$cUDl)`>ZviB+69D)B z>gPO~rB<`|*Kid-ePQBebnRo>K*2W?5HDd~7s~z~H1toVdi0V8$>6q!ROda$f2t5I zO&w1$1OXz>j>u(}CVk>Kb$H&S3_0gSQl>crn2@xZYz2Nh2{TRg`(lh_biHf)*&Fs# zblTR+FQpW1Dhr8wR{2bPO@l+A$s)54k>Cr19(er8XLvJ4n)tEIu6%NJB3?uuMKqq} z(^_uN1y#CCuq-DpY`cprY})bHKZ8l{T>y9vSUM=paNT?>Wu&uM?SmU^54wJWwNw(= zDBEJ$^=m|2CTNrKL^+kAjvOW+E@DJ5ADeAMEAV-gJCbp~{`qCK_*cm|UU z@ZxNg+UY8dUJv9f^iT<9DIKCqL;b(_eSI#P+97S;5hc7Y<;XrTmjv~;3x%9cmATQ7yvBs66S@6Chuyqp;e z&>8P@)VJ--<%L9*f-W4Qi_|^=I~{?VV{sWQxB*xJT8=qN{z229=ZQmcXNalBGxwe; z^1^BcvRndqqKD1$cbWY$UMXkqVC|?s9&p4SWk+ajS0R266~n3@)hy==T_JD~pV#s2dQ7?TCegx%L}=5fWgg} z^Z~lnfKO9B;t9)u%eU5ARdrA_MvrdTec=(IK1lu=4t1ILwWUR$(K1VL{5ZMpPiz$W zk%eb-ZTC|{m2v)=-b@81KZlyLtiE^PVhKkn)GDPpP7zy?0s|FrjVHiCmmPt!79%!8 ztNoHJ<8GA7D@OuGipmOODsSkGE-k?}4}Nx9@W$r8Nb!&Gk>+?ty zV&O3eZ+5aO8ec!*FvxP92k%_Z!1C0Zd}FT3af|9~c94u4!LDnf)?(z)$Nxzeo_8H! z&G%v-TLj+p@*W7>X_~S_#u}AhQn^$R+m@U>5%6frHxW}y?BgjK_@x{Tg})&xCGizP zX^TJE8YJ%O0-JWlZ=Q7cyEP@cGHQQage5`92X7L!H&m`|m@iuz>=;n~gO z?2aU>5kfxW0eS6z2NfcN(5t6L>}iH0;~c8$im470-T}EwMS=Pil)_c~5{)VrpVnLY zCfFW0$=LT2lPd*E*A|Xc;81lU8T}pKcJE)G8yL*#CP_c*?10W!rOMLDDWvBINPAGuLqgv!kowkmxQo_{K8%M5hYZ4g56!TbCF zzu$&g9@CfkcDv#ZSsDTdcSqt*O@Sw{Ns;;9TOuplZv$>1pHGjj=ff+uw)%qe@vQIe zo-YeL{XotqCu{dohj+G_cZU8R(~_FyCyv_s<9QaggI;o3m*k`ED$+i+F^-3O9u8`E zAI_go-hOdG*w#za!^>+S)_c~o`=eBL)X+;B`dvrPZi%q8H2`1~RpE;%mQBS%6v^&O zM(($FHeUPnf^R#jRY||p*t?rrh=)&^@aPYkR_VNB@TyHxj6|5kTrcQv9HX#OG~=yQ z^jjWY&$OC{O;rjhCD x$HHOLd$4V{Qxjup3Jd68LO5>2iY>gP)hV>=eDeVx1?AyE9 zW!u1g&_}x7(H-CdTl8Y(vN6pb4jkek-go~s8}m&^ccHhkDEoB&@YlMO8hHABqG``q zLK`r8@E+KX1z9`#yw@KL5A~Z4tIoRttcgn;=u*T=%z@>>j!qvO$cmHMf1pA2n@85a z|E`b=O?^b&6@O%%aa?Es%AW9Aj~UN8I=mO3?3@@L>r8SC9}r!xW||9aLZN0Me5-f# zm22rEeMza~jb6WA@FHD<*ste4LmW6RVf}h-G3I(bv&ihV4Ll0n*T6yS{~rJ3uf-qT z1^IuM{o{ULe|+rZbJv*QIa<`_F+oPcX%6Zpik0EDkbm%IIBvabX`ag{QOtEe^oa|{ z#I_M--@zr8KEf-@>gbsduc2!r3@BWCevUjav*4%i7`pF)WzB$b8*rol?hA2m*Izys zF}o9c^kcnCKXgQ)&EF_UOdv3i;RE2h;DP8(l0vs#3K9;Pa>F?TDf77$j z5b4J~g@2bfIq))t{J-cQSRGp;8X3US(VV`vrV$v}%uqL~r-U?#W*FJpP!oO=r@#A3 zEE5+KUoky~_1BUi1SSz)*)!;-$o#<{Ysk}p78Z#Odw4i}b9y{H86KV7M1%87?uQ7S zW{c2x360x{OYRsfRuZ5D1*!PVQ1=Qh5bGv1Cx2v0CoUoLj^+%suQ#3`>(BHRWY32h z!{V3P3mZ6JBQ_8-dE-$R;X?Jk-Zj=0)+8gpS<%ZL?#B4HI@d;{7^GUwm>4AxVJ5{* zC(vkL3shRtM7TIF)PczgoEI}cp|2YK-;W*tE2@-z%+R!a2CwjEe4~F_kHvs-0ssV%dS92jSHH#RxI_sbV zD3kh0f;qacVz!;%;JzA3gP{cBiS4W?3;?JCz{d#4@crw}Y=ta}us_EacuqLDvuS7e zcLlIXY%ENtsB}yd6hL{h69JNM6veX!wSRE{oI+lR%+)ZB6^G_nz-CaK&kUy8g$Xf5 za}=){NCB%FN#oODT3YR-S*F@YucA{^O0?ntvk)_b>vLF6!DwiR?Wo zYyP}?8eIa)tMIRSV6!1Ifpk|GBfQYQBf4bozv0VI*Po~VetS0l{qyACZ_lRh{yjOn z{#Aqc4!H!|4D9ZJiU(>}UX+ysdATHO8LBI{AGGv1S0vVl5_y zi9Fr|gn1tsKl8fZa6|f~gazc6)$V|`f_h9^d>YkJ>5r9VWnd$J81qj;T(qqHkje7zP!dAOn5u>` zD@C(WzG@NcNx4em1tlixGrof^H^`gitZcdsahH^M%VAsPus15U{GS`)uPmw!ahHU2 z%Uj#!tvo~i!gh#D&jAJ1Ie#glA&cY~$n1`h$>ZrtQJpm1t;EayeipBCX2z$Jw8qHP zT*en3#MIM(=0R(IAj-@TUWgBcX^@x!O$~+Ay4nTRkf+UwYeQ3;J!nAtQzS=rG@y-% z2@UPV_a!ox8p0+rcr*)W_R6BjU~dl`VBA78Y=yD*OCCo8+DBGu34gq0H@OJ=Lx0V~ zp7V#n>>pr2F#PVB%cF-O7#`8t(7T1U^B;2mUuX3|Z0A2Sdk!Dw!^1fmt{#q7D|k4# zJJcVm#&&W;>TRUyDG?;kW4lBit&Sb6cDAH$uO_X7)35!*)L-OZ>d~9J;LX+;&+r6L4Of)hs?JYF>b9|z@H)| z(*mLGr~v<2L^q@hB2#f)2^1QUDog%*WMHPuuRRrI1svB{(twOitfp2x_HmKtg>eCM~sbG zSGAz8;kF!Yf$kuVo?o1EekODYT|R=wcFIUT)#lEF*vNi*e^S-bF#3b@;qQce`S8#E zKfnL=uRHkb|54||(V6Fd{_U;t<^I>V@27)by?5lp^}YM?^6vNlqw~t?8aukj{Mo{l zBwQgR^?w7TXWnfMh!O;{R}5eIM?ToRWOs@dI5c6;yxXF6#I*)CvU*t1#ngu>F0XO| z0V~OqMv%ce(bJ=){`j+x2*;8n`n}1X2Dir~DRj(&}7=uu!htVF-p@96LD z9mG22|D3}A{z3H7;oz~OvoQei<$mbmOo~BA=YM&KBmH04>iso+yEO1`OZdOP{GvaW zJf_PZYKa;_S5=Y;l3zoqM&g?))~J?s!Y8tHqKTLpm5#+`S*7?Ihn4GCW~ND;aY{_E zV695qy*qm8LsN1SM}B^MKj}$AA0c{%YZA4AK2Ax3_-;_d2gouozeavDc%RY1G{|Qc zQhz%@Q6zl5eV_q~f5sj^h9koD33#v}ru+-s0gK^q>fr@8TnIeKLAo5|rpPO$*U6e! z%gu|Ax-8@+K2Swi+Ao=wolz)6#ZShEz^c4iAd66AI|d}g+ie5wwt;rrz!L#Vt`m5P zJQ@$yR9g~}(8}9V5HC1h0*qnd-U4|m8-LtskkL=cmV@k)o85cx7I@V%@Grazj3@$> zxLMidtiTjvKj%LE_9OH>uz;D{0zT0tbSE%THFjDU1q)bwK=ImPW??0@OR}iku}iYZr0M75SLtw;v$lS0^dG>^p1F;-E8DQJ@)TX(D1TgA zsyxSTB`2AzjAbU*KAU|9C$hsIW6)c9-F%HkGyBcbMB80w?XI(S*IB#ktlf219{qFU zi40^fut6Al(Hi+vQfe}dkX(1+w92W5bUPbT5|WwDnQg3rt6@Ctg+y%Xkb zx!&ZtZyzBACZJ#f=rX%Ncv60*f|b^5Etb||*$s;&;KWKGI9bgs*SmiL0Gh7cY0fd|8t(X2Pm*Ei4;@@WX)@#eYzpb&E-H zJ6W;3IM;a&S&QY#zxVSocV$O6M}}hqdl6;CWXA6L`g4HHtP*Eab+dOn$Dk^7Im7-{ zsk2#2IFQM*7X>|qOm_6~%5_UG?E6-t0Z}C@QFWx3Z~=^miAyLkh{Rycvp6T*jgJJYC}W25E@c4K9Yj2^zk!rDMO z9^34;RX5pY)Ybr{(0_KQr%|H0;(fd0w%u{t?zr75eC1|POzZBroyvCHa*$%5)9b!P ziwjISGOaxr@>`V0OMo!9s5+W+C@xk8rU}i^qU>nSo2=(bz!r8fj%BPRMs=h6KjNcR zlhmGGQe~6o11(1A}<;(+;B50Fefe z-60jCV?%p#r-gfRxlYL1&#H>!YxFmMhEu!ucHj5jwm=aB3R$clVT2cN852}Ohp@V2 zo4znWu8}i^v@oh4C$^bQ`SI?g5n=7E3KWcy83yoqMpO9fSBMr%s*j2z^7|NblU*Oh zO$+Y>q~nY8%zwoc6|P$+Qm~<%@u9=3P~yU_U#W;2GaA{rPHH!mKjEhGOu$II-9Q%uy66Sq@@^bvo5sB)VD_NsnsOAm+gu(*_kQUPb=|w zl7!3^W{wG(0|R!)z_R?rFur^ndBe_7dzCErPRK**J`*T z4S&~SZHu*QSStzL`T&M7u+Tqs-0dtvTSDLUHd`2OVYr3i7KV2ahGna?&_(L94nnrGq?GH*)h3|ghrDxFgeqzs*rdyo=uNADS_QO&3JA82PpgQYiz3Qh1WzD#UEe1n zR7nvDKpG>{YfuZdZcwX*c2WyP+^)za;R0o2C&x@1rB{;iqQ=W@YKwn{t(?))s@qoG zmQc63xd#3*w>7iQVpO)1V~}jH&3`XwRq0Ns(iVMN^i87gK$2g;j17brXEWla4|Y}o z-Z%kO%q@V{HIf@YY_dhv7ExQcNWz6=QP>yFsj-CSIRcA1kP#A*!G^e|?FC}Ez{K;% zW6|MM8I}vnvQ|<< zHote6LhC{EAT~XI+8=wb*}{So_ZND+rl*0$!i5VqkNhw=QMCIF3;)T}7-ni{9#sY& z{vXOn)7mG-$-hv>v5PrHD}Stmlu(PpI?uXy_&q=tG{qe7q7I;J3MYyB-B)=wbL>#x zkKUquR4LYav*t$9qkB^I*e|K$d^ziA(@T1JRkJ)64QNu5*xxTyu%|ag4 z$Xs*$H$Hj?~YhR$D>JA|#7UM3#Y=n8JW&Ch3g_HrKnkG4?xc@1(8I z;j%kv#SDgdJ9XmTBgj z8nQ}1Rj{Fr4(tR7_Kav{*nl%$$P2 zwH{i-?s>{jZXhFcA!-OuNTX57iBl~So3L!;MI@X}vRpLAz3n4hbrN~%WzzstO4f!#t@l`y+YTY!c$e68Q?zUj&1}MBMIIks; ztbcyCDzSm{Rq)nLz_GxB<`tYPik?rpcIsdjgUUOX`WU%IRW?wcp7eUX*jDO`bH0bV zqko#uJxJM)!;?41N4-vX>pcJc$XPOHFI1R{RKhR|+ElU3qV6h{E_>O0Y|B=08q_{W zVs1pkV^F@TC5wWBYM3y*iR`8JB2p+9cz;e5UxIZfk5|ccT{;un<)z6M#2y)udg{?BuCoa#W(L4H>*!HbQ?#8@&Q?f3c|$bZCf zVR>c3q&uD<8g(i0;0jWg1L@(QqhFIC58waJm%yX%TzCB_nT~!*LJHz+LO!^Yj(%oP zbSKtJMMpaNyL$(@$wzF&Um#j6sXo$sdPn~gx@eA|nR;=75kwhysm1_QEm8}~8js@B@rMi4}+-zcSIBhDQJ|><0+BJ|o$hGk!Hg<#&Y5>;!J<0ZI%GZ0J#7J8C1pc7c(55|h+KiNs2!og}uD zqwsg4;~%Hx*63DAxq8T3(;u<2BMF`Wq;E61xBk- z_pyCT3HmX{@YPx-HwMaS&xRb+eBrPI1LQGVJ(WY1z4QPEJ=yJNfkEwCRCQbcK|=14 zuZ50o5T>f;en?tw=lj1jgy)1=`b`CTB|Xpd)w9c~SgV0yI_eL41+I4kg0`C7}Qi2>4| zMoI9JG~~x$vbie~t`w~;oO|htaDxI%CLH&cFB{-X+46(W2b2P%V*XZ~Lf(6oyWZe_L z%O0K_(8_WqBFb5Becv+`BZ&1@#gh`FjyX~(itFxw&dbxc2?ka@@)|h6K=gqJ zW6$mYvcQb>{IsLLpMNHQUqKJLcdRd`{A2PzKYRQ^*`Q%WSSe;0c2cEuqTfoVZ?mze z6pX%r690H=CEGK58_+1SWi%o1Z=pxeEQ=U*luAgX_^5Az^sE=x4-ORWMrg}V(La#i zWfYa40J^;938XK+-ejq#0%ttk!6&%gC^_hj`6~U*_$rC%*?(B)hlj&Ar^myS;n7J0 z!$@B}lV&MJ6W8C))ueTs#mQMG!c?Lj8*Kigs-)0hT5>BSCWD$b!{0T=q`_18fD z1fc@2!oO7~nSP!C`;>Ax^fNA0JdX9Q#BrXH+BnwpA*z9jl6O&gKZF{ZE8l1#xyPcM zUAwgm_XZWkPk*Gg4|gat=DAwh(%Uh7d~%7?0N{pH8b(`1kc~ILRZF@>*A`v3imolR zMbIX_*+ihidxXvU&=yfCUagE>g)A{0hv~RNU`@s%3H};f*?2;tt6McRTZB=$F!<*{ zm|URzDZ?(iWQNLxJSWtWQS@gExJ+Cm`kPg!jjlAJ4uA7kX)}4Ogd$}13$6Q?+KbBJ zaKC}AT%oxuc6$~?C`4h~Rph;i^zH-j1N$$u^)BNlisc5^LO2RL0Ph3O8Nvo zs*0UYb$<(T7bXto30#TnLRCrAUz>n}*AAddy+Dd&8<+NsQ}YASm&CZO$*8x@L}=>8 zqWyksEehR+UY<$l<>whj8>5a;PkK?7K#MEr5SRW6fkkU2P{--0Ey4M}L={n84dB+A z+RDb7U2@qAv1aY7s-Chy0Tmu_GYP&OJ>wLEl7AR4+Yui0wap-|nL%88uWSpoF{sUt zfBH%Gl_8eZSBZZnvicSMA)ofM4}Grhh-mjq>=WAUmp?1LoK1-Iwfj)lrS(Z{+N*sr zBSbY`8g;34a9anrb#Noq<);)pMV`Tq1EuWRyf4JPh3=SO%;ca7P9`YS+R?sFNp6WH zN`Gn;T9&v*1rrl$l&~&{H$?l+i+bACWZdBX;dUSV({ZvSge`dDH!`bs@@G_CzRa{?<|n-)2w|825gch5)AFg+P(PtSKYu>zo%Cm*cW~67nFoh&z?*}a(K|T+^WK|z z-xwUhH)eE-S^>pt;N3DAhQNA#*L%%4ou4T8n(6eb75)Mc)ki%ZU~#EF(g(dk@1Wm5 z=nt-Yr=!7O)I0u5@94i0kNTf|Nj$1=WSur@BqIVOK4S@LqC&H0oY~<1LJ;g9b$@gl z;n=VBB#mf(cE$uu2Y+IV*C98kAK|x~gA1AYhdw8+4H#RSfWiZcY?yUYq7ae_$2sn) zpDDZCQ9$IB0$NAw@cJrhXC0e)MK!e(w6O)fliAAxy(6pBoZk7N^Ax=Vs0!rfF&DCO z4PPdqJy||@$N-i;8XO;<93P+dj(<)M4?FDD^gD3$QGal9a(LS7pPZcZ_~R&QGw2^4 zAD#@44o^Dzc=FlPM@NSp{R98oR~>wqeAfEI;jwljrFkh;C delta 8001 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 DNC=VS diff --git a/build/openrpc/worker.json.gz b/build/openrpc/worker.json.gz index 3c05c7970e9de77ba40672c2767e8e3a537ec3dc..85c12082ac3ffab03c2dff7e0700e056e867c80f 100644 GIT binary patch literal 2578 zcmV+t3hnhDiwFP!00000|Lk4eZ`(K$|5pg!OS4e?-E8Z(o^!hwpdY?udwpmEgqFrO z8;R7CR8lwa|9+q-%aSFHs zY))D0o#Tut*nlnUfV2hmm1*Mk_IAdv*)(7_dL_|sU%0pfaiv6Hs}r;$d*yeK&6$Gl zsx>mdE#!BRR9ui5w64HbTwKD`LVgc@y&)6Y`pkUdIA9vMqCdbwejgP*8N_X!6GcwY zEB%p9i4Cn0aovk^3xN<^oS?^RcC7@cKrkd*7fgX*#BC*5)vReobced>lWVrTX1BMu z*upaKJR%l&h}RvMG3##RHNzJ6i6EYtf@x6|oZ_+#Kr^ryE=3y%fo)4vuz;NhF)jfIKhglrLa zEVvIsQQgjzxDqVvCyG`rS0IR(SPLREj96HO46Hsc*DP+YyNpZt9; zZk84n@OMB3IC!W;0QzP40&JpmNjSKrwSHT@%-brE*i!F|1+IHzVX7b!ip&VoeVMsJ zR3d^l2KjYGbEDtws*DIh7S%`D&-7ezjCID5n3ry+H@L|eS;b)t3RO!RigH?2tSOp- zQXJjrN-}VjZad;_(?sQRx>2Ew4KOz2NjspG8Fzj+|4ws$tp4FzRw5GS$5NuZ)jD|O z`-N$+duCyevc#vTvXSwnl^FZsS*qHmCtId9#Q@hiw{vFtPpam@1PSoC0_0M5hb~S#B+cC(+-@ctPhw~q*79cB@mag8BQ=Il?Y3AIs{E3$SuK#>?0tpS4N4z`CLIDwZ%A3a z1Wtvi{WP6QQ03+}%(m2=n40OJ2H+ZiJ63>un-6doY|1ObnkLE5S8T2k=$hMLDr#*n z$%{OGp3vV^r7}AFZ!T5bm3E}jJ#hnVMlV6~K-iJwf;XNoB(;f)hl8G_3$7lBAAh(6 z3F2PMjV>6CKTGr1pW$EH;4Uj__?olFm4HXC{&Iu43XK}Z$|&HUQO(%IWO&##38^_M z(?x39N)nh!Z>&OW6?*-AtMGx}!H9X%m4~^SGAfDUFO( zQjZjIzMcdDby}Z4Z}+Y;jpb{JA=D;Y5aw_X>9!G{qKA+*Bn`cVX23w`w-Hrz7wBOQ zEUI_SeA6G2iU?J0$622G#pBG(Ve(0drIb=IxR;$y+kx%4RH7g^e+i`4F3p&(C_tgS z^+iwxA`VP8!L#rS{y-V>M!#B(uMCxjmkQU$$p#tIOU5Sce&4 zJ+*9}(T+WCpd57iZ}zi?^7(89Z34z7U_8=*v0t!+aS1LE37?q+QhfvF2KBFArO$TX z7)Y8eGXCq0E=JUm zHN466P&V^Lx}V0YJP7zY1g|nHzDIa~w@?wE^;okRstuE}_YfiMX%sE{MUxU*Os3F< z15D8=O-k~5)$jihFeGN~^%kS04R1-S6(<~qkGQQ3CYdgJlOMze(tnW7{A|%D{WCS` z%RBJ=Z~Xh;ez4%4|83$;VKMCwrnDa{&Vm32z59W2xOdrJMN_%%yxRLrPD2&ST{=Te z^w&gxN8sDOE?E3uyLDLS#rZW8C6wBR~ahIe{4XPsW$S*NqY-7Tu#{jzJqBKK>; zxw~cmSE`XG_JGCJkVnLwL~0Hzu_h9MdDdQucME;w3aL5KKd3|!l%i~Q%{;O|irfJr zXbKNVg3XyDk>W_3R5Bw9^an-c9n$jZ1nv%Zoc)^1ZJRA!YoY(WXxYoSW$zp(JV#tr z^3&AC(TAXHwoL&tZ53<5&LW5~(NXTU(d6YryS&}=#Y$vB;`=Wm@6L_+swj;K%0fv5 z;%b{F!*0yoH&04sy3PGdWQM=;cj310J8e4*t=hdtLU$pd1^F8NY77Y15Ku$7Q~8XW zY{c^=iRYVwcy0A+s$TFC)8)r(9`V0s-~Wwa>2bL&l1sLpFQcm(==3`1bozkI-6cOA zp>1J2GfUg+mr35NL|zNfO6rG?;f6t_YEQ6wnq z-j|nm0mW0_9G1I?{uS_@q$JpofLJaEutfe8Aq z`Ho5p5j5fs*yz>6oZ53lnM3subst{fDr)Per#`Xoz=?ZNp({?9g9!SdYt4zAx6*>M zni3>dsG!-b>A>ynTz8%bVr@0ssL2|Frb%76N+!0KUorng9R* delta 2557 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 T&EkIn009605z@QC{dxcZJ7fS- diff --git a/build/params_nerpanet.go b/build/params_nerpanet.go index 8d3207bcc..b45295b76 100644 --- a/build/params_nerpanet.go +++ b/build/params_nerpanet.go @@ -39,7 +39,7 @@ const UpgradeClausHeight = 250 const UpgradeOrangeHeight = 300 -const UpgradeActorsV3Height = 600 +const UpgradeTrustHeight = 600 const UpgradeNorwegianHeight = 201000 const UpgradeActorsV4Height = 203000 const UpgradeHyperdriveHeight = 999999999 diff --git a/testplans/lotus-soup/go.mod b/testplans/lotus-soup/go.mod index 918b1f508..cefdc799b 100644 --- a/testplans/lotus-soup/go.mod +++ b/testplans/lotus-soup/go.mod @@ -10,7 +10,7 @@ require ( github.com/filecoin-project/go-address v0.0.5 github.com/filecoin-project/go-fil-markets v1.1.9 github.com/filecoin-project/go-jsonrpc v0.1.4-0.20210217175800-45ea43ac2bec - github.com/filecoin-project/go-state-types v0.1.0 + github.com/filecoin-project/go-state-types v0.1.1-0.20210506134452-99b279731c48 github.com/filecoin-project/go-storedcounter v0.0.0-20200421200003-1c99c62e8a5b github.com/filecoin-project/lotus v1.5.2 github.com/filecoin-project/specs-actors v0.9.13 diff --git a/testplans/lotus-soup/go.sum b/testplans/lotus-soup/go.sum index 67eb30cba..6d83c40a1 100644 --- a/testplans/lotus-soup/go.sum +++ b/testplans/lotus-soup/go.sum @@ -252,7 +252,6 @@ github.com/filecoin-project/go-amt-ipld/v2 v2.1.1-0.20201006184820-924ee87a1349/ github.com/filecoin-project/go-amt-ipld/v3 v3.0.0 h1:Ou/q82QeHGOhpkedvaxxzpBYuqTxLCcj5OChkDNx4qc= github.com/filecoin-project/go-amt-ipld/v3 v3.0.0/go.mod h1:Qa95YNAbtoVCTSVtX38aAC1ptBnJfPma1R/zZsKmx4o= github.com/filecoin-project/go-bitfield v0.2.0/go.mod h1:CNl9WG8hgR5mttCnUErjcQjGvuiZjRqK9rHVBsQF4oM= -github.com/filecoin-project/go-bitfield v0.2.3 h1:pedK/7maYF06Z+BYJf2OeFFqIDEh6SP6mIOlLFpYXGs= github.com/filecoin-project/go-bitfield v0.2.3/go.mod h1:CNl9WG8hgR5mttCnUErjcQjGvuiZjRqK9rHVBsQF4oM= github.com/filecoin-project/go-bitfield v0.2.4 h1:uZ7MeE+XfM5lqrHJZ93OnhQKc/rveW8p9au0C68JPgk= github.com/filecoin-project/go-bitfield v0.2.4/go.mod h1:CNl9WG8hgR5mttCnUErjcQjGvuiZjRqK9rHVBsQF4oM= @@ -262,6 +261,9 @@ github.com/filecoin-project/go-commp-utils v0.0.0-20201119054358-b88f7a96a434 h1 github.com/filecoin-project/go-commp-utils v0.0.0-20201119054358-b88f7a96a434/go.mod h1:6s95K91mCyHY51RPWECZieD3SGWTqIFLf1mPOes9l5U= github.com/filecoin-project/go-crypto v0.0.0-20191218222705-effae4ea9f03 h1:2pMXdBnCiXjfCYx/hLqFxccPoqsSveQFxVLvNxy9bus= github.com/filecoin-project/go-crypto v0.0.0-20191218222705-effae4ea9f03/go.mod h1:+viYnvGtUTgJRdy6oaeF4MTFKAfatX071MPDPBL11EQ= +github.com/filecoin-project/go-data-transfer v1.0.1/go.mod h1:UxvfUAY9v3ub0a21BSK9u3pB2aq30Y0KMsG+w9/ysyo= +github.com/filecoin-project/go-data-transfer v1.2.7 h1:WE5Cpp9eMt5BDoWOVR64QegSn6bwHQaDzyyjVU377Y0= +github.com/filecoin-project/go-data-transfer v1.2.7/go.mod h1:mvjZ+C3NkBX10JP4JMu27DCjUouHFjHwUGh+Xc4yvDA= github.com/filecoin-project/go-ds-versioning v0.1.0 h1:y/X6UksYTsK8TLCI7rttCKEvl8btmWxyFMEeeWGUxIQ= github.com/filecoin-project/go-ds-versioning v0.1.0/go.mod h1:mp16rb4i2QPmxBnmanUx8i/XANp+PFCCJWiAb+VW4/s= github.com/filecoin-project/go-fil-commcid v0.0.0-20200716160307-8f644712406f/go.mod h1:Eaox7Hvus1JgPrL5+M3+h7aSPHc0cVqpSxA+TxIEpZQ= @@ -288,8 +290,9 @@ github.com/filecoin-project/go-state-types v0.0.0-20200903145444-247639ffa6ad/go github.com/filecoin-project/go-state-types v0.0.0-20200904021452-1883f36ca2f4/go.mod h1:IQ0MBPnonv35CJHtWSN3YY1Hz2gkPru1Q9qoaYLxx9I= github.com/filecoin-project/go-state-types v0.0.0-20200928172055-2df22083d8ab/go.mod h1:ezYnPf0bNkTsDibL/psSz5dy4B5awOJ/E7P2Saeep8g= github.com/filecoin-project/go-state-types v0.0.0-20201102161440-c8033295a1fc/go.mod h1:ezYnPf0bNkTsDibL/psSz5dy4B5awOJ/E7P2Saeep8g= -github.com/filecoin-project/go-state-types v0.1.0 h1:9r2HCSMMCmyMfGyMKxQtv0GKp6VT/m5GgVk8EhYbLJU= github.com/filecoin-project/go-state-types v0.1.0/go.mod h1:ezYnPf0bNkTsDibL/psSz5dy4B5awOJ/E7P2Saeep8g= +github.com/filecoin-project/go-state-types v0.1.1-0.20210506134452-99b279731c48 h1:Jc4OprDp3bRDxbsrXNHPwJabZJM3iDy+ri8/1e0ZnX4= +github.com/filecoin-project/go-state-types v0.1.1-0.20210506134452-99b279731c48/go.mod h1:ezYnPf0bNkTsDibL/psSz5dy4B5awOJ/E7P2Saeep8g= github.com/filecoin-project/go-statemachine v0.0.0-20200925024713-05bd7c71fbfe h1:dF8u+LEWeIcTcfUcCf3WFVlc81Fr2JKg8zPzIbBDKDw= github.com/filecoin-project/go-statemachine v0.0.0-20200925024713-05bd7c71fbfe/go.mod h1:FGwQgZAt2Gh5mjlwJUlVB62JeYdo+if0xWxSEfBD9ig= github.com/filecoin-project/go-statestore v0.1.0 h1:t56reH59843TwXHkMcwyuayStBIiWBRilQjQ+5IiwdQ= @@ -304,10 +307,15 @@ github.com/filecoin-project/specs-actors v0.9.13 h1:rUEOQouefi9fuVY/2HOroROJlZbO github.com/filecoin-project/specs-actors v0.9.13/go.mod h1:TS1AW/7LbG+615j4NsjMK1qlpAwaFsG9w0V2tg2gSao= github.com/filecoin-project/specs-actors/v2 v2.0.1/go.mod h1:v2NZVYinNIKA9acEMBm5wWXxqv5+frFEbekBFemYghY= github.com/filecoin-project/specs-actors/v2 v2.3.2/go.mod h1:UuJQLoTx/HPvvWeqlIFmC/ywlOLHNe8SNQ3OunFbu2Y= -github.com/filecoin-project/specs-actors/v2 v2.3.4 h1:NZK2oMCcA71wNsUzDBmLQyRMzcCnX9tDGvwZ53G67j8= github.com/filecoin-project/specs-actors/v2 v2.3.4/go.mod h1:UuJQLoTx/HPvvWeqlIFmC/ywlOLHNe8SNQ3OunFbu2Y= -github.com/filecoin-project/specs-actors/v3 v3.0.3 h1:bq9B1Jnq+Z0A+Yj3KnYhN3kcTpUyP6Umo3MZgai0BRE= +github.com/filecoin-project/specs-actors/v2 v2.3.5-0.20210114162132-5b58b773f4fb h1:orr/sMzrDZUPAveRE+paBdu1kScIUO5zm+HYeh+VlhA= +github.com/filecoin-project/specs-actors/v2 v2.3.5-0.20210114162132-5b58b773f4fb/go.mod h1:LljnY2Mn2homxZsmokJZCpRuhOPxfXhvcek5gWkmqAc= github.com/filecoin-project/specs-actors/v3 v3.0.3/go.mod h1:oMcmEed6B7H/wHabM3RQphTIhq0ibAKsbpYs+bQ/uxQ= +github.com/filecoin-project/specs-actors/v3 v3.1.0 h1:s4qiPw8pgypqBGAy853u/zdZJ7K9cTZdM1rTiSonHrg= +github.com/filecoin-project/specs-actors/v3 v3.1.0/go.mod h1:mpynccOLlIRy0QnR008BwYBwT9fen+sPR13MA1VmMww= +github.com/filecoin-project/specs-actors/v4 v4.0.0/go.mod h1:TkHXf/l7Wyw4ZejyXIPS2rK8bBO0rdwhTZyQQgaglng= +github.com/filecoin-project/specs-actors/v5 v5.0.0-20210512015452-4fe3889fff57 h1:N6IBsnGXfAMXd677G6EiOKewFwQ7Ulcuupi4U6wYmXE= +github.com/filecoin-project/specs-actors/v5 v5.0.0-20210512015452-4fe3889fff57/go.mod h1:283yBMMUSDB2abcjP/hhrwTkhb9h3sfM6KGrep/ZlBI= github.com/filecoin-project/specs-storage v0.1.1-0.20201105051918-5188d9774506 h1:Ur/l2+6qN+lQiqjozWWc5p9UDaAMDZKTlDS98oRnlIw= github.com/filecoin-project/specs-storage v0.1.1-0.20201105051918-5188d9774506/go.mod h1:nJRRM7Aa9XVvygr3W9k6xGF46RWzr2zxF/iGoAIfA/g= github.com/filecoin-project/test-vectors/schema v0.0.5/go.mod h1:iQ9QXLpYWL3m7warwvK1JC/pTri8mnfEmKygNDqqY6E= @@ -529,7 +537,6 @@ github.com/ipfs/go-bitswap v0.1.8/go.mod h1:TOWoxllhccevbWFUR2N7B1MTSVVge1s6XSMi github.com/ipfs/go-bitswap v0.3.2 h1:TdKx7lpidYe2dMAKfdeNS26y6Pc/AZX/i8doI1GV210= github.com/ipfs/go-bitswap v0.3.2/go.mod h1:AyWWfN3moBzQX0banEtfKOfbXb3ZeoOeXnZGNPV9S6w= github.com/ipfs/go-block-format v0.0.1/go.mod h1:DK/YYcsSUIVAFNwo/KZCdIIbpN0ROH/baNLgayt4pFc= -github.com/ipfs/go-block-format v0.0.2 h1:qPDvcP19izTjU8rgo6p7gTXZlkMkF5bz5G3fqIsSCPE= github.com/ipfs/go-block-format v0.0.2/go.mod h1:AWR46JfpcObNfg3ok2JHDUfdiHRgWhJgCQF+KIgOPJY= github.com/ipfs/go-block-format v0.0.3 h1:r8t66QstRp/pd/or4dpnbVfXT5Gt7lOqRvC+/dDTpMc= github.com/ipfs/go-block-format v0.0.3/go.mod h1:4LmD4ZUw0mhO+JSKdpWwrzATiEfM7WWgQ8H5l6P8MVk= @@ -588,8 +595,8 @@ github.com/ipfs/go-filestore v1.0.0/go.mod h1:/XOCuNtIe2f1YPbiXdYvD0BKLA0JR1MgPi github.com/ipfs/go-fs-lock v0.0.6 h1:sn3TWwNVQqSeNjlWy6zQ1uUGAZrV3hPOyEA6y1/N2a0= github.com/ipfs/go-fs-lock v0.0.6/go.mod h1:OTR+Rj9sHiRubJh3dRhD15Juhd/+w6VPOY28L7zESmM= github.com/ipfs/go-graphsync v0.1.0/go.mod h1:jMXfqIEDFukLPZHqDPp8tJMbHO9Rmeb9CEGevngQbmE= +github.com/ipfs/go-graphsync v0.4.2/go.mod h1:/VmbZTUdUMTbNkgzAiCEucIIAU3BkLE2cZrDCVUhyi0= github.com/ipfs/go-graphsync v0.4.3/go.mod h1:mPOwDYv128gf8gxPFgXnz4fNrSYPsWyqisJ7ych+XDY= -github.com/ipfs/go-graphsync v0.5.2 h1:USD+daaSC+7pLHCxROThSaF6SF7WYXF03sjrta0rCfA= github.com/ipfs/go-graphsync v0.5.2/go.mod h1:e2ZxnClqBBYAtd901g9vXMJzS47labjAtOzsWtOzKNk= github.com/ipfs/go-graphsync v0.6.0 h1:x6UvDUGA7wjaKNqx5Vbo7FGT8aJ5ryYA0dMQ5jN3dF0= github.com/ipfs/go-graphsync v0.6.0/go.mod h1:e2ZxnClqBBYAtd901g9vXMJzS47labjAtOzsWtOzKNk= @@ -786,7 +793,6 @@ github.com/kpacha/opencensus-influxdb v0.0.0-20181102202715-663e2683a27c/go.mod github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -1264,8 +1270,6 @@ github.com/nikkolasg/hexjson v0.0.0-20181101101858-78e39397e00c h1:5bFTChQxSKNwy github.com/nikkolasg/hexjson v0.0.0-20181101101858-78e39397e00c/go.mod h1:7qN3Y0BvzRUf4LofcoJplQL10lsFDb4PYlePTVwrP28= github.com/nkovacs/streamquote v0.0.0-20170412213628-49af9bddb229 h1:E2B8qYyeSgv5MXpmzZXRNp8IAQ4vjxIjhpAf5hv/tAg= github.com/nkovacs/streamquote v0.0.0-20170412213628-49af9bddb229/go.mod h1:0aYXnNPJ8l7uZxf45rWW1a/uME32OF0rhiYGNQ2oF2E= -github.com/nonsense/go-data-transfer v0.0.2 h1:WmkpzXYsGFeNTCpuEtJXJauT0qehWJsKITWWqTOFDzE= -github.com/nonsense/go-data-transfer v0.0.2/go.mod h1:n8kbDQXWrY1c4UgfMa9KERxNCWbOTDwdNhf2MpN9dpo= github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= @@ -1532,7 +1536,6 @@ github.com/whyrusleeping/cbor-gen v0.0.0-20200806213330-63aa96ca5488/go.mod h1:f github.com/whyrusleeping/cbor-gen v0.0.0-20200810223238-211df3b9e24c/go.mod h1:fgkXqYy7bV2cFeIEOkVTZS/WjXARfBqSH6Q2qHL33hQ= github.com/whyrusleeping/cbor-gen v0.0.0-20200812213548-958ddffe352c/go.mod h1:fgkXqYy7bV2cFeIEOkVTZS/WjXARfBqSH6Q2qHL33hQ= github.com/whyrusleeping/cbor-gen v0.0.0-20200826160007-0b9f6c5fb163/go.mod h1:fgkXqYy7bV2cFeIEOkVTZS/WjXARfBqSH6Q2qHL33hQ= -github.com/whyrusleeping/cbor-gen v0.0.0-20210118024343-169e9d70c0c2 h1:7HzUKl5d/dELS9lLeT4W6YvliZx+s9k/eOOIdHKrA/w= github.com/whyrusleeping/cbor-gen v0.0.0-20210118024343-169e9d70c0c2/go.mod h1:fgkXqYy7bV2cFeIEOkVTZS/WjXARfBqSH6Q2qHL33hQ= github.com/whyrusleeping/cbor-gen v0.0.0-20210219115102-f37d292932f2 h1:bsUlNhdmbtlfdLVXAVfuvKQ01RnWAM09TVrJkI7NZs4= github.com/whyrusleeping/cbor-gen v0.0.0-20210219115102-f37d292932f2/go.mod h1:fgkXqYy7bV2cFeIEOkVTZS/WjXARfBqSH6Q2qHL33hQ= @@ -1832,7 +1835,6 @@ golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200812155832-6a926be9bd1d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200926100807-9d91bd62050c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1980,7 +1982,6 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From b13169f071f48c5347dbf965cb96b5cbdad301bf Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Mon, 7 Jun 2021 19:42:14 -0400 Subject: [PATCH 081/160] Rename deadlines to cutoffs in the batchers --- extern/storage-sealing/commit_batch.go | 50 +++++++++++------------ extern/storage-sealing/precommit_batch.go | 38 ++++++++--------- 2 files changed, 44 insertions(+), 44 deletions(-) diff --git a/extern/storage-sealing/commit_batch.go b/extern/storage-sealing/commit_batch.go index 7d128fe76..a31509c08 100644 --- a/extern/storage-sealing/commit_batch.go +++ b/extern/storage-sealing/commit_batch.go @@ -51,9 +51,9 @@ type CommitBatcher struct { getConfig GetSealingConfigFunc prover ffiwrapper.Prover - deadlines map[abi.SectorNumber]time.Time - todo map[abi.SectorNumber]AggregateInput - waiting map[abi.SectorNumber][]chan sealiface.CommitBatchRes + cutoffs map[abi.SectorNumber]time.Time + todo map[abi.SectorNumber]AggregateInput + waiting map[abi.SectorNumber][]chan sealiface.CommitBatchRes notify, stop, stopped chan struct{} force chan chan []sealiface.CommitBatchRes @@ -70,9 +70,9 @@ func NewCommitBatcher(mctx context.Context, maddr address.Address, api CommitBat getConfig: getConfig, prover: prov, - deadlines: map[abi.SectorNumber]time.Time{}, - todo: map[abi.SectorNumber]AggregateInput{}, - waiting: map[abi.SectorNumber][]chan sealiface.CommitBatchRes{}, + cutoffs: map[abi.SectorNumber]time.Time{}, + todo: map[abi.SectorNumber]AggregateInput{}, + waiting: map[abi.SectorNumber][]chan sealiface.CommitBatchRes{}, notify: make(chan struct{}, 1), force: make(chan chan []sealiface.CommitBatchRes), @@ -132,30 +132,30 @@ func (b *CommitBatcher) batchWait(maxWait, slack time.Duration) <-chan time.Time return nil } - var deadline time.Time + var cutoff time.Time for sn := range b.todo { - sectorDeadline := b.deadlines[sn] - if deadline.IsZero() || (!sectorDeadline.IsZero() && sectorDeadline.Before(deadline)) { - deadline = sectorDeadline + sectorCutoff := b.cutoffs[sn] + if cutoff.IsZero() || (!sectorCutoff.IsZero() && sectorCutoff.Before(cutoff)) { + cutoff = sectorCutoff } } for sn := range b.waiting { - sectorDeadline := b.deadlines[sn] - if deadline.IsZero() || (!sectorDeadline.IsZero() && sectorDeadline.Before(deadline)) { - deadline = sectorDeadline + sectorCutoff := b.cutoffs[sn] + if cutoff.IsZero() || (!sectorCutoff.IsZero() && sectorCutoff.Before(cutoff)) { + cutoff = sectorCutoff } } - if deadline.IsZero() { + if cutoff.IsZero() { return time.After(maxWait) } - deadline = deadline.Add(-slack) - if deadline.Before(now) { + cutoff = cutoff.Add(-slack) + if cutoff.Before(now) { return time.After(time.Nanosecond) // can't return 0 } - wait := deadline.Sub(now) + wait := cutoff.Sub(now) if wait > maxWait { wait = maxWait } @@ -208,7 +208,7 @@ func (b *CommitBatcher) maybeStartBatch(notif, after bool) ([]sealiface.CommitBa delete(b.waiting, sn) delete(b.todo, sn) - delete(b.deadlines, sn) + delete(b.cutoffs, sn) } } @@ -378,7 +378,7 @@ func (b *CommitBatcher) AddCommit(ctx context.Context, s SectorInfo, in Aggregat sn := s.SectorNumber b.lk.Lock() - b.deadlines[sn] = getSectorDeadline(curEpoch, s) + b.cutoffs[sn] = getSectorCutoff(curEpoch, s) b.todo[sn] = in sent := make(chan sealiface.CommitBatchRes, 1) @@ -452,24 +452,24 @@ func (b *CommitBatcher) Stop(ctx context.Context) error { } } -func getSectorDeadline(curEpoch abi.ChainEpoch, si SectorInfo) time.Time { - deadlineEpoch := si.TicketEpoch + policy.MaxPreCommitRandomnessLookback +func getSectorCutoff(curEpoch abi.ChainEpoch, si SectorInfo) time.Time { + cutoffEpoch := si.TicketEpoch + policy.MaxPreCommitRandomnessLookback for _, p := range si.Pieces { if p.DealInfo == nil { continue } startEpoch := p.DealInfo.DealSchedule.StartEpoch - if startEpoch < deadlineEpoch { - deadlineEpoch = startEpoch + if startEpoch < cutoffEpoch { + cutoffEpoch = startEpoch } } - if deadlineEpoch <= curEpoch { + if cutoffEpoch <= curEpoch { return time.Now() } - return time.Now().Add(time.Duration(deadlineEpoch-curEpoch) * time.Duration(build.BlockDelaySecs) * time.Second) + return time.Now().Add(time.Duration(cutoffEpoch-curEpoch) * time.Duration(build.BlockDelaySecs) * time.Second) } func (b *CommitBatcher) getSectorCollateral(sn abi.SectorNumber, tok TipSetToken) (abi.TokenAmount, error) { diff --git a/extern/storage-sealing/precommit_batch.go b/extern/storage-sealing/precommit_batch.go index dd674d331..e1ad70911 100644 --- a/extern/storage-sealing/precommit_batch.go +++ b/extern/storage-sealing/precommit_batch.go @@ -40,9 +40,9 @@ type PreCommitBatcher struct { feeCfg FeeConfig getConfig GetSealingConfigFunc - deadlines map[abi.SectorNumber]time.Time - todo map[abi.SectorNumber]*preCommitEntry - waiting map[abi.SectorNumber][]chan sealiface.PreCommitBatchRes + cutoffs map[abi.SectorNumber]time.Time + todo map[abi.SectorNumber]*preCommitEntry + waiting map[abi.SectorNumber][]chan sealiface.PreCommitBatchRes notify, stop, stopped chan struct{} force chan chan []sealiface.PreCommitBatchRes @@ -58,9 +58,9 @@ func NewPreCommitBatcher(mctx context.Context, maddr address.Address, api PreCom feeCfg: feeCfg, getConfig: getConfig, - deadlines: map[abi.SectorNumber]time.Time{}, - todo: map[abi.SectorNumber]*preCommitEntry{}, - waiting: map[abi.SectorNumber][]chan sealiface.PreCommitBatchRes{}, + cutoffs: map[abi.SectorNumber]time.Time{}, + todo: map[abi.SectorNumber]*preCommitEntry{}, + waiting: map[abi.SectorNumber][]chan sealiface.PreCommitBatchRes{}, notify: make(chan struct{}, 1), force: make(chan chan []sealiface.PreCommitBatchRes), @@ -120,30 +120,30 @@ func (b *PreCommitBatcher) batchWait(maxWait, slack time.Duration) <-chan time.T return nil } - var deadline time.Time + var cutoff time.Time for sn := range b.todo { - sectorDeadline := b.deadlines[sn] - if deadline.IsZero() || (!sectorDeadline.IsZero() && sectorDeadline.Before(deadline)) { - deadline = sectorDeadline + sectorCutoff := b.cutoffs[sn] + if cutoff.IsZero() || (!sectorCutoff.IsZero() && sectorCutoff.Before(cutoff)) { + cutoff = sectorCutoff } } for sn := range b.waiting { - sectorDeadline := b.deadlines[sn] - if deadline.IsZero() || (!sectorDeadline.IsZero() && sectorDeadline.Before(deadline)) { - deadline = sectorDeadline + sectorCutoff := b.cutoffs[sn] + if cutoff.IsZero() || (!sectorCutoff.IsZero() && sectorCutoff.Before(cutoff)) { + cutoff = sectorCutoff } } - if deadline.IsZero() { + if cutoff.IsZero() { return time.After(maxWait) } - deadline = deadline.Add(-slack) - if deadline.Before(now) { + cutoff = cutoff.Add(-slack) + if cutoff.Before(now) { return time.After(time.Nanosecond) // can't return 0 } - wait := deadline.Sub(now) + wait := cutoff.Sub(now) if wait > maxWait { wait = maxWait } @@ -191,7 +191,7 @@ func (b *PreCommitBatcher) maybeStartBatch(notif, after bool) ([]sealiface.PreCo delete(b.waiting, sn) delete(b.todo, sn) - delete(b.deadlines, sn) + delete(b.cutoffs, sn) } } @@ -254,7 +254,7 @@ func (b *PreCommitBatcher) AddPreCommit(ctx context.Context, s SectorInfo, depos sn := s.SectorNumber b.lk.Lock() - b.deadlines[sn] = getSectorDeadline(curEpoch, s) + b.cutoffs[sn] = getSectorCutoff(curEpoch, s) b.todo[sn] = &preCommitEntry{ deposit: deposit, pci: in, From ba27d452141b6516e23d4a4917dae24af25f96a7 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Tue, 8 Jun 2021 16:46:35 -0400 Subject: [PATCH 082/160] Split the getSectorCutoff methods between precommit and commit batchers --- extern/storage-sealing/commit_batch.go | 45 +++++++++++++++++------ extern/storage-sealing/precommit_batch.go | 25 ++++++++++++- 2 files changed, 58 insertions(+), 12 deletions(-) diff --git a/extern/storage-sealing/commit_batch.go b/extern/storage-sealing/commit_batch.go index a31509c08..b88dfe984 100644 --- a/extern/storage-sealing/commit_batch.go +++ b/extern/storage-sealing/commit_batch.go @@ -7,6 +7,10 @@ import ( "sync" "time" + "github.com/filecoin-project/go-state-types/network" + + "github.com/filecoin-project/lotus/chain/actors" + "github.com/ipfs/go-cid" "golang.org/x/xerrors" @@ -34,6 +38,7 @@ type CommitBatcherApi interface { StateSectorPreCommitInfo(ctx context.Context, maddr address.Address, sectorNumber abi.SectorNumber, tok TipSetToken) (*miner.SectorPreCommitOnChainInfo, error) StateMinerInitialPledgeCollateral(context.Context, address.Address, miner.SectorPreCommitInfo, TipSetToken) (big.Int, error) + StateNetworkVersion(ctx context.Context, tok TipSetToken) (network.Version, error) } type AggregateInput struct { @@ -369,16 +374,15 @@ func (b *CommitBatcher) processSingle(mi miner.MinerInfo, sn abi.SectorNumber, i // register commit, wait for batch message, return message CID func (b *CommitBatcher) AddCommit(ctx context.Context, s SectorInfo, in AggregateInput) (res sealiface.CommitBatchRes, err error) { - _, curEpoch, err := b.api.ChainHead(b.mctx) - if err != nil { - log.Errorf("getting chain head: %s", err) - return sealiface.CommitBatchRes{}, nil - } - sn := s.SectorNumber + cu, err := b.getCommitCutoff(s) + if err != nil { + return sealiface.CommitBatchRes{}, err + } + b.lk.Lock() - b.cutoffs[sn] = getSectorCutoff(curEpoch, s) + b.cutoffs[sn] = cu b.todo[sn] = in sent := make(chan sealiface.CommitBatchRes, 1) @@ -452,8 +456,27 @@ func (b *CommitBatcher) Stop(ctx context.Context) error { } } -func getSectorCutoff(curEpoch abi.ChainEpoch, si SectorInfo) time.Time { - cutoffEpoch := si.TicketEpoch + policy.MaxPreCommitRandomnessLookback +func (b *CommitBatcher) getCommitCutoff(si SectorInfo) (time.Time, error) { + tok, curEpoch, err := b.api.ChainHead(b.mctx) + if err != nil { + log.Errorf("getting chain head: %s", err) + return time.Now(), nil + } + + nv, err := b.api.StateNetworkVersion(b.mctx, tok) + if err != nil { + log.Errorf("getting network version: %s", err) + return time.Now(), err + } + + pci, err := b.api.StateSectorPreCommitInfo(b.mctx, b.maddr, si.SectorNumber, tok) + if err != nil { + log.Errorf("getting precommit info: %s", err) + return time.Now(), err + } + + cutoffEpoch := pci.PreCommitEpoch + policy.GetMaxProveCommitDuration(actors.VersionForNetwork(nv), si.SectorType) + for _, p := range si.Pieces { if p.DealInfo == nil { continue @@ -466,10 +489,10 @@ func getSectorCutoff(curEpoch abi.ChainEpoch, si SectorInfo) time.Time { } if cutoffEpoch <= curEpoch { - return time.Now() + return time.Now(), nil } - return time.Now().Add(time.Duration(cutoffEpoch-curEpoch) * time.Duration(build.BlockDelaySecs) * time.Second) + return time.Now().Add(time.Duration(cutoffEpoch-curEpoch) * time.Duration(build.BlockDelaySecs) * time.Second), nil } func (b *CommitBatcher) getSectorCollateral(sn abi.SectorNumber, tok TipSetToken) (abi.TokenAmount, error) { diff --git a/extern/storage-sealing/precommit_batch.go b/extern/storage-sealing/precommit_batch.go index e1ad70911..e1dd42368 100644 --- a/extern/storage-sealing/precommit_batch.go +++ b/extern/storage-sealing/precommit_batch.go @@ -7,6 +7,9 @@ import ( "sync" "time" + "github.com/filecoin-project/lotus/build" + "github.com/filecoin-project/lotus/chain/actors/policy" + "github.com/ipfs/go-cid" "golang.org/x/xerrors" @@ -254,7 +257,7 @@ func (b *PreCommitBatcher) AddPreCommit(ctx context.Context, s SectorInfo, depos sn := s.SectorNumber b.lk.Lock() - b.cutoffs[sn] = getSectorCutoff(curEpoch, s) + b.cutoffs[sn] = getPreCommitCutoff(curEpoch, s) b.todo[sn] = &preCommitEntry{ deposit: deposit, pci: in, @@ -330,3 +333,23 @@ func (b *PreCommitBatcher) Stop(ctx context.Context) error { return ctx.Err() } } + +func getPreCommitCutoff(curEpoch abi.ChainEpoch, si SectorInfo) time.Time { + cutoffEpoch := si.TicketEpoch + policy.MaxPreCommitRandomnessLookback + for _, p := range si.Pieces { + if p.DealInfo == nil { + continue + } + + startEpoch := p.DealInfo.DealSchedule.StartEpoch + if startEpoch < cutoffEpoch { + cutoffEpoch = startEpoch + } + } + + if cutoffEpoch <= curEpoch { + return time.Now() + } + + return time.Now().Add(time.Duration(cutoffEpoch-curEpoch) * time.Duration(build.BlockDelaySecs) * time.Second) +} From 71909c5642abe06ecde832ab8fc4ed157f4b9aee Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Tue, 8 Jun 2021 16:46:53 -0400 Subject: [PATCH 083/160] Fix nerpa build --- build/params_nerpanet.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/params_nerpanet.go b/build/params_nerpanet.go index b45295b76..6663a9162 100644 --- a/build/params_nerpanet.go +++ b/build/params_nerpanet.go @@ -41,7 +41,7 @@ const UpgradeOrangeHeight = 300 const UpgradeTrustHeight = 600 const UpgradeNorwegianHeight = 201000 -const UpgradeActorsV4Height = 203000 +const UpgradeTurboHeight = 203000 const UpgradeHyperdriveHeight = 999999999 func init() { From dbb4e9fcc5900f68bdeb469fe096a1068e420787 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Wed, 9 Jun 2021 12:26:20 -0400 Subject: [PATCH 084/160] Drop soms logs --- extern/storage-sealing/commit_batch.go | 6 +++--- extern/storage-sealing/precommit_batch.go | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/extern/storage-sealing/commit_batch.go b/extern/storage-sealing/commit_batch.go index b88dfe984..a943129b1 100644 --- a/extern/storage-sealing/commit_batch.go +++ b/extern/storage-sealing/commit_batch.go @@ -456,17 +456,17 @@ func (b *CommitBatcher) Stop(ctx context.Context) error { } } +// TODO: If this returned epochs, it would make testing much easier func (b *CommitBatcher) getCommitCutoff(si SectorInfo) (time.Time, error) { tok, curEpoch, err := b.api.ChainHead(b.mctx) if err != nil { - log.Errorf("getting chain head: %s", err) - return time.Now(), nil + return time.Now(), xerrors.Errorf("getting chain head: %s", err) } nv, err := b.api.StateNetworkVersion(b.mctx, tok) if err != nil { log.Errorf("getting network version: %s", err) - return time.Now(), err + return time.Now(), xerrors.Errorf("getting network version: %s", err) } pci, err := b.api.StateSectorPreCommitInfo(b.mctx, b.maddr, si.SectorNumber, tok) diff --git a/extern/storage-sealing/precommit_batch.go b/extern/storage-sealing/precommit_batch.go index e1dd42368..329d2fffc 100644 --- a/extern/storage-sealing/precommit_batch.go +++ b/extern/storage-sealing/precommit_batch.go @@ -334,6 +334,7 @@ func (b *PreCommitBatcher) Stop(ctx context.Context) error { } } +// TODO: If this returned epochs, it would make testing much easier func getPreCommitCutoff(curEpoch abi.ChainEpoch, si SectorInfo) time.Time { cutoffEpoch := si.TicketEpoch + policy.MaxPreCommitRandomnessLookback for _, p := range si.Pieces { From b0c9dd49f06f428c612b8c412b6824267356dc68 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Tue, 8 Jun 2021 21:40:52 -0400 Subject: [PATCH 085/160] Fund miners with the aggregate fee when ProveCommitting --- chain/actors/policy/policy.go | 30 ++++++++++++++++++++++++++ chain/actors/policy/policy.go.template | 17 +++++++++++++++ extern/storage-sealing/commit_batch.go | 16 +++++++++++++- extern/storage-sealing/sealing.go | 1 + storage/adapter_storage_miner.go | 14 ++++++++++++ 5 files changed, 77 insertions(+), 1 deletion(-) diff --git a/chain/actors/policy/policy.go b/chain/actors/policy/policy.go index bb35025ec..22ec4c742 100644 --- a/chain/actors/policy/policy.go +++ b/chain/actors/policy/policy.go @@ -3,6 +3,8 @@ package policy import ( "sort" + "github.com/filecoin-project/go-state-types/big" + "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/network" "github.com/filecoin-project/lotus/chain/actors" @@ -367,3 +369,31 @@ func GetDeclarationsMax(nwVer network.Version) int { panic("unsupported network version") } } + +func AggregateNetworkFee(nwVer network.Version, aggregateSize int, baseFee abi.TokenAmount) abi.TokenAmount { + switch actors.VersionForNetwork(nwVer) { + + case actors.Version0: + + return big.Zero() + + case actors.Version2: + + return big.Zero() + + case actors.Version3: + + return big.Zero() + + case actors.Version4: + + return big.Zero() + + case actors.Version5: + + return miner5.AggregateNetworkFee(aggregateSize, baseFee) + + default: + panic("unsupported network version") + } +} diff --git a/chain/actors/policy/policy.go.template b/chain/actors/policy/policy.go.template index cd2eca4ea..5d8100675 100644 --- a/chain/actors/policy/policy.go.template +++ b/chain/actors/policy/policy.go.template @@ -3,6 +3,8 @@ package policy import ( "sort" + "github.com/filecoin-project/go-state-types/big" + "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/network" "github.com/filecoin-project/lotus/chain/actors" @@ -246,3 +248,18 @@ func GetDeclarationsMax(nwVer network.Version) int { panic("unsupported network version") } } + +func AggregateNetworkFee(nwVer network.Version, aggregateSize int, baseFee abi.TokenAmount) abi.TokenAmount { + switch actors.VersionForNetwork(nwVer) { + {{range .versions}} + case actors.Version{{.}}: + {{if (le . 4)}} + return big.Zero() + {{else}} + return miner{{.}}.AggregateNetworkFee(aggregateSize, baseFee) + {{end}} + {{end}} + default: + panic("unsupported network version") + } +} diff --git a/extern/storage-sealing/commit_batch.go b/extern/storage-sealing/commit_batch.go index a943129b1..d1f6122be 100644 --- a/extern/storage-sealing/commit_batch.go +++ b/extern/storage-sealing/commit_batch.go @@ -35,6 +35,7 @@ type CommitBatcherApi interface { SendMsg(ctx context.Context, from, to address.Address, method abi.MethodNum, value, maxFee abi.TokenAmount, params []byte) (cid.Cid, error) StateMinerInfo(context.Context, address.Address, TipSetToken) (miner.MinerInfo, error) ChainHead(ctx context.Context) (TipSetToken, abi.ChainEpoch, error) + ChainBaseFee(context.Context, TipSetToken) (abi.TokenAmount, error) StateSectorPreCommitInfo(ctx context.Context, maddr address.Address, sectorNumber abi.SectorNumber, tok TipSetToken) (*miner.SectorPreCommitOnChainInfo, error) StateMinerInitialPledgeCollateral(context.Context, address.Address, miner.SectorPreCommitInfo, TipSetToken) (big.Int, error) @@ -290,7 +291,20 @@ func (b *CommitBatcher) processBatch(cfg sealiface.Config) ([]sealiface.CommitBa return []sealiface.CommitBatchRes{res}, xerrors.Errorf("couldn't get miner info: %w", err) } - goodFunds := big.Add(b.feeCfg.MaxCommitGasFee, collateral) + bf, err := b.api.ChainBaseFee(b.mctx, tok) + if err != nil { + return []sealiface.CommitBatchRes{res}, xerrors.Errorf("couldn't get base fee: %w", err) + } + + nv, err := b.api.StateNetworkVersion(b.mctx, tok) + if err != nil { + log.Errorf("getting network version: %s", err) + return []sealiface.CommitBatchRes{res}, xerrors.Errorf("getting network version: %s", err) + } + + aggFee := policy.AggregateNetworkFee(nv, len(infos), bf) + + goodFunds := big.Add(b.feeCfg.MaxCommitGasFee, big.Add(collateral, aggFee)) from, _, err := b.addrSel(b.mctx, mi, api.CommitAddr, goodFunds, collateral) if err != nil { diff --git a/extern/storage-sealing/sealing.go b/extern/storage-sealing/sealing.go index e69ce5be0..dbbd49ee6 100644 --- a/extern/storage-sealing/sealing.go +++ b/extern/storage-sealing/sealing.go @@ -66,6 +66,7 @@ type SealingAPI interface { StateMinerPartitions(ctx context.Context, m address.Address, dlIdx uint64, tok TipSetToken) ([]api.Partition, error) SendMsg(ctx context.Context, from, to address.Address, method abi.MethodNum, value, maxFee abi.TokenAmount, params []byte) (cid.Cid, error) ChainHead(ctx context.Context) (TipSetToken, abi.ChainEpoch, error) + ChainBaseFee(context.Context, TipSetToken) (abi.TokenAmount, error) ChainGetMessage(ctx context.Context, mc cid.Cid) (*types.Message, error) ChainGetRandomnessFromBeacon(ctx context.Context, tok TipSetToken, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte) (abi.Randomness, error) ChainGetRandomnessFromTickets(ctx context.Context, tok TipSetToken, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte) (abi.Randomness, error) diff --git a/storage/adapter_storage_miner.go b/storage/adapter_storage_miner.go index 3ebe874a9..a37361769 100644 --- a/storage/adapter_storage_miner.go +++ b/storage/adapter_storage_miner.go @@ -360,6 +360,20 @@ func (s SealingAPIAdapter) ChainHead(ctx context.Context) (sealing.TipSetToken, return head.Key().Bytes(), head.Height(), nil } +func (s SealingAPIAdapter) ChainBaseFee(ctx context.Context, tok sealing.TipSetToken) (abi.TokenAmount, error) { + tsk, err := types.TipSetKeyFromBytes(tok) + if err != nil { + return big.Zero(), err + } + + ts, err := s.delegate.ChainGetTipSet(ctx, tsk) + if err != nil { + return big.Zero(), err + } + + return ts.Blocks()[0].ParentBaseFee, nil +} + func (s SealingAPIAdapter) ChainGetMessage(ctx context.Context, mc cid.Cid) (*types.Message, error) { return s.delegate.ChainGetMessage(ctx, mc) } From f0a2e97cb59e566ef7a510211de74bf869eabfd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 8 Jun 2021 15:43:43 +0200 Subject: [PATCH 086/160] fee config for sector batching --- extern/storage-sealing/commit_batch.go | 15 ++++++---- extern/storage-sealing/precommit_batch.go | 10 ++++--- extern/storage-sealing/sealing.go | 11 ++------ extern/storage-sealing/states_sealing.go | 8 +++--- extern/storage-sealing/terminate_batch.go | 9 +++--- node/config/def.go | 34 ++++++++++++++++++++--- storage/miner.go | 8 +----- 7 files changed, 58 insertions(+), 37 deletions(-) diff --git a/extern/storage-sealing/commit_batch.go b/extern/storage-sealing/commit_batch.go index d1f6122be..819cb7fc7 100644 --- a/extern/storage-sealing/commit_batch.go +++ b/extern/storage-sealing/commit_batch.go @@ -27,6 +27,7 @@ import ( "github.com/filecoin-project/lotus/chain/actors/policy" "github.com/filecoin-project/lotus/extern/sector-storage/ffiwrapper" "github.com/filecoin-project/lotus/extern/storage-sealing/sealiface" + "github.com/filecoin-project/lotus/node/config" ) const arp = abi.RegisteredAggregationProof_SnarkPackV1 @@ -53,7 +54,7 @@ type CommitBatcher struct { maddr address.Address mctx context.Context addrSel AddrSel - feeCfg FeeConfig + feeCfg config.MinerFeeConfig getConfig GetSealingConfigFunc prover ffiwrapper.Prover @@ -66,7 +67,7 @@ type CommitBatcher struct { lk sync.Mutex } -func NewCommitBatcher(mctx context.Context, maddr address.Address, api CommitBatcherApi, addrSel AddrSel, feeCfg FeeConfig, getConfig GetSealingConfigFunc, prov ffiwrapper.Prover) *CommitBatcher { +func NewCommitBatcher(mctx context.Context, maddr address.Address, api CommitBatcherApi, addrSel AddrSel, feeCfg config.MinerFeeConfig, getConfig GetSealingConfigFunc, prov ffiwrapper.Prover) *CommitBatcher { b := &CommitBatcher{ api: api, maddr: maddr, @@ -291,6 +292,8 @@ func (b *CommitBatcher) processBatch(cfg sealiface.Config) ([]sealiface.CommitBa return []sealiface.CommitBatchRes{res}, xerrors.Errorf("couldn't get miner info: %w", err) } + maxFee := b.feeCfg.MaxCommitBatchGasFee.FeeForSectors(len(infos)) + bf, err := b.api.ChainBaseFee(b.mctx, tok) if err != nil { return []sealiface.CommitBatchRes{res}, xerrors.Errorf("couldn't get base fee: %w", err) @@ -304,14 +307,14 @@ func (b *CommitBatcher) processBatch(cfg sealiface.Config) ([]sealiface.CommitBa aggFee := policy.AggregateNetworkFee(nv, len(infos), bf) - goodFunds := big.Add(b.feeCfg.MaxCommitGasFee, big.Add(collateral, aggFee)) + goodFunds := big.Add(maxFee, big.Add(collateral, aggFee)) from, _, err := b.addrSel(b.mctx, mi, api.CommitAddr, goodFunds, collateral) if err != nil { return []sealiface.CommitBatchRes{res}, xerrors.Errorf("no good address found: %w", err) } - mcid, err := b.api.SendMsg(b.mctx, from, b.maddr, miner.Methods.ProveCommitAggregate, collateral, b.feeCfg.MaxCommitGasFee, enc.Bytes()) + mcid, err := b.api.SendMsg(b.mctx, from, b.maddr, miner.Methods.ProveCommitAggregate, collateral, maxFee, enc.Bytes()) if err != nil { return []sealiface.CommitBatchRes{res}, xerrors.Errorf("sending message failed: %w", err) } @@ -371,14 +374,14 @@ func (b *CommitBatcher) processSingle(mi miner.MinerInfo, sn abi.SectorNumber, i return cid.Undef, err } - goodFunds := big.Add(collateral, b.feeCfg.MaxCommitGasFee) + goodFunds := big.Add(collateral, big.Int(b.feeCfg.MaxCommitGasFee)) from, _, err := b.addrSel(b.mctx, mi, api.CommitAddr, goodFunds, collateral) if err != nil { return cid.Undef, xerrors.Errorf("no good address to send commit message from: %w", err) } - mcid, err := b.api.SendMsg(b.mctx, from, b.maddr, miner.Methods.ProveCommitSector, collateral, b.feeCfg.MaxCommitGasFee, enc.Bytes()) + mcid, err := b.api.SendMsg(b.mctx, from, b.maddr, miner.Methods.ProveCommitSector, collateral, big.Int(b.feeCfg.MaxCommitGasFee), enc.Bytes()) if err != nil { return cid.Undef, xerrors.Errorf("pushing message to mpool: %w", err) } diff --git a/extern/storage-sealing/precommit_batch.go b/extern/storage-sealing/precommit_batch.go index 329d2fffc..9439ae14c 100644 --- a/extern/storage-sealing/precommit_batch.go +++ b/extern/storage-sealing/precommit_batch.go @@ -22,6 +22,7 @@ import ( "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/chain/actors/builtin/miner" "github.com/filecoin-project/lotus/extern/storage-sealing/sealiface" + "github.com/filecoin-project/lotus/node/config" ) type PreCommitBatcherApi interface { @@ -40,7 +41,7 @@ type PreCommitBatcher struct { maddr address.Address mctx context.Context addrSel AddrSel - feeCfg FeeConfig + feeCfg config.MinerFeeConfig getConfig GetSealingConfigFunc cutoffs map[abi.SectorNumber]time.Time @@ -52,7 +53,7 @@ type PreCommitBatcher struct { lk sync.Mutex } -func NewPreCommitBatcher(mctx context.Context, maddr address.Address, api PreCommitBatcherApi, addrSel AddrSel, feeCfg FeeConfig, getConfig GetSealingConfigFunc) *PreCommitBatcher { +func NewPreCommitBatcher(mctx context.Context, maddr address.Address, api PreCommitBatcherApi, addrSel AddrSel, feeCfg config.MinerFeeConfig, getConfig GetSealingConfigFunc) *PreCommitBatcher { b := &PreCommitBatcher{ api: api, maddr: maddr, @@ -227,14 +228,15 @@ func (b *PreCommitBatcher) processBatch(cfg sealiface.Config) ([]sealiface.PreCo return []sealiface.PreCommitBatchRes{res}, xerrors.Errorf("couldn't get miner info: %w", err) } - goodFunds := big.Add(deposit, b.feeCfg.MaxPreCommitGasFee) + maxFee := b.feeCfg.MaxPreCommitBatchGasFee.FeeForSectors(len(params.Sectors)) + goodFunds := big.Add(deposit, maxFee) from, _, err := b.addrSel(b.mctx, mi, api.PreCommitAddr, goodFunds, deposit) if err != nil { return []sealiface.PreCommitBatchRes{res}, xerrors.Errorf("no good address found: %w", err) } - mcid, err := b.api.SendMsg(b.mctx, from, b.maddr, miner.Methods.PreCommitSectorBatch, deposit, b.feeCfg.MaxPreCommitGasFee, enc.Bytes()) + mcid, err := b.api.SendMsg(b.mctx, from, b.maddr, miner.Methods.PreCommitSectorBatch, deposit, maxFee, enc.Bytes()) if err != nil { return []sealiface.PreCommitBatchRes{res}, xerrors.Errorf("sending message failed: %w", err) } diff --git a/extern/storage-sealing/sealing.go b/extern/storage-sealing/sealing.go index dbbd49ee6..cfe4b9f90 100644 --- a/extern/storage-sealing/sealing.go +++ b/extern/storage-sealing/sealing.go @@ -28,6 +28,7 @@ import ( sectorstorage "github.com/filecoin-project/lotus/extern/sector-storage" "github.com/filecoin-project/lotus/extern/sector-storage/ffiwrapper" "github.com/filecoin-project/lotus/extern/storage-sealing/sealiface" + "github.com/filecoin-project/lotus/node/config" ) const SectorStorePrefix = "/sectors" @@ -79,7 +80,7 @@ type AddrSel func(ctx context.Context, mi miner.MinerInfo, use api.AddrUse, good type Sealing struct { api SealingAPI - feeCfg FeeConfig + feeCfg config.MinerFeeConfig events Events maddr address.Address @@ -112,12 +113,6 @@ type Sealing struct { dealInfo *CurrentDealInfoManager } -type FeeConfig struct { - MaxPreCommitGasFee abi.TokenAmount - MaxCommitGasFee abi.TokenAmount - MaxTerminateGasFee abi.TokenAmount -} - type openSector struct { used abi.UnpaddedPieceSize // change to bitfield/rle when AddPiece gains offset support to better fill sectors @@ -134,7 +129,7 @@ type pendingPiece struct { accepted func(abi.SectorNumber, abi.UnpaddedPieceSize, error) } -func New(api SealingAPI, fc FeeConfig, events Events, maddr address.Address, ds datastore.Batching, sealer sectorstorage.SectorManager, sc SectorIDCounter, verif ffiwrapper.Verifier, prov ffiwrapper.Prover, pcp PreCommitPolicy, gc GetSealingConfigFunc, notifee SectorStateNotifee, as AddrSel) *Sealing { +func New(api SealingAPI, fc config.MinerFeeConfig, events Events, maddr address.Address, ds datastore.Batching, sealer sectorstorage.SectorManager, sc SectorIDCounter, verif ffiwrapper.Verifier, prov ffiwrapper.Prover, pcp PreCommitPolicy, gc GetSealingConfigFunc, notifee SectorStateNotifee, as AddrSel) *Sealing { s := &Sealing{ api: api, feeCfg: fc, diff --git a/extern/storage-sealing/states_sealing.go b/extern/storage-sealing/states_sealing.go index b6ebe32c5..4cd8afd9c 100644 --- a/extern/storage-sealing/states_sealing.go +++ b/extern/storage-sealing/states_sealing.go @@ -334,7 +334,7 @@ func (m *Sealing) handlePreCommitting(ctx statemachine.Context, sector SectorInf return nil } - goodFunds := big.Add(deposit, m.feeCfg.MaxPreCommitGasFee) + goodFunds := big.Add(deposit, big.Int(m.feeCfg.MaxPreCommitGasFee)) from, _, err := m.addrSel(ctx.Context(), mi, api.PreCommitAddr, goodFunds, deposit) if err != nil { @@ -342,7 +342,7 @@ func (m *Sealing) handlePreCommitting(ctx statemachine.Context, sector SectorInf } log.Infof("submitting precommit for sector %d (deposit: %s): ", sector.SectorNumber, deposit) - mcid, err := m.api.SendMsg(ctx.Context(), from, m.maddr, miner.Methods.PreCommitSector, deposit, m.feeCfg.MaxPreCommitGasFee, enc.Bytes()) + mcid, err := m.api.SendMsg(ctx.Context(), from, m.maddr, miner.Methods.PreCommitSector, deposit, big.Int(m.feeCfg.MaxPreCommitGasFee), enc.Bytes()) if err != nil { if params.ReplaceCapacity { m.remarkForUpgrade(params.ReplaceSectorNumber) @@ -566,7 +566,7 @@ func (m *Sealing) handleSubmitCommit(ctx statemachine.Context, sector SectorInfo collateral = big.Zero() } - goodFunds := big.Add(collateral, m.feeCfg.MaxCommitGasFee) + goodFunds := big.Add(collateral, big.Int(m.feeCfg.MaxCommitGasFee)) from, _, err := m.addrSel(ctx.Context(), mi, api.CommitAddr, goodFunds, collateral) if err != nil { @@ -574,7 +574,7 @@ func (m *Sealing) handleSubmitCommit(ctx statemachine.Context, sector SectorInfo } // TODO: check seed / ticket / deals are up to date - mcid, err := m.api.SendMsg(ctx.Context(), from, m.maddr, miner.Methods.ProveCommitSector, collateral, m.feeCfg.MaxCommitGasFee, enc.Bytes()) + mcid, err := m.api.SendMsg(ctx.Context(), from, m.maddr, miner.Methods.ProveCommitSector, collateral, big.Int(m.feeCfg.MaxCommitGasFee), enc.Bytes()) if err != nil { return ctx.Send(SectorCommitFailed{xerrors.Errorf("pushing message to mpool: %w", err)}) } diff --git a/extern/storage-sealing/terminate_batch.go b/extern/storage-sealing/terminate_batch.go index d545f443f..13fa281c3 100644 --- a/extern/storage-sealing/terminate_batch.go +++ b/extern/storage-sealing/terminate_batch.go @@ -19,6 +19,7 @@ import ( "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/chain/actors/builtin/miner" + "github.com/filecoin-project/lotus/node/config" ) type TerminateBatcherApi interface { @@ -34,7 +35,7 @@ type TerminateBatcher struct { maddr address.Address mctx context.Context addrSel AddrSel - feeCfg FeeConfig + feeCfg config.MinerFeeConfig getConfig GetSealingConfigFunc todo map[SectorLocation]*bitfield.BitField // MinerSectorLocation -> BitField @@ -46,7 +47,7 @@ type TerminateBatcher struct { lk sync.Mutex } -func NewTerminationBatcher(mctx context.Context, maddr address.Address, api TerminateBatcherApi, addrSel AddrSel, feeCfg FeeConfig, getConfig GetSealingConfigFunc) *TerminateBatcher { +func NewTerminationBatcher(mctx context.Context, maddr address.Address, api TerminateBatcherApi, addrSel AddrSel, feeCfg config.MinerFeeConfig, getConfig GetSealingConfigFunc) *TerminateBatcher { b := &TerminateBatcher{ api: api, maddr: maddr, @@ -214,12 +215,12 @@ func (b *TerminateBatcher) processBatch(notif, after bool) (*cid.Cid, error) { return nil, xerrors.Errorf("couldn't get miner info: %w", err) } - from, _, err := b.addrSel(b.mctx, mi, api.TerminateSectorsAddr, b.feeCfg.MaxTerminateGasFee, b.feeCfg.MaxTerminateGasFee) + from, _, err := b.addrSel(b.mctx, mi, api.TerminateSectorsAddr, big.Int(b.feeCfg.MaxTerminateGasFee), big.Int(b.feeCfg.MaxTerminateGasFee)) if err != nil { return nil, xerrors.Errorf("no good address found: %w", err) } - mcid, err := b.api.SendMsg(b.mctx, from, b.maddr, miner.Methods.TerminateSectors, big.Zero(), b.feeCfg.MaxTerminateGasFee, enc.Bytes()) + mcid, err := b.api.SendMsg(b.mctx, from, b.maddr, miner.Methods.TerminateSectors, big.Zero(), big.Int(b.feeCfg.MaxTerminateGasFee), enc.Bytes()) if err != nil { return nil, xerrors.Errorf("sending message failed: %w", err) } diff --git a/node/config/def.go b/node/config/def.go index 636265560..08129b7f9 100644 --- a/node/config/def.go +++ b/node/config/def.go @@ -6,6 +6,8 @@ import ( "github.com/ipfs/go-cid" + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/big" miner5 "github.com/filecoin-project/specs-actors/v5/actors/builtin/miner" "github.com/filecoin-project/lotus/chain/types" @@ -114,9 +116,23 @@ type SealingConfig struct { // todo TargetSectors - stop auto-pleding new sectors after this many sectors are sealed, default CC upgrade for deals sectors if above } +type BatchFeeConfig struct { + Base types.FIL + PerSector types.FIL +} + +func (b *BatchFeeConfig) FeeForSectors(nSectors int) abi.TokenAmount { + return big.Add(big.Int(b.Base), big.Mul(big.NewInt(int64(nSectors)), big.Int(b.PerSector))) +} + type MinerFeeConfig struct { - MaxPreCommitGasFee types.FIL - MaxCommitGasFee types.FIL + MaxPreCommitGasFee types.FIL + MaxCommitGasFee types.FIL + + // maxBatchFee = maxBase + maxPerSector * nSectors + MaxPreCommitBatchGasFee BatchFeeConfig + MaxCommitBatchGasFee BatchFeeConfig + MaxTerminateGasFee types.FIL MaxWindowPoStGasFee types.FIL MaxPublishDealsFee types.FIL @@ -309,8 +325,18 @@ func DefaultStorageMiner() *StorageMiner { }, Fees: MinerFeeConfig{ - MaxPreCommitGasFee: types.MustParseFIL("0.025"), - MaxCommitGasFee: types.MustParseFIL("0.05"), + MaxPreCommitGasFee: types.MustParseFIL("0.025"), + MaxCommitGasFee: types.MustParseFIL("0.05"), + + MaxPreCommitBatchGasFee: BatchFeeConfig{ + Base: types.MustParseFIL("0.025"), // todo: come up with good values + PerSector: types.MustParseFIL("0.025"), + }, + MaxCommitBatchGasFee: BatchFeeConfig{ + Base: types.MustParseFIL("0.05"), + PerSector: types.MustParseFIL("0.05"), + }, + MaxTerminateGasFee: types.MustParseFIL("0.5"), MaxWindowPoStGasFee: types.MustParseFIL("5"), MaxPublishDealsFee: types.MustParseFIL("0.05"), diff --git a/storage/miner.go b/storage/miner.go index 1c1a9a0bc..6fa9c5922 100644 --- a/storage/miner.go +++ b/storage/miner.go @@ -148,12 +148,6 @@ func (m *Miner) Run(ctx context.Context) error { return xerrors.Errorf("getting miner info: %w", err) } - fc := sealing.FeeConfig{ - MaxPreCommitGasFee: abi.TokenAmount(m.feeCfg.MaxPreCommitGasFee), - MaxCommitGasFee: abi.TokenAmount(m.feeCfg.MaxCommitGasFee), - MaxTerminateGasFee: abi.TokenAmount(m.feeCfg.MaxTerminateGasFee), - } - evts := events.NewEvents(ctx, m.api) adaptedAPI := NewSealingAPIAdapter(m.api) // TODO: Maybe we update this policy after actor upgrades? @@ -163,7 +157,7 @@ func (m *Miner) Run(ctx context.Context) error { return m.addrSel.AddressFor(ctx, m.api, mi, use, goodFunds, minFunds) } - m.sealing = sealing.New(adaptedAPI, fc, NewEventsAdapter(evts), m.maddr, m.ds, m.sealer, m.sc, m.verif, m.prover, &pcp, sealing.GetSealingConfigFunc(m.getSealConfig), m.handleSealingNotifications, as) + m.sealing = sealing.New(adaptedAPI, m.feeCfg, NewEventsAdapter(evts), m.maddr, m.ds, m.sealer, m.sc, m.verif, m.prover, &pcp, sealing.GetSealingConfigFunc(m.getSealConfig), m.handleSealingNotifications, as) go m.sealing.Run(ctx) //nolint:errcheck // logged intside the function From 0cd0faa6fb30db0308c9d917400c858d892d6396 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Tue, 8 Jun 2021 15:20:10 +0200 Subject: [PATCH 087/160] Increase message size limit Add test Signed-off-by: Jakub Sztandera --- chain/messagepool/messagepool.go | 2 +- chain/messagepool/messagepool_test.go | 68 +++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 1 deletion(-) diff --git a/chain/messagepool/messagepool.go b/chain/messagepool/messagepool.go index 94d7c68ef..78e17165f 100644 --- a/chain/messagepool/messagepool.go +++ b/chain/messagepool/messagepool.go @@ -665,7 +665,7 @@ func (mp *MessagePool) Push(ctx context.Context, m *types.SignedMessage) (cid.Ci func (mp *MessagePool) checkMessage(m *types.SignedMessage) error { // big messages are bad, anti DOS - if m.Size() > 32*1024 { + if m.Size() > 64*1024 { return xerrors.Errorf("mpool message too large (%dB): %w", m.Size(), ErrMessageTooBig) } diff --git a/chain/messagepool/messagepool_test.go b/chain/messagepool/messagepool_test.go index 1210dd2aa..6c0178c86 100644 --- a/chain/messagepool/messagepool_test.go +++ b/chain/messagepool/messagepool_test.go @@ -14,12 +14,14 @@ import ( builtin2 "github.com/filecoin-project/specs-actors/v2/actors/builtin" + "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/chain/messagepool/gasguess" "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types/mock" "github.com/filecoin-project/lotus/chain/wallet" _ "github.com/filecoin-project/lotus/lib/sigs/bls" _ "github.com/filecoin-project/lotus/lib/sigs/secp" + "github.com/stretchr/testify/assert" ) func init() { @@ -257,6 +259,72 @@ func TestMessagePool(t *testing.T) { assertNonce(t, mp, sender, 2) } +func TestCheckMessageBig(t *testing.T) { + tma := newTestMpoolAPI() + + w, err := wallet.NewWallet(wallet.NewMemKeyStore()) + assert.NoError(t, err) + + from, err := w.WalletNew(context.Background(), types.KTBLS) + assert.NoError(t, err) + + tma.setBalance(from, 1000e9) + + ds := datastore.NewMapDatastore() + + mp, err := New(tma, ds, "mptest", nil) + assert.NoError(t, err) + + to := mock.Address(1001) + + { + msg := &types.Message{ + To: to, + From: from, + Value: types.NewInt(1), + Nonce: 0, + GasLimit: 50000000, + GasFeeCap: types.NewInt(100), + GasPremium: types.NewInt(1), + Params: make([]byte, 41<<10), // 41KiB payload + } + + sig, err := w.WalletSign(context.TODO(), from, msg.Cid().Bytes(), api.MsgMeta{}) + if err != nil { + panic(err) + } + sm := &types.SignedMessage{ + Message: *msg, + Signature: *sig, + } + mustAdd(t, mp, sm) + } + + { + msg := &types.Message{ + To: to, + From: from, + Value: types.NewInt(1), + Nonce: 0, + GasLimit: 50000000, + GasFeeCap: types.NewInt(100), + GasPremium: types.NewInt(1), + Params: make([]byte, 64<<10), // 64KiB payload + } + + sig, err := w.WalletSign(context.TODO(), from, msg.Cid().Bytes(), api.MsgMeta{}) + if err != nil { + panic(err) + } + sm := &types.SignedMessage{ + Message: *msg, + Signature: *sig, + } + err = mp.Add(context.TODO(), sm) + assert.ErrorIs(t, err, ErrMessageTooBig) + } +} + func TestMessagePoolMessagesInEachBlock(t *testing.T) { tma := newTestMpoolAPI() From 18043810c01fe949c2d701078c6625edc068a207 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Tue, 8 Jun 2021 16:07:43 +0200 Subject: [PATCH 088/160] Create MaxMessageSize constant Signed-off-by: Jakub Sztandera --- chain/messagepool/messagepool.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/chain/messagepool/messagepool.go b/chain/messagepool/messagepool.go index 78e17165f..68390885c 100644 --- a/chain/messagepool/messagepool.go +++ b/chain/messagepool/messagepool.go @@ -59,6 +59,8 @@ var MaxUntrustedActorPendingMessages = 10 var MaxNonceGap = uint64(4) +const MaxMessageSize = 64 << 10 // 64KiB + var ( ErrMessageTooBig = errors.New("message too big") @@ -665,7 +667,7 @@ func (mp *MessagePool) Push(ctx context.Context, m *types.SignedMessage) (cid.Ci func (mp *MessagePool) checkMessage(m *types.SignedMessage) error { // big messages are bad, anti DOS - if m.Size() > 64*1024 { + if m.Size() > MaxMessageSize { return xerrors.Errorf("mpool message too large (%dB): %w", m.Size(), ErrMessageTooBig) } From 626d482990bdbd229cfeb96a8dcd10deee5ff4b4 Mon Sep 17 00:00:00 2001 From: wangchao Date: Wed, 9 Jun 2021 15:19:35 +0800 Subject: [PATCH 089/160] correct the change of message size limit --- chain/sub/incoming.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chain/sub/incoming.go b/chain/sub/incoming.go index e262fe271..65447bc11 100644 --- a/chain/sub/incoming.go +++ b/chain/sub/incoming.go @@ -557,7 +557,7 @@ func (mv *MessageValidator) validateLocalMessage(ctx context.Context, msg *pubsu return pubsub.ValidationIgnore } - if m.Size() > 32*1024 { + if m.Size() > messagepool.MaxMessageSize { log.Warnf("local message is too large! (%dB)", m.Size()) recordFailure(ctx, metrics.MessageValidationFailure, "oversize") return pubsub.ValidationIgnore From 29ebdc07c9727d13a95b83fd3f248f457930d8cf Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Wed, 9 Jun 2021 18:07:46 -0400 Subject: [PATCH 090/160] Update to specs-actors v5-rc-3 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 0a4f4b532..3e9792843 100644 --- a/go.mod +++ b/go.mod @@ -47,7 +47,7 @@ require ( github.com/filecoin-project/specs-actors/v2 v2.3.5-0.20210114162132-5b58b773f4fb github.com/filecoin-project/specs-actors/v3 v3.1.0 github.com/filecoin-project/specs-actors/v4 v4.0.0 - github.com/filecoin-project/specs-actors/v5 v5.0.0-20210602024058-0c296bb386bf + github.com/filecoin-project/specs-actors/v5 v5.0.0-20210609212542-73e0409ac77c github.com/filecoin-project/specs-storage v0.1.1-0.20201105051918-5188d9774506 github.com/filecoin-project/test-vectors/schema v0.0.5 github.com/gbrlsnchs/jwt/v3 v3.0.0-beta.1 diff --git a/go.sum b/go.sum index 45c913aa1..f13c674a9 100644 --- a/go.sum +++ b/go.sum @@ -318,8 +318,8 @@ github.com/filecoin-project/specs-actors/v3 v3.1.0/go.mod h1:mpynccOLlIRy0QnR008 github.com/filecoin-project/specs-actors/v4 v4.0.0 h1:vMALksY5G3J5rj3q9rbcyB+f4Tk1xrLqSgdB3jOok4s= github.com/filecoin-project/specs-actors/v4 v4.0.0/go.mod h1:TkHXf/l7Wyw4ZejyXIPS2rK8bBO0rdwhTZyQQgaglng= github.com/filecoin-project/specs-actors/v5 v5.0.0-20210512015452-4fe3889fff57/go.mod h1:283yBMMUSDB2abcjP/hhrwTkhb9h3sfM6KGrep/ZlBI= -github.com/filecoin-project/specs-actors/v5 v5.0.0-20210602024058-0c296bb386bf h1:xt9A1omyhSDbQvpVk7Na1J15a/n8y0y4GQDLeiWLpFs= -github.com/filecoin-project/specs-actors/v5 v5.0.0-20210602024058-0c296bb386bf/go.mod h1:b/btpRl84Q9SeDKlyIoORBQwe2OTmq14POrYrVvBWCM= +github.com/filecoin-project/specs-actors/v5 v5.0.0-20210609212542-73e0409ac77c h1:GnDJ6q3QEm2ytTKjPFQSvczAltgCSb3j9F1FeynwvPA= +github.com/filecoin-project/specs-actors/v5 v5.0.0-20210609212542-73e0409ac77c/go.mod h1:b/btpRl84Q9SeDKlyIoORBQwe2OTmq14POrYrVvBWCM= github.com/filecoin-project/specs-storage v0.1.1-0.20201105051918-5188d9774506 h1:Ur/l2+6qN+lQiqjozWWc5p9UDaAMDZKTlDS98oRnlIw= github.com/filecoin-project/specs-storage v0.1.1-0.20201105051918-5188d9774506/go.mod h1:nJRRM7Aa9XVvygr3W9k6xGF46RWzr2zxF/iGoAIfA/g= github.com/filecoin-project/test-vectors/schema v0.0.5 h1:w3zHQhzM4pYxJDl21avXjOKBLF8egrvwUwjpT8TquDg= From 52199c9af3509c4d572eaa1dacaf9d1e941c3b7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 10 Jun 2021 00:17:13 +0200 Subject: [PATCH 091/160] Proofs v8.0.1 --- extern/filecoin-ffi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/filecoin-ffi b/extern/filecoin-ffi index 8b97bd823..1c7190dcc 160000 --- a/extern/filecoin-ffi +++ b/extern/filecoin-ffi @@ -1 +1 @@ -Subproject commit 8b97bd8230b77bd32f4f27e4766a6d8a03b4e801 +Subproject commit 1c7190dcc5bdef8042ca091129d6d3c10898dbdb From 0379adc9f1d88a09a1c9f130ed3b506d1f798335 Mon Sep 17 00:00:00 2001 From: Jennifer Wang Date: Wed, 9 Jun 2021 23:27:58 -0400 Subject: [PATCH 092/160] Set ntwk v13 HyperDrive Calibration upgrade epoch --- build/params_calibnet.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build/params_calibnet.go b/build/params_calibnet.go index 4685ec30c..d4cea7e07 100644 --- a/build/params_calibnet.go +++ b/build/params_calibnet.go @@ -45,7 +45,8 @@ const UpgradeNorwegianHeight = 114000 const UpgradeTurboHeight = 193789 -const UpgradeHyperdriveHeight = 9999999 +// 2021-06-11T14:30:00Z +const UpgradeHyperdriveHeight = 321519 func init() { policy.SetConsensusMinerMinPower(abi.NewStoragePower(32 << 30)) From c4c71802f2f95d88d4b374b3beb25de5db2652a9 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Wed, 9 Jun 2021 23:21:58 -0400 Subject: [PATCH 093/160] Lotus version 1.10.0-rc2 --- CHANGELOG.md | 6 +++--- build/openrpc/full.json.gz | Bin 22811 -> 22811 bytes build/openrpc/miner.json.gz | Bin 8066 -> 8066 bytes build/openrpc/worker.json.gz | Bin 2578 -> 2578 bytes build/version.go | 2 +- 5 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f7fdd052e..bfc1b98e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,8 @@ # Lotus changelog -# 1.10.0-rc1 / 2021-06-02 +# 1.10.0-rc2 / 2021-06-09 -This is the first release candidate for Lotus v1.10.0, an upcoming mandatory release of Lotus that will introduce Filecoin network v13. Included in the new network version are the following FIPs: +This is the second release candidate for Lotus v1.10.0, an upcoming mandatory release of Lotus that will introduce Filecoin network v13. Included in the new network version are the following FIPs: - [FIP-0008](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0008.md): Add miner batched sector pre-commit method - [FIP-0011](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0011.md): Remove reward auction from reporting consensus faults @@ -10,7 +10,7 @@ This is the first release candidate for Lotus v1.10.0, an upcoming mandatory rel - [FIP-0013](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0013.md): Add ProveCommitSectorAggregated method to reduce on-chain congestion - [FIP-0015](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0015.md): Revert FIP-0009(Exempt Window PoSts from BaseFee burn) -This release candidate does not set the upgrade epochs for any of the networks, including test networks. It is primarily intended for node operators to begin integration work, especially miners wishing to get familiar with the new `ProveCommit` aggregation. +This release candidate does not set the upgrade epochs for mainnet, but does set the upgrade epoch for the calibration network to 321519. Note that this release is built on top of Lotus v1.9.0. Enterprising users can use the `master` branch of Lotus to get the latest functionality, including all changes in this release candidate. diff --git a/build/openrpc/full.json.gz b/build/openrpc/full.json.gz index b99a3c9f54e84458ac8c66345852b6ec53ef944a..388ae30271184f5d05b85a9b83010946b4c009d2 100644 GIT binary patch delta 23 fcmbQeiE;KO#tGewc^iA$BRFg;E=sD_urL4sbkPWp delta 23 ecmbQeiE;KO#tGewej9t*BRHbU-Aq(#SQr3tHwYU5 diff --git a/build/openrpc/miner.json.gz b/build/openrpc/miner.json.gz index 3035cb96f1de98163745ef63eaa3957fc50fef18..a712e6abe57b5275b9cded86927e5de83c42189f 100644 GIT binary patch delta 22 ecmZp&Z?d1z!Nl`rW7h{cj{UP&b1vy;WB>qeatSa1 delta 22 ecmZp&Z?d1z!Sv Date: Wed, 9 Jun 2021 18:04:11 -0400 Subject: [PATCH 094/160] updated configuration comments for docs --- node/config/def.go | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/node/config/def.go b/node/config/def.go index 08129b7f9..3b981940f 100644 --- a/node/config/def.go +++ b/node/config/def.go @@ -93,7 +93,7 @@ type SealingConfig struct { MinPreCommitBatch int // how long to wait before submitting a batch after crossing the minimum batch size PreCommitBatchWait Duration - // time buffer for forceful batch submission before sectors in batch would start expiring + // time buffer for forceful batch submission before sectors/deal in batch would start expiring PreCommitBatchSlack Duration // enable / disable commit aggregation (takes effect after nv13) @@ -103,7 +103,7 @@ type SealingConfig struct { MaxCommitBatch int // how long to wait before submitting a batch after crossing the minimum batch size CommitBatchWait Duration - // time buffer for forceful batch submission before sectors in batch would start expiring + // time buffer for forceful batch submission before sectors/deals in batch would start expiring CommitBatchSlack Duration TerminateBatchMax uint64 @@ -281,16 +281,16 @@ func DefaultStorageMiner() *StorageMiner { AlwaysKeepUnsealedCopy: true, BatchPreCommits: true, - MinPreCommitBatch: 1, // we must have at least one proof to aggregate - MaxPreCommitBatch: miner5.PreCommitSectorBatchMaxSize, // - PreCommitBatchWait: Duration(24 * time.Hour), // this can be up to 6 days - PreCommitBatchSlack: Duration(3 * time.Hour), + MinPreCommitBatch: 1, // we must have at least one precommit to batch + MaxPreCommitBatch: miner5.PreCommitSectorBatchMaxSize, // up to 256 sectors + PreCommitBatchWait: Duration(24 * time.Hour), // this should be less than 31.5 hours, which is the expiration of a precommit ticket + PreCommitBatchSlack: Duration(3 * time.Hour), // time buffer for forceful batch submission before sectors/deals in batch would start expiring, higher value will lower the chances for message fail due to expiration AggregateCommits: true, - MinCommitBatch: miner5.MinAggregatedSectors, // we must have at least four proofs to aggregate - MaxCommitBatch: miner5.MaxAggregatedSectors, // this is the maximum aggregation per FIP13 - CommitBatchWait: Duration(24 * time.Hour), // this can be up to 6 days - CommitBatchSlack: Duration(1 * time.Hour), + MinCommitBatch: miner5.MinAggregatedSectors, // per FIP13, we must have at least four proofs to aggregate, where 4 is the cross over point where aggregation wins out on single provecommit gas costs + MaxCommitBatch: miner5.MaxAggregatedSectors, // maximum 819 sectors, this is the maximum aggregation per FIP13 + CommitBatchWait: Duration(24 * time.Hour), // this can be up to 30 days + CommitBatchSlack: Duration(1 * time.Hour), // time buffer for forceful batch submission before sectors/deals in batch would start expiring, higher value will lower the chances for message fail due to expiration TerminateBatchMin: 1, TerminateBatchMax: 100, @@ -329,12 +329,12 @@ func DefaultStorageMiner() *StorageMiner { MaxCommitGasFee: types.MustParseFIL("0.05"), MaxPreCommitBatchGasFee: BatchFeeConfig{ - Base: types.MustParseFIL("0.025"), // todo: come up with good values - PerSector: types.MustParseFIL("0.025"), + Base: types.MustParseFIL("0.025"), // TODO: update before v1.10.0 + PerSector: types.MustParseFIL("0.025"), // TODO: update before v1.10.0 }, MaxCommitBatchGasFee: BatchFeeConfig{ - Base: types.MustParseFIL("0.05"), - PerSector: types.MustParseFIL("0.05"), + Base: types.MustParseFIL("0.05"), // TODO: update before v1.10.0 + PerSector: types.MustParseFIL("0.05"), // TODO: update before v1.10.0 }, MaxTerminateGasFee: types.MustParseFIL("0.5"), From ec06f086ef92758cadc2ed861dcb74ab5b84ec77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 11 Jun 2021 11:41:28 +0200 Subject: [PATCH 095/160] sealing: Early finalization option --- cmd/lotus-storage-miner/info.go | 2 ++ extern/storage-sealing/fsm.go | 9 ++++++++ extern/storage-sealing/fsm_events.go | 9 ++++++++ extern/storage-sealing/sealiface/config.go | 2 ++ extern/storage-sealing/sector_state.go | 6 +++++- extern/storage-sealing/states_sealing.go | 25 +++++++++++++++++++++- node/config/def.go | 4 ++++ node/modules/storageminer.go | 2 ++ 8 files changed, 57 insertions(+), 2 deletions(-) diff --git a/cmd/lotus-storage-miner/info.go b/cmd/lotus-storage-miner/info.go index 0fe14f1ff..e7c5e7904 100644 --- a/cmd/lotus-storage-miner/info.go +++ b/cmd/lotus-storage-miner/info.go @@ -295,6 +295,7 @@ var stateList = []stateMeta{ {col: color.FgYellow, state: sealing.PreCommitBatchWait}, {col: color.FgYellow, state: sealing.WaitSeed}, {col: color.FgYellow, state: sealing.Committing}, + {col: color.FgYellow, state: sealing.CommitFinalize}, {col: color.FgYellow, state: sealing.SubmitCommit}, {col: color.FgYellow, state: sealing.CommitWait}, {col: color.FgYellow, state: sealing.SubmitCommitAggregate}, @@ -315,6 +316,7 @@ var stateList = []stateMeta{ {col: color.FgRed, state: sealing.PreCommitFailed}, {col: color.FgRed, state: sealing.ComputeProofFailed}, {col: color.FgRed, state: sealing.CommitFailed}, + {col: color.FgRed, state: sealing.CommitFinalizeFailed}, {col: color.FgRed, state: sealing.PackingFailed}, {col: color.FgRed, state: sealing.FinalizeFailed}, {col: color.FgRed, state: sealing.Faulty}, diff --git a/extern/storage-sealing/fsm.go b/extern/storage-sealing/fsm.go index 594f9f2f5..a85c6959b 100644 --- a/extern/storage-sealing/fsm.go +++ b/extern/storage-sealing/fsm.go @@ -102,6 +102,10 @@ var fsmPlanners = map[SectorState]func(events []statemachine.Event, state *Secto on(SectorChainPreCommitFailed{}, PreCommitFailed), ), Committing: planCommitting, + CommitFinalize: planOne( + on(SectorFinalized{}, SubmitCommit), + on(SectorFinalizeFailed{}, CommitFinalizeFailed), + ), SubmitCommit: planOne( on(SectorCommitSubmitted{}, CommitWait), on(SectorSubmitCommitAggregate{}, SubmitCommitAggregate), @@ -372,6 +376,8 @@ func (m *Sealing) plan(events []statemachine.Event, state *SectorInfo) (func(sta fallthrough case CommitWait: return m.handleCommitWait, processed, nil + case CommitFinalize: + fallthrough case FinalizeSector: return m.handleFinalizeSector, processed, nil @@ -474,6 +480,9 @@ func planCommitting(events []statemachine.Event, state *SectorInfo) (uint64, err case SectorCommitted: // the normal case e.apply(state) state.State = SubmitCommit + case SectorProofReady: // early finalize + e.apply(state) + state.State = CommitFinalize case SectorSeedReady: // seed changed :/ if e.SeedEpoch == state.SeedEpoch && bytes.Equal(e.SeedValue, state.SeedValue) { log.Warnf("planCommitting: got SectorSeedReady, but the seed didn't change") diff --git a/extern/storage-sealing/fsm_events.go b/extern/storage-sealing/fsm_events.go index 7ec8f3dfc..3dab6d403 100644 --- a/extern/storage-sealing/fsm_events.go +++ b/extern/storage-sealing/fsm_events.go @@ -245,6 +245,15 @@ func (evt SectorCommitted) apply(state *SectorInfo) { state.Proof = evt.Proof } +// like SectorCommitted, but finalizes before sending the proof to the chain +type SectorProofReady struct { + Proof []byte +} + +func (evt SectorProofReady) apply(state *SectorInfo) { + state.Proof = evt.Proof +} + type SectorSubmitCommitAggregate struct{} func (evt SectorSubmitCommitAggregate) apply(*SectorInfo) {} diff --git a/extern/storage-sealing/sealiface/config.go b/extern/storage-sealing/sealiface/config.go index 54ba2ef58..499a2befa 100644 --- a/extern/storage-sealing/sealiface/config.go +++ b/extern/storage-sealing/sealiface/config.go @@ -18,6 +18,8 @@ type Config struct { AlwaysKeepUnsealedCopy bool + FinalizeEarly bool + BatchPreCommits bool MaxPreCommitBatch int MinPreCommitBatch int diff --git a/extern/storage-sealing/sector_state.go b/extern/storage-sealing/sector_state.go index 23c7695e7..3e1494aeb 100644 --- a/extern/storage-sealing/sector_state.go +++ b/extern/storage-sealing/sector_state.go @@ -17,6 +17,8 @@ var ExistSectorStateList = map[SectorState]struct{}{ PreCommitBatchWait: {}, WaitSeed: {}, Committing: {}, + CommitFinalize: {}, + CommitFinalizeFailed: {}, SubmitCommit: {}, CommitWait: {}, SubmitCommitAggregate: {}, @@ -65,6 +67,8 @@ const ( WaitSeed SectorState = "WaitSeed" // waiting for seed Committing SectorState = "Committing" // compute PoRep + CommitFinalize SectorState = "CommitFinalize" // cleanup sector metadata before submitting the proof (early finalize) + CommitFinalizeFailed SectorState = "CommitFinalizeFailed" // single commit SubmitCommit SectorState = "SubmitCommit" // send commit message to the chain @@ -106,7 +110,7 @@ func toStatState(st SectorState) statSectorState { switch st { case UndefinedSectorState, Empty, WaitDeals, AddPiece: return sstStaging - case Packing, GetTicket, PreCommit1, PreCommit2, PreCommitting, PreCommitWait, SubmitPreCommitBatch, PreCommitBatchWait, WaitSeed, Committing, SubmitCommit, CommitWait, SubmitCommitAggregate, CommitAggregateWait, FinalizeSector: + case Packing, GetTicket, PreCommit1, PreCommit2, PreCommitting, PreCommitWait, SubmitPreCommitBatch, PreCommitBatchWait, WaitSeed, Committing, CommitFinalize, SubmitCommit, CommitWait, SubmitCommitAggregate, CommitAggregateWait, FinalizeSector: return sstSealing case Proving, Removed, Removing, Terminating, TerminateWait, TerminateFinality, TerminateFailed: return sstProving diff --git a/extern/storage-sealing/states_sealing.go b/extern/storage-sealing/states_sealing.go index 4cd8afd9c..4f0f1dc80 100644 --- a/extern/storage-sealing/states_sealing.go +++ b/extern/storage-sealing/states_sealing.go @@ -478,6 +478,11 @@ func (m *Sealing) handleCommitting(ctx statemachine.Context, sector SectorInfo) } } + cfg, err := m.getConfig() + if err != nil { + return xerrors.Errorf("getting config: %w", err) + } + log.Info("scheduling seal proof computation...") log.Infof("KOMIT %d %x(%d); %x(%d); %v; r:%x; d:%x", sector.SectorNumber, sector.TicketValue, sector.TicketEpoch, sector.SeedValue, sector.SeedEpoch, sector.pieceInfos(), sector.CommR, sector.CommD) @@ -500,6 +505,24 @@ func (m *Sealing) handleCommitting(ctx statemachine.Context, sector SectorInfo) return ctx.Send(SectorComputeProofFailed{xerrors.Errorf("computing seal proof failed(2): %w", err)}) } + { + tok, _, err := m.api.ChainHead(ctx.Context()) + if err != nil { + log.Errorf("handleCommitting: api error, not proceeding: %+v", err) + return nil + } + + if err := m.checkCommit(ctx.Context(), sector, proof, tok); err != nil { + return ctx.Send(SectorComputeProofFailed{xerrors.Errorf("commit check error: %w", err)}) + } + } + + if cfg.FinalizeEarly { + return ctx.Send(SectorProofReady{ + Proof: proof, + }) + } + return ctx.Send(SectorCommitted{ Proof: proof, }) @@ -524,7 +547,7 @@ func (m *Sealing) handleSubmitCommit(ctx statemachine.Context, sector SectorInfo tok, _, err := m.api.ChainHead(ctx.Context()) if err != nil { - log.Errorf("handleCommitting: api error, not proceeding: %+v", err) + log.Errorf("handleSubmitCommit: api error, not proceeding: %+v", err) return nil } diff --git a/node/config/def.go b/node/config/def.go index 3b981940f..ccd738420 100644 --- a/node/config/def.go +++ b/node/config/def.go @@ -86,6 +86,9 @@ type SealingConfig struct { AlwaysKeepUnsealedCopy bool + // Run sector finalization before submitting sector proof to the chain + FinalizeEarly bool + // enable / disable precommit batching (takes effect after nv13) BatchPreCommits bool // maximum precommit batch size - batches will be sent immediately above this size @@ -279,6 +282,7 @@ func DefaultStorageMiner() *StorageMiner { MaxSealingSectorsForDeals: 0, WaitDealsDelay: Duration(time.Hour * 6), AlwaysKeepUnsealedCopy: true, + FinalizeEarly: true, BatchPreCommits: true, MinPreCommitBatch: 1, // we must have at least one precommit to batch diff --git a/node/modules/storageminer.go b/node/modules/storageminer.go index 122bec519..55b18eac0 100644 --- a/node/modules/storageminer.go +++ b/node/modules/storageminer.go @@ -826,6 +826,7 @@ func NewSetSealConfigFunc(r repo.LockedRepo) (dtypes.SetSealingConfigFunc, error MaxSealingSectorsForDeals: cfg.MaxSealingSectorsForDeals, WaitDealsDelay: config.Duration(cfg.WaitDealsDelay), AlwaysKeepUnsealedCopy: cfg.AlwaysKeepUnsealedCopy, + FinalizeEarly: cfg.FinalizeEarly, BatchPreCommits: cfg.BatchPreCommits, MinPreCommitBatch: cfg.MinPreCommitBatch, @@ -857,6 +858,7 @@ func NewGetSealConfigFunc(r repo.LockedRepo) (dtypes.GetSealingConfigFunc, error MaxSealingSectorsForDeals: cfg.Sealing.MaxSealingSectorsForDeals, WaitDealsDelay: time.Duration(cfg.Sealing.WaitDealsDelay), AlwaysKeepUnsealedCopy: cfg.Sealing.AlwaysKeepUnsealedCopy, + FinalizeEarly: cfg.Sealing.FinalizeEarly, BatchPreCommits: cfg.Sealing.BatchPreCommits, MinPreCommitBatch: cfg.Sealing.MinPreCommitBatch, From 05d9b5ce0f1050d905dd0dfa26f5c57f84c5090e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 11 Jun 2021 11:42:54 +0200 Subject: [PATCH 096/160] sealing: Add missing planner for CommitFinalizeFailed --- extern/storage-sealing/fsm.go | 5 +++++ extern/storage-sealing/sector_state.go | 8 ++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/extern/storage-sealing/fsm.go b/extern/storage-sealing/fsm.go index a85c6959b..a765d2617 100644 --- a/extern/storage-sealing/fsm.go +++ b/extern/storage-sealing/fsm.go @@ -154,6 +154,9 @@ var fsmPlanners = map[SectorState]func(events []statemachine.Event, state *Secto on(SectorRetryComputeProof{}, Committing), on(SectorSealPreCommit1Failed{}, SealPreCommit1Failed), ), + CommitFinalizeFailed: planOne( + on(SectorRetryFinalize{}, CommitFinalizeFailed), + ), CommitFailed: planOne( on(SectorSealPreCommit1Failed{}, SealPreCommit1Failed), on(SectorRetryWaitSeed{}, WaitSeed), @@ -392,6 +395,8 @@ func (m *Sealing) plan(events []statemachine.Event, state *SectorInfo) (func(sta return m.handleComputeProofFailed, processed, nil case CommitFailed: return m.handleCommitFailed, processed, nil + case CommitFinalizeFailed: + fallthrough case FinalizeFailed: return m.handleFinalizeFailed, processed, nil case PackingFailed: // DEPRECATED: remove this for the next reset diff --git a/extern/storage-sealing/sector_state.go b/extern/storage-sealing/sector_state.go index 3e1494aeb..deb5e9f28 100644 --- a/extern/storage-sealing/sector_state.go +++ b/extern/storage-sealing/sector_state.go @@ -65,10 +65,10 @@ const ( SubmitPreCommitBatch SectorState = "SubmitPreCommitBatch" PreCommitBatchWait SectorState = "PreCommitBatchWait" - WaitSeed SectorState = "WaitSeed" // waiting for seed - Committing SectorState = "Committing" // compute PoRep - CommitFinalize SectorState = "CommitFinalize" // cleanup sector metadata before submitting the proof (early finalize) - CommitFinalizeFailed SectorState = "CommitFinalizeFailed" + WaitSeed SectorState = "WaitSeed" // waiting for seed + Committing SectorState = "Committing" // compute PoRep + CommitFinalize SectorState = "CommitFinalize" // cleanup sector metadata before submitting the proof (early finalize) + CommitFinalizeFailed SectorState = "CommitFinalizeFailed" // single commit SubmitCommit SectorState = "SubmitCommit" // send commit message to the chain From 94be3a973adfa6372aa94899d7d59cf39b1bb28a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 11 Jun 2021 11:45:20 +0200 Subject: [PATCH 097/160] Don't enable early finalization by default --- node/config/def.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/config/def.go b/node/config/def.go index ccd738420..1f3505e7d 100644 --- a/node/config/def.go +++ b/node/config/def.go @@ -282,7 +282,7 @@ func DefaultStorageMiner() *StorageMiner { MaxSealingSectorsForDeals: 0, WaitDealsDelay: Duration(time.Hour * 6), AlwaysKeepUnsealedCopy: true, - FinalizeEarly: true, + FinalizeEarly: false, BatchPreCommits: true, MinPreCommitBatch: 1, // we must have at least one precommit to batch From 733240a2bb0a84625682642c033e8858c528c27a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 11 Jun 2021 11:52:00 +0200 Subject: [PATCH 098/160] sealing: Test early finalization fsm planners --- extern/storage-sealing/fsm_test.go | 67 ++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/extern/storage-sealing/fsm_test.go b/extern/storage-sealing/fsm_test.go index b0ffdecf3..644ddedb4 100644 --- a/extern/storage-sealing/fsm_test.go +++ b/extern/storage-sealing/fsm_test.go @@ -87,6 +87,73 @@ func TestHappyPath(t *testing.T) { } } +func TestHappyPathFinalizeEarly(t *testing.T) { + var notif []struct{ before, after SectorInfo } + ma, _ := address.NewIDAddress(55151) + m := test{ + s: &Sealing{ + maddr: ma, + stats: SectorStats{ + bySector: map[abi.SectorID]statSectorState{}, + }, + notifee: func(before, after SectorInfo) { + notif = append(notif, struct{ before, after SectorInfo }{before, after}) + }, + }, + t: t, + state: &SectorInfo{State: Packing}, + } + + m.planSingle(SectorPacked{}) + require.Equal(m.t, m.state.State, GetTicket) + + m.planSingle(SectorTicket{}) + require.Equal(m.t, m.state.State, PreCommit1) + + m.planSingle(SectorPreCommit1{}) + require.Equal(m.t, m.state.State, PreCommit2) + + m.planSingle(SectorPreCommit2{}) + require.Equal(m.t, m.state.State, PreCommitting) + + m.planSingle(SectorPreCommitted{}) + require.Equal(m.t, m.state.State, PreCommitWait) + + m.planSingle(SectorPreCommitLanded{}) + require.Equal(m.t, m.state.State, WaitSeed) + + m.planSingle(SectorSeedReady{}) + require.Equal(m.t, m.state.State, Committing) + + m.planSingle(SectorProofReady{}) + require.Equal(m.t, m.state.State, CommitFinalize) + + m.planSingle(SectorFinalized{}) + require.Equal(m.t, m.state.State, SubmitCommit) + + m.planSingle(SectorSubmitCommitAggregate{}) + require.Equal(m.t, m.state.State, SubmitCommitAggregate) + + m.planSingle(SectorCommitAggregateSent{}) + require.Equal(m.t, m.state.State, CommitWait) + + m.planSingle(SectorProving{}) + require.Equal(m.t, m.state.State, FinalizeSector) + + m.planSingle(SectorFinalized{}) + require.Equal(m.t, m.state.State, Proving) + + expected := []SectorState{Packing, GetTicket, PreCommit1, PreCommit2, PreCommitting, PreCommitWait, WaitSeed, Committing, CommitFinalize, SubmitCommit, SubmitCommitAggregate, CommitWait, FinalizeSector, Proving} + for i, n := range notif { + if n.before.State != expected[i] { + t.Fatalf("expected before state: %s, got: %s", expected[i], n.before.State) + } + if n.after.State != expected[i+1] { + t.Fatalf("expected after state: %s, got: %s", expected[i+1], n.after.State) + } + } +} + func TestSeedRevert(t *testing.T) { ma, _ := address.NewIDAddress(55151) m := test{ From 534badad2a75d5e5c2bac84efc74f753bf883c8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 11 Jun 2021 13:19:26 +0200 Subject: [PATCH 099/160] mpool: Add more metrics --- chain/messagepool/messagepool.go | 32 ++++++++++++++++++++------- chain/sub/incoming.go | 24 ++++++++++++++++++++ metrics/metrics.go | 38 ++++++++++++++++++++++++++++++++ 3 files changed, 86 insertions(+), 8 deletions(-) diff --git a/chain/messagepool/messagepool.go b/chain/messagepool/messagepool.go index 68390885c..59f7b0f75 100644 --- a/chain/messagepool/messagepool.go +++ b/chain/messagepool/messagepool.go @@ -34,6 +34,7 @@ import ( "github.com/filecoin-project/lotus/chain/vm" "github.com/filecoin-project/lotus/journal" "github.com/filecoin-project/lotus/lib/sigs" + "github.com/filecoin-project/lotus/metrics" "github.com/filecoin-project/lotus/node/modules/dtypes" "github.com/raulk/clock" @@ -577,7 +578,7 @@ func (mp *MessagePool) addLocal(ctx context.Context, m *types.SignedMessage) err return nil } -// verifyMsgBeforeAdd verifies that the message meets the minimum criteria for block inclusio +// verifyMsgBeforeAdd verifies that the message meets the minimum criteria for block inclusion // and whether the message has enough funds to be included in the next 20 blocks. // If the message is not valid for block inclusion, it returns an error. // For local messages, if the message can be included in the next 20 blocks, it returns true to @@ -631,6 +632,9 @@ func (mp *MessagePool) verifyMsgBeforeAdd(m *types.SignedMessage, curTs *types.T } func (mp *MessagePool) Push(ctx context.Context, m *types.SignedMessage) (cid.Cid, error) { + done := metrics.Timer(ctx, metrics.MpoolPushDuration) + defer done() + err := mp.checkMessage(m) if err != nil { return cid.Undef, err @@ -697,6 +701,9 @@ func (mp *MessagePool) checkMessage(m *types.SignedMessage) error { } func (mp *MessagePool) Add(ctx context.Context, m *types.SignedMessage) error { + done := metrics.Timer(ctx, metrics.MpoolAddDuration) + defer done() + err := mp.checkMessage(m) if err != nil { return err @@ -752,7 +759,7 @@ func (mp *MessagePool) VerifyMsgSig(m *types.SignedMessage) error { } func (mp *MessagePool) checkBalance(ctx context.Context, m *types.SignedMessage, curTs *types.TipSet) error { - balance, err := mp.getStateBalance(m.Message.From, curTs) + balance, err := mp.getStateBalance(ctx, m.Message.From, curTs) if err != nil { return xerrors.Errorf("failed to check sender balance: %s: %w", err, ErrSoftValidationFailure) } @@ -785,7 +792,10 @@ func (mp *MessagePool) checkBalance(ctx context.Context, m *types.SignedMessage, } func (mp *MessagePool) addTs(ctx context.Context, m *types.SignedMessage, curTs *types.TipSet, local, untrusted bool) (bool, error) { - snonce, err := mp.getStateNonce(m.Message.From, curTs) + done := metrics.Timer(ctx, metrics.MpoolAddTsDuration) + defer done() + + snonce, err := mp.getStateNonce(ctx, m.Message.From, curTs) if err != nil { return false, xerrors.Errorf("failed to look up actor state nonce: %s: %w", err, ErrSoftValidationFailure) } @@ -833,7 +843,7 @@ func (mp *MessagePool) addLoaded(ctx context.Context, m *types.SignedMessage) er return xerrors.Errorf("current tipset not loaded") } - snonce, err := mp.getStateNonce(m.Message.From, curTs) + snonce, err := mp.getStateNonce(ctx, m.Message.From, curTs) if err != nil { return xerrors.Errorf("failed to look up actor state nonce: %s: %w", err, ErrSoftValidationFailure) } @@ -885,7 +895,7 @@ func (mp *MessagePool) addLocked(ctx context.Context, m *types.SignedMessage, st } if !ok { - nonce, err := mp.getStateNonce(m.Message.From, mp.curTs) + nonce, err := mp.getStateNonce(ctx, m.Message.From, mp.curTs) if err != nil { return xerrors.Errorf("failed to get initial actor nonce: %w", err) } @@ -939,7 +949,7 @@ func (mp *MessagePool) GetNonce(ctx context.Context, addr address.Address) (uint } func (mp *MessagePool) getNonceLocked(ctx context.Context, addr address.Address, curTs *types.TipSet) (uint64, error) { - stateNonce, err := mp.getStateNonce(addr, curTs) // sanity check + stateNonce, err := mp.getStateNonce(ctx, addr, curTs) // sanity check if err != nil { return 0, err } @@ -963,7 +973,10 @@ func (mp *MessagePool) getNonceLocked(ctx context.Context, addr address.Address, return stateNonce, nil } -func (mp *MessagePool) getStateNonce(addr address.Address, curTs *types.TipSet) (uint64, error) { +func (mp *MessagePool) getStateNonce(ctx context.Context, addr address.Address, curTs *types.TipSet) (uint64, error) { + done := metrics.Timer(ctx, metrics.MpoolGetNonceDuration) + defer done() + act, err := mp.api.GetActorAfter(addr, curTs) if err != nil { return 0, err @@ -972,7 +985,10 @@ func (mp *MessagePool) getStateNonce(addr address.Address, curTs *types.TipSet) return act.Nonce, nil } -func (mp *MessagePool) getStateBalance(addr address.Address, ts *types.TipSet) (types.BigInt, error) { +func (mp *MessagePool) getStateBalance(ctx context.Context, addr address.Address, ts *types.TipSet) (types.BigInt, error) { + done := metrics.Timer(ctx, metrics.MpoolGetBalanceDuration) + defer done() + act, err := mp.api.GetActorAfter(addr, ts) if err != nil { return types.EmptyInt, err diff --git a/chain/sub/incoming.go b/chain/sub/incoming.go index 65447bc11..7452d31a9 100644 --- a/chain/sub/incoming.go +++ b/chain/sub/incoming.go @@ -507,6 +507,12 @@ func (mv *MessageValidator) Validate(ctx context.Context, pid peer.ID, msg *pubs return mv.validateLocalMessage(ctx, msg) } + start := time.Now() + defer func() { + ms := time.Now().Sub(start).Microseconds() + stats.Record(ctx, metrics.MessageValidationDuration.M(float64(ms)/1000)) + }() + stats.Record(ctx, metrics.MessageReceived.M(1)) m, err := types.DecodeSignedMessage(msg.Message.GetData()) if err != nil { @@ -538,6 +544,12 @@ func (mv *MessageValidator) Validate(ctx context.Context, pid peer.ID, msg *pubs return pubsub.ValidationReject } } + + ctx, _ = tag.New( + ctx, + tag.Upsert(metrics.MsgValid, "true"), + ) + stats.Record(ctx, metrics.MessageValidationSuccess.M(1)) return pubsub.ValidationAccept } @@ -547,6 +559,13 @@ func (mv *MessageValidator) validateLocalMessage(ctx context.Context, msg *pubsu ctx, tag.Upsert(metrics.Local, "true"), ) + + start := time.Now() + defer func() { + ms := time.Now().Sub(start).Microseconds() + stats.Record(ctx, metrics.MessageValidationDuration.M(float64(ms)/1000)) + }() + // do some lightweight validation stats.Record(ctx, metrics.MessagePublished.M(1)) @@ -581,6 +600,11 @@ func (mv *MessageValidator) validateLocalMessage(ctx context.Context, msg *pubsu return pubsub.ValidationIgnore } + ctx, _ = tag.New( + ctx, + tag.Upsert(metrics.MsgValid, "true"), + ) + stats.Record(ctx, metrics.MessageValidationSuccess.M(1)) return pubsub.ValidationAccept } diff --git a/metrics/metrics.go b/metrics/metrics.go index 5428a81bc..08c20e634 100644 --- a/metrics/metrics.go +++ b/metrics/metrics.go @@ -38,6 +38,7 @@ var ( MessageTo, _ = tag.NewKey("message_to") MessageNonce, _ = tag.NewKey("message_nonce") ReceivedFrom, _ = tag.NewKey("received_from") + MsgValid, _ = tag.NewKey("message_valid") Endpoint, _ = tag.NewKey("endpoint") APIInterface, _ = tag.NewKey("api") // to distinguish between gateway api and full node api endpoint calls @@ -61,6 +62,12 @@ var ( MessageReceived = stats.Int64("message/received", "Counter for total received messages", stats.UnitDimensionless) MessageValidationFailure = stats.Int64("message/failure", "Counter for message validation failures", stats.UnitDimensionless) MessageValidationSuccess = stats.Int64("message/success", "Counter for message validation successes", stats.UnitDimensionless) + MessageValidationDuration = stats.Float64("message/validation_ms", "Duration of message validation", stats.UnitMilliseconds) + MpoolGetNonceDuration = stats.Float64("mpool/getnonce_ms", "Duration of getStateNonce in mpool", stats.UnitMilliseconds) + MpoolGetBalanceDuration = stats.Float64("mpool/getbalance_ms", "Duration of getStateBalance in mpool", stats.UnitMilliseconds) + MpoolAddTsDuration = stats.Float64("mpool/addts_ms", "Duration of addTs in mpool", stats.UnitMilliseconds) + MpoolAddDuration = stats.Float64("mpool/add_ms", "Duration of Add in mpool", stats.UnitMilliseconds) + MpoolPushDuration = stats.Float64("mpool/push_ms", "Duration of Push in mpool", stats.UnitMilliseconds) BlockPublished = stats.Int64("block/published", "Counter for total locally published blocks", stats.UnitDimensionless) BlockReceived = stats.Int64("block/received", "Counter for total received blocks", stats.UnitDimensionless) BlockValidationFailure = stats.Int64("block/failure", "Counter for block validation failures", stats.UnitDimensionless) @@ -163,6 +170,31 @@ var ( Measure: MessageValidationSuccess, Aggregation: view.Count(), } + MessageValidationDurationView = &view.View{ + Measure: MessageValidationDuration, + Aggregation: defaultMillisecondsDistribution, + TagKeys: []tag.Key{MsgValid, Local}, + } + MpoolGetNonceDurationView = &view.View{ + Measure: MpoolGetNonceDuration, + Aggregation: defaultMillisecondsDistribution, + } + MpoolGetBalanceDurationView = &view.View{ + Measure: MpoolGetBalanceDuration, + Aggregation: defaultMillisecondsDistribution, + } + MpoolAddTsDurationView = &view.View{ + Measure: MpoolAddTsDuration, + Aggregation: defaultMillisecondsDistribution, + } + MpoolAddDurationView = &view.View{ + Measure: MpoolAddDuration, + Aggregation: defaultMillisecondsDistribution, + } + MpoolPushDurationView = &view.View{ + Measure: MpoolPushDuration, + Aggregation: defaultMillisecondsDistribution, + } PeerCountView = &view.View{ Measure: PeerCount, Aggregation: view.LastValue(), @@ -278,6 +310,12 @@ var ChainNodeViews = append([]*view.View{ MessageReceivedView, MessageValidationFailureView, MessageValidationSuccessView, + MessageValidationDurationView, + MpoolGetNonceDurationView, + MpoolGetBalanceDurationView, + MpoolAddTsDurationView, + MpoolAddDurationView, + MpoolPushDurationView, PubsubPublishMessageView, PubsubDeliverMessageView, PubsubRejectMessageView, From ff43d29bbdac1da4c6375384744321b55229654d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 14 Jun 2021 16:39:20 +0200 Subject: [PATCH 100/160] Update ffi with fixed multicore sdr support --- extern/filecoin-ffi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/filecoin-ffi b/extern/filecoin-ffi index 1c7190dcc..57a91e861 160000 --- a/extern/filecoin-ffi +++ b/extern/filecoin-ffi @@ -1 +1 @@ -Subproject commit 1c7190dcc5bdef8042ca091129d6d3c10898dbdb +Subproject commit 57a91e861d4858379b509db42603a9cbaf0421aa From 4da30931c9ff107a6a3bf39c27aa06cf0b6795cc Mon Sep 17 00:00:00 2001 From: wangchao Date: Fri, 11 Jun 2021 10:33:57 +0800 Subject: [PATCH 101/160] failed sectors should be added into res correctly --- extern/storage-sealing/commit_batch.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/extern/storage-sealing/commit_batch.go b/extern/storage-sealing/commit_batch.go index 819cb7fc7..61553601a 100644 --- a/extern/storage-sealing/commit_batch.go +++ b/extern/storage-sealing/commit_batch.go @@ -246,6 +246,8 @@ func (b *CommitBatcher) processBatch(cfg sealiface.Config) ([]sealiface.CommitBa break } + res.Sectors = append(res.Sectors, id) + sc, err := b.getSectorCollateral(id, tok) if err != nil { res.FailedSectors[id] = err.Error() @@ -254,7 +256,6 @@ func (b *CommitBatcher) processBatch(cfg sealiface.Config) ([]sealiface.CommitBa collateral = big.Add(collateral, sc) - res.Sectors = append(res.Sectors, id) params.SectorNumbers.Set(uint64(id)) infos = append(infos, p.info) } From b00cbcaf5efe335b0e8e00918812a6944339b4a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 14 Jun 2021 18:54:59 +0200 Subject: [PATCH 102/160] Update to go-praamfetch with fslocks --- go.mod | 4 ++-- go.sum | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index 3e9792843..d3ea7c57e 100644 --- a/go.mod +++ b/go.mod @@ -38,7 +38,7 @@ require ( github.com/filecoin-project/go-jsonrpc v0.1.4-0.20210217175800-45ea43ac2bec github.com/filecoin-project/go-multistore v0.0.3 github.com/filecoin-project/go-padreader v0.0.0-20200903213702-ed5fae088b20 - github.com/filecoin-project/go-paramfetch v0.0.2-0.20210330140417-936748d3f5ec + github.com/filecoin-project/go-paramfetch v0.0.2-0.20210614165157-25a6c7769498 github.com/filecoin-project/go-state-types v0.1.1-0.20210506134452-99b279731c48 github.com/filecoin-project/go-statemachine v0.0.0-20200925024713-05bd7c71fbfe github.com/filecoin-project/go-statestore v0.1.1 @@ -87,7 +87,7 @@ require ( github.com/ipfs/go-ipfs-util v0.0.2 github.com/ipfs/go-ipld-cbor v0.0.5 github.com/ipfs/go-ipld-format v0.2.0 - github.com/ipfs/go-log/v2 v2.1.2-0.20200626104915-0016c0b4b3e4 + github.com/ipfs/go-log/v2 v2.1.3 github.com/ipfs/go-merkledag v0.3.2 github.com/ipfs/go-metrics-interface v0.0.1 github.com/ipfs/go-metrics-prometheus v0.0.2 diff --git a/go.sum b/go.sum index f13c674a9..08d6a0689 100644 --- a/go.sum +++ b/go.sum @@ -289,8 +289,8 @@ github.com/filecoin-project/go-multistore v0.0.3 h1:vaRBY4YiA2UZFPK57RNuewypB8u0 github.com/filecoin-project/go-multistore v0.0.3/go.mod h1:kaNqCC4IhU4B1uyr7YWFHd23TL4KM32aChS0jNkyUvQ= github.com/filecoin-project/go-padreader v0.0.0-20200903213702-ed5fae088b20 h1:+/4aUeUoKr6AKfPE3mBhXA5spIV6UcKdTYDPNU2Tdmg= github.com/filecoin-project/go-padreader v0.0.0-20200903213702-ed5fae088b20/go.mod h1:mPn+LRRd5gEKNAtc+r3ScpW2JRU/pj4NBKdADYWHiak= -github.com/filecoin-project/go-paramfetch v0.0.2-0.20210330140417-936748d3f5ec h1:gExwWUiT1TcARkxGneS4nvp9C+wBsKU0bFdg7qFpNco= -github.com/filecoin-project/go-paramfetch v0.0.2-0.20210330140417-936748d3f5ec/go.mod h1:fZzmf4tftbwf9S37XRifoJlz7nCjRdIrMGLR07dKLCc= +github.com/filecoin-project/go-paramfetch v0.0.2-0.20210614165157-25a6c7769498 h1:G10ezOvpH1CLXQ19EA9VWNwyL0mg536ujSayjV0yg0k= +github.com/filecoin-project/go-paramfetch v0.0.2-0.20210614165157-25a6c7769498/go.mod h1:1FH85P8U+DUEmWk1Jkw3Bw7FrwTVUNHk/95PSPG+dts= github.com/filecoin-project/go-state-types v0.0.0-20200903145444-247639ffa6ad/go.mod h1:IQ0MBPnonv35CJHtWSN3YY1Hz2gkPru1Q9qoaYLxx9I= github.com/filecoin-project/go-state-types v0.0.0-20200904021452-1883f36ca2f4/go.mod h1:IQ0MBPnonv35CJHtWSN3YY1Hz2gkPru1Q9qoaYLxx9I= github.com/filecoin-project/go-state-types v0.0.0-20200928172055-2df22083d8ab/go.mod h1:ezYnPf0bNkTsDibL/psSz5dy4B5awOJ/E7P2Saeep8g= @@ -678,8 +678,9 @@ github.com/ipfs/go-log/v2 v2.0.3/go.mod h1:O7P1lJt27vWHhOwQmcFEvlmo49ry2VY2+JfBW github.com/ipfs/go-log/v2 v2.0.5/go.mod h1:eZs4Xt4ZUJQFM3DlanGhy7TkwwawCZcSByscwkWG+dw= github.com/ipfs/go-log/v2 v2.0.8/go.mod h1:eZs4Xt4ZUJQFM3DlanGhy7TkwwawCZcSByscwkWG+dw= github.com/ipfs/go-log/v2 v2.1.1/go.mod h1:2v2nsGfZsvvAJz13SyFzf9ObaqwHiHxsPLEHntrv9KM= -github.com/ipfs/go-log/v2 v2.1.2-0.20200626104915-0016c0b4b3e4 h1:3bijxqzQ1O9yg7gd7Aqk80oaEvsJ+uXw0zSvi2qR3Jw= github.com/ipfs/go-log/v2 v2.1.2-0.20200626104915-0016c0b4b3e4/go.mod h1:2v2nsGfZsvvAJz13SyFzf9ObaqwHiHxsPLEHntrv9KM= +github.com/ipfs/go-log/v2 v2.1.3 h1:1iS3IU7aXRlbgUpN8yTTpJ53NXYjAe37vcI5+5nYrzk= +github.com/ipfs/go-log/v2 v2.1.3/go.mod h1:/8d0SH3Su5Ooc31QlL1WysJhvyOTDCjcCZ9Axpmri6g= github.com/ipfs/go-merkledag v0.0.3/go.mod h1:Oc5kIXLHokkE1hWGMBHw+oxehkAaTOqtEb7Zbh6BhLA= github.com/ipfs/go-merkledag v0.0.6/go.mod h1:QYPdnlvkOg7GnQRofu9XZimC5ZW5Wi3bKys/4GQQfto= github.com/ipfs/go-merkledag v0.2.3/go.mod h1:SQiXrtSts3KGNmgOzMICy5c0POOpUNQLvB3ClKnBAlk= From 18b13f6f58e80e66afd095f2a19a3c0c1f322948 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Mon, 14 Jun 2021 20:19:26 -0400 Subject: [PATCH 103/160] Update to fixed Bellperson --- extern/filecoin-ffi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/filecoin-ffi b/extern/filecoin-ffi index 57a91e861..d2e3aa7d6 160000 --- a/extern/filecoin-ffi +++ b/extern/filecoin-ffi @@ -1 +1 @@ -Subproject commit 57a91e861d4858379b509db42603a9cbaf0421aa +Subproject commit d2e3aa7d61501d69bed6e898de13d1312b021e62 From ad6ddcb5902d17997f824089045bf79a94df7aeb Mon Sep 17 00:00:00 2001 From: Jennifer Wang Date: Mon, 14 Jun 2021 21:44:03 -0400 Subject: [PATCH 104/160] update lotus version to v1.10.0-rc3 --- build/version.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/version.go b/build/version.go index 2fb070853..cf53219d8 100644 --- a/build/version.go +++ b/build/version.go @@ -29,7 +29,7 @@ func buildType() string { } // BuildVersion is the local build version, set by build system -const BuildVersion = "1.10.0-rc2" +const BuildVersion = "1.10.0-rc3" func UserVersion() string { return BuildVersion + buildType() + CurrentCommit From 9d7f94bc67b92d1fbe6d68283ad4cffc1c8d36cc Mon Sep 17 00:00:00 2001 From: Jennifer Wang Date: Mon, 14 Jun 2021 22:02:00 -0400 Subject: [PATCH 105/160] make gen happy --- build/openrpc/full.json.gz | Bin 22811 -> 22811 bytes build/openrpc/miner.json.gz | Bin 8066 -> 8066 bytes build/openrpc/worker.json.gz | Bin 2578 -> 2578 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/build/openrpc/full.json.gz b/build/openrpc/full.json.gz index 388ae30271184f5d05b85a9b83010946b4c009d2..13a431803af44f680aec5369493ee23b7fbc7d5b 100644 GIT binary patch delta 23 fcmbQeiE;KO#tGewaT|NuBRC#3EoM@!VPOCOcgP6m delta 23 fcmbQeiE;KO#tGewc^iA$BRFg;E=sD_urL4sbkPWp diff --git a/build/openrpc/miner.json.gz b/build/openrpc/miner.json.gz index a712e6abe57b5275b9cded86927e5de83c42189f..c9dcd099c6ac4bf41f173b01ffc95c82f961aa12 100644 GIT binary patch delta 20 ccmZp&Z?d1z$@q0+*GD;a<=5+$^fNL50AR=n(*OVf delta 20 ccmZp&Z?d1z$@pbs*GD<_*{eC1^fNL50AET7WdHyG diff --git a/build/openrpc/worker.json.gz b/build/openrpc/worker.json.gz index d8087c424125941d1480d3fd164a8589628ab2d0..85a2c6aa5e6c2bc184ba4f5795ab61d4cc589e73 100644 GIT binary patch delta 21 dcmbOvGD&1Y6J!6z=5|hwy(drnVX9_e003Wl2!a3r delta 21 ccmbOvGD&1Y6Jz_v=5|gF$&jQJrfLQT08v{8V*mgE From a79e6cb8c5bac943a00976bff0729d21a19c82f5 Mon Sep 17 00:00:00 2001 From: Jennifer Wang Date: Mon, 14 Jun 2021 22:16:00 -0400 Subject: [PATCH 106/160] update changelog --- CHANGELOG.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bfc1b98e4..a3359a29c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,10 @@ # Lotus changelog -# 1.10.0-rc2 / 2021-06-09 +# 1.10.0-rc3 / 2021-06-11 -This is the second release candidate for Lotus v1.10.0, an upcoming mandatory release of Lotus that will introduce Filecoin network v13. Included in the new network version are the following FIPs: +> Note: If you are running a lotus miner, check out the doc [here](https://docs.filecoin.io/mine/lotus/miner-configuration/#precommitsectorsbatch) for new lotus miner configurations explanations of the new features! + +This is the third release candidate for Lotus v1.10.0, an upcoming mandatory release of Lotus that will introduce Filecoin network v13. Included in the new network version are the following FIPs: - [FIP-0008](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0008.md): Add miner batched sector pre-commit method - [FIP-0011](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0011.md): Remove reward auction from reporting consensus faults From ad8b6baa0327293f09d5d81abd5d8a4ec1fc1dd2 Mon Sep 17 00:00:00 2001 From: Cory Schwartz Date: Tue, 15 Jun 2021 11:58:59 -0700 Subject: [PATCH 107/160] bring appimage to v1.10.0-rc3 --- AppDir/usr/share/icons/icon.svg | 1 + AppImageBuilder.yml | 73 +++++++++++++++++++++++++++++++++ Makefile | 17 +++----- scripts/build-bundle.sh | 3 ++ scripts/publish-release.sh | 14 ++++++- 5 files changed, 95 insertions(+), 13 deletions(-) create mode 100644 AppDir/usr/share/icons/icon.svg create mode 100644 AppImageBuilder.yml diff --git a/AppDir/usr/share/icons/icon.svg b/AppDir/usr/share/icons/icon.svg new file mode 100644 index 000000000..da992296a --- /dev/null +++ b/AppDir/usr/share/icons/icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/AppImageBuilder.yml b/AppImageBuilder.yml new file mode 100644 index 000000000..19c74e4a2 --- /dev/null +++ b/AppImageBuilder.yml @@ -0,0 +1,73 @@ +version: 1 +AppDir: + path: ./AppDir + app_info: + id: io.filecoin.lotus + name: Lotus + icon: icon + version: latest + exec: usr/bin/lotus + exec_args: $@ + apt: + arch: amd64 + allow_unauthenticated: true + sources: + - sourceline: deb http://archive.ubuntu.com/ubuntu/ focal main restricted + - sourceline: deb http://archive.ubuntu.com/ubuntu/ focal-updates main restricted + - sourceline: deb http://archive.ubuntu.com/ubuntu/ focal universe + - sourceline: deb http://archive.ubuntu.com/ubuntu/ focal-updates universe + - sourceline: deb http://archive.ubuntu.com/ubuntu/ focal multiverse + - sourceline: deb http://archive.ubuntu.com/ubuntu/ focal-updates multiverse + - sourceline: deb http://archive.ubuntu.com/ubuntu/ focal-backports main restricted + universe multiverse + - sourceline: deb http://security.ubuntu.com/ubuntu focal-security main restricted + - sourceline: deb http://security.ubuntu.com/ubuntu focal-security universe + - sourceline: deb http://security.ubuntu.com/ubuntu focal-security multiverse + - sourceline: deb https://cli-assets.heroku.com/apt ./ + - sourceline: deb http://ppa.launchpad.net/openjdk-r/ppa/ubuntu focal main + - sourceline: deb http://ppa.launchpad.net/git-core/ppa/ubuntu focal main + - sourceline: deb http://archive.canonical.com/ubuntu focal partner + include: + - ocl-icd-libopencl1 + - libhwloc15 + exclude: [] + files: + include: + - /usr/lib/x86_64-linux-gnu/libgcc_s.so.1 + - /usr/lib/x86_64-linux-gnu/libpthread-2.31.so + - /usr/lib/x86_64-linux-gnu/libm-2.31.so + - /usr/lib/x86_64-linux-gnu/libdl-2.31.so + - /usr/lib/x86_64-linux-gnu/libc-2.31.so + - /usr/lib/x86_64-linux-gnu/libudev.so.1.6.17 + exclude: + - usr/share/man + - usr/share/doc/*/README.* + - usr/share/doc/*/changelog.* + - usr/share/doc/*/NEWS.* + - usr/share/doc/*/TODO.* + test: + fedora: + image: appimagecrafters/tests-env:fedora-30 + command: ./AppRun + use_host_x: true + debian: + image: appimagecrafters/tests-env:debian-stable + command: ./AppRun + use_host_x: true + arch: + image: appimagecrafters/tests-env:archlinux-latest + command: ./AppRun + use_host_x: true + centos: + image: appimagecrafters/tests-env:centos-7 + command: ./AppRun + use_host_x: true + ubuntu: + image: appimagecrafters/tests-env:ubuntu-xenial + command: ./AppRun + use_host_x: true +AppImage: + arch: x86_64 + update-information: guess + sign-key: None + diff --git a/Makefile b/Makefile index 93b647942..59e1a1cd4 100644 --- a/Makefile +++ b/Makefile @@ -56,6 +56,12 @@ build/.update-modules: # end git modules +build/rice-box.go: + go run github.com/GeertJohan/go.rice/rice embed-go -i ./build + +BUILD_DEPS+=build/rice-box.go +CLEAN+=build/rice-box.go + ## MAIN BINARIES CLEAN+=build/.update-modules @@ -84,7 +90,6 @@ butterflynet: build-devnets lotus: $(BUILD_DEPS) rm -f lotus go build $(GOFLAGS) -o lotus ./cmd/lotus - go run github.com/GeertJohan/go.rice/rice append --exec lotus -i ./build .PHONY: lotus BINS+=lotus @@ -92,21 +97,18 @@ BINS+=lotus lotus-miner: $(BUILD_DEPS) rm -f lotus-miner go build $(GOFLAGS) -o lotus-miner ./cmd/lotus-storage-miner - go run github.com/GeertJohan/go.rice/rice append --exec lotus-miner -i ./build .PHONY: lotus-miner BINS+=lotus-miner lotus-worker: $(BUILD_DEPS) rm -f lotus-worker go build $(GOFLAGS) -o lotus-worker ./cmd/lotus-seal-worker - go run github.com/GeertJohan/go.rice/rice append --exec lotus-worker -i ./build .PHONY: lotus-worker BINS+=lotus-worker lotus-shed: $(BUILD_DEPS) rm -f lotus-shed go build $(GOFLAGS) -o lotus-shed ./cmd/lotus-shed - go run github.com/GeertJohan/go.rice/rice append --exec lotus-shed -i ./build .PHONY: lotus-shed BINS+=lotus-shed @@ -138,7 +140,6 @@ install-worker: lotus-seed: $(BUILD_DEPS) rm -f lotus-seed go build $(GOFLAGS) -o lotus-seed ./cmd/lotus-seed - go run github.com/GeertJohan/go.rice/rice append --exec lotus-seed -i ./build .PHONY: lotus-seed BINS+=lotus-seed @@ -172,13 +173,11 @@ lotus-townhall-front: .PHONY: lotus-townhall-front lotus-townhall-app: lotus-touch lotus-townhall-front - go run github.com/GeertJohan/go.rice/rice append --exec lotus-townhall -i ./cmd/lotus-townhall -i ./build .PHONY: lotus-townhall-app lotus-fountain: rm -f lotus-fountain go build -o lotus-fountain ./cmd/lotus-fountain - go run github.com/GeertJohan/go.rice/rice append --exec lotus-fountain -i ./cmd/lotus-fountain -i ./build .PHONY: lotus-fountain BINS+=lotus-fountain @@ -191,28 +190,24 @@ BINS+=lotus-chainwatch lotus-bench: rm -f lotus-bench go build -o lotus-bench ./cmd/lotus-bench - go run github.com/GeertJohan/go.rice/rice append --exec lotus-bench -i ./build .PHONY: lotus-bench BINS+=lotus-bench lotus-stats: rm -f lotus-stats go build $(GOFLAGS) -o lotus-stats ./cmd/lotus-stats - go run github.com/GeertJohan/go.rice/rice append --exec lotus-stats -i ./build .PHONY: lotus-stats BINS+=lotus-stats lotus-pcr: rm -f lotus-pcr go build $(GOFLAGS) -o lotus-pcr ./cmd/lotus-pcr - go run github.com/GeertJohan/go.rice/rice append --exec lotus-pcr -i ./build .PHONY: lotus-pcr BINS+=lotus-pcr lotus-health: rm -f lotus-health go build -o lotus-health ./cmd/lotus-health - go run github.com/GeertJohan/go.rice/rice append --exec lotus-health -i ./build .PHONY: lotus-health BINS+=lotus-health diff --git a/scripts/build-bundle.sh b/scripts/build-bundle.sh index 7d37edff8..fe1c88611 100755 --- a/scripts/build-bundle.sh +++ b/scripts/build-bundle.sh @@ -49,4 +49,7 @@ do ipfs add -q "lotus_${CIRCLE_TAG}_${ARCH}-amd64.tar.gz" > "lotus_${CIRCLE_TAG}_${ARCH}-amd64.tar.gz.cid" done +cp "../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 diff --git a/scripts/publish-release.sh b/scripts/publish-release.sh index e77a6a949..22572de60 100755 --- a/scripts/publish-release.sh +++ b/scripts/publish-release.sh @@ -29,13 +29,20 @@ RELEASE_ID=`echo "${RELEASE_RESPONSE}" | jq '.id'` if [ "${RELEASE_ID}" = "null" ]; then echo "creating release" + COND_CREATE_DISCUSSION="" + PRERELEASE=true + if [[ ${CIRCLE_TAG} =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + COND_CREATE_DISCUSSION="\"discussion_category_name\": \"announcement\"," + PRERELEASE=false + fi + RELEASE_DATA="{ \"tag_name\": \"${CIRCLE_TAG}\", \"target_commitish\": \"${CIRCLE_SHA1}\", - \"discussion_category_name\": \"announcement\", + ${COND_CREATE_DISCUSSION} \"name\": \"${CIRCLE_TAG}\", \"body\": \"\", - \"prerelease\": false + \"prerelease\": ${PRERELEASE} }" # create it if it doesn't exist yet @@ -61,6 +68,9 @@ artifacts=( "lotus_${CIRCLE_TAG}_darwin-amd64.tar.gz" "lotus_${CIRCLE_TAG}_darwin-amd64.tar.gz.cid" "lotus_${CIRCLE_TAG}_darwin-amd64.tar.gz.sha512" + "Lotus-${CIRCLE_TAG}-x86_64.AppImage" + "Lotus-${CIRCLE_TAG}-x86_64.AppImage.cid" + "Lotus-${CIRCLE_TAG}-x86_64.AppImage.sha512" ) for RELEASE_FILE in "${artifacts[@]}" From 45b063648d4e0266785756461f41f6c2472b309b Mon Sep 17 00:00:00 2001 From: Cory Schwartz Date: Tue, 15 Jun 2021 11:59:47 -0700 Subject: [PATCH 108/160] tmp: test build in circleci --- .circleci/config.yml | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 11e8ac506..29b2e1478 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -428,6 +428,40 @@ jobs: - "~/.rustup" - "~/.cargo" + build-appimage: + machine: + image: ubuntu-2004:202104-01 + steps: + - checkout + - attach_workspace: + at: "." + - run: + name: install appimage-builder + command: | + # docs: https://appimage-builder.readthedocs.io/en/latest/intro/install.html + sudo apt update + sudo apt install -y python3-pip python3-setuptools patchelf desktop-file-utils libgdk-pixbuf2.0-dev fakeroot strace + sudo curl -Lo /usr/local/bin/appimagetool https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage + sudo chmod +x /usr/local/bin/appimagetool + sudo pip3 install appimage-builder + - run: + name: install lotus dependencies + command: sudo apt install ocl-icd-opencl-dev libhwloc-dev + - run: + name: build appimage + command: | + sed -i "s/version: latest/version: ${CIRCLE_TAG:-latest}/" AppImageBuilder.yml + make appimage + - run: + name: prepare workspace + command: | + mkdir appimage + mv Lotus-latest-x86_64.AppImage appimage + - persist_to_workspace: + root: "." + paths: + - appimage + gofmt: executor: golang steps: @@ -805,6 +839,7 @@ workflows: requires: - build-all - build-macos + - build-appimage filters: branches: ignore: @@ -812,6 +847,8 @@ workflows: tags: only: - /^v\d+\.\d+\.\d+(-rc\d+)?$/ + + - build-appimage - build-and-push-image: dockerfile: Dockerfile.lotus path: . From 041bf9990e5162fe11325e754233537a7c78614e Mon Sep 17 00:00:00 2001 From: Cory Schwartz Date: Tue, 15 Jun 2021 12:01:56 -0700 Subject: [PATCH 109/160] yaml syntax --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 29b2e1478..254e7814b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -428,7 +428,7 @@ jobs: - "~/.rustup" - "~/.cargo" - build-appimage: + build-appimage: machine: image: ubuntu-2004:202104-01 steps: From 9dc67abee6274fdddb2aa16a85499441132c7e3b Mon Sep 17 00:00:00 2001 From: Cory Schwartz Date: Tue, 15 Jun 2021 12:05:30 -0700 Subject: [PATCH 110/160] add make appimage --- Makefile | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Makefile b/Makefile index 59e1a1cd4..a8857556f 100644 --- a/Makefile +++ b/Makefile @@ -330,6 +330,15 @@ api-gen: goimports -w api/apistruct .PHONY: api-gen +appimage: lotus + rm -rf appimage-builder-cache || true + rm AppDir/io.filecoin.lotus.desktop || true + rm AppDir/icon.svg || true + rm Appdir/AppRun || true + mkdir -p AppDir/usr/bin + cp ./lotus AppDir/usr/bin/ + appimage-builder + docsgen: docsgen-md docsgen-openrpc docsgen-md-bin: actors-gen From 2b7e809efed8cacc89f57f5cada2f0f53bd33973 Mon Sep 17 00:00:00 2001 From: Cory Schwartz Date: Tue, 15 Jun 2021 12:20:19 -0700 Subject: [PATCH 111/160] filter build-appimage --- .circleci/config.yml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 254e7814b..2df973dfb 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -835,6 +835,16 @@ workflows: tags: only: - /^v\d+\.\d+\.\d+(-rc\d+)?$/ + - build-appimage: + requires: + - test-short + filters: + branches: + ignore: + - /.*/ + tags: + only: + - /^v\d+\.\d+\.\d+(-rc\d+)?$/ - publish: requires: - build-all @@ -848,7 +858,6 @@ workflows: only: - /^v\d+\.\d+\.\d+(-rc\d+)?$/ - - build-appimage - build-and-push-image: dockerfile: Dockerfile.lotus path: . From 21ba7408dd0a55115e0c485a78c78a08cc775aca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 15 Jun 2021 21:04:11 +0200 Subject: [PATCH 112/160] sealing: Fix restartSectors race --- extern/storage-sealing/fsm.go | 3 +++ extern/storage-sealing/garbage.go | 2 ++ extern/storage-sealing/input.go | 4 ++++ extern/storage-sealing/sealing.go | 7 +++++++ 4 files changed, 16 insertions(+) diff --git a/extern/storage-sealing/fsm.go b/extern/storage-sealing/fsm.go index a765d2617..1ad2d7ec0 100644 --- a/extern/storage-sealing/fsm.go +++ b/extern/storage-sealing/fsm.go @@ -514,6 +514,8 @@ func planCommitting(events []statemachine.Event, state *SectorInfo) (uint64, err } func (m *Sealing) restartSectors(ctx context.Context) error { + defer m.startupWait.Done() + trackedSectors, err := m.ListSectors() if err != nil { log.Errorf("loading sector list: %+v", err) @@ -531,6 +533,7 @@ func (m *Sealing) restartSectors(ctx context.Context) error { } func (m *Sealing) ForceSectorState(ctx context.Context, id abi.SectorNumber, state SectorState) error { + m.startupWait.Wait() return m.sectors.Send(id, SectorForceState{state}) } diff --git a/extern/storage-sealing/garbage.go b/extern/storage-sealing/garbage.go index c8ec21a84..d429b5b43 100644 --- a/extern/storage-sealing/garbage.go +++ b/extern/storage-sealing/garbage.go @@ -9,6 +9,8 @@ import ( ) func (m *Sealing) PledgeSector(ctx context.Context) (storage.SectorRef, error) { + m.startupWait.Wait() + m.inputLk.Lock() defer m.inputLk.Unlock() diff --git a/extern/storage-sealing/input.go b/extern/storage-sealing/input.go index 44d2e8275..1ba8b4c2c 100644 --- a/extern/storage-sealing/input.go +++ b/extern/storage-sealing/input.go @@ -376,6 +376,8 @@ func (m *Sealing) updateInput(ctx context.Context, sp abi.RegisteredSealProof) e } func (m *Sealing) tryCreateDealSector(ctx context.Context, sp abi.RegisteredSealProof) error { + m.startupWait.Wait() + cfg, err := m.getConfig() if err != nil { return xerrors.Errorf("getting storage config: %w", err) @@ -422,6 +424,8 @@ func (m *Sealing) createSector(ctx context.Context, cfg sealiface.Config, sp abi } func (m *Sealing) StartPacking(sid abi.SectorNumber) error { + m.startupWait.Wait() + return m.sectors.Send(uint64(sid), SectorStartPacking{}) } diff --git a/extern/storage-sealing/sealing.go b/extern/storage-sealing/sealing.go index cfe4b9f90..ee29a724f 100644 --- a/extern/storage-sealing/sealing.go +++ b/extern/storage-sealing/sealing.go @@ -83,6 +83,8 @@ type Sealing struct { feeCfg config.MinerFeeConfig events Events + startupWait sync.WaitGroup + maddr address.Address sealer sectorstorage.SectorManager @@ -161,6 +163,7 @@ func New(api SealingAPI, fc config.MinerFeeConfig, events Events, maddr address. bySector: map[abi.SectorID]statSectorState{}, }, } + s.startupWait.Add(1) s.sectors = statemachine.New(namespace.Wrap(ds, datastore.NewKey(SectorStorePrefix)), s, SectorInfo{}) @@ -188,10 +191,14 @@ func (m *Sealing) Stop(ctx context.Context) error { } func (m *Sealing) Remove(ctx context.Context, sid abi.SectorNumber) error { + m.startupWait.Wait() + return m.sectors.Send(uint64(sid), SectorRemove{}) } func (m *Sealing) Terminate(ctx context.Context, sid abi.SectorNumber) error { + m.startupWait.Wait() + return m.sectors.Send(uint64(sid), SectorTerminate{}) } From 81b412399ef9a5d6956267d6471850479d1b7c91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 15 Jun 2021 21:04:11 +0200 Subject: [PATCH 113/160] sealing: Fix restartSectors race --- extern/storage-sealing/fsm.go | 3 +++ extern/storage-sealing/garbage.go | 2 ++ extern/storage-sealing/input.go | 4 ++++ extern/storage-sealing/sealing.go | 7 +++++++ 4 files changed, 16 insertions(+) diff --git a/extern/storage-sealing/fsm.go b/extern/storage-sealing/fsm.go index a765d2617..1ad2d7ec0 100644 --- a/extern/storage-sealing/fsm.go +++ b/extern/storage-sealing/fsm.go @@ -514,6 +514,8 @@ func planCommitting(events []statemachine.Event, state *SectorInfo) (uint64, err } func (m *Sealing) restartSectors(ctx context.Context) error { + defer m.startupWait.Done() + trackedSectors, err := m.ListSectors() if err != nil { log.Errorf("loading sector list: %+v", err) @@ -531,6 +533,7 @@ func (m *Sealing) restartSectors(ctx context.Context) error { } func (m *Sealing) ForceSectorState(ctx context.Context, id abi.SectorNumber, state SectorState) error { + m.startupWait.Wait() return m.sectors.Send(id, SectorForceState{state}) } diff --git a/extern/storage-sealing/garbage.go b/extern/storage-sealing/garbage.go index c8ec21a84..d429b5b43 100644 --- a/extern/storage-sealing/garbage.go +++ b/extern/storage-sealing/garbage.go @@ -9,6 +9,8 @@ import ( ) func (m *Sealing) PledgeSector(ctx context.Context) (storage.SectorRef, error) { + m.startupWait.Wait() + m.inputLk.Lock() defer m.inputLk.Unlock() diff --git a/extern/storage-sealing/input.go b/extern/storage-sealing/input.go index 44d2e8275..1ba8b4c2c 100644 --- a/extern/storage-sealing/input.go +++ b/extern/storage-sealing/input.go @@ -376,6 +376,8 @@ func (m *Sealing) updateInput(ctx context.Context, sp abi.RegisteredSealProof) e } func (m *Sealing) tryCreateDealSector(ctx context.Context, sp abi.RegisteredSealProof) error { + m.startupWait.Wait() + cfg, err := m.getConfig() if err != nil { return xerrors.Errorf("getting storage config: %w", err) @@ -422,6 +424,8 @@ func (m *Sealing) createSector(ctx context.Context, cfg sealiface.Config, sp abi } func (m *Sealing) StartPacking(sid abi.SectorNumber) error { + m.startupWait.Wait() + return m.sectors.Send(uint64(sid), SectorStartPacking{}) } diff --git a/extern/storage-sealing/sealing.go b/extern/storage-sealing/sealing.go index cfe4b9f90..ee29a724f 100644 --- a/extern/storage-sealing/sealing.go +++ b/extern/storage-sealing/sealing.go @@ -83,6 +83,8 @@ type Sealing struct { feeCfg config.MinerFeeConfig events Events + startupWait sync.WaitGroup + maddr address.Address sealer sectorstorage.SectorManager @@ -161,6 +163,7 @@ func New(api SealingAPI, fc config.MinerFeeConfig, events Events, maddr address. bySector: map[abi.SectorID]statSectorState{}, }, } + s.startupWait.Add(1) s.sectors = statemachine.New(namespace.Wrap(ds, datastore.NewKey(SectorStorePrefix)), s, SectorInfo{}) @@ -188,10 +191,14 @@ func (m *Sealing) Stop(ctx context.Context) error { } func (m *Sealing) Remove(ctx context.Context, sid abi.SectorNumber) error { + m.startupWait.Wait() + return m.sectors.Send(uint64(sid), SectorRemove{}) } func (m *Sealing) Terminate(ctx context.Context, sid abi.SectorNumber) error { + m.startupWait.Wait() + return m.sectors.Send(uint64(sid), SectorTerminate{}) } From c1529714e63b0ea6932da7aef2a55c8f330d7395 Mon Sep 17 00:00:00 2001 From: Jennifer Wang Date: Tue, 15 Jun 2021 22:16:41 -0400 Subject: [PATCH 114/160] v1.10.0-rc4 have this version base off the right head = v1.9.0 tag --- CHANGELOG.md | 4 ++-- build/version.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a3359a29c..73e27e9ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,10 @@ # Lotus changelog -# 1.10.0-rc3 / 2021-06-11 +# 1.10.0-rc4 / 2021-06-11 > Note: If you are running a lotus miner, check out the doc [here](https://docs.filecoin.io/mine/lotus/miner-configuration/#precommitsectorsbatch) for new lotus miner configurations explanations of the new features! -This is the third release candidate for Lotus v1.10.0, an upcoming mandatory release of Lotus that will introduce Filecoin network v13. Included in the new network version are the following FIPs: +This is the 4th release candidate for Lotus v1.10.0, an upcoming mandatory release of Lotus that will introduce Filecoin network v13. Included in the new network version are the following FIPs: - [FIP-0008](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0008.md): Add miner batched sector pre-commit method - [FIP-0011](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0011.md): Remove reward auction from reporting consensus faults diff --git a/build/version.go b/build/version.go index cf53219d8..c9d1a5cce 100644 --- a/build/version.go +++ b/build/version.go @@ -29,7 +29,7 @@ func buildType() string { } // BuildVersion is the local build version, set by build system -const BuildVersion = "1.10.0-rc3" +const BuildVersion = "1.10.0-rc4" func UserVersion() string { return BuildVersion + buildType() + CurrentCommit From 1f914053b43cb924766d1d9f38c8287bec8a0d48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 16 Jun 2021 11:43:06 +0200 Subject: [PATCH 115/160] sealing: Wire up context to batchers --- extern/storage-sealing/sealing.go | 8 ++++---- storage/miner.go | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/extern/storage-sealing/sealing.go b/extern/storage-sealing/sealing.go index ee29a724f..e753085ef 100644 --- a/extern/storage-sealing/sealing.go +++ b/extern/storage-sealing/sealing.go @@ -131,7 +131,7 @@ type pendingPiece struct { accepted func(abi.SectorNumber, abi.UnpaddedPieceSize, error) } -func New(api SealingAPI, fc config.MinerFeeConfig, events Events, maddr address.Address, ds datastore.Batching, sealer sectorstorage.SectorManager, sc SectorIDCounter, verif ffiwrapper.Verifier, prov ffiwrapper.Prover, pcp PreCommitPolicy, gc GetSealingConfigFunc, notifee SectorStateNotifee, as AddrSel) *Sealing { +func New(mctx context.Context, api SealingAPI, fc config.MinerFeeConfig, events Events, maddr address.Address, ds datastore.Batching, sealer sectorstorage.SectorManager, sc SectorIDCounter, verif ffiwrapper.Verifier, prov ffiwrapper.Prover, pcp PreCommitPolicy, gc GetSealingConfigFunc, notifee SectorStateNotifee, as AddrSel) *Sealing { s := &Sealing{ api: api, feeCfg: fc, @@ -152,9 +152,9 @@ func New(api SealingAPI, fc config.MinerFeeConfig, events Events, maddr address. notifee: notifee, addrSel: as, - terminator: NewTerminationBatcher(context.TODO(), maddr, api, as, fc, gc), - precommiter: NewPreCommitBatcher(context.TODO(), maddr, api, as, fc, gc), - commiter: NewCommitBatcher(context.TODO(), maddr, api, as, fc, gc, prov), + terminator: NewTerminationBatcher(mctx, maddr, api, as, fc, gc), + precommiter: NewPreCommitBatcher(mctx, maddr, api, as, fc, gc), + commiter: NewCommitBatcher(mctx, maddr, api, as, fc, gc, prov), getConfig: gc, dealInfo: &CurrentDealInfoManager{api}, diff --git a/storage/miner.go b/storage/miner.go index a5b2ea82b..e1829a7d6 100644 --- a/storage/miner.go +++ b/storage/miner.go @@ -157,7 +157,7 @@ func (m *Miner) Run(ctx context.Context) error { return m.addrSel.AddressFor(ctx, m.api, mi, use, goodFunds, minFunds) } - m.sealing = sealing.New(adaptedAPI, m.feeCfg, NewEventsAdapter(evts), m.maddr, m.ds, m.sealer, m.sc, m.verif, m.prover, &pcp, sealing.GetSealingConfigFunc(m.getSealConfig), m.handleSealingNotifications, as) + m.sealing = sealing.New(ctx, adaptedAPI, m.feeCfg, NewEventsAdapter(evts), m.maddr, m.ds, m.sealer, m.sc, m.verif, m.prover, &pcp, sealing.GetSealingConfigFunc(m.getSealConfig), m.handleSealingNotifications, as) go m.sealing.Run(ctx) //nolint:errcheck // logged intside the function From 7640ae47deba94aca2b3516c208a234f9e16bbfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 16 Jun 2021 14:51:40 +0200 Subject: [PATCH 116/160] docsgen --- build/openrpc/full.json.gz | Bin 22485 -> 22484 bytes build/openrpc/miner.json.gz | Bin 8089 -> 8089 bytes build/openrpc/worker.json.gz | Bin 2579 -> 2579 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/build/openrpc/full.json.gz b/build/openrpc/full.json.gz index 1eb1ebf942b6dc8ee9d4eea0dcbc656d37db061a..2fee80bb1d1fe56d7cad5f1d80d458fe0ab222cd 100644 GIT binary patch delta 22342 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 delta 22338 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 diff --git a/build/openrpc/miner.json.gz b/build/openrpc/miner.json.gz index d4d24cdd539c616b66c9ef471c391d0a13027209..da845c6ee1c48c7335ecec8f72fee5b5ddd94b68 100644 GIT binary patch delta 22 ecmbPfKhu6f2b0u{ja}^W9EbMIxVo>0kpTc`@d+FN delta 22 ecmbPfKhu6f2h*$B8@t%$IldS3mF(+bWB>qf(+KbY diff --git a/build/openrpc/worker.json.gz b/build/openrpc/worker.json.gz index 19d041474e1b52cbe1c66613982bb5bb4e5f834b..945825467fb0703833cb416aba1486f9faac7026 100644 GIT binary patch delta 21 dcmbO%GFfCoBjfLlO`V(^`Bx8h_*XG7003e!2!H?p delta 21 ccmbO%GFfCoBV+Z(rcO?dJoBhB|0)Is08|YI=Kufz From 3acd846dcd9a5fc425fcb2dd4463b8cb34507f01 Mon Sep 17 00:00:00 2001 From: Peter Rabbitson Date: Fri, 21 May 2021 15:00:21 +0200 Subject: [PATCH 117/160] Fix logging around mineOne - A nil MiningBaseInfo is *NOT* unexpected: it happens when one is in penalty https://github.com/filecoin-project/lotus/blob/v1.9.0/chain/stmgr/utils.go#L500-L502 - Remove the log from IsRoundWinner(): all we care about is the randbase epoch --- chain/gen/gen.go | 9 ------- miner/miner.go | 67 ++++++++++++++++++++++++++++++++---------------- 2 files changed, 45 insertions(+), 31 deletions(-) diff --git a/chain/gen/gen.go b/chain/gen/gen.go index 0cbdb2188..aa0f7df88 100644 --- a/chain/gen/gen.go +++ b/chain/gen/gen.go @@ -658,15 +658,6 @@ func IsRoundWinner(ctx context.Context, ts *types.TipSet, round abi.ChainEpoch, ep := &types.ElectionProof{VRFProof: vrfout} j := ep.ComputeWinCount(mbi.MinerPower, mbi.NetworkPower) ep.WinCount = j - - log.Infow("completed winAttemptVRF", - "beaconRound", brand.Round, - "beaconDataB64", b64.EncodeToString(brand.Data), - "electionRandB64", b64.EncodeToString(electionRand), - "vrfB64", b64.EncodeToString(vrfout), - "winCount", j, - ) - if j < 1 { return nil, nil } diff --git a/miner/miner.go b/miner/miner.go index a77e1c18b..5fbc9aae3 100644 --- a/miner/miner.go +++ b/miner/miner.go @@ -18,6 +18,7 @@ import ( "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/big" "github.com/filecoin-project/go-state-types/crypto" lru "github.com/hashicorp/golang-lru" @@ -415,36 +416,51 @@ func (m *Miner) GetBestMiningCandidate(ctx context.Context) (*MiningBase, error) // This method does the following: // // 1. -func (m *Miner) mineOne(ctx context.Context, base *MiningBase) (*types.BlockMsg, error) { +func (m *Miner) mineOne(ctx context.Context, base *MiningBase) (minedBlock *types.BlockMsg, err error) { log.Debugw("attempting to mine a block", "tipset", types.LogCids(base.TipSet.Cids())) start := build.Clock.Now() round := base.TipSet.Height() + base.NullRounds + 1 - mbi, err := m.api.MinerGetBaseInfo(ctx, m.address, round, base.TipSet.Key()) - if err != nil { - return nil, xerrors.Errorf("failed to get mining base info: %w", err) - } - if mbi == nil { - log.Warnf("mineOne: unexpectedly nil MiningBaseInfo for round %d, off tipset %d/%s", round, base.TipSet.Height(), base.TipSet.Key().String()) - return nil, nil - } - - // always write out a log from this point out + // always write out a log var winner *types.ElectionProof + var mbi *api.MiningBaseInfo + var rbase types.BeaconEntry defer func() { + // mbi can be nil if we are deep in penalty and there are 0 eligible sectors + // in the current deadline. If this case - put together a dummy one for reporting + // https://github.com/filecoin-project/lotus/blob/v1.9.0/chain/stmgr/utils.go#L500-L502 + if mbi == nil { + mbi = &api.MiningBaseInfo{ + NetworkPower: big.NewInt(-1), // we do not know how big the network is at this point + EligibleForMining: false, + MinerPower: big.NewInt(0), // but we do know we do not have anything + } + } + log.Infow( "completed mineOne", "forRound", int64(round), "baseEpoch", int64(base.TipSet.Height()), + "beaconEpoch", rbase.Round, "lookbackEpochs", int64(policy.ChainFinality), // hardcoded as it is unlikely to change again: https://github.com/filecoin-project/lotus/blob/v1.8.0/chain/actors/policy/policy.go#L180-L186 "networkPowerAtLookback", mbi.NetworkPower.String(), "minerPowerAtLookback", mbi.MinerPower.String(), "isEligible", mbi.EligibleForMining, "isWinner", (winner != nil), + "error", err, ) }() + mbi, err = m.api.MinerGetBaseInfo(ctx, m.address, round, base.TipSet.Key()) + if err != nil { + err = xerrors.Errorf("failed to get mining base info: %w", err) + return nil, err + } + if mbi == nil { + return nil, nil + } + if !mbi.EligibleForMining { // slashed or just have no power yet return nil, nil @@ -461,19 +477,21 @@ func (m *Miner) mineOne(ctx context.Context, base *MiningBase) (*types.BlockMsg, log.Infof("Time delta between now and our mining base: %ds (nulls: %d)", uint64(build.Clock.Now().Unix())-base.TipSet.MinTimestamp(), base.NullRounds) - rbase := beaconPrev + rbase = beaconPrev if len(bvals) > 0 { rbase = bvals[len(bvals)-1] } ticket, err := m.computeTicket(ctx, &rbase, base, mbi) if err != nil { - return nil, xerrors.Errorf("scratching ticket failed: %w", err) + err = xerrors.Errorf("scratching ticket failed: %w", err) + return nil, err } winner, err = gen.IsRoundWinner(ctx, base.TipSet, round, m.address, rbase, mbi, m.api) if err != nil { - return nil, xerrors.Errorf("failed to check if we win next round: %w", err) + err = xerrors.Errorf("failed to check if we win next round: %w", err) + return nil, err } if winner == nil { @@ -484,12 +502,14 @@ func (m *Miner) mineOne(ctx context.Context, base *MiningBase) (*types.BlockMsg, buf := new(bytes.Buffer) if err := m.address.MarshalCBOR(buf); err != nil { - return nil, xerrors.Errorf("failed to marshal miner address: %w", err) + err = xerrors.Errorf("failed to marshal miner address: %w", err) + return nil, err } rand, err := store.DrawRandomness(rbase.Data, crypto.DomainSeparationTag_WinningPoStChallengeSeed, round, buf.Bytes()) if err != nil { - return nil, xerrors.Errorf("failed to get randomness for winning post: %w", err) + err = xerrors.Errorf("failed to get randomness for winning post: %w", err) + return nil, err } prand := abi.PoStRandomness(rand) @@ -498,7 +518,8 @@ func (m *Miner) mineOne(ctx context.Context, base *MiningBase) (*types.BlockMsg, postProof, err := m.epp.ComputeProof(ctx, mbi.Sectors, prand) if err != nil { - return nil, xerrors.Errorf("failed to compute winning post proof: %w", err) + err = xerrors.Errorf("failed to compute winning post proof: %w", err) + return nil, err } tProof := build.Clock.Now() @@ -506,15 +527,17 @@ func (m *Miner) mineOne(ctx context.Context, base *MiningBase) (*types.BlockMsg, // get pending messages early, msgs, err := m.api.MpoolSelect(context.TODO(), base.TipSet.Key(), ticket.Quality()) if err != nil { - return nil, xerrors.Errorf("failed to select messages for block: %w", err) + err = xerrors.Errorf("failed to select messages for block: %w", err) + return nil, err } tPending := build.Clock.Now() // TODO: winning post proof - b, err := m.createBlock(base, m.address, ticket, winner, bvals, postProof, msgs) + minedBlock, err = m.createBlock(base, m.address, ticket, winner, bvals, postProof, msgs) if err != nil { - return nil, xerrors.Errorf("failed to create block: %w", err) + err = xerrors.Errorf("failed to create block: %w", err) + return nil, err } tCreateBlock := build.Clock.Now() @@ -523,7 +546,7 @@ func (m *Miner) mineOne(ctx context.Context, base *MiningBase) (*types.BlockMsg, for i, header := range base.TipSet.Blocks() { parentMiners[i] = header.Miner } - log.Infow("mined new block", "cid", b.Cid(), "height", int64(b.Header.Height), "miner", b.Header.Miner, "parents", parentMiners, "parentTipset", base.TipSet.Key().String(), "took", dur) + log.Infow("mined new block", "cid", minedBlock.Cid(), "height", int64(minedBlock.Header.Height), "miner", minedBlock.Header.Miner, "parents", parentMiners, "parentTipset", base.TipSet.Key().String(), "took", dur) if dur > time.Second*time.Duration(build.BlockDelaySecs) { log.Warnw("CAUTION: block production took longer than the block delay. Your computer may not be fast enough to keep up", "tMinerBaseInfo ", tMBI.Sub(start), @@ -536,7 +559,7 @@ func (m *Miner) mineOne(ctx context.Context, base *MiningBase) (*types.BlockMsg, "tCreateBlock ", tCreateBlock.Sub(tPending)) } - return b, nil + return minedBlock, nil } func (m *Miner) computeTicket(ctx context.Context, brand *types.BeaconEntry, base *MiningBase, mbi *api.MiningBaseInfo) (*types.Ticket, error) { From 429419f2101210fd3dd59ef145e0eee0286c06a3 Mon Sep 17 00:00:00 2001 From: Peter Rabbitson Date: Fri, 21 May 2021 15:30:08 +0200 Subject: [PATCH 118/160] Forgotten deadcode --- chain/gen/gen.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/chain/gen/gen.go b/chain/gen/gen.go index aa0f7df88..98bb3ea91 100644 --- a/chain/gen/gen.go +++ b/chain/gen/gen.go @@ -3,7 +3,6 @@ package gen import ( "bytes" "context" - "encoding/base64" "fmt" "io" "io/ioutil" @@ -635,8 +634,6 @@ func (wpp *wppProvider) ComputeProof(context.Context, []proof5.SectorInfo, abi.P return ValidWpostForTesting, nil } -var b64 = base64.URLEncoding.WithPadding(base64.NoPadding) - func IsRoundWinner(ctx context.Context, ts *types.TipSet, round abi.ChainEpoch, miner address.Address, brand types.BeaconEntry, mbi *api.MiningBaseInfo, a MiningCheckAPI) (*types.ElectionProof, error) { From 400780606804e19fc3f77f6a9bf0a55c86ff9e8e Mon Sep 17 00:00:00 2001 From: Peter Rabbitson Date: Sat, 22 May 2021 17:39:56 +0200 Subject: [PATCH 119/160] Incorporate the 'Time delta between now...' log into the 'completed mineOne' --- miner/miner.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/miner/miner.go b/miner/miner.go index 5fbc9aae3..7b85e558e 100644 --- a/miner/miner.go +++ b/miner/miner.go @@ -440,9 +440,12 @@ func (m *Miner) mineOne(ctx context.Context, base *MiningBase) (minedBlock *type log.Infow( "completed mineOne", + "tookMilliseconds", (build.Clock.Now().UnixNano()-start.UnixNano())/1_000_000, "forRound", int64(round), "baseEpoch", int64(base.TipSet.Height()), - "beaconEpoch", rbase.Round, + "baseDeltaSeconds", uint64(start.Unix())-base.TipSet.MinTimestamp(), + "nullRounds", int64(base.NullRounds), + "beaconEpoch", uint64(rbase.Round), "lookbackEpochs", int64(policy.ChainFinality), // hardcoded as it is unlikely to change again: https://github.com/filecoin-project/lotus/blob/v1.8.0/chain/actors/policy/policy.go#L180-L186 "networkPowerAtLookback", mbi.NetworkPower.String(), "minerPowerAtLookback", mbi.MinerPower.String(), @@ -475,8 +478,6 @@ func (m *Miner) mineOne(ctx context.Context, base *MiningBase) (minedBlock *type tPowercheck := build.Clock.Now() - log.Infof("Time delta between now and our mining base: %ds (nulls: %d)", uint64(build.Clock.Now().Unix())-base.TipSet.MinTimestamp(), base.NullRounds) - rbase = beaconPrev if len(bvals) > 0 { rbase = bvals[len(bvals)-1] From ef3ef8596081a6c19fec2c390c195b1998824bdd Mon Sep 17 00:00:00 2001 From: Peter Rabbitson Date: Sat, 22 May 2021 23:55:32 +0200 Subject: [PATCH 120/160] Add a `lateStart` indicator, differentiate on Error/Warn/Info --- miner/miner.go | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/miner/miner.go b/miner/miner.go index 7b85e558e..b2da25d8e 100644 --- a/miner/miner.go +++ b/miner/miner.go @@ -13,6 +13,7 @@ import ( proof2 "github.com/filecoin-project/specs-actors/v2/actors/runtime/proof" + "github.com/filecoin-project/lotus/chain/actors/builtin" "github.com/filecoin-project/lotus/chain/actors/policy" "github.com/filecoin-project/lotus/chain/gen/slashfilter" @@ -438,13 +439,15 @@ func (m *Miner) mineOne(ctx context.Context, base *MiningBase) (minedBlock *type } } - log.Infow( - "completed mineOne", - "tookMilliseconds", (build.Clock.Now().UnixNano()-start.UnixNano())/1_000_000, + isLate := uint64(start.Unix()) > (base.TipSet.MinTimestamp() + uint64(base.NullRounds*builtin.EpochDurationSeconds) + build.PropagationDelaySecs) + + logStruct := []interface{}{ + "tookMilliseconds", (build.Clock.Now().UnixNano() - start.UnixNano()) / 1_000_000, "forRound", int64(round), "baseEpoch", int64(base.TipSet.Height()), - "baseDeltaSeconds", uint64(start.Unix())-base.TipSet.MinTimestamp(), + "baseDeltaSeconds", uint64(start.Unix()) - base.TipSet.MinTimestamp(), "nullRounds", int64(base.NullRounds), + "lateStart", isLate, "beaconEpoch", uint64(rbase.Round), "lookbackEpochs", int64(policy.ChainFinality), // hardcoded as it is unlikely to change again: https://github.com/filecoin-project/lotus/blob/v1.8.0/chain/actors/policy/policy.go#L180-L186 "networkPowerAtLookback", mbi.NetworkPower.String(), @@ -452,7 +455,15 @@ func (m *Miner) mineOne(ctx context.Context, base *MiningBase) (minedBlock *type "isEligible", mbi.EligibleForMining, "isWinner", (winner != nil), "error", err, - ) + } + + if err != nil { + log.Errorw("completed mineOne", logStruct...) + } else if isLate { + log.Warnw("completed mineOne", logStruct...) + } else { + log.Infow("completed mineOne", logStruct...) + } }() mbi, err = m.api.MinerGetBaseInfo(ctx, m.address, round, base.TipSet.Key()) From c198e7aa9a67d48008bf4c3d4fdb006428367687 Mon Sep 17 00:00:00 2001 From: Jennifer Wang Date: Mon, 24 May 2021 10:04:37 -0400 Subject: [PATCH 121/160] make lint happy --- miner/miner.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/miner/miner.go b/miner/miner.go index b2da25d8e..62bec5b55 100644 --- a/miner/miner.go +++ b/miner/miner.go @@ -448,7 +448,7 @@ func (m *Miner) mineOne(ctx context.Context, base *MiningBase) (minedBlock *type "baseDeltaSeconds", uint64(start.Unix()) - base.TipSet.MinTimestamp(), "nullRounds", int64(base.NullRounds), "lateStart", isLate, - "beaconEpoch", uint64(rbase.Round), + "beaconEpoch", rbase.Round, "lookbackEpochs", int64(policy.ChainFinality), // hardcoded as it is unlikely to change again: https://github.com/filecoin-project/lotus/blob/v1.8.0/chain/actors/policy/policy.go#L180-L186 "networkPowerAtLookback", mbi.NetworkPower.String(), "minerPowerAtLookback", mbi.MinerPower.String(), From 36bc96a6bc1472b2493c6165592712bc3a266028 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Wed, 16 Jun 2021 13:41:52 -0400 Subject: [PATCH 122/160] Fix docsgen --- build/openrpc/full.json.gz | Bin 22485 -> 22484 bytes build/openrpc/miner.json.gz | Bin 8089 -> 8089 bytes build/openrpc/worker.json.gz | Bin 2579 -> 2579 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/build/openrpc/full.json.gz b/build/openrpc/full.json.gz index 1eb1ebf942b6dc8ee9d4eea0dcbc656d37db061a..2fee80bb1d1fe56d7cad5f1d80d458fe0ab222cd 100644 GIT binary patch delta 22342 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 delta 22338 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 diff --git a/build/openrpc/miner.json.gz b/build/openrpc/miner.json.gz index d4d24cdd539c616b66c9ef471c391d0a13027209..da845c6ee1c48c7335ecec8f72fee5b5ddd94b68 100644 GIT binary patch delta 22 ecmbPfKhu6f2b0u{ja}^W9EbMIxVo>0kpTc`@d+FN delta 22 ecmbPfKhu6f2h*$B8@t%$IldS3mF(+bWB>qf(+KbY diff --git a/build/openrpc/worker.json.gz b/build/openrpc/worker.json.gz index 19d041474e1b52cbe1c66613982bb5bb4e5f834b..945825467fb0703833cb416aba1486f9faac7026 100644 GIT binary patch delta 21 dcmbO%GFfCoBjfLlO`V(^`Bx8h_*XG7003e!2!H?p delta 21 ccmbO%GFfCoBV+Z(rcO?dJoBhB|0)Is08|YI=Kufz From b9f864ba9eb29ee55573d1b84d768520e65c71eb Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Wed, 16 Jun 2021 14:15:40 -0400 Subject: [PATCH 123/160] Expand on Drand change testing --- chain/sync_test.go | 52 +++++++++++++++++++++++++++++++++------------- 1 file changed, 38 insertions(+), 14 deletions(-) diff --git a/chain/sync_test.go b/chain/sync_test.go index 2cbd7e70c..a1bd4794b 100644 --- a/chain/sync_test.go +++ b/chain/sync_test.go @@ -85,6 +85,7 @@ type syncTestUtil struct { blocks []*store.FullTipSet nds []api.FullNode + us stmgr.UpgradeSchedule } func prepSyncTest(t testing.TB, h int) *syncTestUtil { @@ -104,9 +105,10 @@ func prepSyncTest(t testing.TB, h int) *syncTestUtil { mn: mocknet.New(ctx), g: g, + us: stmgr.DefaultUpgradeSchedule(), } - tu.addSourceNode(stmgr.DefaultUpgradeSchedule(), h) + tu.addSourceNode(h) //tu.checkHeight("source", source, h) @@ -119,7 +121,7 @@ func prepSyncTest(t testing.TB, h int) *syncTestUtil { func prepSyncTestWithV5Height(t testing.TB, h int, v5height abi.ChainEpoch) *syncTestUtil { logging.SetLogLevel("*", "INFO") - us := stmgr.UpgradeSchedule{{ + sched := stmgr.UpgradeSchedule{{ // prepare for upgrade. Network: network.Version9, Height: 1, @@ -138,7 +140,7 @@ func prepSyncTestWithV5Height(t testing.TB, h int, v5height abi.ChainEpoch) *syn Migration: stmgr.UpgradeActorsV5, }} - g, err := gen.NewGeneratorWithUpgradeSchedule(us) + g, err := gen.NewGeneratorWithUpgradeSchedule(sched) if err != nil { t.Fatalf("%+v", err) @@ -153,9 +155,10 @@ func prepSyncTestWithV5Height(t testing.TB, h int, v5height abi.ChainEpoch) *syn mn: mocknet.New(ctx), g: g, + us: sched, } - tu.addSourceNode(us, h) + tu.addSourceNode(h) //tu.checkHeight("source", source, h) // separate logs @@ -266,7 +269,7 @@ func (tu *syncTestUtil) mineNewBlock(src int, miners []int) { tu.g.CurTipset = mts } -func (tu *syncTestUtil) addSourceNode(us stmgr.UpgradeSchedule, gen int) { +func (tu *syncTestUtil) addSourceNode(gen int) { if tu.genesis != nil { tu.t.Fatal("source node already exists") } @@ -282,7 +285,7 @@ func (tu *syncTestUtil) addSourceNode(us stmgr.UpgradeSchedule, gen int) { node.Test(), node.Override(new(modules.Genesis), modules.LoadGenesis(genesis)), - node.Override(new(stmgr.UpgradeSchedule), us), + node.Override(new(stmgr.UpgradeSchedule), tu.us), ) require.NoError(tu.t, err) tu.t.Cleanup(func() { _ = stop(context.Background()) }) @@ -315,6 +318,7 @@ func (tu *syncTestUtil) addClientNode() int { node.Test(), node.Override(new(modules.Genesis), modules.LoadGenesis(tu.genesis)), + node.Override(new(stmgr.UpgradeSchedule), tu.us), ) require.NoError(tu.t, err) tu.t.Cleanup(func() { _ = stop(context.Background()) }) @@ -1010,18 +1014,24 @@ func TestDrandNull(t *testing.T) { build.UpgradeHyperdriveHeight = v5h tu := prepSyncTestWithV5Height(t, H, v5h) + p0 := tu.addClientNode() + p1 := tu.addClientNode() + + tu.loadChainToNode(p0) + tu.loadChainToNode(p1) + entropy := []byte{0, 2, 3, 4} // arbitrarily chosen pers := crypto.DomainSeparationTag_WinningPoStChallengeSeed beforeNull := tu.g.CurTipset - afterNull := tu.mineOnBlock(beforeNull, 0, nil, false, false, nil, 2) + afterNull := tu.mineOnBlock(beforeNull, p0, nil, false, false, nil, 2) nullHeight := beforeNull.TipSet().Height() + 1 if afterNull.TipSet().Height() == nullHeight { t.Fatal("didn't inject nulls as expected") } - rand, err := tu.nds[0].ChainGetRandomnessFromBeacon(tu.ctx, afterNull.TipSet().Key(), pers, nullHeight, entropy) + rand, err := tu.nds[p0].ChainGetRandomnessFromBeacon(tu.ctx, afterNull.TipSet().Key(), pers, nullHeight, entropy) require.NoError(t, err) // calculate the expected randomness based on the beacon BEFORE the null @@ -1032,20 +1042,20 @@ func TestDrandNull(t *testing.T) { require.Equal(t, []byte(rand), expectedRand) // zoom zoom to past the v5 upgrade by injecting many many nulls - postUpgrade := tu.mineOnBlock(afterNull, 0, nil, false, false, nil, v5h) - nv, err := tu.nds[0].StateNetworkVersion(tu.ctx, types.EmptyTSK) + postUpgrade := tu.mineOnBlock(afterNull, p0, nil, false, false, nil, v5h) + nv, err := tu.nds[p0].StateNetworkVersion(tu.ctx, postUpgrade.TipSet().Key()) require.NoError(t, err) if nv != network.Version13 { t.Fatal("expect to be v13 by now") } - afterNull = tu.mineOnBlock(postUpgrade, 0, nil, false, false, nil, 2) + afterNull = tu.mineOnBlock(postUpgrade, p0, nil, false, false, nil, 2) nullHeight = postUpgrade.TipSet().Height() + 1 if afterNull.TipSet().Height() == nullHeight { t.Fatal("didn't inject nulls as expected") } - rand, err = tu.nds[0].ChainGetRandomnessFromBeacon(tu.ctx, afterNull.TipSet().Key(), pers, nullHeight, entropy) + rand0, err := tu.nds[p0].ChainGetRandomnessFromBeacon(tu.ctx, afterNull.TipSet().Key(), pers, nullHeight, entropy) require.NoError(t, err) // calculate the expected randomness based on the beacon AFTER the null @@ -1053,7 +1063,21 @@ func TestDrandNull(t *testing.T) { expectedRand, err = store.DrawRandomness(expectedBE[len(expectedBE)-1].Data, pers, nullHeight, entropy) require.NoError(t, err) - require.Equal(t, []byte(rand), expectedRand) - build.UpgradeHyperdriveHeight = ov5h + require.Equal(t, []byte(rand0), expectedRand) + // Introduce p1 to friendly p0 who has all the blocks + require.NoError(t, tu.mn.LinkAll()) + tu.connect(p0, p1) + tu.waitUntilNodeHasTs(p1, afterNull.TipSet().Key()) + p1Head := tu.getHead(p1) + + // Yes, p1 syncs well to p0's chain + require.Equal(tu.t, p1Head.Key(), afterNull.TipSet().Key()) + + // Yes, p1 sources the same randomness as p0 + rand1, err := tu.nds[p1].ChainGetRandomnessFromBeacon(tu.ctx, afterNull.TipSet().Key(), pers, nullHeight, entropy) + require.NoError(t, err) + require.Equal(t, rand0, rand1) + + build.UpgradeHyperdriveHeight = ov5h } From 526674cefafc15d6665726cb8ef60700aba54c7c Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 16 Jun 2021 16:39:23 -0700 Subject: [PATCH 124/160] fix: pick the correct partitions-per-post limit --- chain/actors/policy/policy.go | 6 +++--- chain/actors/policy/policy.go.template | 6 +++--- chain/actors/policy/policy_test.go | 10 ++++++++++ storage/wdpost_run.go | 2 +- storage/wdpost_run_test.go | 3 ++- 5 files changed, 19 insertions(+), 8 deletions(-) diff --git a/chain/actors/policy/policy.go b/chain/actors/policy/policy.go index 22ec4c742..c159dc98f 100644 --- a/chain/actors/policy/policy.go +++ b/chain/actors/policy/policy.go @@ -278,13 +278,13 @@ func GetMaxSectorExpirationExtension() abi.ChainEpoch { return miner5.MaxSectorExpirationExtension } -// TODO: we'll probably need to abstract over this better in the future. -func GetMaxPoStPartitions(p abi.RegisteredPoStProof) (int, error) { +func GetMaxPoStPartitions(nv network.Version, p abi.RegisteredPoStProof) (int, error) { sectorsPerPart, err := builtin5.PoStProofWindowPoStPartitionSectors(p) if err != nil { return 0, err } - return int(miner5.AddressedSectorsMax / sectorsPerPart), nil + maxSectors := uint64(GetAddressedSectorsMax(nv)) + return int(maxSectors / sectorsPerPart), nil } func GetDefaultSectorSize() abi.SectorSize { diff --git a/chain/actors/policy/policy.go.template b/chain/actors/policy/policy.go.template index 5d8100675..e27aac6e8 100644 --- a/chain/actors/policy/policy.go.template +++ b/chain/actors/policy/policy.go.template @@ -182,13 +182,13 @@ func GetMaxSectorExpirationExtension() abi.ChainEpoch { return miner{{.latestVersion}}.MaxSectorExpirationExtension } -// TODO: we'll probably need to abstract over this better in the future. -func GetMaxPoStPartitions(p abi.RegisteredPoStProof) (int, error) { +func GetMaxPoStPartitions(nv network.Version, p abi.RegisteredPoStProof) (int, error) { sectorsPerPart, err := builtin{{.latestVersion}}.PoStProofWindowPoStPartitionSectors(p) if err != nil { return 0, err } - return int(miner{{.latestVersion}}.AddressedSectorsMax / sectorsPerPart), nil + maxSectors := uint64(GetAddressedSectorsMax(nv)) + return int(maxSectors / sectorsPerPart), nil } func GetDefaultSectorSize() abi.SectorSize { diff --git a/chain/actors/policy/policy_test.go b/chain/actors/policy/policy_test.go index 24e47aaa0..f40250fba 100644 --- a/chain/actors/policy/policy_test.go +++ b/chain/actors/policy/policy_test.go @@ -6,6 +6,7 @@ import ( "github.com/stretchr/testify/require" "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/network" builtin0 "github.com/filecoin-project/specs-actors/actors/builtin" miner0 "github.com/filecoin-project/specs-actors/actors/builtin/miner" paych0 "github.com/filecoin-project/specs-actors/actors/builtin/paych" @@ -68,3 +69,12 @@ func TestPartitionSizes(t *testing.T) { require.Equal(t, sizeOld, sizeNew) } } + +func TestPoStSize(t *testing.T) { + v12PoStSize, err := GetMaxPoStPartitions(network.Version12, abi.RegisteredPoStProof_StackedDrgWindow64GiBV1) + require.Equal(t, 4, v12PoStSize) + require.NoError(t, err) + v13PoStSize, err := GetMaxPoStPartitions(network.Version13, abi.RegisteredPoStProof_StackedDrgWindow64GiBV1) + require.NoError(t, err) + require.Equal(t, 10, v13PoStSize) +} diff --git a/storage/wdpost_run.go b/storage/wdpost_run.go index d590e3814..eeff94418 100644 --- a/storage/wdpost_run.go +++ b/storage/wdpost_run.go @@ -695,7 +695,7 @@ func (s *WindowPoStScheduler) batchPartitions(partitions []api.Partition, nv net // sectors per partition 3: ooo // partitions per message 2: oooOOO // <1><2> (3rd doesn't fit) - partitionsPerMsg, err := policy.GetMaxPoStPartitions(s.proofType) + partitionsPerMsg, err := policy.GetMaxPoStPartitions(nv, s.proofType) if err != nil { return nil, xerrors.Errorf("getting sectors per partition: %w", err) } diff --git a/storage/wdpost_run_test.go b/storage/wdpost_run_test.go index 916929724..3a009d5c7 100644 --- a/storage/wdpost_run_test.go +++ b/storage/wdpost_run_test.go @@ -31,6 +31,7 @@ import ( "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/chain/actors/builtin/miner" + "github.com/filecoin-project/lotus/chain/actors/policy" "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/extern/sector-storage/storiface" "github.com/filecoin-project/lotus/journal" @@ -185,8 +186,8 @@ func TestWDPostDoPost(t *testing.T) { // Work out the number of partitions that can be included in a message // without exceeding the message sector limit + partitionsPerMsg, err := policy.GetMaxPoStPartitions(network.Version13, proofType) require.NoError(t, err) - partitionsPerMsg := int(miner5.AddressedSectorsMax / sectorsPerPartition) if partitionsPerMsg > miner5.AddressedPartitionsMax { partitionsPerMsg = miner5.AddressedPartitionsMax } From 71785f909942b1f4bfbc057fb0533cccc511c03d Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Wed, 16 Jun 2021 21:51:04 -0400 Subject: [PATCH 125/160] Lotus version 1.10.0-rc5 --- CHANGELOG.md | 4 ++-- build/openrpc/full.json.gz | Bin 22484 -> 22485 bytes build/openrpc/miner.json.gz | Bin 8089 -> 8089 bytes build/openrpc/worker.json.gz | Bin 2579 -> 2579 bytes build/version.go | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 73e27e9ed..fbf620f50 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,10 @@ # Lotus changelog -# 1.10.0-rc4 / 2021-06-11 +# 1.10.0-rc5 / 2021-06-16 > Note: If you are running a lotus miner, check out the doc [here](https://docs.filecoin.io/mine/lotus/miner-configuration/#precommitsectorsbatch) for new lotus miner configurations explanations of the new features! -This is the 4th release candidate for Lotus v1.10.0, an upcoming mandatory release of Lotus that will introduce Filecoin network v13. Included in the new network version are the following FIPs: +This is the 5th release candidate for Lotus v1.10.0, an upcoming mandatory release of Lotus that will introduce Filecoin network v13. Included in the new network version are the following FIPs: - [FIP-0008](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0008.md): Add miner batched sector pre-commit method - [FIP-0011](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0011.md): Remove reward auction from reporting consensus faults diff --git a/build/openrpc/full.json.gz b/build/openrpc/full.json.gz index 2fee80bb1d1fe56d7cad5f1d80d458fe0ab222cd..82226eccfca98a437a2ba640e842c63f26ca58c9 100644 GIT binary patch delta 22344 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 delta 22338 zcmV)xK$E}KuL0Dr0g#A)wtD~cds8GP)aSjOAAk1y!b6f+* zqe~*+>b4We0igoSkN`u32nZSVBM)f`58>Jn}uWfE2(?p?Dyox^>%*5V1EA7?=kdegc0uCAS zppZ{SFyQE)pRy5;0wE`2KBasFN^`1gQuBG4ss7zNqY?3cdpld3{oVl-u(#9uHwo7N zbG|q6$bV1Jzy9mLdU8(C_tjFv;FQr2F@a@&HRXOU%sl$rfcd*OMLt%43^9Rh_JMfR z$7C$G4sx`uuA5$>9v4i$`KKRGr(zFGQ}g?uf711(!#|KGEZ4WsgCm647*uLmeV0(}|&(HiOGyVoHd5BqET z*ng=JOC4S*!K%kA`KG{Ee@V39Bf!uIF+@D%gW)WHgH`sA50-Y*%?HiD;%XI$0LMNe z0*`RTb>GK6VDijxM1;Ka90jA-y3G)iv0hOk0Q7x^I7dFf3A8nEEF`8FQ8v{d48b=| zd-?!^ed;3*yttWQZvrs!u{@V@jA;}I?8&#UL+D*0KN&@y`j-F_A3*R)wwgdbS<7Rf zzk9!bs%tiyZ4Uo}9)g1#?oYO7131_g!vViS)9?>^`)3%<=rsIeI31zcXs|WHgJ`xL zMQCgDdP`mV_==ee+8u>`j)I^9k*bH8T}d!z5Ipw#{J!BnA0e?PiC?$t9QU4kn-kta zw*H8qkJvokp*X;tD;~GZa}MeW50NLYK>3z`ueodfRIhpYz}0dpxh6K8$|p<^>+SSL z>l>SctzN%(fxRoFM*ntpc*-apRdO%=-iH8ra&*(?@7{Mz_Gv^!Z|8k~UgwmhQv8iX zO??UAl%5OIPd___OrD(l&+iD2C!)90TkrLIAI%TNAjU1u1r+FvQuQIr*v}a7C&W2_ z9HTSjAsh<6Jn#na5+UxLobQgu42_{c66%FIk4-G8-2Exy0!~BCysGnbj>op$4=Iz) z2@DiZDnaOF+k+e(BBZvgCFfiGjQe7m_4l^mnPZl@e7yheVS%RnK4;&a<U#juTJOV7JMC7w*t}ASnv8X`96}jfA}PVTM%9S6JBW1U>OL9p*Q-OBC@klmc2tbB3dI$p}d~(?v0)a!0L?2-B3kLzdLV=V-p#Ypt z(IrVn)Y@nS{3r|*!to`R`h^1+a0)`oIUWW`DZ$I&fuLXluhAv(@knX5MEWfwMyL8S z;1mIlh>sY6B7HsKlys_;Xs4AI6gi9aB)e#a+Dm}92Mn#phj?Q zdp_<6@7PUIO;ITPs-eCH_m5?DlIt@bHBR^bhQ1Ke1k#M zL1y?C+o6{u{_11;%sKbM1@3m~On-M(-Bn%ARaJ@-&5b+;)ztHo$+id0GT9L#h~w_k znCV|3hozRf3ml$HiN#LzN+C82T&7*lNH;dmv=GzIB-dd1(H~HMXY@(l+OTFR%VpHM z$*)+6xIUK`dr}AVZIg@05sGD|&Ii==w>B%A_O_=r1wUU!VA=wPps#p{ zA0H(0_9gKoBqOd1%8)ZUKV7*ncO2_*Px{&h`6R@CYg02Bso7cccm<$M#>SD&6fS%{_#QC>N=B!Rxf{)V;yJfINK-acKLRWo0g=L zoe8RZR!2?sMoVIP90vXuwU)M&)WEGLqf4T6<}lh|Ow$%zs}s0X?4GVq8MWu$Su&N& zb6fc`{G+$?cUw$qrqE8up@*s&MlWZb0QW?0>btkiH#KiJ=zM4MVCM6}KYq4kgG&R< z=Z5oLnk;{2p=udhrszoX!etwpV}+k4E1aCh((-2C%tw?`&i>ht6{kO1DXco-w{Ggz z1&3-v1E}_j7y?G`30nmli{$F&G;}nitee&vs}vGn_42jk_dhmBkN6 zl%+Vf0=ep-w<2-{7Za4iFkTD+=E4jh@fHV^px1wbzNQ3eBfi4Z(>#Wc5t)jZ6SRgz zCH=KSfttiV_8pRc!braIrc|K(x^{O<06yGeAveQXa{tParTBfOF2-O; zVVpuSSpcJpNgW=~>i2&`vpxtni_bKU|A?T!KY^qT!>opt!$`36aJrZzm|UV4#RW5T zjhKJ<6^3Cj1M;XUPazHnLO${(ZXF|`l*gAuqU6-G3lb_Oi1mSXp;Gr_M-6{Tfc{U= zp^6~%1kHy}uYP`F5Nx=WUe*OB@>(~ElI+}mQ*_na-okoFgJ_G#z zLr?#O*xP;C2kIkUh~2%+-@7k={=PJPU)T#aD|E3WEZHxqHp7z_8ZLjkF$<-Ow&@ma zG5VEafhww%dwaLEDlw23rG#%VXsbwl2J~fPjtTM%5}!^9;{1@&sg5n`OmC`ywV^*A zn}vd|9ndMny#z1hyL5P4N>wxAm~zQcsFIE zD4Cq27!RSeasJ(RjsXU946}N5_hx2!7Q+NF7`r^AHTwJokVIyDJI#K+8we=9;$R59 zE5%SI(b#ctb%I26j?zn#3l}Fej^wUx-_HSr1R%%)jF^6VN#Y>u*igGXob<@}7^ew$ zNOh%=>Q<9Q^do=dos+8SzVrQBhLi?Ys4)RJhk4TNfHy%Z6U7L?G_@i?x0t7HCoTx) z**JyBV^&&O^Kd@mkRxSgBl{sp9{JUO(`2mrh@ju`;v?uUD~Qv;YP#(R`Z+1diN7$_wD3Q4|C-z>(kymD3!oq;+fHD>u&49#o0UoFeccWY*`F$$#TrlLUD#py*7p zP3JizTU&pJ6Y~9K_=oIgMZePlEOcz9aYA>wfC6)YJr3ZIMp{ocW-y!-sQNE-_{T3= z!}iM%a`cN*I|ZDkv|g3|(K2Zg+aTd5*$N@>@o02OhDh8X(^A|}ppUB~{ZE7RH=Cl} z&a4(wW-v`@ant8EBH%gx%Vftm!9>b`TbnHdrcHl8oA?kt*t1Jxu<^c0)uH*C?+aLs zvJmV|$ok119v>4sa#a{+4(ph04LmvxBY6#P^zXP}T`FyMNp$6S#^vyg*^^MSkjYC6 zQE$EXtaL{2>vTo|Mnvqxz}u(O>1q3`o$o(CQ;BMJW}(_F0dBa;`L@7=ffogeqRRWL zA8LOKZ+e&ILN4thuSS-Wcx}sS*UTHcf{x29C8P=J#`@+~OQPMabj35J-<#`o&R^zs z5Dx_hP{0cY2_IE3yta3}2&nGT`%pk(+N{Nmj}MeA8Y0Bj)ZYUzq%@eP5UL+K>uTJL z9*UiTryfbRTTT_kb4{b+wM{g6ete+#Zxw&dslH%o_@id2?#y*#a3^jXW>f9Z;Kt^` z;5SOYAN_Uv*Khy%_ci+XKO#KZ-sSA;fA4wUZ$IuGo^O8SAL!A=E&FtG{oDVDQFnc8 zliTn8SU8;?>||qeqfY;vxuR!CFpREYaDZTWb=n}Q9Hcrd#o|U&$gYrBlWmU=THt@9 z;wba^lv1x06A?hkm`eU2nHTV7iy`F52b2IkzolWG>d(J3#PepEHLaiB_1UabBddcY zx9$`p5ACZB>~e3iAm&?vB{AJUK9CHT9XnqDzaU31X|W>Q9RG!4H-?;P*lB#9s$Ma3 z^hyhye`h1_oWfZ^p|2%}_x+waqp^S3GT1Z^Dih~+TZ)!~P;P!!d1Ev4ZCUs){q*N2 zLe&=BPOw+yfwRZ0vb!B#ZHH71+<6E!ZkolX48<%)f=eCRe9CCE7H2LGVcrXRd#*@o zt5MH$h>5Sty>vrTad#Vkf>&98z<))E&AyCAh}mJS%s_Me3UbZUJI$2_hR1&u53dn` zz(Y)^@NgWUe$D|fogN1-C_x}JDx8y}aW* zk&IQQ-ui^eDZ^feLIxe{eyeV$$f9fh19tTxX+NN0?koBC4xE2gt*%ozn<63<=Dyas z%xp?lWmp&yuW*GBlS$sSfO;r?eviU?N#HmC(qzFvp>?K7xT09-f5oQ0Yxmd&0ACtPnG! zA>}YwYTcl70fq62O^Q(zalcaciMWDs{Ff>~-j}nw#=hiN(=v2Y!j=SQ=kH$Zgm95X z@d@PRItXc=Rj!VX>&$;?lQy^dy)$^DB0pn4;D45{k?&`mj1{AIikMEHp^NJsL^`XP zZegsXJ8!lIoBiHCLptLbRwrkDeSPD#`rpO+`i}bFe|oAynqm^-V;HaS{sa<&0{tn+ z1PcrWmD|$EL!#e1Lp-EJ?`%g9axQ~^^m$0!g))aiz1L?j;;Mh(;2z}5-Hl_OSqi;i z$NU`W@6x}&(a4*q&n%qI+6(bTOkyFe>(azapzw)23N;t3bw#h-8Tz}Ysg8CHLa&8< zS|P$@E^HfpYjWT2)H+nWw^ltmNpg8)RnM1ZWZUA=Zn1osv)*o+xiekGSEnbu>TRaJ zQ3-VBWLLaR&uD*(&rCaYseP}`GxFo(+)lS~kfpa3-*ZC8ohNoe$CWU@hJM!x&#!ad z?|p{T>YcmOaVv|+8?0?}9kU3(O?zfrj3vS0c9+(WT^q=G=I3dg`wzDv@&xizX2e~N z#ErIN$5KJtVeM(W5nX@U;FBDW+(w6Gi7cwDm7*wtY^f^J?Q=2i z5jRR92t4X9&w)mN)}49dZgtM1U8ONeG_?<`z+Ks^(&%0G5%Lr2b@q6VyDur3U=ilL zs}VQNs04p+CP)R!q;47e#|QE(2C`u=GbM+e3iZjVP+dO35)P;>oU3lp@ie5YLwtRv z^Bi00OYq{AZg_^~SNX`poreQ-Lx9ykT$@NE$u<>?q#+dY+zYCjEIFy@bFrCLnk|xzgl?1_en7jCN$*k#$GboiW-Oqn$C@8Ka+%G5V=VD~(1s z$@Xgn<0A5kflw#0=-2l0N(yeC#9~^4Ko{&%zf2SoSJomiL6`wXsQ~-O2Yd68T4~%=nsh;hOy#2H)ss>}0TpdkmoR_8 zfXs)eJrDvHOGX#`7h3@rnz|;24>ty65`WF2Ay4H#39V#%} z0V{Jze)pd@vDs>LsyjnddW{xx-_3%kBRWcG(GIinUL(sq^##!L{CKm?)vAA9Bje4h zHH=Fm80F8oyS(%3F7f;Vr@yugzBw>lKqi(@T&4ojy;YZ~9@|#WSJ4|lA<#4w90&@S ze(!3{xLPx=*3A9jtVYY9slU=SnS0#54m%~~UA{0MF`X_s3Bj>KMhScy2{3LBu(lnW zIvHsV89D+mPK(SlA|TdflBs{3MQkj zloXbaLxUMlzO4w7UDP`ikepug3F|e@MQpo851|fi%ryR-;}4%5i?QA4{16l63pO*- z!7<^{XoNjg(I6szSEnNj$d*nLt(cMK;pVFq=0d5In7DvJfc%u1;edaVBnxY(aUt-u zQ{E-fre4Sp;3D#_fFB_V4QIvzBRSa9b*gZ8^JHW2w$VCtu7vgO1tydJb@%L5(&AK| zp_kyrtHc?}#s-ufVcykB+JaE|+bRzSRntkS7vQojJ;jf+q;{$<%_GPy`&}ZOW+q;_ zLl?{<3JlLDij7^E_i29+K!I2yY?z1uCX8dXv4I>yE)wY}Npxxy@#?&GVGH7gcVSuW z!muT!G=KS22<>$7l zjKTXlTZRgj+JoLz6t?FIGM(#K0=p6=c|Z-6KhqF+>IVme`Urmv^)p(LdPiCD|`TyF&ixfXFpwlqjD38Zr!r0RkKe$%At`MJoGG-vlQ>aY^K8DkD%j%bN)zz=J^` zN6dXE=4u8;YdL>}T=!iXW8;dAJjji#(bOM7{(%cTg#sNSv=7@~%MyfIc3mzfUo$au zN?)&1FDetFes_Vxb0mI4vzOq7ezJ$h$7F6`Pt_deTNO*a05U&07()(52mud<%VX(t z3t-968v>+R-3yv!kPjK1W`BRC#4|ebZ|cTqHuwng6Fh&#LRAqxg52zL^753SDUPNw zldaH|DmY*SkDcDXNwEH(^Sy~j{(FM{^-Q=P5e$c+kH*(*fVVu#(T$kQ`1sB3 z)ou9ah-}`ByuY@m*PD|my%IM=|Mm*rZt>QIn)O$=Wsx%9-Jz>lKN~hMyP&PL?`G|7 z#xa|1r^`jw`}lWnihQh&*bo!QW*>-0eThirCUUgBm5G_naP_nUN2#@DXM1!EhGVY(r+yW@mtkJ_WWR;#zo=?DJGlGFK-Vi?vH~EWilj zfCD^A8tDgu0(6VKNGvfX(QzJ*^KhKUDsvu=irZ81m`i&cyxWQr^NkwYU7o(h2xT#H ze{P^O7nZ0B^HQpT`0dv6lmxjsWfcW-7#q58dS@vA%>)WX{zHa61o(tTfe(gAvjZc_ z0K6nIr56-T;57mhJf466U!ed`DD?py0ZhCglBft^0)8h$Wr>gve+MX}-h?lYD_Gge z7mq8JtWi2!RjicGxr|92Epig&{Scwef0~G|46-#c%Monv$W;lYg;e><{5VI%KM#>t zqo^+Sw5CG2Nwrc}r#%a@r?QkN3nqvNM_(HxpMNIAxy+ia4(XSSwUr`o*P~+rIWBynI|c-`y0%Ly93TDj<2^edaD@OXL^-adA3f%aT3l`f0b~yYs|_T zWqrqD;xl;D5n;#O9}-z-=3tAc<1Q+=PyWF{L;|W#oI%%d_G+h_TItz3%#PZwAZEgpCDLk-y9)qn-GJ2F3p5P_C{XI%qaJ_a!=^x3j}jM(L=^wyP3@=$C)@zGJdaBcg4`b9(I|=ux69 ze>_fMV{F~Y?|cTCOp^Dl&AuFrU@|_X=faGmGDEgA59epmt(t3`gQkCF9MqN;UhSwo~ue!8Kg8~}@(C7bTe1nPRbS`_SFo{wA1$JIHx zW!~FxQ6CXBWuhx;@z`=L0rKP|PP;QNABbQ$Wr{+odop(xCK$ zw^_i#fmwZWTtXy|T>!~mxW};kB_i;cgrNeqvf&^|Y0uT=y4Ue^KYkiz z|2ZBLz%Wah!(mqg+!klz)LR;u9qva5V#v$qP0`Q`uvAS-z^j){69* z(iG;NfBtX=ZfD?j2JW^7?hMAx$OoDvbmw#vR8?``=XzE)UGXRAby@O|?6?oBN=)C^ z#VF-SPhEDGb~M#MnM+!m@~A0FTSb)Gk==AEbK(%IdKC$AMDCLm-xm>fr_>MaMoyNk2{_=Ak8~T6g&0;eUt!A2a@UN*JevX`;g$b&;!*zU&7GmbcD|^s26P z)OJW|FfVaa7RVe5s+s{${yzsHBJyg+)H0hNK(<{PX_Vh!bxY)l$Q+v3YU`32*`Lum ze{U+ep+`vUQ!>Kk&={+8nsl!?tC=DR>X@l^R(xO_7eSj={PKXofP$A2NkDSgD_NpELkbC7v< zLT}8YGZY5ULwmDBgwCMAR6buD^n0f)eH z)iQOU{;u*F&3&e=Q@~tHd*MrMn7E2ntC(Z4~(TtBKn8KNIf94z_ z00o_5Zw-9qNPleBe1QoU2>Q#DP*={YvkKux)usPdw~*P2-pE0d#qd-!e%&sl>0Eao z%YM=eSOU|r=3GzHFkuc-)>g9vF^jvr+vJ>foe9OM(3}d*sn8w)HQZ`4p>+2{ZE``a zF_N?~k=S{jDyP%?bT{pcq;shDe;HJdDqsSetPl@Zyvk8QM+F@fbO}*iLezWaP2Mz_ zl@@mEweaY*^XKg@!?$(h%__mPh71P53_vb<8>PXK*tV}&9o|=GE@6KR-*ZWFjL%d# z5y}HkNifs(`fnzPfP@hJnyL7&izoxg5cx%NxsWYDKw6R6@AjU-(GXIIzLF*&QtvUx)Y4H)M=c$-bkx$RWSvTO zVYX(w$(`{W1@b6%;%9VUe@oXXrHLH{6pHPw#oVZpVJ)<;x^3b}1wnwhDxhAD7N87Z z;sudBqW}}-2B|zFhXM7joTt?tds;Q>mCU)ZTC9!DwLw?^N9XS4xGKk0J$A0@iMe~d zZE}7-Un~mhyderIXHHGyfo$UUvS8 zYn^Rzy4ghfa$Gr%gj<6}bKSLn# zbY%ie_eHsY^0TdOq(7Zck@h#zebX(b@mx+w9&+Q8#$xMiOv3Mw;+rXCNWP=!j-s#h zY|E6~(MCrbe;)yD+^Dm2N~*AkDWJU_Ad!Ph#IPj|tkmf@5WglR{_9lCj44?xZw_Zb zFi1F5HTk9ZxeSt+j8}lI8pUK&Q{-I#XG+2zgP8XSNSUJ}j*cuuM>d(#5 zOP-_>h~-d~T8*prNZG^VVByF1g2|Qo3o^8}T*_NgC<7b&I71!|aRyiA zUthx@e^LQm>Ptq61RIOCEv@-xSp@|i41!oVFMdK{{%SE~ALVoHqvmdoqi`I>9SO&) zJlnFGJ4+Ajrn>$B2KiN9)3=ZEmc6TpH;|OxZfQ$_pj}x$$D2Fed?DU^P?sKzl`o~3+hB_@qs z_6CcI&)^LKlmQ+hBA%%l#$}DNN96@Nv!X<3RdL&!1~1GbBsv${%ynN3ll}PlN`kvc zwa=FLEMI_gE*h&V!svZK68k8s;xCZ;(!9JU^#To0M>m zCC}T_&wIAL-QdT0gv2-0EfagT_X2cC=@^&7WhYayixEhA|;m3909XcBf3R%QqTR+nzs#p zNe1C!GCubv$d7`~3Y`L&#iSq!(*SHiX!65bi9v;KIkI!D-vJ*9hy$*bI(bpZi7XCz z%|l+9vs_xo{5zX;tjF3u8Eap4d6sj@pJ;Q`j0r>BJm8249-8w}Eq|be;u|kGXl*;8}2nrt})=WS5;doGgIV$PreU zk$sl1Y{!(9k;Gf>&ovCRelV^n3)k2OyL#MUW*e*#t6uJ zkl_nbu0+sFA{iA8!Gvnd!F2ny85o+0A-`|#g-Nb-Nz9d!w=~WjMj0Sf=vo(eOqAtt z!VsU(z|Uk33+N4E`o>a%E(r{%1cJpRH@sPq=PHM<@@yY_PKELB zH^KU)2j3wU=aSZ{u}BmI{ocPK6lMEi>zwG9Hue@@%pda2s> z%C1+(zWJq{f5S1ALZvgC(02SzQPx#^ym-v@V@BwM8n=iF%(yp`r|g0*1F^@j7El)~ z6X|EB(;N=>R0+W2lAj&9a^%X9tLI3rwrY&p3a9VWAdsKfi9VUm_7aDs4!Qu`$rer_ z=M-|S?);k?%ayuWN2ud{KSH*5-73sC!;K!35w*e!e|ie@GyYsm5JM_e9H9ctkN}X( zfJdiOb+UE#xumw?+EYa&kBdir93iSroU}a0wfwVr*^X;YnBgKDwhXj)U!3nEEaX#T z$KEvAN{*0_YjuX8KkJO669{$4+OP~S!HW`#wnM?$5$>Eja}3S{GB{Ppu1q&povy4X zD%ftae+->q-jPj{Ju9xh$^hKu!W(!CU-IgttMg9NqblyPq6h75lg9cpx;ce2$rpD! zZfXBTg@YeIjr!(1M4prm3|wpJAUBoyLW1M^S8TRru0MAq?mpSF$_Cq>zx{A47@XU! zFqS7MYd_Qh6tHueW&4cI|5Ob936paqR6m=Pe`^b!;a<9wzj z>0_7ADWwFolG)04>rJi!e}<3P zpCIq*JVeBY!>+oYN>IyYHj`Q{LrQ}->}6?Mj?;9UrsFgnr|CFN$7woF^Zq!^m8lA3 zDknTdIfYea6*rok`?8GGHyBiyo?5es3e2J`;!Dj1S~G$1pf1$hF@BEmbBv#3{2b%= zC>g&hrsi`Ll$jdyFyjJZhLia0f045+n@z4vG1+GbiUqY=3Pk&I@ktfpINAP7@Zwbt z=9${UR;sLcE-oygq3ruAXoMeS9{EbQlZqo-<{qmX*>I0BueswG$p%KU-#edBCPpv_ zx?~&5bnfVq-0j_Yt-$I5aY&Kv3Oypdcuw+rXC=9jl>T>8%i6lmA;B(eStwNLw5 z=-=Mld4a=oB)hgx2wEPyf7wB+&>m=ry6$Hf4Fl|fD>N%s@f_o8M8NSuQu*_F*5oYg zmep*RbQk+YFvD=bCxVf!!PVRGAR-L;f4v>ulCAL{Zw3Q@K(_|sPqGyuzF7zBV&@*3TSa+861EhbO^>Uj|ZH&{$C0Rg2~L+D)rB)_77 z-vkah63fGFxfQ!Jmjxx)E}{CpLw5_BKh&YCGI+!oasL@8RJUuq z;uPxHhZR18RtQ%1f2>2RukwTu$;8w?_3beKnwpWrDLy`A^!$00)dFic;bGBm}}G}d^gn$uJBQB|tf z{o{jPDb?w482V^@%?5bOqa59c$&8QR++N*=e~!rJ&B*&}dwRV&nbIq9GxTq-;O!P~ ztq$#0KrQp#e;vA-^|Qf>*#&JolD#ssm6|Tz$F7>PF7?~=`D@)RKJ53RBXhjULfOzV zKogj=iAF;Y20@&@ydA)nEI|sxh8&uS{9UGWI6HQSN|xU8gGrq{`_YMkLKgS$uhd?M$jRc2aJ+F2c5*C9imGyCK)MGrYE& zqLuMjkLfsb$C-EJ%(Ls4P@^`wsDyA?Zm*_ZHjQI4*lFRbJ}@PBDH1GQs;Z6|)~m(R z$gEv5D9O405sm(7kA-yDcJHENYfBCS_Dh2amq5kIxcO#!hp zN)$%5u});W__`9gfLF0q0$iaP2bgeyp#T3b&5Yo@(jL+)tx0|Cc4oy&99QF+(puLxv(a0N z^^MKJc28B`@Rz7J{fOWawc!~><{k{7`q5k0#iZiHE~~>WMbw;0Th^wf6t>Z3_7~AmcZ1p2^Ai!L%eoDuMpAUFLt~^ z#c=g;)zw{czO3jLdIET98^CoVIs);60Vqn|Uu&1-LNWu!^B|Jg8Isw}1TmC28ZauT zM+1O`MD2ZrTrmff1T%@aL3~E!g7Ijo7Jhlo2+q{m!V5Kj7At-9^6y)KeyW81_!~;C3y`_e<<|x{q-FBfHH~s)H^R?%00`I8v!N=`T&m#6=$werS2 z%s7^z*cSLw!_u}vBO!t)95d)6`4N!J^fJn8?ni+{^?E^(K5&#tlux1{e~^#>7zWh4 z;y_+f(=pG95)*kw6S^B)Ggn$w5ixLww*t0)|Kv zenA1pQ^`PNd=k@Mm;fJ-Mo4`ULE?t8D{!raHNIBAGM7Y+SG7R;1ugcB@1;Dk`31Tp z5fNB#SMsUyhRcu8Q&oB7V}NG-lR1o84+l61hKUgL-(rm)H9f6R51d=M5G2Kk%r zDHH(p${4@R(Fl1W0e49%HsiHso2nc-e^*odUGA5}gK{2bO%+~a;08(oTkn!Q7G4Gm zsY&(IHQ`#*GeioUpqFIsdaE!0R5Pjc3#JhlU;?iVzNCyHPXHq26|T?J81hS^+$K0! zs$c4(4>2gx^E>yFe|N?eXIxowc9^EnZ6~X>a7!&S?Pc%O$~HF2zB5Y2@|1GM%}hDS zwext3=h064mAJKaano{6cFxJpIoUZUJI8vg6zk!f?3|OGbFy-_a|t31X9pf9w?cKJrg7^3XZ{OXm>K zb)GA2V{(`xcq`-BocUQ=%$lI&EcjDQl=5^6Z-JLI1Q2jR85}1zC3Zxe#DslO8b+UymxkJV5pmV{RM!oDk74&qfv*oDH zs_t}YL2DXWe?@rXB(xXab}6D`@@=+eY&$cDPlE)#UvJ7tbAUYRBg2<|G`~SkjRZ^w8h`SW=qYwCu8cmwEyy1e%80Lg=d(d0zyL&ig@=eqf#DYEJ{0J;EBeYJQZ5e6kT$HxmYCfH0^I6Px9ku zmz?Oif05>dlB8{O1R{>lQ)z7~( zLF(0fgT(|uFqNO8obM^142&3^#+arLxS(Mue_!T8(~_``Wu>#tuBs(l(v)rQUh=@U zHz2x1qxXoW8}%-a!B4%K&eRnc%D(A9SDs)xfs$_QiBZErlQmco15D5&e6j9-AAE5# zk_BI^+s@;Qr?Qm}5Sl;^yZ~Z(-a>kV5_b7i{n*uUW{zogO!Jdunzx!Pb|;ixMd9&5 ze@7%?df$J6vCPO8+Sa(WL>dgEYvYZPmJiF9-ElR=90qeW#qJeJy=k%seS+)?iC9V7 z2~*`jEQYRfpsbKpac~QamFr@J`6yI4>I3%}y-^JB300i1u?FdiS7w8;$g0hO? zPaqdxLtpN4VHyGClkB5^mY78{yN@lp#cSW%7ofB}Z6Tz)WD`wecE2LjI(ul_`50sl zr&R=2awyw=YSkfY3nP2bY-U@1f95;Ha%HY-vifnHL3OZcd_!6s!}6W6`o_`~wCa_) zh7090eX?BBc9Z2<6Pr`W1om(UN!Pm9WjG7)Q0)cO!oXJCRJKn}CEqNiY9=d8J3C%$ zb$P8i5wM|QOOC5nFkBup@IXT2lSPPcn~ab8IGDR!$*Q-&hZ!(UL8tQlfAK+`H+Ltw zGC#7l)-KbnLC(xOd`o$AsR5sQIquA{;my_^#d$hR-NMnkK^A`1S^{=r??Z~c?Np#n zv~{Ab6K(HRw0$Cc+q))j5an96>j`{~^lEn%WJ_=sGr9iw`AYe7t!Zdqh;rRLCL?Oc z%~eguhMg<*%kguDk>lo-f6lERXVh2jXOewV-GiA9@+M;!z~}h7`F?SA$Gs|Db$!#j zOTO4#2wdQd4! zb`5a=hXGp5ld1~TV&q*v*~8;w%i>&hbFilAg|0cxbx4^~)6fxsnvVa%W979b@}?rk z4Hgrh!5ac713W~;c~v>uK99C%&w9;5CNEv(uM(1LbY7Bo^#DOXz@%#kXdcF*QcDcT z9;s!Aw#8;ipHe+ye3l6iHv0>=U7VI(BkR*dSt zs*h-=%%fbKN5d)RsuGst9t((dbA7$hzd@pjLl^~}9UD>ri=osZ79)%Te{BzoL-~h2 zW=)2-MXS0)ic>*6cx<+d>(z~9!&#yS$~oaX4C^qg4Tdcpf9!Gs+=>-na?aYy0x zD~icB8XXhlb*xmR+2b+UeK9PiHFgOwdmLtlw#8twd6mXG?|R@+ltWQgeqs4jTXqL4 zy|?i?_;N$?*8T64UFDyAMmOb$^42jL?8cG*y7XDxeSZSU80C%brwFlQzqixd*gP2g zM(OvXzi$8ff9*g2zD6JaM}$Y)yPSRf?>+DP?Z>^t^UaU^13kL9WuH#2fBPRX>h*gn zw}d?2=~$Tf^yZYF3&oILsFi+~SSppRd9XEj^ZxDN-C+A&t^;I3?OAMRQz`8BDdC9l zh%4UXLjXfnTS2=BwDSSre2(-dtV%IaH0ZwK-ySSEe+#QRr)F?U}MYwFkhG zXlIOtfA5M0z4ry<;BwptCCD>abmvP<%9h&0BW=LrgjT%U2TJGfCa^5S0eI!0+?fY(s-!UORv)7Y7y?L^SP=we^1 zGDM#LSwz!#&uqF%{rkPw5>Y{@FBb>Q4~et9{Rk#lfAN{DumTsOGV%sfqKe8#K&Hr-3r2c|<=yO;%^Lh73Z7#cxA6O!KtgaS?zr6N+iYAoaMbZoU zmCpO>%vBCv1n;&fpEL@XT!6(Q_t8#rbQWf8{4>D+%<2)2PPzj95TTX77X~WN&C|tp zn{=Oa(nV}`a<@|{!bzcqWrDp>WIR+$;!0Q9jJyPrbpU4nF)b7mYr+*m#@H&b zS`*1kQ=yd*F}t(^Hnsks1xlYnr0*@xu?72gDxdRO%p>Jw2FAp*_^Th)eyoUQyWNu4 z;OUrf4Bd(CQ#44)%?b)C663dWBkPcspQUd-{i^+(ijLuXQ5lgN_^S~3ftSI)3h$djqzeKKcy~ zV)L&^1uVE513No-$5cBy;BjM~UWXERE=oh5_&U-LXY6o56;4sKLQnQm&|+m?9K_7= zV_lIpRhJL9p?4y!69{iYV2r_NJw8ET*+d7Y2v=!Ku8^4+^^{ThWwxcD;)SO|D zel1Q|jV)i>vI~DK8q&S4Id_-|?EGo@rz{2c1jUMKlgNjpME~BU2$}2*KJDC?nCbz(kp>~caH!pEBHq1- zG*QOtM;ZWGb#9D_(FmgOUq+3q{PAt2fy_qITv>i%qF8z}$h*)mDf~kar(1IfufL|p z>&pA*zr56k{q9$_>Zo$SiYvmN3z4YVmS$UlD{rcfM9ra|7B)I^CII-e$IaJ86~fF5 zipz976ps-m1DpLTvh3`prUW1wETAJtJpG_e>V&AXam)Te(Cr3Cz>-92`9Oz~{LYZV zCf=9I0nj4eaGdLlDR{7Pm)!; z*jpM@;gzm;2nU@NrbGzB)ofhPbm@2m;Dd|oZm%8q^7Oxl1Uo+RjAR|?xrTTZ1r)4} zDreHD$EZ$qF}kd+WcccnuMhoMm$_qclwn<2l+-8dClrAW)PgUX+_7>W<0^o|_=n!f zj{qN9Edh!b-2FIv37mQx`LWNu$HcLmuy%WZ8ZY7!UGAF79G1uY2!rb!-Dg=~o-|5} z$5I%+r2HKJ^DW`F7Cq~e2BS(YqX0~%V*o@AL8br9g)CO!a?UYiG1}ls&-Mu&XI3+Q zZk+;A#1FYo2ivICYD!Ll+MK*T35-*lzQ?8*6Ke~TzE3|V3)W~{jWaS;-~Wf1(H z5_Ump&B##I#Y;_b&iCLlj+U2V^=o+S*J_v~3|SL)!CKuJRvQm{kb0Pk2E8Tk(2e;F z1U_Fud5;3m0SaSo9_&vtzH#G49v-NDJPGCMwrRE>Tv2~Ww(g~%P?<}10C=Jpki*Sh zj7UGUr~CDsut<^;)e((t%2DbGa7=-CxgCO%F|ac*w01N-F`}^LBGiKRg5$5B`=ibF z9})8R1Jfv>L`!OCJ9&RkEV*^WU3>XTZ_TIeQ_WdL25@Q{CWw~{cFD>G!J763B0l^yK~5TRaJcF#UK5^= zOMd0sSzcxP(|*XS8s2iCl148(pwDfvcTTVuhkiQ5A09k0N35cMy`A|nqxX3upD$9- zNBJ?I&sAls}N}f%xT9%$hijJ(W^W!bR3Led*;}F^#hlIS3-P#rR0n{;`pWi%T=2uXknk^owj}! zj}w*eMS-TryZ;-j_QB1apXd|ols3L2z>fg17Clu(=jv<;%szVRJR7iqTA<>gY2wI8 zlF&WLc7VkC-a>t2^ZP>{dy*W9vzNQ5I8Dr=JmKj*@`-;T9CFQB5lT+LXZm?}l4!E> zceSFBl0I*{IhoDIjNszQdVm7l!zsu^R0*miFy1t+%(=H_WR(H>K+{Dj?B4v+v}Y3> zP)Z)Nk!wpuC9ATxOrnoy-GLf8bZiBdHUpPalptLT}aw@ z@ZAMeJz{G}s7!+kmmM($%H{^jeeoRpuzT zaW{EUl2oDWJHE>B&4JO5$)&w#M4JknAS>cK;h6dQK|E>-s@gJ-_?Y2$Zf&4zx#u0f z0wJnWADu(nAtl$NtYmHt3zI)KBsR-?-6VwQO4Ufm$<8T->KXX*zuO{-m#1e(XC__X zaLTa%zMfi4G*`Nur+Gk8TOAXE9w)J_5+Hbx1jVprZ`&T0xc)~x0{+M60fwZEj$)Q9 z-8*niu|(3FHZwC=Q|!9kO1X62RauiDDmVBLe=d>miByvrSnTxpxvrApo~&A;dOje9 zI4;W}Wud(|xyw=H!nb3SAWmApjGhl$Ih!gLOz9Y>G?>uPbJNzf`hv#Vq!GSy&X0bS zg4jOzTSQ4=?-YZxEVl+I0c)oYX`V{_s1qUy^B8!xAhv$@=4=d~0JfmfxoKtH{ zml>b>5T)cKt>Va5R2mPFVfNkP*L0b?Le*rEHdO%N*7T-aJ7 zqpaU3zsO%u?D(cwha7Gs`8f|8TOA+NH0xWJwx6*fCA{Ay+y3Ju)bzmAb;C5v zgN-aGpOyXzV9Nhp8KPxelbqu?=afThYgfb3ujREvK$!JrgXbs)>CMXUM-QL)-95AS zm^^@LMxq|ic+A_%~*_Vn)C4;Mo0nGkWeR?A&wZmL>`_+{c52-YP& z(orx$G_qO;=WHv=%rE-g%6!NR&;WTt={FY8v7{1^CdF6yjN)@gc_|52S?$m6t|N^$ z)~R`oe&WPldm*dg(l+zIEYLc+U@D_B0UbWfDyqe1aH1k{=V8nb!roiU9CKaXa1|c( zoIbnOg+M9C(Mv7;P2a}lIpkV9!RcQX@kXlJn3&gQ#0?d%MqiQbR3?^iYD251x|;$VEH{!KtB+G_9?dEr*Uw2b zsHjfmt~#VVs0%uQqfMvK0_@&U0oK{lxg#5FO`&l`;7pY!n6H^w$`P}4J8LcH-S*vl(c2pM zl~&K3VKCbYFJc76H$iQmYo8OF9Fv1+-A4aGT19c21KDK84n-OTWeMZE*hS^1VL$&= z!)B)?^|8{y!1m3-SPIGE0nzaaoPU%q^{ilwsJm4R8|g%o1pL9t@CD@TB9 zu+8wUA2AQbd%vSL_8zes+^28#KjxOWt>TLI4Zj1Jx26xZz*{fH|BUQ~$o9vhKd_&e z{3yYkkE&A30(edt+@K0bavDhEB^3l2N?!x}U~l`QT`DtKIWAYsGlm^rpaHnnhYc1n zVE}>w$2aKu$g0mKGF$H)=9n9965dp7aNAro8J~(Nn&sy~+xwMs=WoWul6`JhZ;}KC zDJboDSAQhy`SO#=r30;b=DWxm9kJX^X#HqqW$-BS*M_BnHfe{Mo(7Rh4EERk!mbqT zgZW^84B2p=Utn9J;zGH0v)TRPjcA8?CTN}s;xdR!HLhe-nh!0^+^hD*e{ma+zy+U& z{NO{OE@!D)_%@7f?b*;#CYnAaLcE14U(7U-%=FEUvwI{`i7E*+0s^t|GQqgy9k*ej z2Qm7~0rKNyVoZi|poo;Z@~XYcQFs-KTx_SNeuUrBzD<8+uIWIl|2Z`DDxt396A)$wf zpx|7b#2rnAX>*^qDr(Z~2qbNW%walp*m#--Yaa2tClj?&4R!$(ahA!29Wh$hk&_m3 zz(}hW?Q3>Z9~P=2lc(+J6p5j*uxa^3wa&p5kwl#@b<-MM1?y781Vb%yV8ZGud2}gd z0&_-GzTkx4GP#VZ8OWZ5+B#QBfFnwQU@#oe6Dh=R>xbFkjcbet13gg#9jp|u`J>H= z2B&ywZbqvM)f=gE+7W%Z7+{Px4T zj2q;utN7v<)UZ)tep~lQ7G~z7(nN2QZf<2Mjq07cZF*n$I?fPa@_JqZAc_Wmol+@1 z-fcfN=P$`j8~N-5yN1ct1B*U#8p<1oD{(k05(`t{z&MZvZDheiSmUFoz{iMlNeAQj z{TJ4xRoGeLShKu}A-*-B58kvm6(&E_9=CAx%bvckW*X-^8F|QDHUgm;XzNqe_Up)a zg#3#187-zd{YzW`B#`I;b`Um)LdCMUbMx9d3xn#WYA{HXJY|%~1HiOsWqCF|h-_lp zl7{{$5vxH$Gy%>I>gnIg>NEU0gR8}EW*W)WsP@)Cyo=fVRFd_VFk=_yUcpQ#dI%(p zKB}sw`u$0u=BwjQ{->K}efDGE?B6LR!vjdu8!U@g1$vEAJng9NI)%C|SNoBoZ*$P! zobQD_pQFz1>c>{>JNS6pY0mJ>UhkqwSNNx~ex+a9D1-q!1i7&StlTMZx~ zb1CFx_-dj7uCTQay83t?6g`S}gVM44d<2oCnXl9o#}X<~DmM18RwrVWPWwr&?;NER zAg@)F4|TyF&(bp#__Op3-J1+xp-;_>JtR~C;Ghfquk1_=6RMGUh|~?ce(om`8#f(q zX%@pra1WX8CJ3v7V-HQ~R^WP#`b0(Uo_#J2tKlrq4v&E7;+kN3tYSSEG2?Asxyqcj zW*iWCqktYO5|e)D9JE*_caSUipb@~ZPYuRM)r8Z^tZxnMBrIf1LCtQE3SU;N?^kXw#r2aMrMLNlAirSq`< zw(-Kfm3zDnP|YjdMG8H`Y2SrIF4O1xM_CP{z9!>cO$)I-I7UI(B6uGMh#N-Czk!kE zo|#6m`!qD~zx+k3csoVCO``3iX1{4rkqATi-wwNZ3C!HKB_l^*Sd!PEJd0lh&bLV8 zDnB)KW{Y=U%}EKN?wdP%6}WP>No(WZWHMW1md&4MC9T$VNv`!)f#eYyWIkp+j_0n} zMUU>Z#RSR7x;b0@XKnkD2HmbLn&58T7ahm>`&7n`YpO9^R-&mTOkTwAMta4}yW&|2 z*Y8E1qo$e{ivv*hjkSDXVRc_%{55-|Cwkaz+RU)?tMaSq(-1;B=~%F*-lTP}vKA~2 z5x0@v^xVI1YnDE=qZW)%5Q5e|#t0nH1byL;OHzofhskYYR^@)1qA>xGfn^`5(!a(? z)aTu_rYuawAb4rKv#wwBbVC-Z%^B8%dF>;4T!-rfw4`t(UanT^Y9D>SBnWWml#$;c zR>_`-=UxqNe`MMdsgCQRj{b087$69KkPx2uJ+=TmDms%IJD;bYgI}o%j`mP}q(Y9B zD$SUYOP^aYK|2M$uivsq2Y(>mmP%(NqnX#7 z3)5c*?Y^O#1(jUw2z4C?NywH!9kJ%UoC9D}sb9u3V`bA`hAWGXf}cKo_kQb&#Lbgd zl_!xp=Huy>V}DP%D_52Q5iH1mz$HiR!4tb0oEF_MrplAGsxfQ*kx`%mV@!N*sV0=i z+u)(TxyQMX*a#)+K@DX)%!t-Z8_&Qoc5FOtXekYzY*~=dYbx#TTB=kN*Ipc?sdvY; z&chs{UJSk$@o<+<2XkR7pMW`6YFyM1gjhATn>KzD!|omRF{W2+op^hxNoS`QwM>fO@wKs!Sa1EC2C#piy*+))fSU3rn~-uxTnyZY zgGT8PbtmhjvfafNC3_M!TmPeKb-Jt~5-JpHYAOHC1K_kWkWNry`p+ND1Sig_2z{3Z zKkhSvJ+`1kfAow0`-o2LZ*F|n?RLeg)>ASv%B8_opi8M=19$~PN~i=fGbrf5z2J2t zF@38qFOfN}zx;~}O%3OAJ8!(-ZEdF*duA>>gTziLRZ8$IHuwY%>;2n*HhiT?apBiI zxN!V(%Jb4SZ}XPI{4j#g!G~L6kq#GX-!{5K9~UjLTkMWvbN8d3+wPBn{xR-}I=frB z2riGw6iEx9EY)FUW5ICNf);FFgj+Yql#rs{OJ-eru-vJqbw^DXytqPJg1=czALN0Y zJk^skTK6?n!ngv`Hv}~@=3b^&9=@mye;9HmaoMMaU>EXSE(CoyK%?I_ zoLOtdvu-PjPkdFXUwRuf3j8?V&3+7dCtQWOR|Rg@CNREz%X%S!8_gQVORHK_f#Y6V hw*!fosc|+8rz#M-0 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 literal 8089 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 diff --git a/build/openrpc/worker.json.gz b/build/openrpc/worker.json.gz index 945825467fb0703833cb416aba1486f9faac7026..83f2c5f436cc261667fca9840862065bba4a45f2 100644 GIT binary patch delta 2476 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 delta 2476 zcmV;d2~+ly6q6K?fq(mWVPOx{j5`t!zCB?^JPrCgb;0J8wLUn`h=L8+!VXAVP+u7o zx3{-be$6HUv(X!ge*4PB9f&I>0$UxSCD@h7V>+j=$n3w>zpWZgx=_nbbmr@Xbp+$UYuJ9gy7-` zJzcYFB|rs&A=$cM3Iro=D?zFzO%tLA)TocH+2Wer-riyh%fR!9nBxIn4PfT1hmqF| zTi7RpcxDL#p_g$yKlZt#;gUsPr>lYCIF-O0ck>Xj)>f z01y$eVnk*Lv9JmWSbkotNZj0a5x4O1cd!*sPpSffZ%Yg}3kwVQJD>s_JWwJ4y&1j$ zn<(89PHthXcB`A&tpbiMwP!4F-5U#21rbwZW{@7stbY}v5)rgE$!|-T8@;#7HX;O> zSD$4sGjhc_)|p3QT~0dP{!PxzDo&$cC|X)kl#{ArNznw9qUc6O0n)n?ckBe3xn|F%)%aJiBC~y!{ZAp zG57t`RDZQqPqs}F{S$YfhP${Vx16uoIEAm!DJ){o z-FZw~aF?=PUsjMxaUGUGT*{_6L6lS?Y}t+vml5M~bb0(9Y7^DCag7^yXl`8ZeSsCH zjDNWF2Sj^O(VVA%tLA@}?U#g2Ygy;@(U)erG0%G;Li=Ez*SM@+uBO7$ex6JusB&u? zCRS=LOwG(sW7!(Zc4(HZ|2E&UU9btSXwNiAerjU<96OMHSFDDPx_@IpsdT2)HtbgE; zY(&&dRx~#uOuiRtqFIow$Q0%lJc0^h@Jy%O2I51)t|0Qb4DT&Z;}%x< zr6wu=TO+s`zwRTqi5t?M_w84p(^;v&e~aP#zKX-!f+NF`;5JBk1yUO3sw5XF;CulI z0_wEBeA(W%NE(s`-a=DgAoMmw9o;#4m;sCWT{GW| zhomAxRVyINle>7HnKev)Q{tSYl;PV+;nHSPb|A7)ka)iYQmfiBhAWCiC~tigRDpC7LdrGhUw!tT9lps)?v)hn zLr$_ed~b|fW84nfxV$NnI8SMMD#K?v)_zgE`OTwU=kn8-Kgh*rh|WOTB`kmwNY_ zwsWCr(@mz2^bXdpP+4dkC=_3DVo^;^;@*%pY=6!ii4;d#iIOQ%pg$-g?~rCxCvdk|NA_#poNWrVKK4#ei_Wx+2ll~n z!mGbkSw0P29B2s2rkglFgR7YRw-zjf$&7N3fF@rE+UBF1ua6ZAvf6(Ud3SCURz)^U zKo+tf5Lat484hC}sCkwh(>Qm}iW&OKzfIe|@3ieOe1B?p8`k_8);udn#^^mU_O=FR z8o;eGW{k4o%U8jdZwrF4<(^c1ekF#>56C>?f6H$CjSQim7MsGfB=&sfT=qc6*FmS# z17uzn`RVX$1L2uf+T6cPN@mIHnsb)yK6nZ%{(PeJqKr}*iPmo$Qy2%HTzFRydztFR zIxj{fAb;z=Uzhhv#B)E>m3tU|ifrn%hSaH%F;CB-xi8S9&P%<8_Y&W4IM|=$I!YU^*0ePZ8%6OW=o=bJDG5%fv- znh`l`r5R^CmjRj|?Pv~~nu8|e3%)PNOh&R?(GQHE^5dFVJv{G+t#It*J;|bpt=e~F z^Li(}?z`GnZ(rG`Ae;D=Q1!2Xf~v?sF+EOKF+Sn{^7XJ+Z0RR7Q)xwBRdH?`GZP1ti diff --git a/build/version.go b/build/version.go index c9d1a5cce..d5c126068 100644 --- a/build/version.go +++ b/build/version.go @@ -29,7 +29,7 @@ func buildType() string { } // BuildVersion is the local build version, set by build system -const BuildVersion = "1.10.0-rc4" +const BuildVersion = "1.10.0-rc5" func UserVersion() string { return BuildVersion + buildType() + CurrentCommit From bc27602bea0c1efbda8517b2b4494508135a893e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sun, 30 May 2021 17:20:14 +0200 Subject: [PATCH 126/160] events: Fix handling of multiple matched events per epoch From 5c8498b6036c5a63795da6408e9ae5f49c5e9673 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 14 Apr 2021 20:26:07 +0200 Subject: [PATCH 127/160] storagefsm: Fix batch deal packing behavior --- api/test/deals.go | 133 +++++++++++++++++--------------- cli/test/client.go | 4 +- extern/storage-sealing/fsm.go | 4 + extern/storage-sealing/input.go | 36 ++++++--- node/impl/storminer.go | 6 +- 5 files changed, 106 insertions(+), 77 deletions(-) diff --git a/api/test/deals.go b/api/test/deals.go index e3432ff0d..753cbc230 100644 --- a/api/test/deals.go +++ b/api/test/deals.go @@ -51,7 +51,7 @@ func TestDoubleDealFlow(t *testing.T, b APIBuilder, blocktime time.Duration, sta } func MakeDeal(t *testing.T, ctx context.Context, rseed int, client api.FullNode, miner TestStorageNode, carExport, fastRet bool, startEpoch abi.ChainEpoch) { - res, data, err := CreateClientFile(ctx, client, rseed) + res, data, err := CreateClientFile(ctx, client, rseed, 0) if err != nil { t.Fatal(err) } @@ -72,8 +72,11 @@ func MakeDeal(t *testing.T, ctx context.Context, rseed int, client api.FullNode, testRetrieval(t, ctx, client, fcid, &info.PieceCID, carExport, data) } -func CreateClientFile(ctx context.Context, client api.FullNode, rseed int) (*api.ImportRes, []byte, error) { - data := make([]byte, 1600) +func CreateClientFile(ctx context.Context, client api.FullNode, rseed, size int) (*api.ImportRes, []byte, error) { + if size == 0 { + size = 1600 + } + data := make([]byte, size) rand.New(rand.NewSource(int64(rseed))).Read(data) dir, err := ioutil.TempDir(os.TempDir(), "test-make-deal-") @@ -119,7 +122,7 @@ func TestPublishDealsBatching(t *testing.T, b APIBuilder, blocktime time.Duratio // Starts a deal and waits until it's published runDealTillPublish := func(rseed int) { - res, _, err := CreateClientFile(s.ctx, s.client, rseed) + res, _, err := CreateClientFile(s.ctx, s.client, rseed, 0) require.NoError(t, err) upds, err := client.ClientGetDealUpdates(s.ctx) @@ -186,68 +189,76 @@ func TestPublishDealsBatching(t *testing.T, b APIBuilder, blocktime time.Duratio } func TestBatchDealInput(t *testing.T, b APIBuilder, blocktime time.Duration, startEpoch abi.ChainEpoch) { - publishPeriod := 10 * time.Second - maxDealsPerMsg := uint64(4) + run := func(piece, deals, expectSectors int) func(t *testing.T) { + return func(t *testing.T) { + publishPeriod := 10 * time.Second + maxDealsPerMsg := uint64(deals) - // Set max deals per publish deals message to maxDealsPerMsg - minerDef := []StorageMiner{{ - Full: 0, - Opts: node.Options( - node.Override( - new(*storageadapter.DealPublisher), - storageadapter.NewDealPublisher(nil, storageadapter.PublishMsgConfig{ - Period: publishPeriod, - MaxDealsPerMsg: maxDealsPerMsg, - })), - node.Override(new(dtypes.GetSealingConfigFunc), func() (dtypes.GetSealingConfigFunc, error) { - return func() (sealiface.Config, error) { - return sealiface.Config{ - MaxWaitDealsSectors: 1, - MaxSealingSectors: 1, - MaxSealingSectorsForDeals: 2, - AlwaysKeepUnsealedCopy: true, - }, nil - }, nil - }), - ), - Preseal: PresealGenesis, - }} + // Set max deals per publish deals message to maxDealsPerMsg + minerDef := []StorageMiner{{ + Full: 0, + Opts: node.Options( + node.Override( + new(*storageadapter.DealPublisher), + storageadapter.NewDealPublisher(nil, storageadapter.PublishMsgConfig{ + Period: publishPeriod, + MaxDealsPerMsg: maxDealsPerMsg, + })), + node.Override(new(dtypes.GetSealingConfigFunc), func() (dtypes.GetSealingConfigFunc, error) { + return func() (sealiface.Config, error) { + return sealiface.Config{ + MaxWaitDealsSectors: 1, + MaxSealingSectors: 1, + MaxSealingSectorsForDeals: 2, + AlwaysKeepUnsealedCopy: true, + }, nil + }, nil + }), + ), + Preseal: PresealGenesis, + }} - // Create a connect client and miner node - n, sn := b(t, OneFull, minerDef) - client := n[0].FullNode.(*impl.FullNodeAPI) - miner := sn[0] - s := connectAndStartMining(t, b, blocktime, client, miner) - defer s.blockMiner.Stop() + // Create a connect client and miner node + n, sn := b(t, OneFull, minerDef) + client := n[0].FullNode.(*impl.FullNodeAPI) + miner := sn[0] + s := connectAndStartMining(t, b, blocktime, client, miner) + defer s.blockMiner.Stop() - // Starts a deal and waits until it's published - runDealTillSeal := func(rseed int) { - res, _, err := CreateClientFile(s.ctx, s.client, rseed) - require.NoError(t, err) + // Starts a deal and waits until it's published + runDealTillSeal := func(rseed int) { + res, _, err := CreateClientFile(s.ctx, s.client, rseed, piece) + require.NoError(t, err) - dc := startDeal(t, s.ctx, s.miner, s.client, res.Root, false, startEpoch) - waitDealSealed(t, s.ctx, s.miner, s.client, dc, false) + dc := startDeal(t, s.ctx, s.miner, s.client, res.Root, false, startEpoch) + waitDealSealed(t, s.ctx, s.miner, s.client, dc, false) + } + + // Run maxDealsPerMsg+1 deals in parallel + done := make(chan struct{}, maxDealsPerMsg+1) + for rseed := 1; rseed <= int(maxDealsPerMsg+1); rseed++ { + rseed := rseed + go func() { + runDealTillSeal(rseed) + done <- struct{}{} + }() + } + + // Wait for maxDealsPerMsg of the deals to be published + for i := 0; i < int(maxDealsPerMsg); i++ { + <-done + } + + sl, err := sn[0].SectorsList(s.ctx) + require.NoError(t, err) + require.GreaterOrEqual(t, len(sl), expectSectors) + require.LessOrEqual(t, len(sl), expectSectors+1) + } } - // Run maxDealsPerMsg+1 deals in parallel - done := make(chan struct{}, maxDealsPerMsg+1) - for rseed := 1; rseed <= int(maxDealsPerMsg+1); rseed++ { - rseed := rseed - go func() { - runDealTillSeal(rseed) - done <- struct{}{} - }() - } - - // Wait for maxDealsPerMsg of the deals to be published - for i := 0; i < int(maxDealsPerMsg); i++ { - <-done - } - - sl, err := sn[0].SectorsList(s.ctx) - require.NoError(t, err) - require.GreaterOrEqual(t, len(sl), 4) - require.LessOrEqual(t, len(sl), 5) + t.Run("4-p1600B", run(1600, 4, 4)) + t.Run("4-p513B", run(513, 4, 2)) + t.Run("32-p257B", run(257, 32, 8)) } func TestFastRetrievalDealFlow(t *testing.T, b APIBuilder, blocktime time.Duration, startEpoch abi.ChainEpoch) { @@ -430,7 +441,7 @@ func startSealingWaiting(t *testing.T, ctx context.Context, miner TestStorageNod si, err := miner.SectorsStatus(ctx, snum, false) require.NoError(t, err) - t.Logf("Sector state: %s", si.State) + t.Logf("Sector %d state: %s", snum, si.State) if si.State == api.SectorState(sealing.WaitDeals) { require.NoError(t, miner.SectorStartSealing(ctx, snum)) } diff --git a/cli/test/client.go b/cli/test/client.go index 4a49f732a..2a48b7b64 100644 --- a/cli/test/client.go +++ b/cli/test/client.go @@ -44,7 +44,7 @@ func RunClientTest(t *testing.T, cmds []*lcli.Command, clientNode test.TestNode) // Create a deal (non-interactive) // client deal --start-epoch= 1000000attofil - res, _, err := test.CreateClientFile(ctx, clientNode, 1) + res, _, err := test.CreateClientFile(ctx, clientNode, 1, 0) require.NoError(t, err) startEpoch := fmt.Sprintf("--start-epoch=%d", 2<<12) dataCid := res.Root @@ -60,7 +60,7 @@ func RunClientTest(t *testing.T, cmds []*lcli.Command, clientNode test.TestNode) // // "no" (verified client) // "yes" (confirm deal) - res, _, err = test.CreateClientFile(ctx, clientNode, 2) + res, _, err = test.CreateClientFile(ctx, clientNode, 2, 0) require.NoError(t, err) dataCid2 := res.Root duration = fmt.Sprintf("%d", build.MinDealDuration/builtin.EpochsInDay) diff --git a/extern/storage-sealing/fsm.go b/extern/storage-sealing/fsm.go index 1ad2d7ec0..1baf9203b 100644 --- a/extern/storage-sealing/fsm.go +++ b/extern/storage-sealing/fsm.go @@ -51,6 +51,7 @@ var fsmPlanners = map[SectorState]func(events []statemachine.Event, state *Secto AddPiece: planOne( on(SectorPieceAdded{}, WaitDeals), apply(SectorStartPacking{}), + apply(SectorAddPiece{}), on(SectorAddPieceFailed{}, AddPieceFailed), ), Packing: planOne(on(SectorPacked{}, GetTicket)), @@ -582,6 +583,7 @@ func onReturning(mut mutator) func() (mutator, func(*SectorInfo) (bool, error)) func planOne(ts ...func() (mut mutator, next func(*SectorInfo) (more bool, err error))) func(events []statemachine.Event, state *SectorInfo) (uint64, error) { return func(events []statemachine.Event, state *SectorInfo) (uint64, error) { + eloop: for i, event := range events { if gm, ok := event.User.(globalMutator); ok { gm.applyGlobal(state) @@ -604,6 +606,8 @@ func planOne(ts ...func() (mut mutator, next func(*SectorInfo) (more bool, err e if err != nil || !more { return uint64(i + 1), err } + + continue eloop } _, ok := event.User.(Ignorable) diff --git a/extern/storage-sealing/input.go b/extern/storage-sealing/input.go index 1ba8b4c2c..5cb08c2a5 100644 --- a/extern/storage-sealing/input.go +++ b/extern/storage-sealing/input.go @@ -27,6 +27,14 @@ func (m *Sealing) handleWaitDeals(ctx statemachine.Context, sector SectorInfo) e m.inputLk.Lock() + sid := m.minerSectorID(sector.SectorNumber) + + if len(m.assignedPieces[sid]) > 0 { + m.inputLk.Unlock() + // got assigned more pieces in the AddPiece state + return ctx.Send(SectorAddPiece{}) + } + started, err := m.maybeStartSealing(ctx, sector, used) if err != nil || started { delete(m.openSectors, m.minerSectorID(sector.SectorNumber)) @@ -36,16 +44,16 @@ func (m *Sealing) handleWaitDeals(ctx statemachine.Context, sector SectorInfo) e return err } - m.openSectors[m.minerSectorID(sector.SectorNumber)] = &openSector{ - used: used, - maybeAccept: func(cid cid.Cid) error { - // todo check deal start deadline (configurable) + if _, has := m.openSectors[sid]; !has { + m.openSectors[sid] = &openSector{ + used: used, + maybeAccept: func(cid cid.Cid) error { + // todo check deal start deadline (configurable) + m.assignedPieces[sid] = append(m.assignedPieces[sid], cid) - sid := m.minerSectorID(sector.SectorNumber) - m.assignedPieces[sid] = append(m.assignedPieces[sid], cid) - - return ctx.Send(SectorAddPiece{}) - }, + return ctx.Send(SectorAddPiece{}) + }, + } } go func() { @@ -350,11 +358,19 @@ func (m *Sealing) updateInput(ctx context.Context, sp abi.RegisteredSealProof) e continue } + avail := abi.PaddedPieceSize(ssize).Unpadded() - m.openSectors[mt.sector].used + + if mt.size > avail { + continue + } + err := m.openSectors[mt.sector].maybeAccept(mt.deal) if err != nil { m.pendingPieces[mt.deal].accepted(mt.sector.Number, 0, err) // non-error case in handleAddPiece } + m.openSectors[mt.sector].used += mt.padding + mt.size + m.pendingPieces[mt.deal].assigned = true delete(toAssign, mt.deal) @@ -362,8 +378,6 @@ func (m *Sealing) updateInput(ctx context.Context, sp abi.RegisteredSealProof) e log.Errorf("sector %d rejected deal %s: %+v", mt.sector, mt.deal, err) continue } - - delete(m.openSectors, mt.sector) } if len(toAssign) > 0 { diff --git a/node/impl/storminer.go b/node/impl/storminer.go index 622ac22f1..61c69b2ba 100644 --- a/node/impl/storminer.go +++ b/node/impl/storminer.go @@ -245,13 +245,13 @@ func (sm *StorageMinerAPI) SectorsList(context.Context) ([]abi.SectorNumber, err return nil, err } - out := make([]abi.SectorNumber, len(sectors)) - for i, sector := range sectors { + out := make([]abi.SectorNumber, 0, len(sectors)) + for _, sector := range sectors { if sector.State == sealing.UndefinedSectorState { continue // sector ID not set yet } - out[i] = sector.SectorNumber + out = append(out, sector.SectorNumber) } return out, nil } From 097baeb9b0ad3fcc2fdbe3da239a141c691a0989 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sun, 30 May 2021 15:13:38 +0200 Subject: [PATCH 128/160] Make batch deal input test less flaky --- api/test/deals.go | 73 +++++++++++++++---- api/test/mining.go | 2 +- extern/storage-sealing/fsm.go | 2 + extern/storage-sealing/input.go | 1 + extern/storage-sealing/states_sealing.go | 2 +- .../storageadapter/ondealsectorcommitted.go | 2 + node/node_test.go | 18 +++++ 7 files changed, 84 insertions(+), 16 deletions(-) diff --git a/api/test/deals.go b/api/test/deals.go index 753cbc230..740fb8fea 100644 --- a/api/test/deals.go +++ b/api/test/deals.go @@ -8,6 +8,7 @@ import ( "math/rand" "os" "path/filepath" + "sort" "testing" "time" @@ -63,7 +64,7 @@ func MakeDeal(t *testing.T, ctx context.Context, rseed int, client api.FullNode, // TODO: this sleep is only necessary because deals don't immediately get logged in the dealstore, we should fix this time.Sleep(time.Second) - waitDealSealed(t, ctx, miner, client, deal, false) + waitDealSealed(t, ctx, miner, client, deal, false, false, nil) // Retrieval info, err := client.ClientGetDealInfo(ctx, *deal) @@ -207,10 +208,11 @@ func TestBatchDealInput(t *testing.T, b APIBuilder, blocktime time.Duration, sta node.Override(new(dtypes.GetSealingConfigFunc), func() (dtypes.GetSealingConfigFunc, error) { return func() (sealiface.Config, error) { return sealiface.Config{ - MaxWaitDealsSectors: 1, + MaxWaitDealsSectors: 2, MaxSealingSectors: 1, - MaxSealingSectorsForDeals: 2, + MaxSealingSectorsForDeals: 3, AlwaysKeepUnsealedCopy: true, + WaitDealsDelay: time.Hour, }, nil }, nil }), @@ -225,18 +227,40 @@ func TestBatchDealInput(t *testing.T, b APIBuilder, blocktime time.Duration, sta s := connectAndStartMining(t, b, blocktime, client, miner) defer s.blockMiner.Stop() + checkNoPadding := func() { + sl, err := sn[0].SectorsList(s.ctx) + require.NoError(t, err) + + sort.Slice(sl, func(i, j int) bool { + return sl[i] < sl[j] + }) + + for _, snum := range sl { + si, err := sn[0].SectorsStatus(s.ctx, snum, false) + require.NoError(t, err) + + fmt.Printf("S %d: %+v %s\n", snum, si.Deals, si.State) + + for _, deal := range si.Deals { + if deal == 0 { + fmt.Printf("sector %d had a padding piece!\n", snum) + } + } + } + } + // Starts a deal and waits until it's published runDealTillSeal := func(rseed int) { res, _, err := CreateClientFile(s.ctx, s.client, rseed, piece) require.NoError(t, err) dc := startDeal(t, s.ctx, s.miner, s.client, res.Root, false, startEpoch) - waitDealSealed(t, s.ctx, s.miner, s.client, dc, false) + waitDealSealed(t, s.ctx, s.miner, s.client, dc, false, true, checkNoPadding) } - // Run maxDealsPerMsg+1 deals in parallel - done := make(chan struct{}, maxDealsPerMsg+1) - for rseed := 1; rseed <= int(maxDealsPerMsg+1); rseed++ { + // Run maxDealsPerMsg deals in parallel + done := make(chan struct{}, maxDealsPerMsg) + for rseed := 0; rseed < int(maxDealsPerMsg); rseed++ { rseed := rseed go func() { runDealTillSeal(rseed) @@ -249,10 +273,12 @@ func TestBatchDealInput(t *testing.T, b APIBuilder, blocktime time.Duration, sta <-done } + checkNoPadding() + sl, err := sn[0].SectorsList(s.ctx) require.NoError(t, err) - require.GreaterOrEqual(t, len(sl), expectSectors) - require.LessOrEqual(t, len(sl), expectSectors+1) + require.Equal(t, len(sl), expectSectors) + //require.LessOrEqual(t, len(sl), expectSectors+1) } } @@ -314,12 +340,12 @@ func TestSecondDealRetrieval(t *testing.T, b APIBuilder, blocktime time.Duration // TODO: this sleep is only necessary because deals don't immediately get logged in the dealstore, we should fix this time.Sleep(time.Second) - waitDealSealed(t, s.ctx, s.miner, s.client, deal1, true) + waitDealSealed(t, s.ctx, s.miner, s.client, deal1, true, false, nil) deal2 := startDeal(t, s.ctx, s.miner, s.client, fcid2, true, 0) time.Sleep(time.Second) - waitDealSealed(t, s.ctx, s.miner, s.client, deal2, false) + waitDealSealed(t, s.ctx, s.miner, s.client, deal2, false, false, nil) // Retrieval info, err := s.client.ClientGetDealInfo(s.ctx, *deal2) @@ -375,7 +401,7 @@ func startDeal(t *testing.T, ctx context.Context, miner TestStorageNode, client return deal } -func waitDealSealed(t *testing.T, ctx context.Context, miner TestStorageNode, client api.FullNode, deal *cid.Cid, noseal bool) { +func waitDealSealed(t *testing.T, ctx context.Context, miner TestStorageNode, client api.FullNode, deal *cid.Cid, noseal, noSealStart bool, cb func()) { loop: for { di, err := client.ClientGetDealInfo(ctx, *deal) @@ -387,7 +413,9 @@ loop: if noseal { return } - startSealingWaiting(t, ctx, miner) + if !noSealStart { + startSealingWaiting(t, ctx, miner) + } case storagemarket.StorageDealProposalRejected: t.Fatal("deal rejected") case storagemarket.StorageDealFailing: @@ -398,8 +426,25 @@ loop: fmt.Println("COMPLETE", di) break loop } - fmt.Println("Deal state: ", storagemarket.DealStates[di.State]) + + mds, err := miner.MarketListIncompleteDeals(ctx) + if err != nil { + t.Fatal(err) + } + + var minerState storagemarket.StorageDealStatus + for _, md := range mds { + if md.DealID == di.DealID { + minerState = md.State + break + } + } + + fmt.Printf("Deal %d state: client:%s provider:%s\n", di.DealID, storagemarket.DealStates[di.State], storagemarket.DealStates[minerState]) time.Sleep(time.Second / 2) + if cb != nil { + cb() + } } } diff --git a/api/test/mining.go b/api/test/mining.go index 4a4f1e1a4..d6dea9abf 100644 --- a/api/test/mining.go +++ b/api/test/mining.go @@ -194,7 +194,7 @@ func TestDealMining(t *testing.T, b APIBuilder, blocktime time.Duration, carExpo // TODO: this sleep is only necessary because deals don't immediately get logged in the dealstore, we should fix this time.Sleep(time.Second) - waitDealSealed(t, ctx, provider, client, deal, false) + waitDealSealed(t, ctx, provider, client, deal, false, false, nil) <-minedTwo diff --git a/extern/storage-sealing/fsm.go b/extern/storage-sealing/fsm.go index 1baf9203b..cfd223364 100644 --- a/extern/storage-sealing/fsm.go +++ b/extern/storage-sealing/fsm.go @@ -225,6 +225,8 @@ var fsmPlanners = map[SectorState]func(events []statemachine.Event, state *Secto func (m *Sealing) logEvents(events []statemachine.Event, state *SectorInfo) { for _, event := range events { + log.Debugw("sector event", "sector", state.SectorNumber, "type", fmt.Sprintf("%T", event.User), "event", event.User) + e, err := json.Marshal(event) if err != nil { log.Errorf("marshaling event for logging: %+v", err) diff --git a/extern/storage-sealing/input.go b/extern/storage-sealing/input.go index 5cb08c2a5..df82f0acf 100644 --- a/extern/storage-sealing/input.go +++ b/extern/storage-sealing/input.go @@ -440,6 +440,7 @@ func (m *Sealing) createSector(ctx context.Context, cfg sealiface.Config, sp abi func (m *Sealing) StartPacking(sid abi.SectorNumber) error { m.startupWait.Wait() + log.Infow("starting to seal deal sector", "sector", sid, "trigger", "user") return m.sectors.Send(uint64(sid), SectorStartPacking{}) } diff --git a/extern/storage-sealing/states_sealing.go b/extern/storage-sealing/states_sealing.go index 4f0f1dc80..31029649a 100644 --- a/extern/storage-sealing/states_sealing.go +++ b/extern/storage-sealing/states_sealing.go @@ -37,7 +37,7 @@ func (m *Sealing) handlePacking(ctx statemachine.Context, sector SectorInfo) err } // todo: return to the sealing queue (this is extremely unlikely to happen) - pp.accepted(sector.SectorNumber, 0, xerrors.Errorf("sector entered packing state early")) + pp.accepted(sector.SectorNumber, 0, xerrors.Errorf("sector %d entered packing state early", sector.SectorNumber)) } delete(m.openSectors, m.minerSectorID(sector.SectorNumber)) diff --git a/markets/storageadapter/ondealsectorcommitted.go b/markets/storageadapter/ondealsectorcommitted.go index 31bc0b8bf..f255c9e55 100644 --- a/markets/storageadapter/ondealsectorcommitted.go +++ b/markets/storageadapter/ondealsectorcommitted.go @@ -104,6 +104,8 @@ func (mgr *SectorCommittedManager) OnDealSectorPreCommitted(ctx context.Context, } } + log.Infow("sub precommit", "deal", dealInfo.DealID) + // Not yet active, start matching against incoming messages return false, true, nil } diff --git a/node/node_test.go b/node/node_test.go index 933a0f614..85b3f2e6d 100644 --- a/node/node_test.go +++ b/node/node_test.go @@ -63,6 +63,24 @@ func TestAPIDealFlow(t *testing.T) { }) } +func TestBatchDealInput(t *testing.T) { + logging.SetLogLevel("miner", "ERROR") + logging.SetLogLevel("chainstore", "ERROR") + logging.SetLogLevel("chain", "ERROR") + logging.SetLogLevel("sub", "ERROR") + logging.SetLogLevel("storageminer", "ERROR") + logging.SetLogLevel("sectors", "DEBUG") + + blockTime := 10 * time.Millisecond + + // For these tests where the block time is artificially short, just use + // a deal start epoch that is guaranteed to be far enough in the future + // so that the deal starts sealing in time + dealStartEpoch := abi.ChainEpoch(2 << 12) + + test.TestBatchDealInput(t, builder.MockSbBuilder, blockTime, dealStartEpoch) +} + func TestAPIDealFlowReal(t *testing.T) { if testing.Short() { t.Skip("skipping test in short mode") From b0128bd99ecff308ebad0e75575a4337ab6c35dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sun, 30 May 2021 18:30:38 +0200 Subject: [PATCH 129/160] storagefsm: Fix race spawning more than one new sector at once --- extern/storage-sealing/input.go | 10 ++++++++++ extern/storage-sealing/sealing.go | 1 + 2 files changed, 11 insertions(+) diff --git a/extern/storage-sealing/input.go b/extern/storage-sealing/input.go index df82f0acf..85a5c429f 100644 --- a/extern/storage-sealing/input.go +++ b/extern/storage-sealing/input.go @@ -27,6 +27,10 @@ func (m *Sealing) handleWaitDeals(ctx statemachine.Context, sector SectorInfo) e m.inputLk.Lock() + if m.creating != nil && *m.creating == sector.SectorNumber { + m.creating = nil + } + sid := m.minerSectorID(sector.SectorNumber) if len(m.assignedPieces[sid]) > 0 { @@ -392,6 +396,10 @@ func (m *Sealing) updateInput(ctx context.Context, sp abi.RegisteredSealProof) e func (m *Sealing) tryCreateDealSector(ctx context.Context, sp abi.RegisteredSealProof) error { m.startupWait.Wait() + if m.creating != nil { + return nil // new sector is being created right now + } + cfg, err := m.getConfig() if err != nil { return xerrors.Errorf("getting storage config: %w", err) @@ -410,6 +418,8 @@ func (m *Sealing) tryCreateDealSector(ctx context.Context, sp abi.RegisteredSeal return err } + m.creating = &sid + log.Infow("Creating sector", "number", sid, "type", "deal", "proofType", sp) return m.sectors.Send(uint64(sid), SectorStart{ ID: sid, diff --git a/extern/storage-sealing/sealing.go b/extern/storage-sealing/sealing.go index e753085ef..8a70704c4 100644 --- a/extern/storage-sealing/sealing.go +++ b/extern/storage-sealing/sealing.go @@ -98,6 +98,7 @@ type Sealing struct { sectorTimers map[abi.SectorID]*time.Timer pendingPieces map[cid.Cid]*pendingPiece assignedPieces map[abi.SectorID][]cid.Cid + creating *abi.SectorNumber // used to prevent a race where we could create a new sector more than once upgradeLk sync.Mutex toUpgrade map[abi.SectorNumber]struct{} From 0a5a7cf45df0f14e45f756c0ed4c029e39d42f8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sun, 30 May 2021 19:24:42 +0200 Subject: [PATCH 130/160] storagefsm: Fix too-long log handling --- extern/storage-sealing/fsm.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/extern/storage-sealing/fsm.go b/extern/storage-sealing/fsm.go index cfd223364..24f26a1ee 100644 --- a/extern/storage-sealing/fsm.go +++ b/extern/storage-sealing/fsm.go @@ -237,6 +237,10 @@ func (m *Sealing) logEvents(events []statemachine.Event, state *SectorInfo) { continue // don't log on every fsm restart } + if len(e) > 8000 { + e = []byte(string(e[:8000]) + "... truncated") + } + l := Log{ Timestamp: uint64(time.Now().Unix()), Message: string(e), From 7f247bf553308a4b5f7f248885d79c92bd9c0b9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sun, 30 May 2021 19:26:53 +0200 Subject: [PATCH 131/160] Self-review cleanup --- api/test/deals.go | 15 ++++++++++++--- markets/storageadapter/ondealsectorcommitted.go | 2 -- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/api/test/deals.go b/api/test/deals.go index 740fb8fea..aa7e23bcc 100644 --- a/api/test/deals.go +++ b/api/test/deals.go @@ -19,6 +19,7 @@ import ( "github.com/filecoin-project/go-fil-markets/storagemarket" "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/build" "github.com/filecoin-project/lotus/chain/actors/builtin/market" @@ -227,6 +228,9 @@ func TestBatchDealInput(t *testing.T, b APIBuilder, blocktime time.Duration, sta s := connectAndStartMining(t, b, blocktime, client, miner) defer s.blockMiner.Stop() + err := miner.MarketSetAsk(s.ctx, big.Zero(), big.Zero(), 200, 128, 32<<30) + require.NoError(t, err) + checkNoPadding := func() { sl, err := sn[0].SectorsList(s.ctx) require.NoError(t, err) @@ -239,7 +243,7 @@ func TestBatchDealInput(t *testing.T, b APIBuilder, blocktime time.Duration, sta si, err := sn[0].SectorsStatus(s.ctx, snum, false) require.NoError(t, err) - fmt.Printf("S %d: %+v %s\n", snum, si.Deals, si.State) + // fmt.Printf("S %d: %+v %s\n", snum, si.Deals, si.State) for _, deal := range si.Deals { if deal == 0 { @@ -278,13 +282,18 @@ func TestBatchDealInput(t *testing.T, b APIBuilder, blocktime time.Duration, sta sl, err := sn[0].SectorsList(s.ctx) require.NoError(t, err) require.Equal(t, len(sl), expectSectors) - //require.LessOrEqual(t, len(sl), expectSectors+1) } } t.Run("4-p1600B", run(1600, 4, 4)) t.Run("4-p513B", run(513, 4, 2)) - t.Run("32-p257B", run(257, 32, 8)) + if !testing.Short() { + t.Run("32-p257B", run(257, 32, 8)) + t.Run("32-p10B", run(10, 32, 2)) + + // fixme: this appears to break data-transfer / markets in some really creative ways + //t.Run("128-p10B", run(10, 128, 8)) + } } func TestFastRetrievalDealFlow(t *testing.T, b APIBuilder, blocktime time.Duration, startEpoch abi.ChainEpoch) { diff --git a/markets/storageadapter/ondealsectorcommitted.go b/markets/storageadapter/ondealsectorcommitted.go index f255c9e55..31bc0b8bf 100644 --- a/markets/storageadapter/ondealsectorcommitted.go +++ b/markets/storageadapter/ondealsectorcommitted.go @@ -104,8 +104,6 @@ func (mgr *SectorCommittedManager) OnDealSectorPreCommitted(ctx context.Context, } } - log.Infow("sub precommit", "deal", dealInfo.DealID) - // Not yet active, start matching against incoming messages return false, true, nil } From 87f2a4105205d6cb6ed08464df26a97020c0605d Mon Sep 17 00:00:00 2001 From: Cory Schwartz Date: Thu, 17 Jun 2021 23:46:01 -0700 Subject: [PATCH 132/160] only use embed for appimage --- Makefile | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index a8857556f..7de08dfa5 100644 --- a/Makefile +++ b/Makefile @@ -56,12 +56,6 @@ build/.update-modules: # end git modules -build/rice-box.go: - go run github.com/GeertJohan/go.rice/rice embed-go -i ./build - -BUILD_DEPS+=build/rice-box.go -CLEAN+=build/rice-box.go - ## MAIN BINARIES CLEAN+=build/.update-modules @@ -90,6 +84,7 @@ butterflynet: build-devnets lotus: $(BUILD_DEPS) rm -f lotus go build $(GOFLAGS) -o lotus ./cmd/lotus + go run github.com/GeertJohan/go.rice/rice append --exec lotus -i ./build .PHONY: lotus BINS+=lotus @@ -97,18 +92,21 @@ BINS+=lotus lotus-miner: $(BUILD_DEPS) rm -f lotus-miner go build $(GOFLAGS) -o lotus-miner ./cmd/lotus-storage-miner + go run github.com/GeertJohan/go.rice/rice append --exec lotus-miner -i ./build .PHONY: lotus-miner BINS+=lotus-miner lotus-worker: $(BUILD_DEPS) rm -f lotus-worker go build $(GOFLAGS) -o lotus-worker ./cmd/lotus-seal-worker + go run github.com/GeertJohan/go.rice/rice append --exec lotus-worker -i ./build .PHONY: lotus-worker BINS+=lotus-worker lotus-shed: $(BUILD_DEPS) rm -f lotus-shed go build $(GOFLAGS) -o lotus-shed ./cmd/lotus-shed + go run github.com/GeertJohan/go.rice/rice append --exec lotus-shed -i ./build .PHONY: lotus-shed BINS+=lotus-shed @@ -140,6 +138,7 @@ install-worker: lotus-seed: $(BUILD_DEPS) rm -f lotus-seed go build $(GOFLAGS) -o lotus-seed ./cmd/lotus-seed + go run github.com/GeertJohan/go.rice/rice append --exec lotus-seed -i ./build .PHONY: lotus-seed BINS+=lotus-seed @@ -173,11 +172,13 @@ lotus-townhall-front: .PHONY: lotus-townhall-front lotus-townhall-app: lotus-touch lotus-townhall-front + go run github.com/GeertJohan/go.rice/rice append --exec lotus-townhall -i ./cmd/lotus-townhall -i ./build .PHONY: lotus-townhall-app lotus-fountain: rm -f lotus-fountain go build -o lotus-fountain ./cmd/lotus-fountain + go run github.com/GeertJohan/go.rice/rice append --exec lotus-fountain -i ./cmd/lotus-fountain -i ./build .PHONY: lotus-fountain BINS+=lotus-fountain @@ -190,24 +191,28 @@ BINS+=lotus-chainwatch lotus-bench: rm -f lotus-bench go build -o lotus-bench ./cmd/lotus-bench + go run github.com/GeertJohan/go.rice/rice append --exec lotus-bench -i ./build .PHONY: lotus-bench BINS+=lotus-bench lotus-stats: rm -f lotus-stats go build $(GOFLAGS) -o lotus-stats ./cmd/lotus-stats + go run github.com/GeertJohan/go.rice/rice append --exec lotus-stats -i ./build .PHONY: lotus-stats BINS+=lotus-stats lotus-pcr: rm -f lotus-pcr go build $(GOFLAGS) -o lotus-pcr ./cmd/lotus-pcr + go run github.com/GeertJohan/go.rice/rice append --exec lotus-pcr -i ./build .PHONY: lotus-pcr BINS+=lotus-pcr lotus-health: rm -f lotus-health go build -o lotus-health ./cmd/lotus-health + go run github.com/GeertJohan/go.rice/rice append --exec lotus-health -i ./build .PHONY: lotus-health BINS+=lotus-health @@ -330,15 +335,17 @@ api-gen: goimports -w api/apistruct .PHONY: api-gen -appimage: lotus +appimage: rm -rf appimage-builder-cache || true rm AppDir/io.filecoin.lotus.desktop || true rm AppDir/icon.svg || true rm Appdir/AppRun || true mkdir -p AppDir/usr/bin + rm -rf lotus + go run github.com/GeertJohan/go.rice/rice embed-go -i ./build + go build $(GOFLAGS) -o lotus ./cmd/lotus cp ./lotus AppDir/usr/bin/ appimage-builder - docsgen: docsgen-md docsgen-openrpc docsgen-md-bin: actors-gen From 86baa119370fdd086c3cb58d5e165cd01b4b2a50 Mon Sep 17 00:00:00 2001 From: Cory Schwartz Date: Thu, 17 Jun 2021 23:47:08 -0700 Subject: [PATCH 133/160] test: build appimage in ci --- .circleci/config.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 2df973dfb..cf91e6cde 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -845,6 +845,7 @@ workflows: tags: only: - /^v\d+\.\d+\.\d+(-rc\d+)?$/ + - build-appimage - publish: requires: - build-all From 6e8cf9061a39065616ea33f84a8590ddf9b1c66c Mon Sep 17 00:00:00 2001 From: Cory Schwartz Date: Thu, 17 Jun 2021 23:48:35 -0700 Subject: [PATCH 134/160] temp name --- .circleci/config.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index cf91e6cde..b0e349423 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -845,7 +845,8 @@ workflows: tags: only: - /^v\d+\.\d+\.\d+(-rc\d+)?$/ - - build-appimage + - build-appimage: + name: tmp-build-appimage - publish: requires: - build-all From 7f744b2760945923cc27ef9384c9e0bed6ddc0fe Mon Sep 17 00:00:00 2001 From: Cory Schwartz Date: Thu, 17 Jun 2021 23:52:34 -0700 Subject: [PATCH 135/160] require deps --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 7de08dfa5..4ae34fd51 100644 --- a/Makefile +++ b/Makefile @@ -335,7 +335,7 @@ api-gen: goimports -w api/apistruct .PHONY: api-gen -appimage: +appimage: $(BUILD_DEPS) rm -rf appimage-builder-cache || true rm AppDir/io.filecoin.lotus.desktop || true rm AppDir/icon.svg || true From d35fcd926fe627a0e579402635411c71d02fcb38 Mon Sep 17 00:00:00 2001 From: Cory Schwartz Date: Fri, 18 Jun 2021 00:02:04 -0700 Subject: [PATCH 136/160] remove temp edits --- .circleci/config.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index b0e349423..2df973dfb 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -845,8 +845,6 @@ workflows: tags: only: - /^v\d+\.\d+\.\d+(-rc\d+)?$/ - - build-appimage: - name: tmp-build-appimage - publish: requires: - build-all From 7f00a6e59c07dd05ad2855baa45443eb775d13dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 18 Jun 2021 11:17:18 +0200 Subject: [PATCH 137/160] Proofs v8.0.2 with fixed aggregate lengths --- extern/filecoin-ffi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/filecoin-ffi b/extern/filecoin-ffi index d2e3aa7d6..6f11cdffd 160000 --- a/extern/filecoin-ffi +++ b/extern/filecoin-ffi @@ -1 +1 @@ -Subproject commit d2e3aa7d61501d69bed6e898de13d1312b021e62 +Subproject commit 6f11cdffd4f6c7ae1b05d0e47f578d4c9b21415f From 5516f3492e4a2ee7f595e46b149fa00b88676680 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 18 Jun 2021 12:02:42 +0200 Subject: [PATCH 138/160] ffiwrapper: Assert aggregate length --- extern/sector-storage/ffiwrapper/sealer_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/extern/sector-storage/ffiwrapper/sealer_test.go b/extern/sector-storage/ffiwrapper/sealer_test.go index df657f097..4adec48cf 100644 --- a/extern/sector-storage/ffiwrapper/sealer_test.go +++ b/extern/sector-storage/ffiwrapper/sealer_test.go @@ -544,6 +544,7 @@ func TestSealAndVerifyAggregate(t *testing.T) { avi.Proof, err = ProofProver.AggregateSealProofs(avi, toAggregate) require.NoError(t, err) + require.Len(t, avi.Proof, 11188) aggDone := time.Now() From 738ac926d1274c659bf694b06e306d299dc5fdd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 18 Jun 2021 15:49:59 +0200 Subject: [PATCH 139/160] Update ffi --- Makefile | 2 +- extern/filecoin-ffi | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 400788e2e..542ace849 100644 --- a/Makefile +++ b/Makefile @@ -42,7 +42,7 @@ BUILD_DEPS+=build/.filecoin-install CLEAN+=build/.filecoin-install ffi-version-check: - @[[ "$$(awk '/const Version/{print $$5}' extern/filecoin-ffi/version.go)" -eq 2 ]] || (echo "FFI version mismatch, update submodules"; exit 1) + @[[ "$$(awk '/const Version/{print $$5}' extern/filecoin-ffi/version.go)" -eq 3 ]] || (echo "FFI version mismatch, update submodules"; exit 1) BUILD_DEPS+=ffi-version-check .PHONY: ffi-version-check diff --git a/extern/filecoin-ffi b/extern/filecoin-ffi index 6f11cdffd..d60fc680a 160000 --- a/extern/filecoin-ffi +++ b/extern/filecoin-ffi @@ -1 +1 @@ -Subproject commit 6f11cdffd4f6c7ae1b05d0e47f578d4c9b21415f +Subproject commit d60fc680aa8abeafba698f738fed5b94c9bda33d From 2c2c37a1384348d1e5e4c819a9e8918d6adad888 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 18 Jun 2021 19:15:08 +0200 Subject: [PATCH 140/160] chainstore: Don't take heaviestLk with backlogged reorgCh --- chain/store/store.go | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/chain/store/store.go b/chain/store/store.go index 5414b12fe..e2aa0100d 100644 --- a/chain/store/store.go +++ b/chain/store/store.go @@ -11,6 +11,7 @@ import ( "strconv" "strings" "sync" + "time" "github.com/filecoin-project/lotus/chain/state" @@ -366,7 +367,20 @@ func (cs *ChainStore) PutTipSet(ctx context.Context, ts *types.TipSet) error { // internal state as our new head, if and only if it is heavier than the current // head and does not exceed the maximum fork length. func (cs *ChainStore) MaybeTakeHeavierTipSet(ctx context.Context, ts *types.TipSet) error { - cs.heaviestLk.Lock() + for { + cs.heaviestLk.Lock() + if len(cs.reorgCh) < reorgChBuf/2 { + break + } + cs.heaviestLk.Unlock() + log.Errorf("reorg channel is heavily backlogged, waiting a bit before trying to take process new tipsets") + select { + case <-time.After(time.Second / 2): + case <-ctx.Done(): + return ctx.Err() + } + } + defer cs.heaviestLk.Unlock() w, err := cs.Weight(ctx, ts) if err != nil { @@ -483,8 +497,10 @@ type reorg struct { new *types.TipSet } +const reorgChBuf = 32 + func (cs *ChainStore) reorgWorker(ctx context.Context, initialNotifees []ReorgNotifee) chan<- reorg { - out := make(chan reorg, 32) + out := make(chan reorg, reorgChBuf) notifees := make([]ReorgNotifee, len(initialNotifees)) copy(notifees, initialNotifees) From 65844761b69d268ebfaf1c5beae8fda65ffe8d9f Mon Sep 17 00:00:00 2001 From: Travis Person Date: Fri, 18 Jun 2021 19:45:21 +0000 Subject: [PATCH 141/160] Calibration network reset --- build/bootstrap/calibnet.pi | 8 ++++---- build/genesis/calibnet.car | Bin 1103520 -> 0 bytes build/params_calibnet.go | 14 +++++++------- 3 files changed, 11 insertions(+), 11 deletions(-) delete mode 100644 build/genesis/calibnet.car diff --git a/build/bootstrap/calibnet.pi b/build/bootstrap/calibnet.pi index 0eb9fd2f3..20473eaaa 100644 --- a/build/bootstrap/calibnet.pi +++ b/build/bootstrap/calibnet.pi @@ -1,4 +1,4 @@ -/dns4/bootstrap-0.calibration.fildev.network/tcp/1347/p2p/12D3KooWRLZAseMo9h7fRD6ojn6YYDXHsBSavX5YmjBZ9ngtAEec -/dns4/bootstrap-1.calibration.fildev.network/tcp/1347/p2p/12D3KooWJFtDXgZEQMEkjJPSrbfdvh2xfjVKrXeNFG1t8ioJXAzv -/dns4/bootstrap-2.calibration.fildev.network/tcp/1347/p2p/12D3KooWP1uB9Lo7yCA3S17TD4Y5wStP5Nk7Vqh53m8GsFjkyujD -/dns4/bootstrap-3.calibration.fildev.network/tcp/1347/p2p/12D3KooWLrPM4WPK1YRGPCUwndWcDX8GCYgms3DiuofUmxwvhMCn +/dns4/bootstrap-0.calibration.fildev.network/tcp/1347/p2p/12D3KooWJkikQQkxS58spo76BYzFt4fotaT5NpV2zngvrqm4u5ow +/dns4/bootstrap-1.calibration.fildev.network/tcp/1347/p2p/12D3KooWLce5FDHR4EX4CrYavphA5xS3uDsX6aoowXh5tzDUxJav +/dns4/bootstrap-2.calibration.fildev.network/tcp/1347/p2p/12D3KooWA9hFfQG9GjP6bHeuQQbMD3FDtZLdW1NayxKXUT26PQZu +/dns4/bootstrap-3.calibration.fildev.network/tcp/1347/p2p/12D3KooWMHDi3LVTFG8Szqogt7RkNXvonbQYqSazxBx41A5aeuVz diff --git a/build/genesis/calibnet.car b/build/genesis/calibnet.car deleted file mode 100644 index d2fe7c3afe3c90914600ec094a3591b4bf472543..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1103520 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

i;Q}$0h#yEpT>{!LYJgzmK|=1=qlTgh5X4K4 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 z%0D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 Date: Sat, 19 Jun 2021 00:53:21 +0000 Subject: [PATCH 142/160] Add calibration genesis --- build/genesis/calibnet.car | Bin 0 -> 1101076 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 build/genesis/calibnet.car diff --git a/build/genesis/calibnet.car b/build/genesis/calibnet.car new file mode 100644 index 0000000000000000000000000000000000000000..cbade953f866df5d84bd6256c34f400c0715e421 GIT binary patch literal 1101076 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

g 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%0A_3@}A-6az%m;bp`I!Vf5| zI3X6=0Ve~EgQXEqQHbbsdfux!U9r>fiH+Y0{(9DAU6*<9bS*dcM8@|@r5jmCE?NuU zqMT1$#V76wLPP@xfoDFTKom0W!^629J3(IL z^e1P^bvs_md%e`WF?N6Lk2f-ncT29`nQP>z>0_51XcqKOI6>lWLl3byO8~bpOSLuV zdV!U!;L0NC@e-g-WpMED1V}3mrs7c5xFq}~RAJM7U z>Kx*%pDyF#$kIn=$+s%@Pg~7Dk_~ka=*NNv`tFJd>|S415g#1Rd=@aTCCheK}6;Y0#j=cdZK6-+y=LjSFXYZ<#Z%XMU$++DbN1 z61WcnGTR7g&QUozf&i}$*Ia^T1T_keG6^`GE{>qkA%$+Q#EQ>3Rj-aX%3RJ_B-4q) z<8yXtS9f}(;+~P2&wZ7S2(rKwZ_k5lR}&Zej+Y={fC^Yv@S~29KrsNlCefe;XkFQa zpDd^=doac(K);M4z~Cq90tP`$p@RKqE51wt*p6p=bnw)i*SCoUoSuKQuJp^XOzp~t z_Kr+Qrk!24zP&3|kyU@hw&Irx7ufJNQ@iK#q;k_A4Z?rBc(3z^J0E3v{NuB66Vh}S zkPa|`Wyc4JMQE^t>X{@mpu45BghMF;M~5C63ML@FQ54)Jz)6phQ)epZn)Jdi}2m(gu{+^-cX8RP=JC0>mr23M9OqgQ6xfu zEn=L;NxTNVUJ}EgBoH{11fh8tgukd*UTj{p$EYLc@_ugo)vuP=H`9dtY?cv8Q^s%a zx%yjcViQ20CLD6SJCu?^tPoJ#wny2V&H-->NI(*3V*@OLgL??48L*v*@==jB{m{a3 z-inQ7UW9CHsZqUA)#y2)mAH0Zoi7K}7}bAaP$*rKADSMoo9ASS#AO@e?f}dIG7}Yo zXEGZ>1#sZzf$^xx2yg0k1d%;HBq8rf0;&~+ogW^D#P>TTc;Kf_?#Y*>|mAlx*&hD>#mN~zcw%$4P$%71K8YqvNr+@VDr-naH zADVE;@m87=K~+;@HPY}X28-gnXoE+j!v-MprSL98GCYZa+bpjJ0=#t`7xE@KOrdsC$B!pJYf2qCQsiUF?{&tTDMD`-`vDWIP7@akkm07gX{$>X5b~; z&_s4wnF?INp%9+}u#Cw_xQ2=$hv*+D=zx#ckvY}_UdS&bPymhf~_iDA)CLD6S zm1b?fD4{6iK>?3MX)(YNG$k_O5L7%IA`(HlqQEy4bBv$@P@=3tN;GYy(~{#Po$r1h zwaS&r_a^*SS8T-e?%!4njwtd%n{_+;RE-^6lyJ!LP7ao2K(mS9-R=htPv8L;4vQ3A zXbgcC!ge@>zIW7wy&M@*RnyOhNhbr2B)tz_`Z;xM-!2o{eOUjc96Ou+l(opWecotO zDbi=%`lh?pspqAH!;YIAMwAYt(MB3NjKm#@N8ufU6%?a~Mb;-kZdQPL7w%BMVd;DX zNMSy`?U1(8f8G71=&}(5Pfp#oqV|Y(7V-DI6Yrc^e~CEwsOXJKZ3-stV;whS(cr6&y)6;Bcu|{?ke&{Y6=Ay7*nAz$BmbtD%5mo2p@Yps{lh_*c?pN7F(+ z+T`_f4U*cQ=(Kxp(e&i-b=_S`D%P7ho_F%$Rj1D!?9#2_ z8`svJ>ab$R^}mlzsW~L!u;ZN^9K_;_DME-+qmmbNc^eK17GbE6Hx5P=fuuu(%_@P2O?td9WgBKZ?0!79sKu*4 z*qDYg5`#&OVA8Kx;PP& z(!2nMBhdHJM8jrP^jKw41$|k{=$Dgzf#?0M{p{=?T-;Z7;@xV~a^GB5$GvfH>yc6W zC+0evuXD9W$qBD?ys$Y@3%gv3yEHm_=)U_c*$#y{!fypRKnN&WW?T#B3bZ0(;gBz+ ziFTYvR`-*`H$^VLl6UjKGT9a`I9BSDD#MwJeeXOs`o}kRW_h#Y_RI|#KJdIB!!d9% zZO)V|_}cr=A=T=OK%j<(tiV6Z|2>tM1r4^|-h8Cw1Uwx3&Rg+3nE^xBjMje4D4I-S3ySNg8NgT7N|H?%_4E*u( zk*}}L{C&XHX{`@bOne*0yA4@C88hHjzz6)W_0zNvw6;`AQ#DhL3WO!Yz)26m_}C5c zA++B~Lye^)cQOvKvu0QAHchx{ZQk2g`p0F%TMnKr{dR8E(rL@{Ua$Imt@Wj!k{JGL z*gYVr;M{`QGLjgYIt$6iCAZF+vS&|ha-VNEm><2|tH}qq_U_GJ{O5|XONpcU;$8wW zsfiXCR)z@w3>&KBL^v1%X_yhj5Ura5EoJ*hI5EbA$R~81(BNj5 z`4imc&6juaYTy2|`KP&dT|C*Q#o;muXJ6cU%+g`j%J?~G8+t(*$eNf3VJWbJ1tJI| z7U1Y@G7Pg^i;_(l?lGQ~E^U&b9s46WWcJT%R_(Ppb&cxv_WqGdg<9=<%7O3Qm zy0VF2zc#j2TaUOiV`QP@dAJS*?>^t`a zELvAKoJn|-Bu%L6OBk)d1CRo36+Kz7GjwPI^66yw46A)=>%4U*a`y4u8TpjeUF%=# zeRX7^N*_MV#^nC1N)c&d;P4(H%lb!ZUHxzz-Rlc>;RwcaJ z@xtZ+(}Rwe8MGAdh=EI?Vo^rb7OJ+<@vuaU%n zq}=_V*m6EI5I;gEa;JGH+6=bDn4x6&Du%3%6wH~ z!04Vc_U5SY>%|uj6eu>iw!3IU-GC;irX%E4Bf3MG$bo-AhMaK^=Y*#DP>k`dWFZ=z&u5V6i+~@0x7Y`lwkxDI>rx+rPbm!lbx#jyQaDT5 zj1-1X2E7065B>JEo3yFHn2m298T{koq~V7Pyi#iOkuP8TetpMx6aJBLzYAwkZ~&oY zG74u*l%o-gfYeLw?#)GWHhw$%%$_SocHdcdT-k)fj`wXiXnsmGL&BZj#^W{^<{en9>Iy?CwB?3m z=o831Jc-bEhpjeT(!x*h0RKc^glaAwdHLru4XdoIn)i$AYx@t_U+>-D{yw+p%iJG7 z8gk{E#6cr*H!nP?aokwo>x4+h1Dr7eqC(X3B4Gz|l~mM_WmcfEb$P>~eKrwRGKlb| zAC=oT-}=CPxOvo=5`E3*PB$9$<*lqGI&@!ncTbnX6N>D*lr7=ajvF?~q`U|@N0bB) z>O=t}geQWl2S|-nSQ_HNQp^%{Ibs5sD`Hw56jGm$sb|jamQN@QXU8?FxwlZbYSq7L zb^Z0Hg%diK=v(sdyYB^CDnCrhtK~5UB=rS{;r~z=%5=zjwZf~g;d(o_=9kqa3xrZQN&g1s@l+IOoaR?Vq>7$mHSW7Mgj^ z^e$AW&!!?3ck&%y|1$Rn4NrfXzgXF7{udH|CC6K7xcC@&n}v8ypm?5!@4XSVbWaeV z9}1;&K#`zj;rEGnmpZ(qKqJ{o<>IBDI%&7Tr{gBJ*meAH;W=|kcRn|*LAC-1hHamH ztb672>)$s9E-=4Nc%|c=IBYaXxV~`MayZpC70YF1l7J$(gdr*!1fgGt1$uxGDBu{v zQ3=?oC-mQ^CrjYr5{?v^9t8K1_I}3KYsc&vWK0-)ZhrIq;)Tn< z*6RAnA7AF3dT2v5SEh!|!FCX4*nzMWVIkY>i$FF+0i+@9Vk%-$5x+$SMA+w{$Ku6E zURQWFUF;-nrHSMB7mcVr_4;%{nm(>t?y4^?*^{?u!KJ5;cc|0!or(Q2*&g0;A+ZO& zRXZryXcZG^(NiAWXhW_S!Yhkm1qDSJ*vo`NIZ&B#o%9MPE#zkTJ9HgGS8h~l^74V< z<`2pg=sTd5Q10>WO8aUpIez4jn(!*e-G;iu2rS$_1T=&_TLF{Q9|8|Q#YD!V9713n z9+Yv4h4_a-dY%I3&b01)#umzs5-Ck31gq9e#NKzCH-V7{86rQFEr}YNk7x3<~NsK6Ydn+JJ={S z?!w0{J1^nH&#EN+CF5;#I9d73i0RRo3c;8YO@Guxem+8ZaE%vSh76*cE<_+i<&Q8Rym7wyT;j!Y+>i}9=JJ4} zL;+n>M99ih2E%wk2aY1p+z}!W1q!pK-w6{krU5>}{kQjgjh;)&mHF=e0rQ1*z1&=b z8$FmW&fd(v`B$}D!^aKka3M#+D;@U^2*SFSH6uYdLIJ^aP0xn_5h2cYR;WUj`L;;UjqtRW0jAZgP?$X;#4tg*Iib zJ~!l+St5q%1SNy1M6n>hQp!+>ZfgNk2O+=!^jN*uGL$s5ing$isq zvHQkGW>hb?uspf&=o#-%e*LH8`lwp<#$>XgPeg)=LWF|1F?gg3^lgo(Y!NUE1KQEz zO^-|&$gwe>BNznGHI)1eH4`hHesvWcKaOAa?$3RWo?RRD?`fLbJs?X?ir)|ZW z&)>U$e(xvUBzezVX4d!>b^8qR$~_)@>|u0U))rqsOdMSuH>W+zBf?%R;=v#cB{_yd zQPEI5(b8#{*J}X^(##-T(*y;`m_iw_eoDCqGFFi9mEX8ZMCdXGjia=7F4Fcgs?EtM&Jv%%xd3=DfD}@(lT5g?(LzuU=SXa^g_fxQCt( zC4oQHse)4s8rG%}FRH*BCjyy!C#Win2}M}+Fk$}agLbL|!dhCNEG;?eRUdL!uQg@v z7nN!SZ!cYC7kXv>(PotwTrJstRN1}{vlUHvrQ?MRkA#p7Ia-m0IF$)5x5Mzh7-0rz zPKbsb~&}jLO-mBmC4GF)ifQ$#1mW*tJ?R+xw&Y=Jr708#xLcJUHWO z*E7Ezerw3;$Hx;6Ic`3~`x!#xEKkPZ83(tjC~&W_SO7Ol4DU`G=1y>>H&h=~Ghl`b zliZ9W{PdHfcIl3oI)8D~&K{wQ5U*a8{`&mI$?B*1{XhA#T$#A@gM`D5x6#yniM~Z2h^~$CEl`HE#b_vCiE3 zM9J;bXIGt6z1Ex}$y?iK*IH$6$P&*wECFLJOHjpV5EeHYs&P7o;VegMX!PoVNQ#LZ z`g!n`6)7qfCpk`pcTb-C`2PBvo!@XN@7=Gie55)}CWqHn(yI>sd5M|0=~}##gX2Ab zUSonA;y`~=B=o0MP4_t6in&ZE#iWY1L58tS%BF$MG{Gr(mY4A?Q(#x_AKFj8Yxg|m z{;_ws+NxBi&V`RmtyW;-)q(|w9@;a0b%rwb?Y6%j|2Z~x?wB9@o!-%W%+hYg%)^u4 zJ-*|G!EIXSL|-9uzYE6ZG>N`SnDv3W;g>~#f#A!+bAsiGvdme8&mWduMG{~`gzaDx zsT7}oma%oGeDHYUz-{%pEypjG-SF$9S&jB|9FhIY!{?GF9-DF?nkRF^rp<^Kkia~G zbV>{~Oi_nQs%6ofW7B4sjz&2x5`dGnO<~5)i%|@){<#A>y>;N>k`aYZbsIA;>CtD} z%guw0mn=VVw$;cvtJV$rvTfp=jCf}aYr#gkY3nAK7d|_KAJ=alSgF^ua05OR$ z{A867^kATAfUzc*L031u9wh5$-?yOn8_9h0<{iK5PyHd?Xuf@Ht}G+Sc{2ty&QiL0 z!Ydtj7eo{h6}3r*akx-HR}PzD*l?%F>S9&}+g?@+M@YC4JAnWnaiD3Yq_(P4J1}Xd z`qfpxw-G0muab?+Q_VMeZPBUEHCQlz@jEr!@47Yi;q0@C_j}{6bO-|jS_?730L#Z1 zpG4qZ3I;3!U|G&DLen@bhD;3DB0>r-Jm3ojQd=2MyX)zvdfs*wdn~_ypw}k_$_*Ht zOPkcN8^825ep{`<2b~sIPRvcb-y3(O@iJqhJmvr%y6YY;AIpUgF2(?iUjP-x@X>-A zqsgFSqB778?tgB>8CSY++nXIejBIMw`)L-c$9E* z#Jh;t2p;<~__>M$!iySkp)wAHO_bz0h7Wt5B#Y4L_RxWm;3TX0eE?_ua~r;=Tz)sX zwQt7_uWpqkbhB%J_dSxQ#n>N}!-v-uD_E!M%!EUZmmHjF*?!A1qc*11A(|zfsLnxU zQ$Zn1SUfc7(WO&7A2et=zlt6n?PM6eq;JFTbKUNyby`>I^OaX)PT4C2bENQ3C04e$ z*#4U>-q2j%W%5c#J>C>V4M_G7X7LQKg6prUa0TV@G^0pIXElytVk8XNfLS&e_lYHY z#^UOi!XuZL9kaXchg@v>pm(}?`R-gS`r+>9CssOo=!f?{4KWF?biCx?!iGhu1{gVD zz=TY=&_D@Y6KMx>hv0Kj3fx|Tj)z(RC{-V_#(d&5{Pb!`--Z=tGTzHwgv^V1Kue0%HF75#5@`07Z$J)e)?HnMW*X21{wvRu?>bh%sVF4btvc_>iS0s4?6eEZ9>Aq*IsMF5q zj<`APQ-UPu4ZNzu;Z70YkK!nbBQqR&4`IsGphyJeNtO3Z zfDKW2Xl}Zuf7(hPDpcp}Cid~e%A=34?ee?7T)9!{jdt&i)!Uq3`uzS*x7K8C$h=CR z0SklAf^^gSrS5b)sd7do8etW zv%pMaVHs-&bO)}|P&tn{kULDdS~A{-OA9&$4u4v7^21{%yKh-Q%zbge>I%Er^@m%u zdR%nZ?+>phywdSP=E4dY4Mt-=NNmu8;&PIsham?RqUnGD(*e{?@F;>hdkpq603&%a zpNh6f4LM`qJN%qEvixXoXwCs%mHwq`RoGE{^xDSrc7}8B8x!kUt;^*M4~CbQ|6b|s zTVH4UsMXq;;~oxNlXLipX}K>L8=Kc%+&FjOhs2-E@qQ$$z)+BmfF%mTs}ND4b#B;Z z)E~ug&QonjP0~@_pktJ(U?jl00Uw)+go*Pb+0n1Q`TgXht={=%UG2Hnxg|9o<=Hdx zn?WV>Ehv4x-JH>_GW^KNbzg?KArs~@KaX4^k!&FYutwOiDbD%y6R_U40GiCM!oHV^pj6T>47KWgyAn9(ru3jnIfj$ za7`850GImo%ecd4OfDx>@3UlE@i~(YzFxUW_C?Khm-2QUoPI5@|BEVpXRpiLkOOqc zqhpM&>3}o^(cjR~__t#qSuim)5Nv2Hiz*IYL-o4>3M^OnlL;=dF#a%*+j0vt>}S!W7u+n6kr^&r{LlfR#-&3eU(G15b|_ zTuD%{xhYaB0_i+EI(ude(0*M@ZSwc;*UYzc{ft|oBgDlep{(QAW#V!qt zCmeRX9l+@#u8XLvilQ$1VnIE`8@6KxCDRuQdZwZ1wnH0g)EAaHKIH1QqNKl}(k{BI zks3Qf6O!(9KbCt!+edGYmL6{MIwm#keRJ5d7eAV2|DL%a)1<_hI;Pk$NYw$17o~ah z@j?U$7Odd7QB{%^N5I=V%9FM%=t?ZTIi2CG*?M~IZg<(}t;ajB8gN4EyMM>M+XW^y zf4}_!{kur$pOCaz;&K=tVYkB#W*p2>S&02=uuJg5ROmG;rN zL?q67B0u)e`+3r<*=JMXXMKtT9J3bzJ z_kY#jWN5KjOLPCo)g?MrcyuUt=H?TGVgT0%piPu%k`7WK4etZiuPHF8G)&F37*0@B zOH&|_6ftbs4y1EoGHipJ8#mRuQ}MkDE3W-T9&g|NKqYqJo;wZlGOH^!yxc@DQajVCISj<`CJ=KU;%$b zLlMBKf*cMZ1``D_Ad(s{5itq4F<%DyaC+DccMacB>gzXdpIF@F@Xalyr>q)St8~$i zdl#AbP02r3OMAXgIPAEG4rfK-I|aicz?VU4#xvIQDTW6nSU5(@Mu-EGO0e9p$7zD* zVvuSRX^opf|5MEWFL1_yCjD~1H4rJ4{)CVYw?30?OqVVfn|AV_tkkpR&pVIjnN_mz z$}I0p`g^}NEpa!4cs~;28wx{0c#VjPKID!ChiuVL>x#pwPKXYNY0Ia?NK(}U7+0CR z#oFnJ`m`e|I`sO4^=)%4-QF+PkHsroIo)iY-teP=eFs(8b~?9|JiAiD-zDA=#YtY2 zwn$qO<$!2Fmd_<@LGUaoMBAJnD0B-E4u&owbk4BPud+$s`|&nBDbncNqiT?3!8O0)%BnxsDhQ_4=Ae7ABzU?w1j$< z7=)Fgp@svRs*8>%Ml?Mj!`CT(*sV`rZ}zxZ`|fYfdpCJ}IpOz$i&q`G_T{?2sxf1; zP9OU?Yr?A?@66!{$^t=6VEu;Xp`9l9g1iOUe^?x)NKprTl7s;^2zWu(2S;kz$3IaT zPHpvkm+OBb;r}t)fniP7nyJ=_>3jXt2ll#0kz>pMY<9ll8>t-Sf#&E(r3 z4g2DEE%DJ?++P`F03weHrj1rU%s2^;pdmL4wwnR{U3`lfMruLT(z+qDBG z3;HZXI=FzUsL+Cn!f;Lqzc70H0(6rtN9ioLjS3jaLeX=@K2w&2HNA!NLdMzTRtc>%xaK zH*7QL6L?61MuFfLg_tA8oG_$sV|J9GNcdyHeT|bCizR)43rA%~{x@dbAHwuvRsKHo zoso2)X@#NJ&IxUmq~k-I+Sxk2U2k)d!-{r*iw#W9lZH zQt`eG=YyCBs%-|>t>U1NLp8^E_$*`$p9b_ykw{Uq@dG;_jH#GCd)B{jOuIFEaOdEl z+@;Phs`%YUi^nD}OFsYov6Eliy(04~(|T+;wAwDT zAs<<7?1;>jbzn_~4N;ckSq?P);jISiWklf776wU@D@22COqF4{rbl_s(=5-l2^F*ppO1u>3Seit92Q{0$S6yrXyawZ&(3|LR`$*$40ND?0Q({K)vE;;^O#LmlF;=z@X%0SLeR%pY|M+w?}GM{;R1E<%|MVf!El&rP>l~;RB?Rll{ z&N{*HtXmb=Hx#E{_YQyceC@=gk>XZ$7EOh9pM~<7Hhd7?w<%Y$LqW-+EDe*pVAL0j z!sh~Hmk^AGbk==Fm^7_^uK()vYprwpZ z+jpz}QT1QGIo-^k{e^CyuX6v)+o0P^8}f_^99%p*QxgP!1?V7?5li8M1{vb~wCM$X zCJkj(DIA5TsDk1F{0mZ1;k_32YmjD4IpLosNP1p(AG~qxj*&-JepTbz`k(www!Lya z&&D#_8=ZaBwC7iOh((Fh0^+SUj6?jC$2lByX+;`tS-OYImg5OI;zW4bZ?Uu`>p+VD z0KoIXkVZX`WK7+T^YT2YpZ}{tKA_2jGXJN1URnPgksPVzsjzo}d-wCU)3kfLxktV3 z$&G$rGh^n_je9fo+i;d4@j;&`kwHNENdty>VwCkl9w$U$Xr{)Xv?|$T48(0h^N65A zKI3AKluF;cKU7G*t|y#s(>igLE)hj|N_kc=Ym|?oiPbKYQAQtP)hWQX3N)yZfgezE|?6n?rMaI_JZw zRVKfkzsla8=SNJ+KJJCJQu7xJgc>Kj((%q4V9f$S4SqkSn4-_C(ipCW7!~Cx`nOEL zf;6v!>6#6J&oCc}KsD1&m#xaMd4H!jy%~s}+HiL5l8S+i(&>{Qvz1D0p53E#v8f+0 zb;qpC+^{vl^K-DHCk05R&e!w{YmL;WYL!173^U}Yn->AcGfdA(%$)gyf>_qtb;-G0A#6XU_)?&E9K z_$zwm%Wt}D?%D9C#Niq7k|afUNO0i0M{_B5I}YHA;MIYb|e6`KX!~Zl7?1#9iqa zYeZwDY#9C19V{y+N~9OU4{JX;CbyIj54SA; z#@n$!ZZ#Nvz0ao$e_FD6)$#%-@|RpW;lS*XxGBf*po%P+&z;7ok zY&v7-=J}uYocr;bgSmQl%>M0+mAw|dIx69x8TZSuwCM{&#WoxW+GvWZA&K+_NzTwI zMi(%g5e1*6O3|nU%?A{wcxk3S*fX9u*S0L*`SE+>K5?njC3n}zk+0;41?$bva`m`U z>-N;YH&08P4Hfsx@Zhe80K3#MxAt6(hPt}x*Br}&>Nk*8uqFc?Phz={358Qfr!+2A zukwHQ?z3~=UCnY_Z+v$3-JT1MA5m9#$=7v!m(aH3MEOzEdcD+Zejk5^^J!S_76s?* z?sVyQ;_%{M3Vgh6{DJomUv#rp%U|^)=ebuaev%PU{_;ngMu#SF)9-(3eeC~R_8@=#+x)7X0U_&pqrX&bWa|LW;| zgGPS4Yy7(5uQYB{)NWpG#{KK>X8elhv!ATcIA`lBWn0hq%nr3%xxM0@OOvMbNM7;! z(LS3i*fTRE_Kwoak_wzHaLKIsNuFcgM`l zN5Ub;J3_EcSUM6>fbsTW4jOcWma4*85b8EkOcbEo34=k}2-sYZ@Hx;D)1PQGWGq*@ zCYODE&e){>d#k*?qUFO~yUVv2+A7QF9==ACb3K3e)Vz`jha7jmVB^Mo0Jj8v61>Jz zwz~#H7z5ySP9b%vDBk35OkTrMW1C*FkS#bcnRl@Z%93n7pRISv15OkOU#@D0r$u zcv}nG0&wH}zqn&|Y;vezn}#i>on18i4`J@aRrhkuv4#+%Pv0J{nispimAFD@ys%OA zQJxy%r4)=`G>U6dW*HOdY!VPi35A0gjT1ft^^`>^F7Pjm>in|+>ALsKjL(KNu1S`D zZb9wEo3sCV{LKgNwS4pJiSO^dv;Eum6EDHzCI}BIs2PaH0Q?Ry0v!=uKEn7(s7+Wg zH4q5FxXA%wfUqL~chOeJ$Nk%ZI$b(U_37EU!L}ZgO4Xg6>$CgEb~TJu-!Q7c*EtID zg&r=>aM9Ihb!yV^?Q8a4cx!Rv7GHgu{Se8NUpwfX1N}E=JFig(em8VVA4 zXsCWN;4&tKu`IarFpV+zD1MXznP?J*EIwO{x|AgNSjI_3pr#h>X%`)}mV2YYJB`Gv z_4Y1pvuS#+%F9RYt#x(Xw8}Y?$1Zu)F_T?O;YF%ABIFBzJaT}Qvd|2O8bLtbp$mcT zuO5v6=q$u|#wYtt8*b+R(v}>5?A*~AQ%io<>~Y(LUF$pbpBvI~#@e$5PwpICYI&bJ z3li^a#9e8aG0K`R=ovOasx}9M0TQ!Un~2$x<5KARfmxsuEGbdwz(X@XlA%TL?EU;G z*P|s97;kXxYRGr0E!f1|2D;SeQ4c zBCW}CNTPf`Kz)eR_D#lg`s}m$<>hY+`4^qZ)#k@w?Ghe}uwTE9L;jL{zJV+5P=@GVS{^T0>_=QjM| zg?xqnxY&@Mo;&yZL)NXWe(Cs_>*c&%ns=();l|}lo=7<4cq`39J0CO^N^t@zp)x)n zs|2CRz{W5cAZbW9#zL@$u>7tcCSgL5wSc!wt7$T<^rSs&inU;>4yl;@#^^^($K0_^ zbF!4}`_2Mikvz?QYI504IP7>U%|Yf1E{GP;-He6TEKf#B7I+|ChA9gO_>2~Uike9J zMbo7j*vv$fbnbLoPTyJ&#N&mS!Ir`mOSAXuD@0U+%&$S4M4g26xuBf@B$>=g#3lTe zWARbaU}P8;r~wY60(cB^ykoi6vy;eYul)P3FMnLko0XjWe!d!4_*uV?*j4Pt@$%!N zUz~43m9KyGP=+giR*m;>Z>w;l)s)%ev;T2-{G~i&PTZ(`dIy_#V(&k;ml~SMj^B{< z!7~p?A?Uq85X-eA_!bh4=Mrp?bpp0Dloql4aG;6ALO_cHk^Ik%yD-Tk*Jca=&03AELg?D2}Q-gAl z5Bj-C5YGo)bAjG7-Jl!^2cRVj0rU_72RRMDOy!+GIs`ZEtXU@X`Kx*r?H6(1C}-Z) zIa}L~JMd<|nK`ol@#O@rQLN00ObwZf`hf^j$VdneBbg2Z{}8dwpbPs_5i?RzjhQJ{ z7%|tOVj%|F4X%(n%1do!WY~t4-zwV3nR)Ej-M?SnGwr*Xw-**2{+%!D(Yj}UZ+f{? zn=-2t4m)m6LkEqq@Vbx%Ue_ZrCLlnP8Z9jfdU-(-FcZK|Bre7IPOvmeCbU2tGHv?_ zVfp#uEz5U{EY6yBGW0=perBw>trqn-}*m?^UWDWMo-d0(6>e{r5%Yu2opdgJVkrGMQ%*`sw=-=fXc zmwtZYkCm(1&A##GnS?`*o6mk-CH(^KNZZ6|tr$XZc?g7>Ex(PX$mNrJ9n zi|U5NrjMte>dODubZbCTzbx6aWW@S6FYtAp{x!ON|8w|xFL}g?E`@*VBh9axlz+jI ztG|}y&CDGOa{|FLJdI~IyxL?jU`dt~425_IJE(qCUnCz2xVZ$pEqs?OO*Nya zZvz1f%EtjZ5LTU7kkA|+4{j51APMlpkYZp&`2_Cp^n6SQ{e)hBe0=i8U(NX^O83dp z;iZMG_E#$ZQ8?F*&Mmj*j=j1eai4;CVRMc`1!6v^(+GsalY}NX8YWyihVPOFEEt+h zax`S4PylF>2QLPeZ)uk&4x99_8$X|=?1yK!zg@lKm`@tLe6~rhWAlz4eY;Pt+0>&o zW}&``gJ|Q14TWQtfR7ua(!3^8Boze*Fv0+j9d@ALDFu95n4@5J;V}?mQ7la9(!yT! zKdjP!&58yjC1t5qFr%+a;kxr*`>2h&Nm~=}Uml*dU!5-xw3$A;)k&>X`O(@R^Abm& z$NiP8WGytCEV z+GUbguOGzcuQ#vr@zZk`%)fSXNXr{Ax*T!mP~w=|csBv(6JV?8Ght08nyhNl=vk&f z_xx1A6(Kx@95`!*m-hX6@ zw@+}@o=?83x$jXk_+dOF-#6fAp*<*@c%aaXh=5zuS1iLg<^*8F%dq_ z1@=m|xrh8cvh)k&|E7KRk3a3-uur(Ub(1?#uMwM78}!C!2_HJ~LQcWR`gJ#mrxFMM z86^r;)d;LOFhzvU1e5yY0(~e~NJytqv{l9BpRYt6?Jb&^ZTlHld-`(-f zPYH(|?}SGe)rfMQ>hV$NuCl-jV`5GOHMj&p3SE!ThGf~Q7*LFmZH9d=t$7(_h10|C zys_P;Y2=KxHLfdm-N2<`r%o;$bSiJfz1-H{SNe*7S1#eO*h0*w&MjvdR9HX5 zMsR^WLqdC6HT)MgVL;MbZ)NS6QZ=Mwg+C}f=0=qwZ5Pm0oZCILaIfx5cFk*$bIiS6 zB{oeTS9NLE%>9+P10={1|L?O&=ah1 z#^%Dw%WLw?OwPaRlL=?W^)Hvd+2$s<*DT2L)!Cmvs=svA1ApGk4H-VuK_8FcED0tS zu+j;Tm`4SI0S7X(Y|L~aNgoT67!QI2BBnU>rj~R6+`aUS<3sKqUAi-=h4bq3$ESSW z^`*S!UikLq5AO&0Zi`E7tDJDyad$6}*fyu5X~2aoK4SVHT%yWhAJ0OrnlM3x(xLZk z_;^1oYB(G~ZtP!nZ_VAwD}PPyaaSteq4M{!x0}|PJ$=TY@;Ck--K=Jo0>R*)35Okb z_i_sFMEzuxgN=nj#(?V&>$G3tAax{b8l0naNFR~3t_39ucn@GIA}Pi5KUaIpCjPZt zZOW~t#vY#Z@rWG5_BVQDlHbTiJh2HgMYrTRxHXsviq=2t#*&-@vz*%qsuo*1*jj% z_vg39%UDxjsc!J%%B{I)ezEL0Zt>WCM|45Tgbd8$w&jufaYF z#l*km_VMfUTJL&1e(U9lKhDl>aj%ag_E)I!_X`y|{9e0XmST+)_mqqqwhBv=utXBD zVf8`1#N^$8t1(ec0pwlApbs+EtmsgNzy)F<$XLR*H!Vpv{*O)lU&yQhO`hwQFCzo3 zt2eFJ`Y)U0xqG!+g%10rPYy)eY+Y1gR*C$~ipN#nu`*XUfpc_D*j~SZlb4i+x22Gb&Z9rE!Mv=lOdY)LuKW%KLR}G~*9`QBRFMhCh z;e>aaJYS%W^7oi4NoQtme81JoE=l#eB|guLckZw*m~x(}1O*@m;J@dOq7|aDm@GKp zw8W%9R3to7(rL~MSpiTnO*b6}oZ*o*{jJWQZ8_5FRD*{NuN4?e&=&@G>)(6tk^Q~j zUpi*k2ZP2YyxMU$a99^T%A+Jn0l5Ndk+Kn1!AE422tg2ZgdtoV1BRGNMon81c!ng> zdB#sWh5v;cf+fIl zvNz~bdI{aTls;i`P{J`CrIQIiQmaXf6OFhT<) z8#ol`P^t#*FI%&NepoTbfR+!T>LL6fg{MM$|9R;2+1J6G*l&32UXA-Voc#IX!@b|C zcJYPJ-y2J2>wNUUE6;6Ac%|d-UYw0O9WngA0HZ(%3I@0asS@F+UxaS7Dmwv~T!--Q zf=D=i(NnB+09}S{Q2Wby4=U{W`@qaEqFTOMO4FKm8$HKVdA&oCuV%e=rgx3Rw?VwH zu_-|hjXFL~a7|GqXa?#yev=SFBxJ>W5e@990J2B`CCZgF$&A_Q1E8m`-~Ylp4QNuP z>HpCb(8FK1t~97jmA~#%?9$_ty3?)4<;fjAy#CFtJuA1M7Jrzj-v&dhuok5~4)z^X zgrLy*aYb7oOo0j$Ocdx*!-jU6W(g!=hM?aZ6H@1i|2$c*HYpxBQ@;PBd%1!`cS%{o zFWtQLV9?^b?en&fzkl`StBH?{;%;6GYQez}hf%+25Q3kvL%{hte%;T)5=eF}pXA|P zph2=RW0AYfIsnGL%ef`Wq}1@L#}J4SRcry;rM9> zL%R?Yb6Am6t)NNcVFe{aA*ecpX=~4jPCva6{tE^$ASp+dtf`};^xHbpHfcjr@a?N* zS@z6`p+he0=~83=m1b|1J6(~U`u$94K*Ap(zq88T#ZJ~X)^5njQFQlK*`kELI~ z8!rCT^+fHnqw;Qe_}({bUwm!o_XlR*SvlpyRr6+F_;6&=cN1Ric&9516*u@bI+kP` zjG_TDYFPm*rJLe+X~^D({i3T!00neK({vTp1%N8$m5^~zck}gcdP~cUhNW)5OU}Bv zc=sRsirw#ggDSju(3Ul;dept0aL93YZ-|gMC^m#yUh+$#OGIcrCx)Jr4Losz4?T{*}rFzq#DsT`&RBaF!J>`r&g_NT`kv(pA>mz%-370 z%>CxJe?Zd1zaO8RR5Bw`<<^?cj|V*Re>3QVNs2T!IJ4TC8{g--Fy`0QuQ)%!D)=1E zhIC|Pf$HZ2t zvj4d2)}Z^}N9vy{SDKoCr$DVm`4SE}Ug~l1A%X>gVj}a<&sLRKiu`pnz!Hof)21j1 zBrO$*MmY2fGy~$(yp<|?mofE*1_b#){+E;Aou+sF;f_3N$DMPnu7A_}M6IF=f4p3N zKqeDZ!<+=+B^4T#G@~0r)RLH}j{s*CCb^jFt2#@2Dj}(?7-WN-9ROWEZEA_+NZ;i7 ze|u?IuU7r0oHBg_TUPsW`O#HJ7mPnU@`dJqj$N_htUq(-c~H;P4b_9Ek|HS@ybm?e z3rFFc188ALfg&UY@+PAInCd8DJLM{fN+RcY z9z1Tmzc+_geYw)!3lGn9uaG$LD(=bQk1`AgH$;;~smbvHG>E{AXTy}A zR>9qX^&sSzqFj&;YH(Hphs8=4q)!X^t0kw;&QZF5c6a=pBFDdK+UAq?!=%f7le|jX zcN81idT`>fpLij2T8iHm9h5yc9AGKUVlBo6n&XKq zQ02zFEXz=Vg?T>Blb1GAl1D2D#7mE>` zp_pKG8;)vm;Qs`LfCFKDLJa}W7B*?!4p|^NKn?aV*;$~?~ZEB*CX ziN6`+CP*~Iv!q9;F^@-;C_+`&l|z7#2JDETgDnHChp^8>xgWxtG86>nB(>B0zXW*} zHv7@aJXKd*zIFD-^E>ss-*+y4>hmJ|2mTV8{dn-=h6nzvyeY$v>`~+I(@To=D)pe{ zr;=H;&H4W4w#sk(xX;b~^TY=Eia$J>a5}~Nk*o-rf1eP9ixNevu%u`Fj^7J87MgD< z$ZIpGvSGpRhW$`RgK}&NQ7G+H&u~O$?J>T1bMDIn<6iu2VTBd-W+dNXhHU(5)D*q% zChRU*8UmPRgq~Q9 zWW4r^UAn@2`G@i6shnFIRbIcX;}`7MbB(G^Z~W4b0U!H9cM?A$QX-pURtG&dH(nJH$HB^ zWZcG;CB|?oKkr}Yl78mRZ{JQhIpT&4NC|B7z}x_uoM#YBmIbM{K?^^-`=a#qi9n5t>gOi9y_apI{sjlg^j+t)x5m# z#Hij0haESk4Lu;h^8j>eR??!B8G&AHKnC*EFEC+}3xr}J){Ihe2wsW-l2?OIO#U*K zv7cm}xNK;N&ujK-(u?0ar)<}95?OwGn=@7Sbsgu;JXpMK!Xd|7X;y)u3kf&S0BG~T zXK(?LBSQ=XJdGfjU@qfCeG)kTrsYJTcpuUTC$$^+bfwcD45cpp`0}Qc*@sR1dT=dY zjb!ugw&a;F|0*2a=#(($wCH>&;jrVbv`9w^^+8=iA@T%hBxUZ(XU1EyLSKB$fOi_t$f; zW^TwbC?{~3=3#pm5kv5^Qp4!XfNBbT`yj>8TreO)Cc_Pwp|Hk^VMk4!)@DqOBO~sU z_ZyKzKd-{Bu`A_lcQ6an>!aT;d|iZY-MDF+ru!2PIc{=5^8s!RK23^1r>Wc;n?X9$cKL=|n&X zMAl zV_NL5)2wQ*7hn2*pwyyX=QZ>CjmvQDx7A*r)9!^piH_Yz)W1j8-nqN_g!6iZwH=i! zfysmK`R^y3QgPcKjKKK`KZSdf06>ky=*qP%U;CW3Q z5_2$Vg|CD7%oIICkrn;$?YGu;FZyay=YePQOrF{7_m>Bk?bmC@FQ+RH?7iF_o_ISb z?zsbkCR%$=h!h5ZgK_$;oloEkoHPk5p!gnE6VTeF}InYyQ0&&`U{bw4VjGBLX z`Mzwet;2P%+}-(Nx$Ip+zqXjW|K4zJ&F_gno#Q456lOdKvU$1`RYB=cY1s=Z5m#Zr zDul!-4WnwPN(J#oWn3PtNjILw_Vuo98g!X*cIx?1)oGW9eRg5#FK_&i{BF7I9p>b7 z>`k|4C!8GdR+)D5~i7`1VdstfJdM(M?y|K5(u-LB>TlMuB;G6N&K^B z=FdJej-9HSG?s5R>1N+j#_RQSzOkw9ly+I$AGx_cc;dp3Gb(ETkF~Q7v#NUEK8Q3( zOLrsXPC!ITx=R{%o*ifR*-1!ucXun@2ue!`h#*}e-K8M;J~PM+^W%Ikuj{v`_`b0hJ!Rvc zIgifnGUq|n{$r`$Wl}cC-R!3ZOO>aaUz&rSp03U&x$bSNM;7BM)V};gSlIr+;f^O- zWj+6}_?0}D#_BcNq%Lp1iFpE}on|7o)luUAa2v~xh0bGLT)d9u69+OeO67W@ckcIok>-CFFA_^3lh zJPR(&icdm|6zZ$Mz%&#wLb*6ZKv+P40XuKV*1!!L1f+_fkd&BcIZARM^z7DrK!y3; z4|KVnt9rK|I(k*RZ`@W=?{fc4(H!2Wo4b-Z62f5vG)!^<>q(BSxr)Pr3(z1GnFg^c zXleOS80;4q#E=+Ve>^E^C6P6_R%axTkWm#YJ>M!xUWzoKPs_$RHF3rldn*5!nlQo|p*A zOSqxlXg~drNVOijN>&XOJSbQ1Go*9thQ}I?%`x)e(7x5y{+uu7AxAq9z{biNrpd)c z?dk>>0#BC=A7;Z)O$~^nA{5~R2Oc)406yNpA^^pn$XraaIC7L+F)_#QRmWvpo<48E zJ|$A~%dVc>{j|x78lB7Tc8kwnk-WnW03V_V7K(s|Q#aX&VM3CFf%=F_BUVTiG|C2K zkl;`rY)*`cSXLswEn%j;Q?KhhuDA8!k7B;F#4nCGYgF^DJ1&k+ZRG6pbViRZv3rK2 zE)EeMvtTNNAR>jt@i4^s#N|n=I1Ab01PA_MiWi{*q;PHo`iQEEy$zL4`W85Dbr|wV zqt-bmj+x!&<4qfzSJ{%bT&JI37B9YK)yBfDBZZT7mT5K&if|isUFZ)AI^=O_q-}~g z;wi2Mk6sHpae)W!Zr~}JxB%#4`G33x8trT~r1Jjj4+>`4@lnkx^4+41zMbKZ?0Wa7 zw*OdZtF=qsnFh0g&4a`X8ib|*In4wQ(r^>Fz$n<|Z46h!iUY4-7932hP662n7n3At zBvuk?EMADJr#$B?VQrJuGavOzvuoGNET=AX?)~Ozrpj&emWz3|qn^{yEC-hd_(Op{ z3%TP45C9-00PHAX(h5tQj`$J=xCl=3LkNg}3Cxd|jUx!9p75a35f|4CGaZ12Hxj@q3BnMQ zM2398@<9S1&G~kOc#9p6Qng&3t|`uhGPTF%d-;PIZO-+5*{XTdA#IvG`uvlXBZnT^ z1!qm-8|~{#`+Du@!nSPrqaljfUC4FG3|EyH+&WD$;kNJ;`dPFib4blY_ z!loI_W@OGod@ z{3DU#6MyM$R_8`!%qcZI_SgA7eU^NEZ&|HJNAflrQf+F-*stGF7atF9b=3-4nnVx^ zSYbopXyH|OFMts?2sxx{M{HKpCP3>H^kF6o9Gse?DM%}3u~RNm%J)| z8ka;gaC{zsPxPc7_;t&Gwrjcow)o)8K;e(QOJn7=YG8c|Cw;`|WR+lHa@Sc>RC0 z61|Ah){IHpM|`xAzI5}u2Bl*!BSkyVDB86s+*cta4U>S}0H6%WIYbaxRTSsyHskXI zY;RQuFg0aD``M=x3wB9%?yq2B0pm zd_+avy{>Aqq%BAoAE;okQNg+;%HZ8)MInSb5I~?{0bKJP6k^9Q!PQ(TzOnM*?mar- z;rip(=aycWdGx2*ij^LlyN3A;pIYg+M{Ty$DOAUfy;Ko(_o519OU8A%a8=L&TL)Yz z*tifM#_}eRV&E18^d>QmbSD8fn5W3M{h)V+=)|*f&x-4n=Yw((@b@(k8*bMG9dMQ-~bE10cU0?o58JmLQY@1$iyJBu!p04d}gxIhzVofs3)c6oB3z z!k6Op3?Htkwx_W@xpw#KQ}Oq$qd%e2^~R>UXQpO2Qg}-0KR)Rj6=*12h0h|F^Le6H3=YVn>MS9sLz8AH2xhHe*t{@ zZ)ntiRg%EonCateNudb6$3ExIf3)~&RjV>dZ_c(#E^ufGYP4;#>&ch1k1YJGe9S*i z)W1?yT!l8&uq9Ifns#*yiono*)FY(kdVt0=a9C@AeuVd&52{HkcnjM2_nQ0Oo$eK+ z%hlU+XVHa&g-^FHH~#Er8wx!vd}Df+IxU#+^ezPkB=4|oTJjy6hio9=gpgt26H|;D z@+^4InUahH{}k6TKo5`x3AXrvQ2cn#>4(EMc6F78Y_FH9?!lKutyM=GZLIKRjcN-= zEzVNoqSlVv6}!J8>TH8|2vl|8ZS+vdKtLxCl!O2ccpW4^Bz!F(uPf~HGH4(GTorJp zZ6;9(Bz_fs==?(|abCt0*g*tBx4UxvSWvA+5Px6PDFW$w;vv2$U}vmN!|f!7*> z$czb;EpQRZ3IxoA#JE2L44dG@BLW+vX+T4H0cH6z&P$Pa@Y#nSvn+U#ecAdw>fPbv zO~2+XR=?ql9W&=jThBCh*8Z~OLDe%t%)^d0+cZQ>>Qn^s?jfAW3)vhYLU$E%_reVD ztr?7$F$8#!5M6`;+$R7=!h2$>BsZRYkE+*w_5Ck}9_oG?e&zGW<%{=Rvnzkv^C!F9 zu9#=4%O>lvDJZmpSPDlW9}^D;gi1*PWZTlH%4n!XM>JC*6g(`%lOU3qBJD0W=)YY>2-8$&8oj0E7ADmmy@(rcV>qseOh;2+D-%O6y02|*p|7UmrY)n zJ8b|yL)LLB$_;DQXbe-u4)Coal=3MJZTL&Nfq>fPnF0m(1In-iEAXI$M&lwwizW`&1rs{oIxiYP z_tRxKnBpIjAKrE{zInZQEpF$EVI((Iobnj(X?- zZwS_i7|{^WMu@znU@U~3=n_SVKuO?bGi(#I$nzq|3v9$fa5KIg@!_E}sp$C~8ILwT zM%2vNxoYM$!Kez){T2WG+VyhLGbOq$S=HqFxxtf**3B`0OV8C6lXp$Q%f&D(NC6FnA;pd`Kpli4j3S^s?t_024+VkC z#p!L~K}`sP01C6{ce(~?eq7v)yE&}-l|CK%l4I(3?9ptza!G0WG~1r#XIqamd zXgso1zSh$ZcfHSET-X1r9ff~7_*JViMyX-3$2~{g3Sn1pQ5K%+A)iBS@VW!tCj$2c z5PRUd076gJhLAx^RSAhj2}5AzLLYAphTHYJ``0&{=SC#aJw_Y;KWOOEho8PtaLtG zKDYYRRb>uu%}xLK6n&nm^M;QakGj0;{@en0ZVhi6^KTRNUSz4FX@g9N!(K52f<-tNZ5ln>SMmCpn{<9SZhrDp0mqMdh0e zQz!1Jx+geNJm6mAh1xVPlREQ(*nRy`|4g3Za4?4JVUY<0KZJqn0v8}^oOk^I^#T(i zA)E&X3^)hjnpBP*f)u-O$dVw!#3fK44Peun0g3uS#D<(xLNeb6KfJFPIk{n3 z>MO!%(2pFiCW)%#6IvTLT#oR{j`8805@`go{1v~lzH z1#?_Mil$V*IqpuaFndV#G|9dE6cE91hOdHYJD><(^i1D^)@l41XT8Xui2?eJw+cH}aCOIvw!%UV4xrPH^3>G5mQRqko7i+R}5&M3-< zH3Vvc2xch(V;HVsu&xZjm6*#1SfB?26zT{?3W=&BC}Dy1BmaZN_Xk>gZN@tTZ*895 z=H}Bc^PV^uIbXKV%u&5^cVGPS%;v1gOouZSN)5xK4E04d@WP@E+1nVx60BhXtd{~C zpkpe&!C58_u^J9UUB=5K-d$P~Uo}#uc3Xr$%Fey|C~cF{`_B*lXVZpHY%bcvOf2MEI;TNQV+eqC0xxiy1|fVVW#PP!K^jn5}(wV;g&)E6vQ`bnpp?S|jRenbY-S3v;le1qw$oKH@C*6*2C>Xmg zEZWIVnSvTH;LWjR*>i13p2K~J4&qQG#Iqcb?;{fKn+`;_I$)O40*Eh-zck0M>4cO0 z>8HhhVh8?p`|G1ivn=X1dY!z#Vu}6cXX5b7e{`Q1{{OJGL|YtG;K>Z7U_PdW2njHl zszR%j05(y91OpO|L5&$<0BHzw4bd6kvwWM*{_e?}XagupJvmshPwgTTzaBoi;-}lI ztWLeDW9P!-a;1}{1zkoI#A9C5(Pok2*e!S4}yG+-|?HHG-?unUp_OKCqBQ)xeaa}gmAP`XuLis|B=JJFTvS4io zkVv9J`-{+U)MG&K2(@tBf)J3z4t0_*;xct;qZ>aj`*UmS%?~S1{;1Z*-t!H*fA+j@ zhHk2|I-Qfe4l`O6fe)iVKp986JRTM$5#hZENUCrGzLFA&(yj`5wK%QYsxGpE@b*Lg z?)sbfX_;hI9d>F#TTZWarB2yf#?vF=^Lu~DySi1q6UV)tUzSZ?=??|1bud~8J{I`k zjgwVB#7l75(lHPAAO+}JR%1+4HBk!}DHMk$d?H&q;q*9PxKQ!j8PI;?(?88V^Xj!{ zXL2yr>ks`aO@q}%9xQMFV=`+ULG;C7aF1m@U4xV$PU1WWO$81u^xsItMb!{4yMj+J zREUJFlp=Ub?oAS+P*hQ(;1?>HP~X|7%f;a#@{Sxh@s@IOdHT^{f#!p0&Av0C$NW z2tZ1eT{P^Vrbz{;59R!T1ZyUWYhe&T0S6GuI#Fs0E#{m@tr9J6ygc>S)#c}~F<+%y z&P=~@v%}QxX%_y{dgzdthaBye0tQzXG7*Rn6o(a!!x4yV2+K4H&PxT+QQfh8Xh*}w zj`Na&YTdEL#CL1LQsP?@HLD;0;HzizkgjS4G-ERu=ZA5XeQWmr_e1pl6Qlfw%eQAA z?(fzqw{~?_GtHf9^Y4idGValcXyh^^M8Jg_6Pe$a(7s9BbB#5cY#$?{=V{i z$~JAgw_OrGf9db?n`0h!)S1>He22klcvS!y25j^|ksv}~-{BeE4bzkuFti)-7*6pa zgdOO?0{UM0qkXBj^^YUJ7i2&Aw#r9Cc6)Y9KXI4#hm= zs55P&2Gr|i9w#LuA~C>Q*5LU-qp||!Re?5ij^g0n4svqCffh3$;S)>vNzeZtjx>2p z`w`nKKD&GF;uo|0hs)9wYqJj9H{enseiL=N9c~7b@L8v+(z^*@o5K zb8X|0#)YVI_nzm;+`4F)qsjY6(h$uGiL<%_oHmFykzGp$jhk+Z0?h?H0!~Sm5#vN* zK(Yw~_YIZe+bi+QDd8}!v#a@wa*H@TcQy_L?13eypP1-G7|(GErn=EaFcYn+S>>kj#m-Cy8$JB zX(zY)@%Yf46vb~m9;x{G4>QIc{`2^uM#PTX9Y?2Mc6a{nn1>y8If)dd(*i``ig3>J zDhHSVN8y2I3_-b=$VF(4m36SyAe>BaxJp5dJfT;TFw@hTg-O4BTV`>ki`v`b;(N1f?FAZf%8S-_2k1~R;wC|R^2 z#=*2u%$7vf&}E3Gf`SV){zX(b%(payq%%GG$#lyfG48AO#oMQ7T1$TX=MPO^QXS;= z-+Vdk*S(GI#2%9ob*4ieL}rDx2y`}NG=TQ2t0=6fhd>%4N-k9AK}#WrM4GfCz({or z$fx*lJ^xyz_m+b>|9U?Bk4EM1r}=wVcv&ktOZ{8-Q@kjWde_L`mc=~F(LO@7Lt>I6 zFjxS+c_GX*ajFo;_k0LT42-bgIU&sl`il&NK|bPID)yejH_4Ol$uDyz559kB_3+{^ zN?hx5XG!VJPlUgp_iDMw-u0rxz|Cu79(L53hGbHo=P{Q7ilgmACq{Q^6=P*8Y>6Tb z8ck@=aB2t%PypD4M4Wn?p%UfxSamjb++4G;?fv_mvz(ZlZ*3>HeVvat4VlsTbcaVx zD<6v!f+X&M(m6z@Df@*xwQRYKKa3bZGWCR>X+~@xJiSEmm%0APaAk4Ke`M4hpb}mL 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�+Rv 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
Vm?gp;G& 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 }a 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 Date: Fri, 18 Jun 2021 22:05:19 -0400 Subject: [PATCH 143/160] version bump to lotus v1.10.0-rc6 --- CHANGELOG.md | 4 ++-- build/version.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fbf620f50..be302a28a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,10 @@ # Lotus changelog -# 1.10.0-rc5 / 2021-06-16 +# 1.10.0-rc5 / 2021-06-18 > Note: If you are running a lotus miner, check out the doc [here](https://docs.filecoin.io/mine/lotus/miner-configuration/#precommitsectorsbatch) for new lotus miner configurations explanations of the new features! -This is the 5th release candidate for Lotus v1.10.0, an upcoming mandatory release of Lotus that will introduce Filecoin network v13. Included in the new network version are the following FIPs: +This is the 6th release candidate for Lotus v1.10.0, an upcoming mandatory release of Lotus that will introduce Filecoin network v13. Use this release for syncing with the [reset calibration net](https://github.com/filecoin-project/community/discussions/74#discussioncomment-885580). also Included in the new network version are the following FIPs: - [FIP-0008](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0008.md): Add miner batched sector pre-commit method - [FIP-0011](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0011.md): Remove reward auction from reporting consensus faults diff --git a/build/version.go b/build/version.go index d5c126068..93022740d 100644 --- a/build/version.go +++ b/build/version.go @@ -29,7 +29,7 @@ func buildType() string { } // BuildVersion is the local build version, set by build system -const BuildVersion = "1.10.0-rc5" +const BuildVersion = "1.10.0-rc6" func UserVersion() string { return BuildVersion + buildType() + CurrentCommit From e1c2567136cfd7d6e839b76a668808711201a7c1 Mon Sep 17 00:00:00 2001 From: Jennifer Wang Date: Fri, 18 Jun 2021 22:17:53 -0400 Subject: [PATCH 144/160] docs gen --- CHANGELOG.md | 2 +- build/openrpc/full.json.gz | Bin 22485 -> 22485 bytes build/openrpc/miner.json.gz | Bin 8089 -> 8090 bytes build/openrpc/worker.json.gz | Bin 2579 -> 2579 bytes 4 files changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index be302a28a..c71a931d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ > Note: If you are running a lotus miner, check out the doc [here](https://docs.filecoin.io/mine/lotus/miner-configuration/#precommitsectorsbatch) for new lotus miner configurations explanations of the new features! -This is the 6th release candidate for Lotus v1.10.0, an upcoming mandatory release of Lotus that will introduce Filecoin network v13. Use this release for syncing with the [reset calibration net](https://github.com/filecoin-project/community/discussions/74#discussioncomment-885580). also Included in the new network version are the following FIPs: +This is the 6th release candidate for Lotus v1.10.0, an upcoming mandatory release of Lotus that will introduce Filecoin network v13. Use this release for syncing with the [reset calibration net](https://github.com/filecoin-project/community/discussions/74#discussioncomment-885580). In addition, included in the new network version are the following FIPs: - [FIP-0008](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0008.md): Add miner batched sector pre-commit method - [FIP-0011](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0011.md): Remove reward auction from reporting consensus faults diff --git a/build/openrpc/full.json.gz b/build/openrpc/full.json.gz index 82226eccfca98a437a2ba640e842c63f26ca58c9..5a002a26f20c4ea835f624f5545c42d8271703ef 100644 GIT binary patch delta 22339 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 delta 22369 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%0D06$hA-<# za#9Fw(iTb_r`gQU(X^P^yG(Mhq$4Bfp}+sJq%^r{*=6}I^#o7|G!31Rd$Fvyui6F^WrnqgBZ^z+1dKR* z!uUS@!nMza!t_J!68~Iryu(T@%+^iuDX-c`r98%1ly<_v6OA`{Dzu_0y5t0Nu}I`; z+TVXZp5({RE;-S2Bh3jVN!#WKL>!-|(gdSx#AZe&z$EKu>cTo6AlvS|^x{=5mhD-% zG!L>B#RQv8fgkeD#J;iJaX}2BHa)Is@Cl0Qq~l8YkavSsD8sURld#pL#W&sVgv)eba%iJi&AVCEeH)qlSYfYp@~)n4m@Y zV%`5f_~K+F3%*#loyQkXWh)&ZG=Us=0mSmWh4cm`?DDDlv8&_E9MkNW<|oNCZ#92e z>`o}Xio)ZAj!44vzW)ManUO8Dt#NCKG#EzL#v3CoAC@n><7$dI4CZQz-7AuM(_|0& z1lbi5v68kErpkd>3|-|wSs|<9;1(Dw*To3)QK)d#2ktR?qZr;3syJa|8>+@)F|%`S zoYXXAhua)(n}ge$Z(Z@NjmAcwT;qSYrO$2`YfTKMt-dB6@p(INy=k(h=v_TPFz6^Y zW&qgBjhUQzY5x|OH6Ct`@R(_wRuLm3%5<~|fI}JyBR(oe-Mht{4i|MwzL@S}Q|`rF zE4$Q-o7;o!fqX7MgmM_DzcKPqWi^$q7VFdpduL;Pu&IJWloz;~tXY-)03&}Qvij}V zLp}TK1P)P9E`xF|AQPteqEbNidmo6Oe_+n^DZ?H*Ma;ZZh`H5@F^gn&A6s;b*S@tcKxui}LP&SXCYr|VenqHt z_RzNTF~}TFs|c**P`3TlszZO)7Do1<+03^3%y)?8%3RlE_2W2$>R{9OhO{__+EwU_--}99OMixIAXyfrQ2Mqzf==c8 zqJ{8+TN>Z`$YJ*cTL_P%C&0O6ZjhG)$S_Dmf$RAa{cl1mGb9W)6l*U z<+^!HM%0d*tD24tJ6C_|m*eLQBgf4vom)T7sIT15B>SYg2QwYyO~x*O&+&Eh{o?A5 zdsVvX`lffAxLRF|78z8vM3PUEY9Vx3Wbe%7e9{AbR{;tg_>|jFC5S(OvolqOpgV|| zCI`qLcL*)~a2w(_32KjZ3jD6ao$D+}bYx46VSxNjjF>*Ddar-&2Fvm#-8Oca*|k@c zu)lBgpi-3V8sY#B1GJbYRTZek$h&^BhsVd3#kuO{U`^8tU2~f2kTRvFp(6k_9sh;L z%4<#JO+}6yEG9mKHv~`yc!-Ges&ce_9&OK_^_qoDUb@O(B_!AAyd>}H0fK&jN!Jk2 zJd8!9mKcydQp^u0Rr>u?hhs^XhH|jswiY zNJz4+7}b4MAJI;kN4YqUhEvQ{B`n8177**^`g)^(gG3XDFbX<5HlzR+L#ab7Mi>SD z+8!2%@(+8=nhbA?R&|FIr-FF!*lZWqs~gFNvqTS+bHaaj7}jA}8w^`I*yRMc6)h~* z^G$!_j>79#6q9W=I!1ILr)fi@{{`DvfpC^}wMhhoY?f z!t$rK><(6XZ{v0F<%Z;~``;9e@|{sfXS${XEJ5n{)F zZ>P7hc`$$YjneN&f8GA|+kgIjjXwU52#>aRIs5wGd*1ikk9&vbn;-cHdUSEiKAl|u z_CI3O>-SV{33iXpvFEB!99R4QBZU~BN^{oBF2!S=gc2grolv)In2 zQrPWN!V%#SSG>oE0EVizf_4#T=OIu7%7$AmFRg!Vov@Ntxu}}Yu3-jfr}`a5*qLL} zy6lWTgsm<8=0sKaDP)g;&6+ozjF$b=qiEH!1ghG~!n>7jdQ7#JE!>6T^?hL(<@aR@2t z0i;_RhLDz$hM^9nbc2*M3aDJpjdj<$_sjkT`^$d!^FAd`I^oT5oA>`x+{Q$>*E^0F z^9AVRlchw{X?_!C*ZtRCd1sO~E>MCEkiFCO{+d}cYEc9Vq2Gs0U}cm4Z6XqR_`%r4 zsQ(h|!393UQ8?()NCO?O{;^99EM!TxVjFAo#kwGLt&HrL0`hEqXHD0>ecgc6#;?TY z0PQT(G5SImy5NTSAyBQ-soTv4_=?)d{c#%H(M(38R7cLB$i08(db4mD~+}A z%FUx%;V@;t^z{SY9hcSw+uahs7*5AuBgMeKk>>IO+U;VHQU3mI*xZ%o?||OjD(B9& z{d-)Fph3)!=eI17yw!y;OIe8jfL3PR$%hIhBJ2>^4{$?oX^yu48AkDj-Y+C7t{sq% zTUANpm9Q$00p>ks{z_Usp~dAu_79HMb!?+Qu^0)vxb+s^vV)%UHltI7%>gmgSb2wz z``D2j%7liG2s_?9d+A;WZoQyr8j13EpYcLsFb<9Yv!C@A#dp`uC}$FBl$0sf$o7`t zMu%Dm$B{wRwj6>7@{;r~Ds?=}Y~lZofwxk1aPSL&be(qf~@zBATozdnj@sLo@HU+$2Y|>hp5cH>}2J zhGkeev-9+E+Pv9c(Y`Zv9)jG+#+2@xY@qVayf3&c_$S`efWAX-GZz{v$5%a36p6A^)9q!!_==y`t)v`}pMs^^h17-mFdk9S+Lmy{iz^)_~D^6!H;(%glZ(!1SnRYj_I-a*Q3GM&HK z)A?mbGliK1ZtQNI|0nJwhw{%i%^(cJ*V*F;*mA)tbaotq?rwoSDdE}CfzG;eLNC!u zR=)REC9`vGfwozg@Rr}JR`!qY3G(sV+dn5DRy_3-^kJTK3!LZjw(TCfv|s!VfOhc& z)J_(Q3Z2x5^phTd$I+a8tmlLeDC{QMrkNl>2UA{C1_mlM|(?|CQ7 z=C9XfP9;7iCUQ!r2po*cMk&zPIMDisjSe_Dd>xR*v6KvMx zb>5QE6=ajbi(5blSsU&%mH~umwqI6c*evgEB=`K>aA|V7P^@If{ z(C(`a3xI?Hc7Z^*BVnmJGPS%;^VL^`1+6Cs;T9~OEMG$L!Ai=A{Q2y^dTP$Dra z=Isp`br!d_=_7))di=mepn@dje9=k@c$h?+`0~eu_3j;1m!o@f0Acd71b;BKtC-Z2 zBEjkytQ5#CgE=77K)%H}v)I=-BZ1IPC4onS&=gpgm6*qRajm|`llpu+a#9WND+qaz zdRH*$aKY`}Xhr8+i`dm+F09=vYua;#o2=<_5M+H(vS$=}oaGUYRJ2Fg{+7FF#G;Bv zjqipkd~*IsLYu$ut!+4TJ{G<43gSGO%&71-qQbOtbc1b_glsv+=KeCh7?@PP{{_ya z!V&zm$?H`LIR6hZ*A(e6Y8)7_`CfV<2H50J_V#7t1j)Vi35EgwO0wpPvz^tS9^UNR zY19u+KE?47Z5Eb|;f23`O&I20ZBY?Sh9iFlJHAgh(%rhmKQ*>C#~e zdN3iv=Pys-3Q5Fd%X3!<-6ve_Hk_cAVAs*=yPB|akvFs2|K_iD8M2D-sHv_Cg;XxB zG3>~kRR8~qUqc`rGxrJ<7V)iL(;lzUbL7D~K8=N{N@Fo@r69lqBb}c@tX54WLz!G3^zV$~ z9Nw2(x_1clx8~bdF0`CRTdqua}!^UeyZzXkCc0guARO)xHScIoT>@q zx6zKw5Y}wUC=p8;E2K2n!1CeR!{+|+&JJw@83v_P2S5(X4=WD~of9frIB;qHkOkcG zT>+JdGEJie;enu*SsjOOmBe2;t*Tnvn74>loW-wdEst%UBkN&q$@uF{p|_focCDwG zTdLH$iTf#yO2a7S;g_@Sf$@c4a`>D5^hwUPOCPLk@l>a>G!>4+^)HnrU87s1^*}Cz zjD%v6etw~kqI&9Y?tZFV1 zvBee!1+F6XI#Bim$kd47hLjlAmwaM3K`s7#P|x*C9Bltz67KK6_784AVj|_snlqGf zmL`TmlY_cqpUB|Xqrh43Q^PMj2>rI#h(9}+%$zASN*co-t&Ml#)FcnRPS}AjFs~D?vQKQ3ifnfsbZDsnI9JmN_sj;wCpnPksCIoGk~-& zlaI1uBBN9IfZFgT^p}N<_J!PN?d77wxxLHnXDQ`PWlZnIK>`B*#g{ zaD^Q|6_hlgBco0Rguhxg!`n7&2~eA~r5eZ4z5q!%0G}fk^uZ{ljz~gqhf|*~yJmQu zs4OaF13InzYxYo~Sje)@uLML(lg=DrS9oB~go1Ol1v-tx;OwtMc>TMQ`nWu*p zHXPw;RN(R~@2A3`z8DZUB0`v4eMB>2*8e@e6y1T-DyYOuTb-9m!|cKz_D`m(c2-$t z7?2`(%%LY%-L2!VCfFkU5?rfcTVzFHAYSQ68OQBCO|Zlfblfe_QDS+YWKJp&I`XFC zEMOvGC%3%)09sd!Z90NA3IQc^&AZe%!bhj-D9^HdvDmai3eqX>#Ht4C5{(0YT$oWq ze6+q@+4XO`EKP*ln=duIzRQ7ih`>%!|Fl$YEmq$zrWld^l$%KnD!p9N$!Xnpy z`(cONz5N|PU@Ka)eT&>(CTp;{!|LFtFxz_1?!;};h81+&w}4H!8%yx|c8W6cQV2jX zkR6@Vv5S_B2+~7f!V45R<6fjIrZQz}i2(5)8WC!ldQd!XjSm$AUNZCV7o3IB-n-<= zwCXkOYJg=L@4w^%V9|HKa-t~GyrTdv6vdquxtYF_ErHyu(az!QuMDF|0}@!4#GsddEztuo9jynyQ&?}&BN-JV;rEjG<2|waHaH!etn#EX zm%$xoj^qVoXV2~O4_gnVGhL^<6ovs|qT40OY!-13s!Af~a5RemzrXt+t1+ ze=n-qQOLLV*3aavp5s~;S-oxIk9Nl)82p)4m&W>v=Yeq1eT@~e@l-|Y_(e6DXY)yZ*P))G3K81lNDo{l}4{DBM0i z16m#*ssc#t^}cbW+?EWTPUE+<>ux}~#>3Y}u6ogFcOGOg1)TqS#R|<$B`R5mbg@L-xL9a z1(*@;C-aJ*UX+$IE{g=jokRr_qF%FN@N?!PVw0ZhKGSI%qi)C`ieC=r-9McSh~{of zN|-8Leln!tSw0!kNH+!0I?E;0Xrdi;&<*5%{FfQclLDN{IgRP3bnw(d0wV3^y5ZUK zka&hE)sqYkgDb-6PjW~|8MJ~^W@$BK&bskI(V6LjBhb zV!4$NurWc@uDA3?YVBSI?}2-Vr7S#AyBe#PmT5QFQRi|J3#j|s@;VY|9p5pG9ri>n zdj4P{+5BrG)5z2>8*K04p4jqBnh=Bi)*3DD>pC>=*o_wZH+h$k@v|qR3mm%M%}=Zk zxA)=q@!Km7bRZn>m|2%tFm??tVE)df-pQ$1w!ur?XaVzY3O0; zS(1@wqrol_=nh|VLt$3=EjVTSmEw5LiJ`=i6HTf3COz)-s;cYUc=5qr?Y?B1QY~jL zPl=Qh4)b!2#;h-H=qf;y%uNgC4gCh z4qX5h*#v12sZE}PHgi86m%ov&+yerdrB{ObZr>rl0ZqE7iZF^c7HEnPU&~a6$CS~Yk^4{WeP=*y;Qq|KyhVpEW$uF zj{I=u;`&D7WdhdM-D9N6&yT94e2VZj zqfg#jeWC+?hz*8oZ~*Nk#I6{G2m_crccXGWTI<0g@E+q9_?smT$z(+p3e|LUa&58! zM#AkMfK6EIPtE|MTb(Hm+-$~F4ghijx?%>MkQ*F(vS^H=a)8vIFz z^^V=V{z;hye{yE&reQCq@Pn*;T6deP##OSqvAunXS&^mZs6a&F5x>(!dxiLrJA&iT zEju0Q%RS!Yq(Ttv)eP*bu`liqCejFGy>3J9dIr7Tu|EDWIx6oJnU?T&;96^vo}B)2 z(l+=yeX5U2bEwKWew0UN@My*;Lykw57mU{@5pbl?K1q<1h%aa7oAc*SHu>}`gFof7 z)`zC6Y@@p86vw2S|My_!%0P9rm6rM~P`xU9Ut23AokoZ!2(R-j@9A{IGWc(dC_7Hr zY^IfM$i)McwB(BNzVKWYOM1!F+4>qf_4?TT!lqZ6Zq7PuVr#N<#lm%7q~jRWKJ~|H zeAhI(@&!G3%r$X9ffyGysIY|Bf3o!2Z1{Io`7FbHhTfu<3%z+m7OxY!1_KJ&R#8*% zS!Ps*0d8KbN)E^f66`abtn(?$IgymRtgM%P$T{JPqs&*UP&>Xz zAW$yz*PjJGb2gwHHGFh0WJMmKYj48naqcW-*iwvj0pCn527rI^OqQBv>Z;`)t_mNi R(a;_ruS1+ntIe^{{sTaPY~BC> diff --git a/build/openrpc/miner.json.gz b/build/openrpc/miner.json.gz index 9c07dbc8a46b55d0273aa434a80aea54705f859f..c8c1bcfd20b3184b43300b60e53f4425869d0319 100644 GIT binary patch delta 7559 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 diff --git a/build/openrpc/worker.json.gz b/build/openrpc/worker.json.gz index 83f2c5f436cc261667fca9840862065bba4a45f2..5339558fe6449c5eed7c7fd9bf6fe11b0340494b 100644 GIT binary patch delta 22 ecmbO%GFfCoBh&d08=E^gIc`o7I_O`;zyJVlun6}6 delta 22 ecmbO%GFfCoBa{Efjm;gL99zC>t?{p7U;qGU Date: Tue, 22 Jun 2021 18:05:14 +0200 Subject: [PATCH 145/160] Update default fees for aggregates Resolves #6422 Signed-off-by: Jakub Sztandera --- node/config/def.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/node/config/def.go b/node/config/def.go index 177871cc5..a39c12556 100644 --- a/node/config/def.go +++ b/node/config/def.go @@ -333,12 +333,12 @@ func DefaultStorageMiner() *StorageMiner { MaxCommitGasFee: types.MustParseFIL("0.05"), MaxPreCommitBatchGasFee: BatchFeeConfig{ - Base: types.MustParseFIL("0.025"), // TODO: update before v1.10.0 - PerSector: types.MustParseFIL("0.025"), // TODO: update before v1.10.0 + Base: types.MustParseFIL("0"), + PerSector: types.MustParseFIL("0.02"), }, MaxCommitBatchGasFee: BatchFeeConfig{ - Base: types.MustParseFIL("0.05"), // TODO: update before v1.10.0 - PerSector: types.MustParseFIL("0.05"), // TODO: update before v1.10.0 + Base: types.MustParseFIL("0"), + PerSector: types.MustParseFIL("0.03"), // enough for 6 agg and 1nFIL base fee }, MaxTerminateGasFee: types.MustParseFIL("0.5"), From 6ca2a148199b47d9f0eb0eb40fb4bc2a712967bd Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Tue, 22 Jun 2021 19:16:36 -0400 Subject: [PATCH 146/160] Always flush when timer goes off --- extern/storage-sealing/commit_batch.go | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/extern/storage-sealing/commit_batch.go b/extern/storage-sealing/commit_batch.go index 61553601a..929d73306 100644 --- a/extern/storage-sealing/commit_batch.go +++ b/extern/storage-sealing/commit_batch.go @@ -108,7 +108,7 @@ func (b *CommitBatcher) run() { } lastMsg = nil - var sendAboveMax, sendAboveMin bool + var sendAboveMax bool select { case <-b.stop: close(b.stopped) @@ -116,13 +116,13 @@ func (b *CommitBatcher) run() { case <-b.notify: sendAboveMax = true case <-b.batchWait(cfg.CommitBatchWait, cfg.CommitBatchSlack): - sendAboveMin = true + // do nothing case fr := <-b.force: // user triggered forceRes = fr } var err error - lastMsg, err = b.maybeStartBatch(sendAboveMax, sendAboveMin) + lastMsg, err = b.maybeStartBatch(sendAboveMax) if err != nil { log.Warnw("CommitBatcher processBatch error", "error", err) } @@ -170,7 +170,7 @@ func (b *CommitBatcher) batchWait(maxWait, slack time.Duration) <-chan time.Time return time.After(wait) } -func (b *CommitBatcher) maybeStartBatch(notif, after bool) ([]sealiface.CommitBatchRes, error) { +func (b *CommitBatcher) maybeStartBatch(notif bool) ([]sealiface.CommitBatchRes, error) { b.lk.Lock() defer b.lk.Unlock() @@ -188,10 +188,6 @@ func (b *CommitBatcher) maybeStartBatch(notif, after bool) ([]sealiface.CommitBa return nil, nil } - if after && total < cfg.MinCommitBatch { - return nil, nil - } - var res []sealiface.CommitBatchRes if total < cfg.MinCommitBatch || total < miner5.MinAggregatedSectors { From 93f7cbe58717f78baa75ad556e100177bf07ae0c Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Tue, 22 Jun 2021 19:30:33 -0400 Subject: [PATCH 147/160] Add a helpful comment --- extern/storage-sealing/commit_batch.go | 1 + 1 file changed, 1 insertion(+) diff --git a/extern/storage-sealing/commit_batch.go b/extern/storage-sealing/commit_batch.go index 929d73306..3c0af1176 100644 --- a/extern/storage-sealing/commit_batch.go +++ b/extern/storage-sealing/commit_batch.go @@ -108,6 +108,7 @@ func (b *CommitBatcher) run() { } lastMsg = nil + // indicates whether we should only start a batch if we have reached or exceeded cfg.MaxCommitBatch var sendAboveMax bool select { case <-b.stop: From 2dd498297fd6cade7d8a5f4eb60892f73dee2e14 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Tue, 22 Jun 2021 19:34:03 -0400 Subject: [PATCH 148/160] Set HyperDrive upgrade epoch --- build/params_mainnet.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/params_mainnet.go b/build/params_mainnet.go index 2d708f9e4..d639cbf58 100644 --- a/build/params_mainnet.go +++ b/build/params_mainnet.go @@ -62,8 +62,8 @@ const UpgradeNorwegianHeight = 665280 // 2021-04-29T06:00:00Z const UpgradeTurboHeight = 712320 -// ??? -var UpgradeHyperdriveHeight = abi.ChainEpoch(9999999) +// 2021-06-30T22:00:00Z +var UpgradeHyperdriveHeight = abi.ChainEpoch(892800) func init() { policy.SetConsensusMinerMinPower(abi.NewStoragePower(10 << 40)) From 616e5688fc4a62f3747b1983785b2844e32ba0f2 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Wed, 23 Jun 2021 12:30:32 -0400 Subject: [PATCH 149/160] Remove MinPreCommitBatch --- extern/storage-sealing/precommit_batch.go | 12 ++++-------- extern/storage-sealing/sealiface/config.go | 1 - node/config/def.go | 2 -- node/modules/storageminer.go | 2 -- 4 files changed, 4 insertions(+), 13 deletions(-) diff --git a/extern/storage-sealing/precommit_batch.go b/extern/storage-sealing/precommit_batch.go index 9439ae14c..d1d2f5878 100644 --- a/extern/storage-sealing/precommit_batch.go +++ b/extern/storage-sealing/precommit_batch.go @@ -93,7 +93,7 @@ func (b *PreCommitBatcher) run() { } lastRes = nil - var sendAboveMax, sendAboveMin bool + var sendAboveMax bool select { case <-b.stop: close(b.stopped) @@ -101,13 +101,13 @@ func (b *PreCommitBatcher) run() { case <-b.notify: sendAboveMax = true case <-b.batchWait(cfg.PreCommitBatchWait, cfg.PreCommitBatchSlack): - sendAboveMin = true + // do nothing case fr := <-b.force: // user triggered forceRes = fr } var err error - lastRes, err = b.maybeStartBatch(sendAboveMax, sendAboveMin) + lastRes, err = b.maybeStartBatch(sendAboveMax) if err != nil { log.Warnw("PreCommitBatcher processBatch error", "error", err) } @@ -155,7 +155,7 @@ func (b *PreCommitBatcher) batchWait(maxWait, slack time.Duration) <-chan time.T return time.After(wait) } -func (b *PreCommitBatcher) maybeStartBatch(notif, after bool) ([]sealiface.PreCommitBatchRes, error) { +func (b *PreCommitBatcher) maybeStartBatch(notif bool) ([]sealiface.PreCommitBatchRes, error) { b.lk.Lock() defer b.lk.Unlock() @@ -173,10 +173,6 @@ func (b *PreCommitBatcher) maybeStartBatch(notif, after bool) ([]sealiface.PreCo return nil, nil } - if after && total < cfg.MinPreCommitBatch { - return nil, nil - } - // todo support multiple batches res, err := b.processBatch(cfg) if err != nil && len(res) == 0 { diff --git a/extern/storage-sealing/sealiface/config.go b/extern/storage-sealing/sealiface/config.go index 499a2befa..b237072d3 100644 --- a/extern/storage-sealing/sealiface/config.go +++ b/extern/storage-sealing/sealiface/config.go @@ -22,7 +22,6 @@ type Config struct { BatchPreCommits bool MaxPreCommitBatch int - MinPreCommitBatch int PreCommitBatchWait time.Duration PreCommitBatchSlack time.Duration diff --git a/node/config/def.go b/node/config/def.go index 177871cc5..d1beb843b 100644 --- a/node/config/def.go +++ b/node/config/def.go @@ -93,7 +93,6 @@ type SealingConfig struct { BatchPreCommits bool // maximum precommit batch size - batches will be sent immediately above this size MaxPreCommitBatch int - MinPreCommitBatch int // how long to wait before submitting a batch after crossing the minimum batch size PreCommitBatchWait Duration // time buffer for forceful batch submission before sectors/deal in batch would start expiring @@ -285,7 +284,6 @@ func DefaultStorageMiner() *StorageMiner { FinalizeEarly: false, BatchPreCommits: true, - MinPreCommitBatch: 1, // we must have at least one precommit to batch MaxPreCommitBatch: miner5.PreCommitSectorBatchMaxSize, // up to 256 sectors PreCommitBatchWait: Duration(24 * time.Hour), // this should be less than 31.5 hours, which is the expiration of a precommit ticket PreCommitBatchSlack: Duration(3 * time.Hour), // time buffer for forceful batch submission before sectors/deals in batch would start expiring, higher value will lower the chances for message fail due to expiration diff --git a/node/modules/storageminer.go b/node/modules/storageminer.go index b7aae0f44..3a28d5c85 100644 --- a/node/modules/storageminer.go +++ b/node/modules/storageminer.go @@ -830,7 +830,6 @@ func NewSetSealConfigFunc(r repo.LockedRepo) (dtypes.SetSealingConfigFunc, error FinalizeEarly: cfg.FinalizeEarly, BatchPreCommits: cfg.BatchPreCommits, - MinPreCommitBatch: cfg.MinPreCommitBatch, MaxPreCommitBatch: cfg.MaxPreCommitBatch, PreCommitBatchWait: config.Duration(cfg.PreCommitBatchWait), PreCommitBatchSlack: config.Duration(cfg.PreCommitBatchSlack), @@ -862,7 +861,6 @@ func NewGetSealConfigFunc(r repo.LockedRepo) (dtypes.GetSealingConfigFunc, error FinalizeEarly: cfg.Sealing.FinalizeEarly, BatchPreCommits: cfg.Sealing.BatchPreCommits, - MinPreCommitBatch: cfg.Sealing.MinPreCommitBatch, MaxPreCommitBatch: cfg.Sealing.MaxPreCommitBatch, PreCommitBatchWait: time.Duration(cfg.Sealing.PreCommitBatchWait), PreCommitBatchSlack: time.Duration(cfg.Sealing.PreCommitBatchSlack), From 9521c0b773df169ab1de3bcb4d252e8f6f40aab3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 23 Jun 2021 19:45:08 +0200 Subject: [PATCH 150/160] Add miner-side MaxDealStartDelay config --- node/builder.go | 2 ++ node/config/def.go | 3 +++ node/modules/dtypes/miner.go | 3 +++ node/modules/storageminer.go | 28 ++++++++++++++++++++++++++-- 4 files changed, 34 insertions(+), 2 deletions(-) diff --git a/node/builder.go b/node/builder.go index ce00fc18d..54e58e28a 100644 --- a/node/builder.go +++ b/node/builder.go @@ -436,6 +436,8 @@ var MinerNode = Options( Override(new(dtypes.GetSealingConfigFunc), modules.NewGetSealConfigFunc), Override(new(dtypes.SetExpectedSealDurationFunc), modules.NewSetExpectedSealDurationFunc), Override(new(dtypes.GetExpectedSealDurationFunc), modules.NewGetExpectedSealDurationFunc), + Override(new(dtypes.SetMaxDealStartDelayFunc), modules.NewSetMaxDealStartDelayFunc), + Override(new(dtypes.GetMaxDealStartDelayFunc), modules.NewGetMaxDealStartDelayFunc), ) // Online sets up basic libp2p node diff --git a/node/config/def.go b/node/config/def.go index 407eb8ad6..5c2c8de03 100644 --- a/node/config/def.go +++ b/node/config/def.go @@ -58,6 +58,8 @@ type DealmakingConfig struct { ConsiderUnverifiedStorageDeals bool PieceCidBlocklist []cid.Cid ExpectedSealDuration Duration + // Maximum amount of time proposed deal StartEpoch can be in future + MaxDealStartDelay Duration // The amount of time to wait for more deals to arrive before // publishing PublishMsgPeriod Duration @@ -320,6 +322,7 @@ func DefaultStorageMiner() *StorageMiner { ConsiderUnverifiedStorageDeals: true, PieceCidBlocklist: []cid.Cid{}, // TODO: It'd be nice to set this based on sector size + MaxDealStartDelay: Duration(time.Hour * 24 * 14), ExpectedSealDuration: Duration(time.Hour * 24), PublishMsgPeriod: Duration(time.Hour), MaxDealsPerPublishMsg: 8, diff --git a/node/modules/dtypes/miner.go b/node/modules/dtypes/miner.go index 16af48add..5c8abf3ff 100644 --- a/node/modules/dtypes/miner.go +++ b/node/modules/dtypes/miner.go @@ -88,5 +88,8 @@ type SetExpectedSealDurationFunc func(time.Duration) error // too determine how long sealing is expected to take type GetExpectedSealDurationFunc func() (time.Duration, error) +type SetMaxDealStartDelayFunc func(time.Duration) error +type GetMaxDealStartDelayFunc func() (time.Duration, error) + type StorageDealFilter func(ctx context.Context, deal storagemarket.MinerDeal) (bool, string, error) type RetrievalDealFilter func(ctx context.Context, deal retrievalmarket.ProviderDealState) (bool, string, error) diff --git a/node/modules/storageminer.go b/node/modules/storageminer.go index 3a28d5c85..715fb9a2b 100644 --- a/node/modules/storageminer.go +++ b/node/modules/storageminer.go @@ -59,7 +59,6 @@ import ( "github.com/filecoin-project/lotus/api/v1api" "github.com/filecoin-project/lotus/blockstore" "github.com/filecoin-project/lotus/build" - "github.com/filecoin-project/lotus/chain/actors/builtin" "github.com/filecoin-project/lotus/chain/actors/builtin/miner" "github.com/filecoin-project/lotus/chain/gen" "github.com/filecoin-project/lotus/chain/gen/slashfilter" @@ -486,6 +485,7 @@ func BasicDealFilter(user dtypes.StorageDealFilter) func(onlineOk dtypes.Conside unverifiedOk dtypes.ConsiderUnverifiedStorageDealsConfigFunc, blocklistFunc dtypes.StorageDealPieceCidBlocklistConfigFunc, expectedSealTimeFunc dtypes.GetExpectedSealDurationFunc, + startDelay dtypes.GetMaxDealStartDelayFunc, spn storagemarket.StorageProviderNode) dtypes.StorageDealFilter { return func(onlineOk dtypes.ConsiderOnlineStorageDealsConfigFunc, offlineOk dtypes.ConsiderOfflineStorageDealsConfigFunc, @@ -493,6 +493,7 @@ func BasicDealFilter(user dtypes.StorageDealFilter) func(onlineOk dtypes.Conside unverifiedOk dtypes.ConsiderUnverifiedStorageDealsConfigFunc, blocklistFunc dtypes.StorageDealPieceCidBlocklistConfigFunc, expectedSealTimeFunc dtypes.GetExpectedSealDurationFunc, + startDelay dtypes.GetMaxDealStartDelayFunc, spn storagemarket.StorageProviderNode) dtypes.StorageDealFilter { return func(ctx context.Context, deal storagemarket.MinerDeal) (bool, string, error) { @@ -564,9 +565,14 @@ func BasicDealFilter(user dtypes.StorageDealFilter) func(onlineOk dtypes.Conside return false, fmt.Sprintf("cannot seal a sector before %s", deal.Proposal.StartEpoch), nil } + sd, err := startDelay() + if err != nil { + return false, "miner error", err + } + // Reject if it's more than 7 days in the future // TODO: read from cfg - maxStartEpoch := earliest + abi.ChainEpoch(7*builtin.SecondsInDay/build.BlockDelaySecs) + maxStartEpoch := earliest + abi.ChainEpoch(uint64(sd.Seconds())/build.BlockDelaySecs) if deal.Proposal.StartEpoch > maxStartEpoch { return false, fmt.Sprintf("deal start epoch is too far in the future: %s > %s", deal.Proposal.StartEpoch, maxStartEpoch), nil } @@ -898,6 +904,24 @@ func NewGetExpectedSealDurationFunc(r repo.LockedRepo) (dtypes.GetExpectedSealDu }, nil } +func NewSetMaxDealStartDelayFunc(r repo.LockedRepo) (dtypes.SetMaxDealStartDelayFunc, error) { + return func(delay time.Duration) (err error) { + err = mutateCfg(r, func(cfg *config.StorageMiner) { + cfg.Dealmaking.MaxDealStartDelay = config.Duration(delay) + }) + return + }, nil +} + +func NewGetMaxDealStartDelayFunc(r repo.LockedRepo) (dtypes.GetMaxDealStartDelayFunc, error) { + return func() (out time.Duration, err error) { + err = readCfg(r, func(cfg *config.StorageMiner) { + out = time.Duration(cfg.Dealmaking.MaxDealStartDelay) + }) + return + }, nil +} + func readCfg(r repo.LockedRepo, accessor func(*config.StorageMiner)) error { raw, err := r.Config() if err != nil { From 63626e1d0a3f00a525f1e0359f6175b2624143e4 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 23 Jun 2021 15:02:23 -0700 Subject: [PATCH 151/160] feat(scripts): add the mkreleaselog script --- scripts/mkreleaselog | 253 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 253 insertions(+) create mode 100755 scripts/mkreleaselog diff --git a/scripts/mkreleaselog b/scripts/mkreleaselog new file mode 100755 index 000000000..c59027075 --- /dev/null +++ b/scripts/mkreleaselog @@ -0,0 +1,253 @@ +#!/bin/zsh +set -euo pipefail +export GO111MODULE=on +export GOPATH="$(go env GOPATH)" + +alias jq="jq --unbuffered" + +AUTHORS=( + # orgs + ipfs + ipld + libp2p + multiformats + filecoin-project + ipfs-shipyard + + # Authors of personal repos used by go-ipfs that should be mentioned in the + # release notes. + whyrusleeping + Kubuxu + jbenet + Stebalien + marten-seemann + hsanjuan + lucas-clemente + warpfork +) + +[[ -n "${REPO_FILTER+x}" ]] || REPO_FILTER="github.com/(${$(printf "|%s" "${AUTHORS[@]}"):1})" + +[[ -n "${IGNORED_FILES+x}" ]] || IGNORED_FILES='^\(\.gx\|package\.json\|\.travis\.yml\|go.mod\|go\.sum|\.github|\.circleci\)$' + +NL=$'\n' + +ROOT_DIR="$(git rev-parse --show-toplevel)" + +msg() { + echo "$*" >&2 +} + +statlog() { + local module="$1" + local rpath="$GOPATH/src/$(strip_version "$module")" + local start="${2:-}" + local end="${3:-HEAD}" + local mailmap_file="$rpath/.mailmap" + if ! [[ -e "$mailmap_file" ]]; then + mailmap_file="$ROOT_DIR/.mailmap" + fi + + local stack=() + git -C "$rpath" -c mailmap.file="$mailmap_file" log --use-mailmap --shortstat --no-merges --pretty="tformat:%H%x09%aN%x09%aE" "$start..$end" | while read -r line; do + if [[ -n "$line" ]]; then + stack+=("$line") + continue + fi + + read -r changes + + changed=0 + insertions=0 + deletions=0 + while read count event; do + if [[ "$event" =~ ^file ]]; then + changed=$count + elif [[ "$event" =~ ^insertion ]]; then + insertions=$count + elif [[ "$event" =~ ^deletion ]]; then + deletions=$count + else + echo "unknown event $event" >&2 + exit 1 + fi + done<<<"${changes//,/$NL}" + + for author in "${stack[@]}"; do + IFS=$'\t' read -r hash name email <<<"$author" + jq -n \ + --arg "hash" "$hash" \ + --arg "name" "$name" \ + --arg "email" "$email" \ + --argjson "changed" "$changed" \ + --argjson "insertions" "$insertions" \ + --argjson "deletions" "$deletions" \ + '{Commit: $hash, Author: $name, Email: $email, Files: $changed, Insertions: $insertions, Deletions: $deletions}' + done + stack=() + done +} + +# Returns a stream of deps changed between $1 and $2. +dep_changes() { + { + <"$1" + <"$2" + } | jq -s 'JOIN(INDEX(.[0][]; .Path); .[1][]; .Path; {Path: .[0].Path, Old: (.[1] | del(.Path)), New: (.[0] | del(.Path))}) | select(.New.Version != .Old.Version)' +} + +# resolve_commits resolves a git ref for each version. +resolve_commits() { + jq '. + {Ref: (.Version|capture("^((?.*)\\+incompatible|v.*-(0\\.)?[0-9]{14}-(?[a-f0-9]{12})|(?v.*))$") | .ref1 // .ref2 // .ref3)}' +} + +pr_link() { + local repo="$1" + local prnum="$2" + local ghname="${repo##github.com/}" + printf -- "[%s#%s](https://%s/pull/%s)" "$ghname" "$prnum" "$repo" "$prnum" +} + +# Generate a release log for a range of commits in a single repo. +release_log() { + setopt local_options BASH_REMATCH + + local module="$1" + local start="$2" + local end="${3:-HEAD}" + local repo="$(strip_version "$1")" + local dir="$GOPATH/src/$repo" + + local commit pr + git -C "$dir" log \ + --format='tformat:%H %s' \ + --first-parent \ + "$start..$end" | + while read commit subject; do + # Skip gx-only PRs. + git -C "$dir" diff-tree --no-commit-id --name-only "$commit^" "$commit" | + grep -v "${IGNORED_FILES}" >/dev/null || continue + + if [[ "$subject" =~ '^Merge pull request #([0-9]+) from' ]]; then + local prnum="${BASH_REMATCH[2]}" + local desc="$(git -C "$dir" show --summary --format='tformat:%b' "$commit" | head -1)" + printf -- "- %s (%s)\n" "$desc" "$(pr_link "$repo" "$prnum")" + elif [[ "$subject" =~ '\(#([0-9]+)\)$' ]]; then + local prnum="${BASH_REMATCH[2]}" + printf -- "- %s (%s)\n" "$subject" "$(pr_link "$repo" "$prnum")" + else + printf -- "- %s\n" "$subject" + fi + done +} + +indent() { + sed -e 's/^/ /' +} + +mod_deps() { + go list -mod=mod -json -m all | jq 'select(.Version != null)' +} + +ensure() { + local repo="$(strip_version "$1")" + local commit="$2" + local rpath="$GOPATH/src/$repo" + if [[ ! -d "$rpath" ]]; then + msg "Cloning $repo..." + git clone "http://$repo" "$rpath" >&2 + fi + + if ! git -C "$rpath" rev-parse --verify "$commit" >/dev/null; then + msg "Fetching $repo..." + git -C "$rpath" fetch --all >&2 + fi + + git -C "$rpath" rev-parse --verify "$commit" >/dev/null || return 1 +} + +statsummary() { + jq -s 'group_by(.Author)[] | {Author: .[0].Author, Commits: (. | length), Insertions: (map(.Insertions) | add), Deletions: (map(.Deletions) | add), Files: (map(.Files) | add)}' | + jq '. + {Lines: (.Deletions + .Insertions)}' +} + +strip_version() { + local repo="$1" + if [[ "$repo" =~ '.*/v[0-9]+$' ]]; then + repo="$(dirname "$repo")" + fi + echo "$repo" +} + +recursive_release_log() { + local start="${1:-$(git tag -l | sort -V | grep -v -- '-rc' | grep 'v'| tail -n1)}" + local end="${2:-$(git rev-parse HEAD)}" + local repo_root="$(git rev-parse --show-toplevel)" + local module="$(go list -m)" + local dir="$(go list -m -f '{{.Dir}}')" + + if [[ "${GOPATH}/${module}" -ef "${dir}" ]]; then + echo "This script requires the target module and all dependencies to live in a GOPATH." + return 1 + fi + + ( + local result=0 + local workspace="$(mktemp -d)" + trap "$(printf 'rm -rf "%q"' "$workspace")" INT TERM EXIT + cd "$workspace" + + mkdir extern + ln -s "$repo_root"/extern/filecoin-ffi extern/filecoin-ffi + ln -s "$repo_root"/extern/test-vectors extern/test-vectors + + echo "Computing old deps..." >&2 + git -C "$repo_root" show "$start:go.mod" >go.mod + mod_deps | resolve_commits | jq -s > old_deps.json + + echo "Computing new deps..." >&2 + git -C "$repo_root" show "$end:go.mod" >go.mod + mod_deps | resolve_commits | jq -s > new_deps.json + + rm -f go.mod go.sum + + printf -- "Generating Changelog for %s %s..%s\n" "$module" "$start" "$end" >&2 + + printf -- "- %s:\n" "$module" + release_log "$module" "$start" "$end" | indent + + + statlog "$module" "$start" "$end" > statlog.json + + dep_changes old_deps.json new_deps.json | + jq --arg filter "$REPO_FILTER" 'select(.Path | match($filter))' | + # Compute changelogs + jq -r '"\(.Path) \(.New.Version) \(.New.Ref) \(.Old.Version) \(.Old.Ref // "")"' | + while read module new new_ref old old_ref; do + if ! ensure "$module" "$new_ref"; then + result=1 + local changelog="failed to fetch repo" + else + statlog "$module" "$old_ref" "$new_ref" >> statlog.json + local changelog="$(release_log "$module" "$old_ref" "$new_ref")" + fi + if [[ -n "$changelog" ]]; then + printf -- "- %s (%s -> %s):\n" "$module" "$old" "$new" + echo "$changelog" | indent + fi + done + + echo + echo "Contributors" + echo + + echo "| Contributor | Commits | Lines ± | Files Changed |" + echo "|-------------|---------|---------|---------------|" + statsummary Date: Wed, 23 Jun 2021 18:51:20 -0400 Subject: [PATCH 152/160] pull actor v5.0.1 --- go.mod | 2 +- go.sum | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index d3ea7c57e..a29a108b0 100644 --- a/go.mod +++ b/go.mod @@ -47,7 +47,7 @@ require ( github.com/filecoin-project/specs-actors/v2 v2.3.5-0.20210114162132-5b58b773f4fb github.com/filecoin-project/specs-actors/v3 v3.1.0 github.com/filecoin-project/specs-actors/v4 v4.0.0 - github.com/filecoin-project/specs-actors/v5 v5.0.0-20210609212542-73e0409ac77c + github.com/filecoin-project/specs-actors/v5 v5.0.1 github.com/filecoin-project/specs-storage v0.1.1-0.20201105051918-5188d9774506 github.com/filecoin-project/test-vectors/schema v0.0.5 github.com/gbrlsnchs/jwt/v3 v3.0.0-beta.1 diff --git a/go.sum b/go.sum index 08d6a0689..89af9d973 100644 --- a/go.sum +++ b/go.sum @@ -318,8 +318,8 @@ github.com/filecoin-project/specs-actors/v3 v3.1.0/go.mod h1:mpynccOLlIRy0QnR008 github.com/filecoin-project/specs-actors/v4 v4.0.0 h1:vMALksY5G3J5rj3q9rbcyB+f4Tk1xrLqSgdB3jOok4s= github.com/filecoin-project/specs-actors/v4 v4.0.0/go.mod h1:TkHXf/l7Wyw4ZejyXIPS2rK8bBO0rdwhTZyQQgaglng= github.com/filecoin-project/specs-actors/v5 v5.0.0-20210512015452-4fe3889fff57/go.mod h1:283yBMMUSDB2abcjP/hhrwTkhb9h3sfM6KGrep/ZlBI= -github.com/filecoin-project/specs-actors/v5 v5.0.0-20210609212542-73e0409ac77c h1:GnDJ6q3QEm2ytTKjPFQSvczAltgCSb3j9F1FeynwvPA= -github.com/filecoin-project/specs-actors/v5 v5.0.0-20210609212542-73e0409ac77c/go.mod h1:b/btpRl84Q9SeDKlyIoORBQwe2OTmq14POrYrVvBWCM= +github.com/filecoin-project/specs-actors/v5 v5.0.1 h1:PrYm5AKdMlJ/55eRW5laWcnaX66gyyDYBWvH38kNAMo= +github.com/filecoin-project/specs-actors/v5 v5.0.1/go.mod h1:74euMDIXorusOBs/QL/LNkYsXZdDpLJwojWw6T03pdE= github.com/filecoin-project/specs-storage v0.1.1-0.20201105051918-5188d9774506 h1:Ur/l2+6qN+lQiqjozWWc5p9UDaAMDZKTlDS98oRnlIw= github.com/filecoin-project/specs-storage v0.1.1-0.20201105051918-5188d9774506/go.mod h1:nJRRM7Aa9XVvygr3W9k6xGF46RWzr2zxF/iGoAIfA/g= github.com/filecoin-project/test-vectors/schema v0.0.5 h1:w3zHQhzM4pYxJDl21avXjOKBLF8egrvwUwjpT8TquDg= @@ -684,6 +684,7 @@ github.com/ipfs/go-log/v2 v2.1.3/go.mod h1:/8d0SH3Su5Ooc31QlL1WysJhvyOTDCjcCZ9Ax github.com/ipfs/go-merkledag v0.0.3/go.mod h1:Oc5kIXLHokkE1hWGMBHw+oxehkAaTOqtEb7Zbh6BhLA= github.com/ipfs/go-merkledag v0.0.6/go.mod h1:QYPdnlvkOg7GnQRofu9XZimC5ZW5Wi3bKys/4GQQfto= github.com/ipfs/go-merkledag v0.2.3/go.mod h1:SQiXrtSts3KGNmgOzMICy5c0POOpUNQLvB3ClKnBAlk= +github.com/ipfs/go-merkledag v0.2.4/go.mod h1:SQiXrtSts3KGNmgOzMICy5c0POOpUNQLvB3ClKnBAlk= github.com/ipfs/go-merkledag v0.3.1/go.mod h1:fvkZNNZixVW6cKSZ/JfLlON5OlgTXNdRLz0p6QG/I2M= github.com/ipfs/go-merkledag v0.3.2 h1:MRqj40QkrWkvPswXs4EfSslhZ4RVPRbxwX11js0t1xY= github.com/ipfs/go-merkledag v0.3.2/go.mod h1:fvkZNNZixVW6cKSZ/JfLlON5OlgTXNdRLz0p6QG/I2M= @@ -702,6 +703,7 @@ github.com/ipfs/go-peertaskqueue v0.2.0/go.mod h1:5/eNrBEbtSKWCG+kQK8K8fGNixoYUn github.com/ipfs/go-todocounter v0.0.1/go.mod h1:l5aErvQc8qKE2r7NDMjmq5UNAvuZy0rC8BHOplkWvZ4= github.com/ipfs/go-unixfs v0.0.4/go.mod h1:eIo/p9ADu/MFOuyxzwU+Th8D6xoxU//r590vUpWyfz8= github.com/ipfs/go-unixfs v0.2.1/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 h1:6NwppOXefWIyysZ4LR/qUBPvXd5//8J3jiMdvpbw6Lo= github.com/ipfs/go-unixfs v0.2.4/go.mod h1:SUdisfUjNoSDzzhGVxvCL9QO/nKdwXdr+gbMUdqcbYw= github.com/ipfs/go-verifcid v0.0.1 h1:m2HI7zIuR5TFyQ1b79Da5N9dnnCP1vcu2QqawmWlK2E= @@ -712,13 +714,16 @@ github.com/ipfs/iptb v1.4.0 h1:YFYTrCkLMRwk/35IMyC6+yjoQSHTEcNcefBStLJzgvo= github.com/ipfs/iptb v1.4.0/go.mod h1:1rzHpCYtNp87/+hTxG5TfCVn/yMY3dKnLn8tBiMfdmg= github.com/ipfs/iptb-plugins v0.2.1 h1:au4HWn9/pRPbkxA08pDx2oRAs4cnbgQWgV0teYXuuGA= github.com/ipfs/iptb-plugins v0.2.1/go.mod h1:QXMbtIWZ+jRsW8a4h13qAKU7jcM7qaittO8wOsTP0Rs= +github.com/ipld/go-car v0.1.0/go.mod h1:RCWzaUh2i4mOEkB3W45Vc+9jnS/M6Qay5ooytiBHl3g= github.com/ipld/go-car v0.1.1-0.20200923150018-8cdef32e2da4/go.mod h1:xrMEcuSq+D1vEwl+YAXsg/JfA98XGpXDwnkIL4Aimqw= github.com/ipld/go-car v0.1.1-0.20201119040415-11b6074b6d4d h1:iphSzTuPqyDgH7WUVZsdqUnQNzYgIblsVr1zhVNA33U= github.com/ipld/go-car v0.1.1-0.20201119040415-11b6074b6d4d/go.mod h1:2Gys8L8MJ6zkh1gktTSXreY63t4UbyvNp5JaudTyxHQ= +github.com/ipld/go-ipld-prime v0.0.2-0.20191108012745-28a82f04c785/go.mod h1:bDDSvVz7vaK12FNvMeRYnpRFkSUPNQOiCYQezMD/P3w= github.com/ipld/go-ipld-prime v0.0.2-0.20200428162820-8b59dc292b8e/go.mod h1:uVIwe/u0H4VdKv3kaN1ck7uCb6yD9cFLS9/ELyXbsw8= github.com/ipld/go-ipld-prime v0.5.1-0.20200828233916-988837377a7f/go.mod h1:0xEgdD6MKbZ1vF0GC+YcR/C4SQCAlRuOjIJ2i0HxqzM= github.com/ipld/go-ipld-prime v0.5.1-0.20201021195245-109253e8a018 h1:RbRHv8epkmvBYA5cGfz68GUSbOgx5j/7ObLIl4Rsif0= github.com/ipld/go-ipld-prime v0.5.1-0.20201021195245-109253e8a018/go.mod h1:0xEgdD6MKbZ1vF0GC+YcR/C4SQCAlRuOjIJ2i0HxqzM= +github.com/ipld/go-ipld-prime-proto v0.0.0-20191113031812-e32bd156a1e5/go.mod h1:gcvzoEDBjwycpXt3LBE061wT9f46szXGHAmj9uoP6fU= github.com/ipld/go-ipld-prime-proto v0.0.0-20200428191222-c1ffdadc01e1/go.mod h1:OAV6xBmuTLsPZ+epzKkPB1e25FHk/vCtyatkdHcArLs= github.com/ipld/go-ipld-prime-proto v0.0.0-20200922192210-9a2bfd4440a6/go.mod h1:3pHYooM9Ea65jewRwrb2u5uHZCNkNTe9ABsVB+SrkH0= github.com/ipld/go-ipld-prime-proto v0.1.0 h1:j7gjqrfwbT4+gXpHwEx5iMssma3mnctC7YaCimsFP70= From a290982ca03e912db25ce5bc75ed5ef629624ddf Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Wed, 23 Jun 2021 16:57:34 -0400 Subject: [PATCH 153/160] Lotus version 1.10.0 --- CHANGELOG.md | 34 ++++++++++++++++++++++++++++------ build/openrpc/full.json.gz | Bin 22485 -> 22482 bytes build/openrpc/miner.json.gz | Bin 8090 -> 8086 bytes build/openrpc/worker.json.gz | Bin 2579 -> 2578 bytes build/version.go | 2 +- 5 files changed, 29 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a85726f3..b2db58fec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,10 @@ # Lotus changelog -# 1.10.0-rc6 / 2021-06-18 +# 1.10.0 / 2021-06-23 -> Note: If you are running a lotus miner, check out the doc [here](https://docs.filecoin.io/mine/lotus/miner-configuration/#precommitsectorsbatch) for new lotus miner configurations explanations of the new features! +> Note: If you are running a Lotus miner, check out the documentation[here](https://docs.filecoin.io/mine/lotus/miner-configuration/#precommitsectorsbatch) for details of the new Lotus miner config options and explanations of the new features. -This is the 6th release candidate for Lotus v1.10.0, an upcoming mandatory release of Lotus that will introduce Filecoin network v13. Use this release for syncing with the [reset calibration net](https://github.com/filecoin-project/community/discussions/74#discussioncomment-885580). In addition, included in the new network version are the following FIPs: +This is a mandatory release of Lotus that introduces Filecoin network v13, codenamed the HyperDrive upgrade. The Filecoin mainnet will upgrade at epoch 892800, which is 2021-06-30T22:00:00Z. The network upgrade introduces the following FIPs: - [FIP-0008](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0008.md): Add miner batched sector pre-commit method - [FIP-0011](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0011.md): Remove reward auction from reporting consensus faults @@ -12,11 +12,33 @@ This is the 6th release candidate for Lotus v1.10.0, an upcoming mandatory relea - [FIP-0013](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0013.md): Add ProveCommitSectorAggregated method to reduce on-chain congestion - [FIP-0015](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0015.md): Revert FIP-0009(Exempt Window PoSts from BaseFee burn) -This release candidate does not set the upgrade epochs for mainnet, but does set the upgrade epoch for the calibration network to 321519. - Note that this release is built on top of Lotus v1.9.0. Enterprising users can use the `master` branch of Lotus to get the latest functionality, including all changes in this release candidate. -A detailed changelog will be included in a later release candidate. +## Proof batching and aggregation + +FIPs [0008](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0008.md) and [0013](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0013.md) combine to allow for a significant increase in the rate of onboarding storage on the Filecoin network. It is hoped that this will lead to more useful data being stored on the network, reduced network congestion, and lower network base fee. + +### Projected state tree growth + +As a result of the accelerated onboarding of storage onto the network, it is possible + +### Hardware requirements and suggestions + +As the size of a single state tree grows, so will the size of the minimal datastore needed to sync the blockchain. The Filecoin protocol requires any node validating the chain to have the last 1800 state trees (2 * `ChainFinality`). Depending on how fast storage providers seal new sectors, this could get as large as AMOUNT in 6 months, and grow as fast as 20 GB per day. This increases the necessary hardware requirements to validate the Filecoin blockchain, but can be supported by images such as [Amazon EC2](https://aws.amazon.com/ec2/instance-explorer/?ec2-instances-cards.sort-by=item.additionalFields.category-order&ec2-instances-cards.sort-order=asc&awsf.ec2-instances-filter-category=*all&awsf.ec2-instances-filter-processors=*all&awsf.ec2-instances-filter-accelerators=*all&awsf.ec2-instances-filter-capabilities=additional-capabilities%23instance-storage&ec2-instances-cards.q=ssd&ec2-instances-cards.q_operator=AND&awsm.page-ec2-instances-cards=1) and [Google Cloud Platform](https://cloud.google.com/compute/docs/machine-types). + +### Future improvements + +Various Lotus improvements are planned moving forward to mitigate the effects of the growing state tree size. The primary improvement is the [Lotus splitstore](LINK TO DISCUSSION), which will soon be enabled by default. The feature allows for [online garbage collection](https://github.com/filecoin-project/lotus/issues/6577) for nodes that do not seek to maintain full chain and state history, thus eliminating the need for users to delete their datastores and sync from snapshots. + +Other improvements including better compressed snapshots, faster premigrations, and improved chain exports. + +## WindowPost base fee burn + +Included in the HyperDrive upgrade is [FIP-0015](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0015.md) which eliminates the special-case gas treatment of `SubmitWindowedPoSt` messages that was introduced in [FIP-0009](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0009.md). Although `SubmitWindowedPoSt` messages will be relatively cheap, thanks to the introduction of optimistic acceptance of these proofs in [FIP-0010](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0010.md), storage providers should pay attention to their `MaxWindowPoStGasFee` config option: too low and PoSts may not land on chain; too high and they may cost an exorbitant amount! + +## Changelog + +TODO # 1.9.0 / 2021-05-17 diff --git a/build/openrpc/full.json.gz b/build/openrpc/full.json.gz index 5a002a26f20c4ea835f624f5545c42d8271703ef..fe80856db35425e416b8d25ce4b9c582d08d2f83 100644 GIT binary patch delta 22358 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 Hp#%O0Br!)H delta 22357 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 C6HI~t diff --git a/build/openrpc/miner.json.gz b/build/openrpc/miner.json.gz index c8c1bcfd20b3184b43300b60e53f4425869d0319..989f3b020ec566c763c89dc02d2d7f6a9dae0c78 100644 GIT binary patch delta 8022 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 Yi4*hb>E`MG2LJ&7|GwgoBf5+M0Cs7KF8}}l delta 8026 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 p0uVE2EfR@ Date: Wed, 23 Jun 2021 20:54:52 -0400 Subject: [PATCH 154/160] add changelog --- CHANGELOG.md | 245 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 165 insertions(+), 80 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b2db58fec..8c6c2ab54 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,7 @@ # 1.10.0 / 2021-06-23 -> Note: If you are running a Lotus miner, check out the documentation[here](https://docs.filecoin.io/mine/lotus/miner-configuration/#precommitsectorsbatch) for details of the new Lotus miner config options and explanations of the new features. - -This is a mandatory release of Lotus that introduces Filecoin network v13, codenamed the HyperDrive upgrade. The Filecoin mainnet will upgrade at epoch 892800, which is 2021-06-30T22:00:00Z. The network upgrade introduces the following FIPs: +This is a mandatory release of Lotus that introduces Filecoin network v13, codenamed the HyperDrive upgrade. The Filecoin mainnet will upgrade, which is 2021-06-30T22:00:00Z. The network upgrade introduces the following FIPs: - [FIP-0008](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0008.md): Add miner batched sector pre-commit method - [FIP-0011](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0011.md): Remove reward auction from reporting consensus faults @@ -18,9 +16,16 @@ Note that this release is built on top of Lotus v1.9.0. Enterprising users can u FIPs [0008](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0008.md) and [0013](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0013.md) combine to allow for a significant increase in the rate of onboarding storage on the Filecoin network. It is hoped that this will lead to more useful data being stored on the network, reduced network congestion, and lower network base fee. +**Check out the documentation [here](https://docs.filecoin.io/mine/lotus/miner-configuration/#precommitsectorsbatch) for details of the new Lotus miner sealing config options, [here](https://docs.filecoin.io/mine/lotus/miner-configuration/#fees-section) for fee config options, and explanations of the new features.** + +Note: + - We recommend to keep `PreCommitSectorsBatch` as 1. + - We recommend miners to set `PreCommitBatchWait` lower than 30 hours. + - We recommend miners to set a longer `CommitBatchSlack` and `PreCommitBatchSlac` to prevent message failures due to expirations. + ### Projected state tree growth -As a result of the accelerated onboarding of storage onto the network, it is possible +As a result of the accelerated onboardiPreCommitSectorsBatcng of storage onto the network, it is possible ### Hardware requirements and suggestions @@ -28,9 +33,9 @@ As the size of a single state tree grows, so will the size of the minimal datast ### Future improvements -Various Lotus improvements are planned moving forward to mitigate the effects of the growing state tree size. The primary improvement is the [Lotus splitstore](LINK TO DISCUSSION), which will soon be enabled by default. The feature allows for [online garbage collection](https://github.com/filecoin-project/lotus/issues/6577) for nodes that do not seek to maintain full chain and state history, thus eliminating the need for users to delete their datastores and sync from snapshots. +Various Lotus improvements are planned moving forward to mitigate the effects of the growing state tree size. The primary improvement is the [Lotus splitstore](https://github.com/filecoin-project/lotus/discussions/5788), which will soon be enabled by default. The feature allows for [online garbage collection](https://github.com/filecoin-project/lotus/issues/6577) for nodes that do not seek to maintain full chain and state history, thus eliminating the need for users to delete their datastores and sync from snapshots. -Other improvements including better compressed snapshots, faster premigrations, and improved chain exports. +Other improvements including better compressed snapshots, faster pre-migrations, and improved chain exports are in the roadmap. ## WindowPost base fee burn @@ -38,7 +43,87 @@ Included in the HyperDrive upgrade is [FIP-0015](https://github.com/filecoin-pro ## Changelog -TODO +### New Features + +- Implement FIP-0015 ([filecoin-project/lotus#6361](https://github.com/filecoin-project/lotus/pull/6361)) +- Integrate FIP0013 and FIP0008 ([filecoin-project/lotus#6235](https://github.com/filecoin-project/lotus/pull/6235)) + - [Configuration docs and cli examples](https://docs.filecoin.io/mine/lotus/miner-configuration/#precommitsectorsbatch) + - [cli docs](https://github.com/filecoin-project/lotus/blob/master/documentation/en/cli-lotus-miner.md#lotus-miner-sectors-batching) + - Introduce gas prices for aggregate verifications ([filecoin-project/lotus#6347](https://github.com/filecoin-project/lotus/pull/6347)) +- Introduce v5 actors ([filecoin-project/lotus#6195](https://github.com/filecoin-project/lotus/pull/6195)) +- Robustify commit batcher ([filecoin-project/lotus#6367](https://github.com/filecoin-project/lotus/pull/6367)) +- Always flush when timer goes off ([filecoin-project/lotus#6563](https://github.com/filecoin-project/lotus/pull/6563)) +- Update default fees for aggregates ([filecoin-project/lotus#6548](https://github.com/filecoin-project/lotus/pull/6548)) +- sealing: Early finalization option ([filecoin-project/lotus#6452](https://github.com/filecoin-project/lotus/pull/6452)) + - `./lotus-miner/config.toml/[Sealing.FinalizeEarly]`: default to false. Enable if you want to FinalizeSector before commiting +- Add filplus utils to CLI ([filecoin-project/lotus#6351](https://github.com/filecoin-project/lotus/pull/6351)) + - cli doc can be found [here](https://github.com/filecoin-project/lotus/blob/master/documentation/en/cli-lotus.md#lotus-filplus) +- Add miner-side MaxDealStartDelay config ([filecoin-project/lotus#6576](https://github.com/filecoin-project/lotus/pull/6576)) + + +### Bug Fixes +- chainstore: Don't take heaviestLk with backlogged reorgCh ([filecoin-project/lotus#6526](https://github.com/filecoin-project/lotus/pull/6526)) +- Backport #6041 - storagefsm: Fix batch deal packing behavior ([filecoin-project/lotus#6519](https://github.com/filecoin-project/lotus/pull/6519)) +- backport: pick the correct partitions-per-post limit ([filecoin-project/lotus#6503](https://github.com/filecoin-project/lotus/pull/6503)) +- failed sectors should be added into res correctly ([filecoin-project/lotus#6472](https://github.com/filecoin-project/lotus/pull/6472)) +- sealing: Fix restartSectors race ([filecoin-project/lotus#6491](https://github.com/filecoin-project/lotus/pull/6491)) +- Fund miners with the aggregate fee when ProveCommitting ([filecoin-project/lotus#6428](https://github.com/filecoin-project/lotus/pull/6428)) +- Commit and Precommit batcher cannot share a getSectorDeadline method ([filecoin-project/lotus#6416](https://github.com/filecoin-project/lotus/pull/6416)) +- Fix supported proof type manipulations for v5 actors ([filecoin-project/lotus#6366](https://github.com/filecoin-project/lotus/pull/6366)) +- events: Fix handling of multiple matched events per epoch ([filecoin-project/lotus#6362](https://github.com/filecoin-project/lotus/pull/6362)) +- Fix randomness fetching around null blocks ([filecoin-project/lotus#6240](https://github.com/filecoin-project/lotus/pull/6240)) + +### Improvements +- Appimage v1.10.0 rc3 ([filecoin-project/lotus#6492](https://github.com/filecoin-project/lotus/pull/6492)) +- Expand on Drand change testing ([filecoin-project/lotus#6500](https://github.com/filecoin-project/lotus/pull/6500)) +- Backport Fix logging around mineOne ([filecoin-project/lotus#6499](https://github.com/filecoin-project/lotus/pull/6499)) +- mpool: Add more metrics ([filecoin-project/lotus#6453](https://github.com/filecoin-project/lotus/pull/6453)) +- Merge backported PRs into v1.10 release branch ([filecoin-project/lotus#6436](https://github.com/filecoin-project/lotus/pull/6436)) +- Fix tests ([filecoin-project/lotus#6371](https://github.com/filecoin-project/lotus/pull/6371)) +- Extend the default deal start epoch delay ([filecoin-project/lotus#6350](https://github.com/filecoin-project/lotus/pull/6350)) +- sealing: Wire up context to batchers ([filecoin-project/lotus#6497](https://github.com/filecoin-project/lotus/pull/6497)) +- Improve address resolution for messages ([filecoin-project/lotus#6364](https://github.com/filecoin-project/lotus/pull/6364)) + +### Dependency Updates +- Proofs v8.0.2 ([filecoin-project/lotus#6524](https://github.com/filecoin-project/lotus/pull/6524)) +- Update to fixed Bellperson ([filecoin-project/lotus#6480](https://github.com/filecoin-project/lotus/pull/6480)) +- Update to go-praamfetch with fslocks ([filecoin-project/lotus#6473](https://github.com/filecoin-project/lotus/pull/6473)) +- Update ffi with fixed multicore sdr support ([filecoin-project/lotus#6471](https://github.com/filecoin-project/lotus/pull/6471)) +- github.com/filecoin-project/go-paramfetch (v0.0.2-0.20200701152213-3e0f0afdc261 -> v0.0.2-0.20210614165157-25a6c7769498) +- github.com/filecoin-project/specs-actors/v5 (v5.0.0-20210512015452-4fe3889fff57 -> v5.0.0) +- github.com/filecoin-project/go-hamt-ipld/v3 (v3.0.1 -> v3.1.0) +- github.com/ipfs/go-log/v2 (v2.1.2-0.20200626104915-0016c0b4b3e4 -> v2.1.3) +- github.com/filecoin-project/go-amt-ipld/v3 (v3.0.0 -> v3.1.0) + +### Network Version v13 HyperDrive Upgrade +- Set HyperDrive upgrade epoch ([filecoin-project/lotus#6565](https://github.com/filecoin-project/lotus/pull/6565)) +- version bump to lotus v1.10.0-rc6 ([filecoin-project/lotus#6529](https://github.com/filecoin-project/lotus/pull/6529)) +- Upgrade epochs for calibration reset ([filecoin-project/lotus#6528](https://github.com/filecoin-project/lotus/pull/6528)) +- Lotus version 1.10.0-rc5 ([filecoin-project/lotus#6504](https://github.com/filecoin-project/lotus/pull/6504)) +- Merge releases into v1.10 release ([filecoin-project/lotus#6494](https://github.com/filecoin-project/lotus/pull/6494)) +- update lotus to v1.10.0-rc3 ([filecoin-project/lotus#6481](https://github.com/filecoin-project/lotus/pull/6481)) +- updated configuration comments for docs +- Lotus version 1.10.0-rc2 ([filecoin-project/lotus#6443](https://github.com/filecoin-project/lotus/pull/6443)) +- Set ntwk v13 HyperDrive Calibration upgrade epoch ([filecoin-project/lotus#6442](https://github.com/filecoin-project/lotus/pull/6442)) + + +## Contributors + +💙Thank you to all the contributors! + +| Contributor | Commits | Lines ± | Files Changed | +|-------------|---------|---------|---------------| +| Łukasz Magiera | 75 | +9088/-1452 | 348 | +| Aayush Rajasekaran | 38 | +6525/-651 | 184 | +| Jennifer Wang | 10 | +575/-104 | 21 | +| Steven Allen | 2 | +272/-8 | 6 | +| vyzo | 6 | +155/-66 | 13 | +| Cory Schwartz | 10 | +171/-27 | 14 | +| Jakub Sztandera | 4 | +177/-13 | 7 | +| Peter Rabbitson | 4 | +65/-42 | 5 | +| Travis Person | 2 | +11/-11 | 4 | +| wangchao | 2 | +3/-2 | 2 | + # 1.9.0 / 2021-05-17 @@ -267,7 +352,7 @@ This release also expands the `lotus-miner sectors extend` CLI, with a new optio - The `expiration-cutoff` flag can be passed to skip sectors whose expiration is past a certain point from the current head. It defaults to infinity (no cutoff), but if, say, 28800 was specified, then only sectors expiring in the next 10 days would be extended (2880 epochs in 1 day). -## Changes +## Changes - Util for miners to extend all v1 sectors (https://github.com/filecoin-project/lotus/pull/5924) - Upgrade the butterfly network (https://github.com/filecoin-project/lotus/pull/5929) @@ -278,7 +363,7 @@ This release also expands the `lotus-miner sectors extend` CLI, with a new optio This is a patch release of Lotus that introduces small fixes to the Storage FSM. -## Changes +## Changes - storagefsm: Fix double unlock with ready WaitDeals sectors (https://github.com/filecoin-project/lotus/pull/5783) - backupds: Allow larger values in write log (https://github.com/filecoin-project/lotus/pull/5776) @@ -294,7 +379,7 @@ This is an hotfix release of Lotus that fixes a critical bug introduced in v1.5. # 1.5.1 / 2021-03-10 -This is an optional release of Lotus that introduces an important fix to the WindowPoSt computation process. The change is to wait for some confidence before drawing beacon randomness for the proof. Without this, invalid proofs might be generated as the result of a null tipset. +This is an optional release of Lotus that introduces an important fix to the WindowPoSt computation process. The change is to wait for some confidence before drawing beacon randomness for the proof. Without this, invalid proofs might be generated as the result of a null tipset. ## Splitstore @@ -387,7 +472,7 @@ FIP-0010 introduces the ability to dispute bad Window PoSts. Node operators are ## Changes - [#5341](https://github.com/filecoin-project/lotus/pull/5341) Add a `LOTUS_DISABLE_V3_ACTOR_MIGRATION` envvar - - Setting this envvar to 1 disables the v3 actor migration, should only be used in the event of a failed migration + - Setting this envvar to 1 disables the v3 actor migration, should only be used in the event of a failed migration # 1.4.2 / 2021-02-17 @@ -395,14 +480,14 @@ This is a large, and highly recommended, optional release with new features and - [FIP-0007 h/amt-v3](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0007.md) which improves the performance of the Filecoin HAMT and AMT. - [FIP-0010 off-chain Window PoSt Verification](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0010.md) which reduces the gas consumption of `SubmitWindowedPoSt` messages significantly by optimistically accepting Window PoSt proofs without verification, and allowing them to be disputed later by off-chain verifiers. - + Note that this release does NOT set an upgrade epoch for v3 actors to take effect. That will be done in the upcoming 1.5.0 release. - - ## New Features - - - [#5341](https://github.com/filecoin-project/lotus/pull/5341) Added sector termination API and CLI - - Run `lotus-miner sectors terminate` -- [#5342](https://github.com/filecoin-project/lotus/pull/5342) Added CLI for using a multisig wallet as miner's owner address + +## New Features + +- [#5341](https://github.com/filecoin-project/lotus/pull/5341) Added sector termination API and CLI + - Run `lotus-miner sectors terminate` +- [#5342](https://github.com/filecoin-project/lotus/pull/5342) Added CLI for using a multisig wallet as miner's owner address - See how to set it up [here](https://github.com/filecoin-project/lotus/pull/5342#issue-554009129) - [#5363](https://github.com/filecoin-project/lotus/pull/5363), [#5418](https://github.com/filecoin-project/lotus/pull/), [#5476](https://github.com/filecoin-project/lotus/pull/5476), [#5459](https://github.com/filecoin-project/lotus/pull/5459) Integrated [spec-actor v3](https://github.com/filecoin-pro5418ject/specs-actors/releases/tag/v3.0.0) - [#5472](https://github.com/filecoin-project/lotus/pull/5472) Generate actor v3 methods for pond @@ -413,7 +498,7 @@ Note that this release does NOT set an upgrade epoch for v3 actors to take effec - [#5411](https://github.com/filecoin-project/lotus/pull/5411) Handle batch `PublishStorageDeals` message in sealing recovery - [#5505](https://github.com/filecoin-project/lotus/pull/5505) Exclude expired deals from batching in `PublishStorageDeals` messages - Added `PublishMsgPeriod` and `MaxDealsPerPublishMsg` to miner `Dealmaking` [configuration](https://docs.filecoin.io/mine/lotus/miner-configuration/#dealmaking-section). See how they work [here](https://docs.filecoin.io/mine/lotus/miner-configuration/#publishing-several-deals-in-one-message). - - [#5538](https://github.com/filecoin-project/lotus/pull/5538), [#5549](https://github.com/filecoin-project/lotus/pull/5549) Added a command to list pending deals and force publish messages. + - [#5538](https://github.com/filecoin-project/lotus/pull/5538), [#5549](https://github.com/filecoin-project/lotus/pull/5549) Added a command to list pending deals and force publish messages. - Run `lotus-miner market pending-publish` - [#5428](https://github.com/filecoin-project/lotus/pull/5428) Moved waiting for `PublishStorageDeals` messages' receipt from markets to lotus - [#5510](https://github.com/filecoin-project/lotus/pull/5510) Added `nerpanet` build option @@ -423,32 +508,32 @@ Note that this release does NOT set an upgrade epoch for v3 actors to take effec - [#5219](https://github.com/filecoin-project/lotus/pull/5219) Added interactive mode for lotus-wallet - [5529](https://github.com/filecoin-project/lotus/pull/5529) Added support for minder nodes in `lotus-shed rpc` util - ## Bug Fixes - - - [#5210](https://github.com/filecoin-project/lotus/pull/5210) Miner should not dial client on restart - - [#5403](https://github.com/filecoin-project/lotus/pull/5403) When estimating GasLimit only apply prior messages up to the nonce - - [#5410](https://github.com/filecoin-project/lotus/pull/510) Fix the calibnet build option - - [#5492](https://github.com/filecoin-project/lotus/pull/5492) Fixed `has` for ipfsbstore for non-existing blocks - - [#5361](https://github.com/filecoin-project/lotus/pull/5361) Fixed retrieval hangs when using `IpfsOnlineMode=true` - - [#5493](https://github.com/filecoin-project/lotus/pull/5493) Fixed retrieval failure when price-per-byte is zero - - [#5506](https://github.com/filecoin-project/lotus/pull/5506) Fixed contexts in the storage adpater - - [#5515](https://github.com/filecoin-project/lotus/pull/5515) Properly wire up `StateReadState` on gateway API - - [#5582](https://github.com/filecoin-project/lotus/pull/5582) Fixed error logging format strings - - [#5614](https://github.com/filecoin-project/lotus/pull/5614) Fixed websocket reconnecting handling +## Bug Fixes + +- [#5210](https://github.com/filecoin-project/lotus/pull/5210) Miner should not dial client on restart +- [#5403](https://github.com/filecoin-project/lotus/pull/5403) When estimating GasLimit only apply prior messages up to the nonce +- [#5410](https://github.com/filecoin-project/lotus/pull/510) Fix the calibnet build option +- [#5492](https://github.com/filecoin-project/lotus/pull/5492) Fixed `has` for ipfsbstore for non-existing blocks +- [#5361](https://github.com/filecoin-project/lotus/pull/5361) Fixed retrieval hangs when using `IpfsOnlineMode=true` +- [#5493](https://github.com/filecoin-project/lotus/pull/5493) Fixed retrieval failure when price-per-byte is zero +- [#5506](https://github.com/filecoin-project/lotus/pull/5506) Fixed contexts in the storage adpater +- [#5515](https://github.com/filecoin-project/lotus/pull/5515) Properly wire up `StateReadState` on gateway API +- [#5582](https://github.com/filecoin-project/lotus/pull/5582) Fixed error logging format strings +- [#5614](https://github.com/filecoin-project/lotus/pull/5614) Fixed websocket reconnecting handling - ## Improvements - - - [#5389](https://github.com/filecoin-project/lotus/pull/5389) Show verified indicator for `./lotus-miner storage-deals list` +## Improvements + +- [#5389](https://github.com/filecoin-project/lotus/pull/5389) Show verified indicator for `./lotus-miner storage-deals list` - [#5229](https://github.com/filecoin-project/lotus/pull/5220) Show power for verified deals in `./lotus-miner setocr list` - [#5407](https://github.com/filecoin-project/lotus/pull/5407) Added explicit check of the miner address protocol - - [#5399](https://github.com/filecoin-project/lotus/pull/5399) watchdog: increase heapprof capture threshold to 90% - - [#5398](https://github.com/filecoin-project/lotus/pull/5398) storageadapter: Look at precommits on-chain since deal publish msg - - [#5470](https://github.com/filecoin-project/lotus/pull/5470) Added `--no-timing` option for `./lotus state compute-state --html` +- [#5399](https://github.com/filecoin-project/lotus/pull/5399) watchdog: increase heapprof capture threshold to 90% +- [#5398](https://github.com/filecoin-project/lotus/pull/5398) storageadapter: Look at precommits on-chain since deal publish msg +- [#5470](https://github.com/filecoin-project/lotus/pull/5470) Added `--no-timing` option for `./lotus state compute-state --html` - [#5417](https://github.com/filecoin-project/lotus/pull/5417) Storage Manager: Always unseal full sectors - [#5393](https://github.com/filecoin-project/lotus/pull/5393) Switched to [filecoin-ffi bls api ](https://github.com/filecoin-project/filecoin-ffi/pull/159)for bls signatures -- [#5380](https://github.com/filecoin-project/lotus/pull/5210) Refactor deals API tests -- [#5397](https://github.com/filecoin-project/lotus/pull/5397) Fixed a flake in the sync manager edge case test +- [#5380](https://github.com/filecoin-project/lotus/pull/5210) Refactor deals API tests +- [#5397](https://github.com/filecoin-project/lotus/pull/5397) Fixed a flake in the sync manager edge case test - [#5406](https://github.com/filecoin-project/lotus/pull/5406) Added a test to ensure a correct window post cannot be disputed - [#5294](https://github.com/filecoin-project/lotus/pull/5394) Added jobs to build Lotus docker image and push it to AWS ECR - [#5387](https://github.com/filecoin-project/lotus/pull/5387) Added network info(mainnet|calibnet) in version @@ -457,7 +542,7 @@ Note that this release does NOT set an upgrade epoch for v3 actors to take effec - [#5047](https://github.com/filecoin-project/lotus/pull/5047) Improved the UX for `./lotus-shed bitfield enc` - [#5282](https://github.com/filecoin-project/lotus/pull/5282) Snake a context through the chian blockstore creation - [#5350](https://github.com/filecoin-project/lotus/pull/5350) Avoid using `mp.cfg` directrly to prevent race condition -- [#5449](https://github.com/filecoin-project/lotus/pull/5449) Documented the block-header better +- [#5449](https://github.com/filecoin-project/lotus/pull/5449) Documented the block-header better - [#5404](https://github.com/filecoin-project/lotus/pull/5404) Added retrying proofs if an incorrect one is generated - [#4545](https://github.com/filecoin-project/lotus/pull/4545) Made state tipset usage consistent in the API - [#5540](https://github.com/filecoin-project/lotus/pull/5540) Removed unnecessary database reads in validation check @@ -474,14 +559,14 @@ Note that this release does NOT set an upgrade epoch for v3 actors to take effec - [#5592](https://github.com/filecoin-project/lotus/pull/5592) Verify FFI version before building ## Dependency Updates - - [#5296](https://github.com/filecoin-project/lotus/pull/5396) Upgraded to [raulk/go-watchdog@v1.0.1](https://github.com/raulk/go-watchdog/releases/tag/v1.0.1) - - [#5450](https://github.com/filecoin-project/lotus/pull/5450) Dependency updates - - [#5425](https://github.com/filecoin-project/lotus/pull/5425) Fixed stale imports in testplans/lotus-soup - - [#5535](https://github.com/filecoin-project/lotus/pull/5535) Updated to [go-fil-markets@v1.1.7](https://github.com/filecoin-project/go-fil-markets/releases/tag/v1.1.7) - - [#5616](https://github.com/filecoin-project/lotus/pull/5600) Updated to [filecoin-ffi@b6e0b35fb49ed0fe](https://github.com/filecoin-project/filecoin-ffi/releases/tag/b6e0b35fb49ed0fe) - - [#5599](https://github.com/filecoin-project/lotus/pull/5599) Updated to [go-bitfield@v0.2.4](https://github.com/filecoin-project/go-bitfield/releases/tag/v0.2.4) - - [#5614](https://github.com/filecoin-project/lotus/pull/5614), , [#5621](https://github.com/filecoin-project/lotus/pull/5621) Updated to [go-jsonrpc@v0.1.3](https://github.com/filecoin-project/go-jsonrpc/releases/tag/v0.1.3) - - [#5459](https://github.com/filecoin-project/lotus/pull/5459) Updated to [spec-actors@v3.0.1](https://github.com/filecoin-project/specs-actors/releases/tag/v3.0.1) +- [#5296](https://github.com/filecoin-project/lotus/pull/5396) Upgraded to [raulk/go-watchdog@v1.0.1](https://github.com/raulk/go-watchdog/releases/tag/v1.0.1) +- [#5450](https://github.com/filecoin-project/lotus/pull/5450) Dependency updates +- [#5425](https://github.com/filecoin-project/lotus/pull/5425) Fixed stale imports in testplans/lotus-soup +- [#5535](https://github.com/filecoin-project/lotus/pull/5535) Updated to [go-fil-markets@v1.1.7](https://github.com/filecoin-project/go-fil-markets/releases/tag/v1.1.7) +- [#5616](https://github.com/filecoin-project/lotus/pull/5600) Updated to [filecoin-ffi@b6e0b35fb49ed0fe](https://github.com/filecoin-project/filecoin-ffi/releases/tag/b6e0b35fb49ed0fe) +- [#5599](https://github.com/filecoin-project/lotus/pull/5599) Updated to [go-bitfield@v0.2.4](https://github.com/filecoin-project/go-bitfield/releases/tag/v0.2.4) +- [#5614](https://github.com/filecoin-project/lotus/pull/5614), , [#5621](https://github.com/filecoin-project/lotus/pull/5621) Updated to [go-jsonrpc@v0.1.3](https://github.com/filecoin-project/go-jsonrpc/releases/tag/v0.1.3) +- [#5459](https://github.com/filecoin-project/lotus/pull/5459) Updated to [spec-actors@v3.0.1](https://github.com/filecoin-project/specs-actors/releases/tag/v3.0.1) ## Network Version v10 Upgrade @@ -489,7 +574,7 @@ Note that this release does NOT set an upgrade epoch for v3 actors to take effec - [#5603](https://github.com/filecoin-project/lotus/pull/5603) Set nerpanet's upgrade epochs up to v3 actors - [#5471](https://github.com/filecoin-project/lotus/pull/5471), [#5456](https://github.com/filecoin-project/lotus/pull/5456) Set calibration net actor v3 migration epochs for testing - [#5434](https://github.com/filecoin-project/lotus/pull/5434) Implemented pre-migration framework -- [#5476](https://github.com/filecoin-project/lotus/pull/5477) Tune migration +- [#5476](https://github.com/filecoin-project/lotus/pull/5477) Tune migration # 1.4.1 / 2021-01-20 @@ -688,9 +773,9 @@ This is an optional Lotus release that introduces various improvements to the mi - Error out deals that are not activated by proposed deal start epoch (https://github.com/filecoin-project/lotus/pull/5061) # 1.2.1 / 2020-11-20 - + This is a very small release of Lotus that fixes an issue users are experiencing when importing snapshots. There is no need to upgrade unless you experience an issue with creating a new datastore directory in the Lotus repo. - + ## Changes - fix blockstore directory not created automatically (https://github.com/filecoin-project/lotus/pull/4922) @@ -710,7 +795,7 @@ The changes that break consensus are: - Correction of the VM circulating supply calculation (https://github.com/filecoin-project/lotus/pull/4862) - Retuning gas costs (https://github.com/filecoin-project/lotus/pull/4830) - Avoid sending messages to the zero BLS address (https://github.com/filecoin-project/lotus/pull/4888) - + ## Other Changes - delayed pubsub subscribe for messages topic (https://github.com/filecoin-project/lotus/pull/3646) @@ -867,7 +952,7 @@ This is an optional release of Lotus that upgrades Lotus dependencies, and inclu This is a patch release of Lotus that builds on the fixes involving worker keys that was introduced in v1.1.1. Miners and node operators should update to this release as soon as possible in order to ensure their blocks are propagated and validated. -## Changes +## Changes - Handle worker key changes correctly in runtime (https://github.com/filecoin-project/lotus/pull/4579) @@ -1110,7 +1195,7 @@ This consensus-breaking release of Lotus upgrades the actors version to v2.0.0. - Fix pond (https://github.com/filecoin-project/lotus/pull/4203) - allow manual setting of noncefix fee cap (https://github.com/filecoin-project/lotus/pull/4205) - implement command to get execution traces of any message (https://github.com/filecoin-project/lotus/pull/4200) -- conformance: minor driver refactors (https://github.com/filecoin-project/lotus/pull/4211) +- conformance: minor driver refactors (https://github.com/filecoin-project/lotus/pull/4211) - lotus-pcr: ignore all other messages (https://github.com/filecoin-project/lotus/pull/4218) - lotus-pcr: zero refund (https://github.com/filecoin-project/lotus/pull/4229) @@ -1137,7 +1222,7 @@ We are grateful for every contribution! This optional release of Lotus introduces a new version of markets which switches to CBOR-map encodings, and allows datastore migrations. The release also introduces several improvements to the mining process, a few performance optimizations, and a battery of UX additions and enhancements. -## Changes +## Changes #### Dependencies @@ -1208,7 +1293,7 @@ This consensus-breaking release of Lotus introduces an upgrade to the network. T This release also updates go-fil-markets to fix an incompatibility issue between v0.7.2 and earlier versions. -## Changes +## Changes #### Dependencies @@ -1297,7 +1382,7 @@ This optional release of Lotus introduces some critical fixes to the window PoSt ## Changes -#### Some notable improvements: +#### Some notable improvements: - Correctly construct params for `SubmitWindowedPoSt` messages (https://github.com/filecoin-project/lotus/pull/3909) - Skip sectors correctly for Window PoSt (https://github.com/filecoin-project/lotus/pull/3839) @@ -1333,7 +1418,7 @@ This consensus-breaking release of Lotus is designed to test a network upgrade o - Drand upgrade (https://github.com/filecoin-project/lotus/pull/3670) - Multisig API additions (https://github.com/filecoin-project/lotus/pull/3590) -#### Storage Miner +#### Storage Miner - Increase the number of times precommit2 is attempted before moving back to precommit1 (https://github.com/filecoin-project/lotus/pull/3720) @@ -1376,7 +1461,7 @@ This release introduces some critical fixes to message selection and gas estimat ## Changes -#### Messagepool +#### Messagepool - Warn when optimal selection fails to pack a block and we fall back to random selection (https://github.com/filecoin-project/lotus/pull/3708) - Add basic command for printing gas performance of messages in the mpool (https://github.com/filecoin-project/lotus/pull/3701) @@ -1446,7 +1531,7 @@ This release also introduces many improvements to Lotus! Among them are a new ve - Add additional info about gas premium (https://github.com/filecoin-project/lotus/pull/3578) - Fix GasPremium capping logic (https://github.com/filecoin-project/lotus/pull/3552) -#### Payment channels +#### Payment channels - Get available funds by address or by from/to (https://github.com/filecoin-project/lotus/pull/3547) - Create `lotus paych status` command (https://github.com/filecoin-project/lotus/pull/3523) @@ -1496,7 +1581,7 @@ This patch includes a crucial fix to the message pool selection logic, strongly This patch includes a hotfix to the `GasEstimateFeeCap` method, capping the estimated fee to a reasonable level by default. -## Changes +## Changes - Added target height to sync wait (https://github.com/filecoin-project/lotus/pull/3502) - Disable codecov annotations (https://github.com/filecoin-project/lotus/pull/3514) @@ -1526,7 +1611,7 @@ This patch includes some bugfixes to the sector sealing process, and updates go- # 0.5.7 / 2020-08-31 -This patch release includes some bugfixes and enhancements to the sector lifecycle and message pool logic. +This patch release includes some bugfixes and enhancements to the sector lifecycle and message pool logic. ## Changes @@ -1546,7 +1631,7 @@ Hotfix release that fixes a panic in the sealing scheduler (https://github.com/f # 0.5.5 This patch release introduces a large number of improvements to the sealing process. -It also updates go-fil-markets to +It also updates go-fil-markets to [version 0.5.8](https://github.com/filecoin-project/go-fil-markets/releases/tag/v0.5.8), and go-libp2p-pubsub to [v0.3.5](https://github.com/libp2p/go-libp2p-pubsub/releases/tag/v0.3.5). @@ -1559,16 +1644,16 @@ and go-libp2p-pubsub to [v0.3.5](https://github.com/libp2p/go-libp2p-pubsub/rele - The following improvements were introduced in https://github.com/filecoin-project/lotus/pull/3350. - - Allow `lotus-miner sectors remove` to remove a sector in any state. - - Create a separate state in the storage FSM dedicated to submitting the Commit message. - - Recovery for when the Deal IDs of deals in a sector get changed in a reorg. - - Auto-retry sending Precommit and Commit messages if they run out of gas - - Auto-retry sector remove tasks when they fail - - Compact worker windows, and allow their tasks to be executed in any order + - Allow `lotus-miner sectors remove` to remove a sector in any state. + - Create a separate state in the storage FSM dedicated to submitting the Commit message. + - Recovery for when the Deal IDs of deals in a sector get changed in a reorg. + - Auto-retry sending Precommit and Commit messages if they run out of gas + - Auto-retry sector remove tasks when they fail + - Compact worker windows, and allow their tasks to be executed in any order - Don't simply skip PoSt for bad sectors (https://github.com/filecoin-project/lotus/pull/3323) -#### Message Pool +#### Message Pool - Spam Protection: Track required funds for pending messages (https://github.com/filecoin-project/lotus/pull/3313) @@ -1593,7 +1678,7 @@ A patch release, containing a few nice bugfixes and improvements: # 0.5.3 -Yet another hotfix release. +Yet another hotfix release. A lesson for readers, having people who have been awake for 12+ hours review your hotfix PR is not a good idea. Find someone who has enough slept recently enough to give you good code review, otherwise you'll end up quickly bumping @@ -1612,9 +1697,9 @@ This is a hotfix release. # 0.5.1 / 2020-08-24 -The Space Race release! +The Space Race release! This release contains the genesis car file and bootstrap peers for the space -race network. +race network. Additionally, we included two small fixes to genesis creation: - Randomize ticket value in genesis generation @@ -1632,9 +1717,9 @@ Among the highlights included in this release are: - Gas changes: We implemented EIP-1559 and introduced real gas values. - Deal-making: We now support "Committed Capacity" sectors, "fast-retrieval" deals, -and the packing of multiple deals into a single sector. + and the packing of multiple deals into a single sector. - Renamed features: We renamed some of the binaries, environment variables, and default -paths associated with a Lotus node. + paths associated with a Lotus node. ### Gas changes @@ -1642,19 +1727,19 @@ We made some significant changes to the mechanics of gas in this release. #### Network fee -We implemented something similar to +We implemented something similar to [Ethereum's EIP-1559](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1559.md). The `Message` structure had three changes: - The `GasPrice` field has been removed - A new `GasFeeCap` field has been added, which controls the maximum cost -the sender incurs for the message + the sender incurs for the message - A new `GasPremium` field has been added, which controls the reward a miner -earns for including the message + earns for including the message -A sender will never be charged more than `GasFeeCap * GasLimit`. +A sender will never be charged more than `GasFeeCap * GasLimit`. A miner will typically earn `GasPremium * GasLimit` as a reward. -The `Blockheader` structure has one new field, called `ParentBaseFee`. +The `Blockheader` structure has one new field, called `ParentBaseFee`. Informally speaking,the `ParentBaseFee` is increased when blocks are densely packed with messages, and decreased otherwise. From 64567d7f3223771b73ed25cf40de2b94f5598cf1 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 23 Jun 2021 18:49:52 -0700 Subject: [PATCH 155/160] add simulation projections to changelog --- CHANGELOG.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c6c2ab54..261f41217 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,7 +25,16 @@ Note: ### Projected state tree growth -As a result of the accelerated onboardiPreCommitSectorsBatcng of storage onto the network, it is possible +In order to validate the Hyperdrive changes, we wrote a simulation to seal as many sectors as fast as possible assuming the same number and mix of 32GiB and 64GiB miners as the current network. + +Given these assumptions: + +- We'd expect a network storage growth rate of around 530PiB per day. +- We'd expect network bandwidth dedicated to `SubmitWindowedPoSt` to grow by about 0.02% per day. +- We'd expect the [state-tree](https://spec.filecoin.io/#section-systems.filecoin_vm.state_tree) to grow by 1.6GiB per day. + - Nearly all of the state-tree growth is expected to come from new sector metadata. +- We'd expect the daily lotus datastore growth rate to increase by about 10-15%. + - Most "growth" of the lotus datastore is due to "churn", historical data that's no longer referenced by the latest state-tree. ### Hardware requirements and suggestions From efaa7b0d7600cdfa7be7f8f6f7c6cda728997f14 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 23 Jun 2021 19:02:14 -0700 Subject: [PATCH 156/160] update contributors to include all filecoin projects --- CHANGELOG.md | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 261f41217..62180e227 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -120,18 +120,23 @@ Included in the HyperDrive upgrade is [FIP-0015](https://github.com/filecoin-pro 💙Thank you to all the contributors! -| Contributor | Commits | Lines ± | Files Changed | -|-------------|---------|---------|---------------| -| Łukasz Magiera | 75 | +9088/-1452 | 348 | -| Aayush Rajasekaran | 38 | +6525/-651 | 184 | -| Jennifer Wang | 10 | +575/-104 | 21 | -| Steven Allen | 2 | +272/-8 | 6 | -| vyzo | 6 | +155/-66 | 13 | -| Cory Schwartz | 10 | +171/-27 | 14 | -| Jakub Sztandera | 4 | +177/-13 | 7 | -| Peter Rabbitson | 4 | +65/-42 | 5 | -| Travis Person | 2 | +11/-11 | 4 | -| wangchao | 2 | +3/-2 | 2 | +| Contributor | Commits | Lines ± | Files Changed | +|--------------------|---------|-------------|---------------| +| Łukasz Magiera | 81 | +9606/-1536 | 361 | +| Aayush Rajasekaran | 41 | +6543/-679 | 189 | +| ZenGround0 | 11 | +4074/-727 | 110 | +| Alex | 10 | +2035/-1177 | 55 | +| Ian Davis | 1 | +779/-12 | 5 | +| Frrist | 2 | +722/-6 | 6 | +| Steven Allen | 6 | +368/-24 | 15 | +| Jennifer Wang | 11 | +204/-111 | 19 | +| vyzo | 6 | +155/-66 | 13 | +| Cory Schwartz | 10 | +171/-27 | 14 | +| Jakub Sztandera | 4 | +177/-13 | 7 | +| Peter Rabbitson | 4 | +65/-42 | 5 | +| Travis Person | 2 | +11/-11 | 4 | +| Kirk Baird | 1 | +1/-5 | 1 | +| wangchao | 2 | +3/-2 | 2 | # 1.9.0 / 2021-05-17 From 1411cff69e3dd05dc2be073e104606ab88d34490 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 23 Jun 2021 19:03:24 -0700 Subject: [PATCH 157/160] change mkreleaselog script to only include filecoin-project --- scripts/mkreleaselog | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/scripts/mkreleaselog b/scripts/mkreleaselog index c59027075..c9eaef4fb 100755 --- a/scripts/mkreleaselog +++ b/scripts/mkreleaselog @@ -5,26 +5,7 @@ export GOPATH="$(go env GOPATH)" alias jq="jq --unbuffered" -AUTHORS=( - # orgs - ipfs - ipld - libp2p - multiformats - filecoin-project - ipfs-shipyard - - # Authors of personal repos used by go-ipfs that should be mentioned in the - # release notes. - whyrusleeping - Kubuxu - jbenet - Stebalien - marten-seemann - hsanjuan - lucas-clemente - warpfork -) +AUTHORS=(filecoin-project) [[ -n "${REPO_FILTER+x}" ]] || REPO_FILTER="github.com/(${$(printf "|%s" "${AUTHORS[@]}"):1})" From f5fad636cf92b5660b6f315afbc7fa617750dd3a Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 23 Jun 2021 19:09:43 -0700 Subject: [PATCH 158/160] Apply edits Co-authored-by: MollyM --- CHANGELOG.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 62180e227..b88ad1d40 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,9 +14,9 @@ Note that this release is built on top of Lotus v1.9.0. Enterprising users can u ## Proof batching and aggregation -FIPs [0008](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0008.md) and [0013](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0013.md) combine to allow for a significant increase in the rate of onboarding storage on the Filecoin network. It is hoped that this will lead to more useful data being stored on the network, reduced network congestion, and lower network base fee. +FIPs [0008](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0008.md) and [0013](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0013.md) combine to allow for a significant increase in the rate of onboarding storage on the Filecoin network. This aims to lead to more useful data being stored on the network, reduced network congestion, and lower network base fee. -**Check out the documentation [here](https://docs.filecoin.io/mine/lotus/miner-configuration/#precommitsectorsbatch) for details of the new Lotus miner sealing config options, [here](https://docs.filecoin.io/mine/lotus/miner-configuration/#fees-section) for fee config options, and explanations of the new features.** +**Check out the documentation [here](https://docs.filecoin.io/mine/lotus/miner-configuration/#precommitsectorsbatch) for details on the new Lotus miner sealing config options, [here](https://docs.filecoin.io/mine/lotus/miner-configuration/#fees-section) for fee config options, and explanations of the new features.** Note: - We recommend to keep `PreCommitSectorsBatch` as 1. @@ -25,15 +25,15 @@ Note: ### Projected state tree growth -In order to validate the Hyperdrive changes, we wrote a simulation to seal as many sectors as fast as possible assuming the same number and mix of 32GiB and 64GiB miners as the current network. +In order to validate the Hyperdrive changes, we wrote a simulation to seal as many sectors as quickly as possible, assuming the same number and mix of 32GiB and 64GiB miners as the current network. Given these assumptions: -- We'd expect a network storage growth rate of around 530PiB per day. +- We'd expect a network storage growth rate of around 530PiB per day. 😳 🎉 🥳 😅 - We'd expect network bandwidth dedicated to `SubmitWindowedPoSt` to grow by about 0.02% per day. -- We'd expect the [state-tree](https://spec.filecoin.io/#section-systems.filecoin_vm.state_tree) to grow by 1.6GiB per day. +- We'd expect the [state-tree](https://spec.filecoin.io/#section-systems.filecoin_vm.state_tree) (and therefore [snapshot](https://docs.filecoin.io/get-started/lotus/chain/#lightweight-snapshot)) size to grow by 1.16GiB per day. - Nearly all of the state-tree growth is expected to come from new sector metadata. -- We'd expect the daily lotus datastore growth rate to increase by about 10-15%. +- We'd expect the daily lotus datastore growth rate to increase by about 10-15% (from current ~21GiB/day). - Most "growth" of the lotus datastore is due to "churn", historical data that's no longer referenced by the latest state-tree. ### Hardware requirements and suggestions From 7779d446ce9e235c863fbd183868adb27861b0b6 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 23 Jun 2021 19:18:27 -0700 Subject: [PATCH 159/160] remove hardware suggestion section This isn't useful in its current state and isn't urgent at this point. We can publish recommendations later. --- CHANGELOG.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b88ad1d40..7fac8797c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,10 +36,6 @@ Given these assumptions: - We'd expect the daily lotus datastore growth rate to increase by about 10-15% (from current ~21GiB/day). - Most "growth" of the lotus datastore is due to "churn", historical data that's no longer referenced by the latest state-tree. -### Hardware requirements and suggestions - -As the size of a single state tree grows, so will the size of the minimal datastore needed to sync the blockchain. The Filecoin protocol requires any node validating the chain to have the last 1800 state trees (2 * `ChainFinality`). Depending on how fast storage providers seal new sectors, this could get as large as AMOUNT in 6 months, and grow as fast as 20 GB per day. This increases the necessary hardware requirements to validate the Filecoin blockchain, but can be supported by images such as [Amazon EC2](https://aws.amazon.com/ec2/instance-explorer/?ec2-instances-cards.sort-by=item.additionalFields.category-order&ec2-instances-cards.sort-order=asc&awsf.ec2-instances-filter-category=*all&awsf.ec2-instances-filter-processors=*all&awsf.ec2-instances-filter-accelerators=*all&awsf.ec2-instances-filter-capabilities=additional-capabilities%23instance-storage&ec2-instances-cards.q=ssd&ec2-instances-cards.q_operator=AND&awsm.page-ec2-instances-cards=1) and [Google Cloud Platform](https://cloud.google.com/compute/docs/machine-types). - ### Future improvements Various Lotus improvements are planned moving forward to mitigate the effects of the growing state tree size. The primary improvement is the [Lotus splitstore](https://github.com/filecoin-project/lotus/discussions/5788), which will soon be enabled by default. The feature allows for [online garbage collection](https://github.com/filecoin-project/lotus/issues/6577) for nodes that do not seek to maintain full chain and state history, thus eliminating the need for users to delete their datastores and sync from snapshots. From 28f2f96b65cac8f2ff736d2c6d42c1cb7c6096fa Mon Sep 17 00:00:00 2001 From: Jennifer Wang Date: Wed, 23 Jun 2021 22:29:03 -0400 Subject: [PATCH 160/160] more edits --- CHANGELOG.md | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7fac8797c..a724c841d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,9 @@ # 1.10.0 / 2021-06-23 -This is a mandatory release of Lotus that introduces Filecoin network v13, codenamed the HyperDrive upgrade. The Filecoin mainnet will upgrade, which is 2021-06-30T22:00:00Z. The network upgrade introduces the following FIPs: +This is a mandatory release of Lotus that introduces Filecoin network v13, codenamed the HyperDrive upgrade. The +Filecoin mainnet will upgrade, which is epoch 892800, on 2021-06-30T22:00:00Z. The network upgrade introduces the +following FIPs: - [FIP-0008](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0008.md): Add miner batched sector pre-commit method - [FIP-0011](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0011.md): Remove reward auction from reporting consensus faults @@ -21,7 +23,8 @@ FIPs [0008](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0008.m Note: - We recommend to keep `PreCommitSectorsBatch` as 1. - We recommend miners to set `PreCommitBatchWait` lower than 30 hours. - - We recommend miners to set a longer `CommitBatchSlack` and `PreCommitBatchSlac` to prevent message failures due to expirations. + - We recommend miners to set a longer `CommitBatchSlack` and `PreCommitBatchSlack` to prevent message failures + due to expirations. ### Projected state tree growth @@ -118,21 +121,21 @@ Included in the HyperDrive upgrade is [FIP-0015](https://github.com/filecoin-pro | Contributor | Commits | Lines ± | Files Changed | |--------------------|---------|-------------|---------------| -| Łukasz Magiera | 81 | +9606/-1536 | 361 | -| Aayush Rajasekaran | 41 | +6543/-679 | 189 | -| ZenGround0 | 11 | +4074/-727 | 110 | -| Alex | 10 | +2035/-1177 | 55 | -| Ian Davis | 1 | +779/-12 | 5 | -| Frrist | 2 | +722/-6 | 6 | -| Steven Allen | 6 | +368/-24 | 15 | -| Jennifer Wang | 11 | +204/-111 | 19 | -| vyzo | 6 | +155/-66 | 13 | -| Cory Schwartz | 10 | +171/-27 | 14 | -| Jakub Sztandera | 4 | +177/-13 | 7 | -| Peter Rabbitson | 4 | +65/-42 | 5 | -| Travis Person | 2 | +11/-11 | 4 | -| Kirk Baird | 1 | +1/-5 | 1 | -| wangchao | 2 | +3/-2 | 2 | +| @magik6k | 81 | +9606/-1536 | 361 | +| @arajasek | 41 | +6543/-679 | 189 | +| @ZenGround0 | 11 | +4074/-727 | 110 | +| @anorth | 10 | +2035/-1177 | 55 | +| @iand | 1 | +779/-12 | 5 | +| @frrist | 2 | +722/-6 | 6 | +| @Stebalien | 6 | +368/-24 | 15 | +| @jennijuju | 11 | +204/-111 | 19 | +| @vyzo | 6 | +155/-66 | 13 | +| @coryschwartz | 10 | +171/-27 | 14 | +| @Kubuxu | 4 | +177/-13 | 7 | +| @ribasushi | 4 | +65/-42 | 5 | +| @travisperson | 2 | +11/-11 | 4 | +| @kirk-baird | 1 | +1/-5 | 1 | +| @wangchao | 2 | +3/-2 | 2 | # 1.9.0 / 2021-05-17