From 6854cc59448d9ab9bdb1115bade56f68c0df5824 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Wed, 2 Jun 2021 14:51:49 -0400 Subject: [PATCH 01/46] 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 02/46] 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 03/46] 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 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 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 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 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 04/46] 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 78171055e7afac7bdbf9c10d141399d097bc2499 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 05/46] fee config for sector batching --- extern/storage-sealing/commit_batch.go | 14 ++++++---- 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, 57 insertions(+), 37 deletions(-) diff --git a/extern/storage-sealing/commit_batch.go b/extern/storage-sealing/commit_batch.go index 7d128fe76..d025dc655 100644 --- a/extern/storage-sealing/commit_batch.go +++ b/extern/storage-sealing/commit_batch.go @@ -23,6 +23,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 @@ -47,7 +48,7 @@ type CommitBatcher struct { maddr address.Address mctx context.Context addrSel AddrSel - feeCfg FeeConfig + feeCfg config.MinerFeeConfig getConfig GetSealingConfigFunc prover ffiwrapper.Prover @@ -60,7 +61,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, @@ -285,14 +286,15 @@ 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) + maxFee := b.feeCfg.MaxPreCommitBatchGasFee.FeeForSectors(len(infos)) + goodFunds := big.Add(maxFee, 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) } - 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) } @@ -352,14 +354,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 dd674d331..7dffd848b 100644 --- a/extern/storage-sealing/precommit_batch.go +++ b/extern/storage-sealing/precommit_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/extern/storage-sealing/sealiface" + "github.com/filecoin-project/lotus/node/config" ) type PreCommitBatcherApi interface { @@ -37,7 +38,7 @@ type PreCommitBatcher struct { maddr address.Address mctx context.Context addrSel AddrSel - feeCfg FeeConfig + feeCfg config.MinerFeeConfig getConfig GetSealingConfigFunc deadlines map[abi.SectorNumber]time.Time @@ -49,7 +50,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, @@ -224,14 +225,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 69746268f..dae90d91b 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" @@ -78,7 +79,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 5e8f5269b..c75b2af94 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 c18f60a7a..b69724368 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 106c09291..b4c590ae3 100644 --- a/storage/miner.go +++ b/storage/miner.go @@ -171,12 +171,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), - } - var ( // consumer of chain head changes. evts = events.NewEvents(ctx, m.api) @@ -205,7 +199,7 @@ func (m *Miner) Run(ctx context.Context) error { ) // Instantiate the sealing FSM. - m.sealing = sealing.New(adaptedAPI, fc, evtsAdapter, m.maddr, m.ds, m.sealer, m.sc, m.verif, m.prover, &pcp, cfg, m.handleSealingNotifications, as) + m.sealing = sealing.New(adaptedAPI, m.feeCfg, evtsAdapter, m.maddr, m.ds, m.sealer, m.sc, m.verif, m.prover, &pcp, cfg, m.handleSealingNotifications, as) // Run the sealing FSM. go m.sealing.Run(ctx) //nolint:errcheck // logged intside the function From ba27d452141b6516e23d4a4917dae24af25f96a7 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Tue, 8 Jun 2021 16:46:35 -0400 Subject: [PATCH 06/46] 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 07/46] 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 92bb8743274f47618712800ce83a1945f50b66a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 9 Jun 2021 12:17:18 +0200 Subject: [PATCH 08/46] Use correct batch fee config in commit batcher Co-authored-by: Aayush Rajasekaran --- extern/storage-sealing/commit_batch.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/storage-sealing/commit_batch.go b/extern/storage-sealing/commit_batch.go index d025dc655..b991eaa8d 100644 --- a/extern/storage-sealing/commit_batch.go +++ b/extern/storage-sealing/commit_batch.go @@ -286,7 +286,7 @@ 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.MaxPreCommitBatchGasFee.FeeForSectors(len(infos)) + maxFee := b.feeCfg.MaxCommitBatchGasFee.FeeForSectors(len(infos)) goodFunds := big.Add(maxFee, collateral) from, _, err := b.addrSel(b.mctx, mi, api.CommitAddr, goodFunds, collateral) From dbb4e9fcc5900f68bdeb469fe096a1068e420787 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Wed, 9 Jun 2021 12:26:20 -0400 Subject: [PATCH 09/46] 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 10/46] 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 11/46] 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 12/46] 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 13/46] 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 14/46] 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 15/46] 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 16/46] 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 a64a059780b9d1738da729e15d49cfb69e756c43 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Wed, 27 Jan 2021 18:35:28 -0800 Subject: [PATCH 17/46] implement a command to export a car --- cmd/lotus-shed/export-car.go | 103 +++++++++++++++++++++++++++++++++++ cmd/lotus-shed/main.go | 1 + 2 files changed, 104 insertions(+) create mode 100644 cmd/lotus-shed/export-car.go diff --git a/cmd/lotus-shed/export-car.go b/cmd/lotus-shed/export-car.go new file mode 100644 index 000000000..97e4fb6c6 --- /dev/null +++ b/cmd/lotus-shed/export-car.go @@ -0,0 +1,103 @@ +package main + +import ( + "fmt" + "io" + "os" + + "github.com/ipfs/go-blockservice" + "github.com/ipfs/go-cid" + offline "github.com/ipfs/go-ipfs-exchange-offline" + format "github.com/ipfs/go-ipld-format" + "github.com/ipfs/go-merkledag" + "github.com/ipld/go-car" + "github.com/urfave/cli/v2" + "golang.org/x/xerrors" + + lcli "github.com/filecoin-project/lotus/cli" + "github.com/filecoin-project/lotus/node/repo" +) + +func carWalkFunc(nd format.Node) (out []*format.Link, err error) { + for _, link := range nd.Links() { + if link.Cid.Prefix().Codec == cid.FilCommitmentSealed || link.Cid.Prefix().Codec == cid.FilCommitmentUnsealed { + continue + } + out = append(out, link) + } + return out, nil +} + +var exportCarCmd = &cli.Command{ + Name: "export-car", + Description: "Export a car from repo (requires node to be offline)", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "repo", + Value: "~/.lotus", + }, + }, + Action: func(cctx *cli.Context) error { + if cctx.Args().Len() != 2 { + return lcli.ShowHelp(cctx, fmt.Errorf("must specify file name and object")) + } + + outfile := cctx.Args().First() + var roots []cid.Cid + for _, arg := range cctx.Args().Tail() { + c, err := cid.Decode(arg) + if err != nil { + return err + } + roots = append(roots, c) + } + + ctx := lcli.ReqContext(cctx) + + r, err := repo.NewFS(cctx.String("repo")) + if err != nil { + return xerrors.Errorf("opening fs repo: %w", err) + } + + exists, err := r.Exists() + if err != nil { + return err + } + if !exists { + return xerrors.Errorf("lotus repo doesn't exist") + } + + lr, err := r.Lock(repo.FullNode) + if err != nil { + return err + } + defer lr.Close() //nolint:errcheck + + fi, err := os.Create(outfile) + if err != nil { + return xerrors.Errorf("opening the output file: %w", err) + } + + defer fi.Close() //nolint:errcheck + + bs, err := lr.Blockstore(ctx, repo.UniversalBlockstore) + if err != nil { + return fmt.Errorf("failed to open blockstore: %w", err) + } + + defer func() { + if c, ok := bs.(io.Closer); ok { + if err := c.Close(); err != nil { + log.Warnf("failed to close blockstore: %s", err) + } + } + }() + + dag := merkledag.NewDAGService(blockservice.New(bs, offline.Exchange(bs))) + err = car.WriteCarWithWalker(ctx, dag, roots, fi, carWalkFunc) + if err != nil { + return err + } + return nil + }, +} diff --git a/cmd/lotus-shed/main.go b/cmd/lotus-shed/main.go index da896b4cb..7c4391f18 100644 --- a/cmd/lotus-shed/main.go +++ b/cmd/lotus-shed/main.go @@ -43,6 +43,7 @@ func main() { minerCmd, mpoolStatsCmd, exportChainCmd, + exportCarCmd, consensusCmd, storageStatsCmd, syncCmd, From 95ad74a1567765a6111848db6665fffb875ec6d6 Mon Sep 17 00:00:00 2001 From: Jennifer Wang Date: Wed, 9 Jun 2021 18:04:11 -0400 Subject: [PATCH 18/46] 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 55b77a3c99e80fededc599c0bb1902e80862c75f Mon Sep 17 00:00:00 2001 From: Jennifer Wang Date: Wed, 9 Jun 2021 23:19:27 -0400 Subject: [PATCH 19/46] 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 0379adc9f1d88a09a1c9f130ed3b506d1f798335 Mon Sep 17 00:00:00 2001 From: Jennifer Wang Date: Wed, 9 Jun 2021 23:27:58 -0400 Subject: [PATCH 20/46] 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 21/46] 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 23:32:03 -0400 Subject: [PATCH 22/46] Remove rc changelog, compile the new changelog for final release only --- CHANGELOG.md | 137 ------------------- documentation/misc/RELEASE_ISSUE_TEMPLATE.md | 3 +- 2 files changed, 1 insertion(+), 139 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 462e5b816..fa6330907 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -70,143 +70,6 @@ This is an optional Lotus release that introduces various improvements to the se - fix health report (https://github.com/filecoin-project/lotus/pull/6011) - fix(ci): Use recent ubuntu LTS release; Update release params ((https://github.com/filecoin-project/lotus/pull/6011)) -# 1.9.0-rc4 / 2021-05-13 - -This is an optional Lotus release that introduces various improvements to the sealing, mining, and deal-making processes. - -## Highlights - -- OpenRPC Support (https://github.com/filecoin-project/lotus/pull/5843) -- Take latency into account when making interactive deals (https://github.com/filecoin-project/lotus/pull/5876) -- Update go-commp-utils for >10x faster client commp calculation (https://github.com/filecoin-project/lotus/pull/5892) -- add `lotus client cancel-retrieval` cmd to lotus CLI (https://github.com/filecoin-project/lotus/pull/5871) -- add `inspect-deal` command to `lotus client` (https://github.com/filecoin-project/lotus/pull/5833) -- Local retrieval support (https://github.com/filecoin-project/lotus/pull/5917) -- go-fil-markets v1.1.9 -> v1.2.5 - - For a detailed changelog see https://github.com/filecoin-project/go-fil-markets/blob/master/CHANGELOG.md -- rust-fil-proofs v5.4.1 -> v7.0.1 - - For a detailed changelog see https://github.com/filecoin-project/rust-fil-proofs/blob/master/CHANGELOG.md - -## Changes -- storagefsm: Apply global events even in broken states (https://github.com/filecoin-project/lotus/pull/5962) -- Default the AlwaysKeepUnsealedCopy flag to true (https://github.com/filecoin-project/lotus/pull/5743) -- splitstore: compact hotstore prior to garbage collection (https://github.com/filecoin-project/lotus/pull/5778) -- ipfs-force bootstrapper update (https://github.com/filecoin-project/lotus/pull/5799) -- better logging when unsealing fails (https://github.com/filecoin-project/lotus/pull/5851) -- perf: add cache for gas permium estimation (https://github.com/filecoin-project/lotus/pull/5709) -- backupds: Compact log on restart (https://github.com/filecoin-project/lotus/pull/5875) -- backupds: Improve truncated log handling (https://github.com/filecoin-project/lotus/pull/5891) -- State CLI improvements (State CLI improvements) -- API proxy struct codegen (https://github.com/filecoin-project/lotus/pull/5854) -- move DI stuff for paychmgr into modules (https://github.com/filecoin-project/lotus/pull/5791) -- Implement Event observer and Settings for 3rd party dep injection (https://github.com/filecoin-project/lotus/pull/5693) -- Export developer and network commands for consumption by derivatives of Lotus (https://github.com/filecoin-project/lotus/pull/5864) -- mock sealer: Simulate randomness sideeffects (https://github.com/filecoin-project/lotus/pull/5805) -- localstorage: Demote reservation stat error to debug (https://github.com/filecoin-project/lotus/pull/5976) -- shed command to unpack miner info dumps (https://github.com/filecoin-project/lotus/pull/5800) -- Add two utils to Lotus-shed (https://github.com/filecoin-project/lotus/pull/5867) -- add shed election estimate command (https://github.com/filecoin-project/lotus/pull/5092) -- Add --actor flag in lotus-shed sectors terminate (https://github.com/filecoin-project/lotus/pull/5819) -- Move lotus mpool clear to lotus-shed (https://github.com/filecoin-project/lotus/pull/5900) -- Centralize everything on ipfs/go-log/v2 (https://github.com/filecoin-project/lotus/pull/5974) -- expose NextID from nice market actor interface (https://github.com/filecoin-project/lotus/pull/5850) -- add available options for perm on error (https://github.com/filecoin-project/lotus/pull/5814) -- API docs clarification: Document StateSearchMsg replaced message behavior (https://github.com/filecoin-project/lotus/pull/5838) -- api: Document StateReplay replaced message behavior (https://github.com/filecoin-project/lotus/pull/5840) -- add godocs to miner objects (https://github.com/filecoin-project/lotus/pull/2184) -- Add description to the client deal CLI command (https://github.com/filecoin-project/lotus/pull/5999) -- lint: don't skip builtin (https://github.com/filecoin-project/lotus/pull/5881) -- use deal duration from actors (https://github.com/filecoin-project/lotus/pull/5270) -- remote calc winningpost proof (https://github.com/filecoin-project/lotus/pull/5884) -- packer: other network images (https://github.com/filecoin-project/lotus/pull/5930) -- Convert the chainstore lock to RW (https://github.com/filecoin-project/lotus/pull/5971) -- Remove CachedBlockstore (https://github.com/filecoin-project/lotus/pull/5972) -- remove messagepool CapGasFee duplicate code (https://github.com/filecoin-project/lotus/pull/5992) -- Add a mining-heartbeat INFO line at every epoch (https://github.com/filecoin-project/lotus/pull/6183) -- chore(ci): Enable build on RC tags (https://github.com/filecoin-project/lotus/pull/6245) -- Upgrade nerpa to actor v4 and bump the version to rc4 (https://github.com/filecoin-project/lotus/pull/6249) -## Fixes -- return buffers after canceling badger operation (https://github.com/filecoin-project/lotus/pull/5796) -- avoid holding a lock while calling the View callback (https://github.com/filecoin-project/lotus/pull/5792) -- storagefsm: Trigger input processing when below limits (https://github.com/filecoin-project/lotus/pull/5801) -- After importing a previously deleted key, be able to delete it again (https://github.com/filecoin-project/lotus/pull/4653) -- fix StateManager.Replay on reward actor (https://github.com/filecoin-project/lotus/pull/5804) -- make sure atomic 64bit fields are 64bit aligned (https://github.com/filecoin-project/lotus/pull/5794) -- Import secp sigs in paych tests (https://github.com/filecoin-project/lotus/pull/5879) -- fix ci build-macos (https://github.com/filecoin-project/lotus/pull/5934) -- Fix creation of remainder account when it's not a multisig (https://github.com/filecoin-project/lotus/pull/5807) -- Fix fallback chainstore (https://github.com/filecoin-project/lotus/pull/6003) -- fix 4857: show help for set-addrs (https://github.com/filecoin-project/lotus/pull/5943) -- fix health report (https://github.com/filecoin-project/lotus/pull/6011) - - -# 1.9.0-rc2 / 2021-04-30 - -This is an optional Lotus release that introduces various improvements to the sealing, mining, and deal-making processes. - -## Highlights - -- OpenRPC Support (https://github.com/filecoin-project/lotus/pull/5843) -- Take latency into account when making interactive deals (https://github.com/filecoin-project/lotus/pull/5876) -- Update go-commp-utils for >10x faster client commp calculation (https://github.com/filecoin-project/lotus/pull/5892) -- add `lotus client cancel-retrieval` cmd to lotus CLI (https://github.com/filecoin-project/lotus/pull/5871) -- add `inspect-deal` command to `lotus client` (https://github.com/filecoin-project/lotus/pull/5833) -- Local retrieval support (https://github.com/filecoin-project/lotus/pull/5917) -- go-fil-markets v1.1.9 -> v1.2.5 - - For a detailed changelog see https://github.com/filecoin-project/go-fil-markets/blob/master/CHANGELOG.md -- rust-fil-proofs v5.4.1 -> v7 - - For a detailed changelog see https://github.com/filecoin-project/rust-fil-proofs/blob/master/CHANGELOG.md - -## Changes -- storagefsm: Apply global events even in broken states (https://github.com/filecoin-project/lotus/pull/5962) -- Default the AlwaysKeepUnsealedCopy flag to true (https://github.com/filecoin-project/lotus/pull/5743) -- splitstore: compact hotstore prior to garbage collection (https://github.com/filecoin-project/lotus/pull/5778) -- ipfs-force bootstrapper update (https://github.com/filecoin-project/lotus/pull/5799) -- better logging when unsealing fails (https://github.com/filecoin-project/lotus/pull/5851) -- perf: add cache for gas permium estimation (https://github.com/filecoin-project/lotus/pull/5709) -- backupds: Compact log on restart (https://github.com/filecoin-project/lotus/pull/5875) -- backupds: Improve truncated log handling (https://github.com/filecoin-project/lotus/pull/5891) -- State CLI improvements (State CLI improvements) -- API proxy struct codegen (https://github.com/filecoin-project/lotus/pull/5854) -- move DI stuff for paychmgr into modules (https://github.com/filecoin-project/lotus/pull/5791) -- Implement Event observer and Settings for 3rd party dep injection (https://github.com/filecoin-project/lotus/pull/5693) -- Export developer and network commands for consumption by derivatives of Lotus (https://github.com/filecoin-project/lotus/pull/5864) -- mock sealer: Simulate randomness sideeffects (https://github.com/filecoin-project/lotus/pull/5805) -- localstorage: Demote reservation stat error to debug (https://github.com/filecoin-project/lotus/pull/5976) -- shed command to unpack miner info dumps (https://github.com/filecoin-project/lotus/pull/5800) -- Add two utils to Lotus-shed (https://github.com/filecoin-project/lotus/pull/5867) -- add shed election estimate command (https://github.com/filecoin-project/lotus/pull/5092) -- Add --actor flag in lotus-shed sectors terminate (https://github.com/filecoin-project/lotus/pull/5819) -- Move lotus mpool clear to lotus-shed (https://github.com/filecoin-project/lotus/pull/5900) -- Centralize everything on ipfs/go-log/v2 (https://github.com/filecoin-project/lotus/pull/5974) -- expose NextID from nice market actor interface (https://github.com/filecoin-project/lotus/pull/5850) -- add available options for perm on error (https://github.com/filecoin-project/lotus/pull/5814) -- API docs clarification: Document StateSearchMsg replaced message behavior (https://github.com/filecoin-project/lotus/pull/5838) -- api: Document StateReplay replaced message behavior (https://github.com/filecoin-project/lotus/pull/5840) -- add godocs to miner objects (https://github.com/filecoin-project/lotus/pull/2184) -- Add description to the client deal CLI command (https://github.com/filecoin-project/lotus/pull/5999) -- lint: don't skip builtin (https://github.com/filecoin-project/lotus/pull/5881) -- use deal duration from actors (https://github.com/filecoin-project/lotus/pull/5270) -- remote calc winningpost proof (https://github.com/filecoin-project/lotus/pull/5884) -- packer: other network images (https://github.com/filecoin-project/lotus/pull/5930) -- Convert the chainstore lock to RW (https://github.com/filecoin-project/lotus/pull/5971) -- Remove CachedBlockstore (https://github.com/filecoin-project/lotus/pull/5972) -- remove messagepool CapGasFee duplicate code (https://github.com/filecoin-project/lotus/pull/5992) - -## Fixes -- return buffers after canceling badger operation (https://github.com/filecoin-project/lotus/pull/5796) -- avoid holding a lock while calling the View callback (https://github.com/filecoin-project/lotus/pull/5792) -- storagefsm: Trigger input processing when below limits (https://github.com/filecoin-project/lotus/pull/5801) -- After importing a previously deleted key, be able to delete it again (https://github.com/filecoin-project/lotus/pull/4653) -- fix StateManager.Replay on reward actor (https://github.com/filecoin-project/lotus/pull/5804) -- make sure atomic 64bit fields are 64bit aligned (https://github.com/filecoin-project/lotus/pull/5794) -- Import secp sigs in paych tests (https://github.com/filecoin-project/lotus/pull/5879) -- fix ci build-macos (https://github.com/filecoin-project/lotus/pull/5934) -- Fix creation of remainder account when it's not a multisig (https://github.com/filecoin-project/lotus/pull/5807) -- Fix fallback chainstore (https://github.com/filecoin-project/lotus/pull/6003) -- fix 4857: show help for set-addrs (https://github.com/filecoin-project/lotus/pull/5943) -- fix health report (https://github.com/filecoin-project/lotus/pull/6011) - # 1.8.0 / 2021-04-05 This is a mandatory release of Lotus that upgrades the network to version 12, which introduces various performance improvements to the cron processing of the power actor. The network will upgrade at height 712320, which is 2021-04-29T06:00:00Z. diff --git a/documentation/misc/RELEASE_ISSUE_TEMPLATE.md b/documentation/misc/RELEASE_ISSUE_TEMPLATE.md index 4731c4edb..0912a8681 100644 --- a/documentation/misc/RELEASE_ISSUE_TEMPLATE.md +++ b/documentation/misc/RELEASE_ISSUE_TEMPLATE.md @@ -25,7 +25,6 @@ We're happy to announce Lotus X.Y.Z... First steps: - [ ] Fork a new branch (`release/vX.Y.Z`) from `master` and make any further release related changes to this branch. If any "non-trivial" changes get added to the release, uncheck all the checkboxes and return to this stage. - - [ ] Prep the changelog using `scripts/mkreleaselog`, and add it to `CHANGELOG.md` - [ ] Bump the version in `version.go` in the `master` branch to `vX.(Y+1).0-dev`. Prepping an RC: @@ -93,7 +92,7 @@ Testing an RC: - [ ] Final preparation - [ ] Verify that version string in [`version.go`](https://github.com/ipfs/go-ipfs/tree/master/version.go) has been updated. - [ ] Ensure that [CHANGELOG.md](https://github.com/filecoin-project/lotus/blob/master/CHANGELOG.md) is up to date - - [ ] Ensure that [README.md](https://github.com/filecoin-project/lotus/blob/master/README.md) is up to date + - [ ] Prep the changelog using `scripts/mkreleaselog`, and add it to `CHANGELOG.md` - [ ] Merge `release-vX.Y.Z` into the `releases` branch. - [ ] Tag this merge commit (on the `releases` branch) with `vX.Y.Z` - [ ] Cut the release [here](https://github.com/filecoin-project/lotus/releases/new?prerelease=true&target=releases). From 8f426b49ec7f33d8c321cdb5a623c7b9382cfc62 Mon Sep 17 00:00:00 2001 From: Jennifer Wang Date: Wed, 9 Jun 2021 18:04:11 -0400 Subject: [PATCH 23/46] 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 d3fc6833a52f716f9bd4cb9d0a6fadfd1fe5e77f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Kripalani?= Date: Thu, 10 Jun 2021 12:05:35 +0100 Subject: [PATCH 24/46] itests/kit: add guard to ensure imports from tests only. --- itests/kit/init.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/itests/kit/init.go b/itests/kit/init.go index 57d60ad2a..2f40ca0f0 100644 --- a/itests/kit/init.go +++ b/itests/kit/init.go @@ -3,6 +3,7 @@ package kit import ( "fmt" "os" + "strings" "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/lotus/build" @@ -11,6 +12,11 @@ import ( ) func init() { + bin := os.Args[0] + if !strings.HasSuffix(bin, ".test") { + panic("package itests/kit must only be imported from tests") + } + _ = logging.SetLogLevel("*", "INFO") policy.SetConsensusMinerMinPower(abi.NewStoragePower(2048)) @@ -22,4 +28,5 @@ func init() { panic(fmt.Sprintf("failed to set BELLMAN_NO_GPU env variable: %s", err)) } build.InsecurePoStValidation = true + } From 06195bc8e12e2dc3ca32905d9754f3038193c7e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 9 Jun 2021 17:18:09 +0200 Subject: [PATCH 25/46] Unit tests for sector batchers --- extern/storage-sealing/commit_batch.go | 18 +- extern/storage-sealing/commit_batch_test.go | 299 ++++++++++++++++++ .../mocks/mock_commit_batcher.go | 149 +++++++++ .../mocks/mock_precommit_batcher.go | 87 +++++ extern/storage-sealing/precommit_batch.go | 4 +- .../storage-sealing/precommit_batch_test.go | 258 +++++++++++++++ extern/storage-sealing/states_sealing.go | 6 +- 7 files changed, 809 insertions(+), 12 deletions(-) create mode 100644 extern/storage-sealing/commit_batch_test.go create mode 100644 extern/storage-sealing/mocks/mock_commit_batcher.go create mode 100644 extern/storage-sealing/mocks/mock_precommit_batcher.go create mode 100644 extern/storage-sealing/precommit_batch_test.go diff --git a/extern/storage-sealing/commit_batch.go b/extern/storage-sealing/commit_batch.go index 819cb7fc7..0d7bd899f 100644 --- a/extern/storage-sealing/commit_batch.go +++ b/extern/storage-sealing/commit_batch.go @@ -32,6 +32,8 @@ import ( const arp = abi.RegisteredAggregationProof_SnarkPackV1 +//go:generate go run github.com/golang/mock/mockgen -destination=mocks/mock_commit_batcher.go -package=mocks . CommitBatcherApi + 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) @@ -44,9 +46,9 @@ type CommitBatcherApi interface { } type AggregateInput struct { - spt abi.RegisteredSealProof - info proof5.AggregateSealVerifyInfo - proof []byte + Spt abi.RegisteredSealProof + Info proof5.AggregateSealVerifyInfo + Proof []byte } type CommitBatcher struct { @@ -256,7 +258,7 @@ func (b *CommitBatcher) processBatch(cfg sealiface.Config) ([]sealiface.CommitBa res.Sectors = append(res.Sectors, id) params.SectorNumbers.Set(uint64(id)) - infos = append(infos, p.info) + infos = append(infos, p.Info) } sort.Slice(infos, func(i, j int) bool { @@ -264,7 +266,7 @@ func (b *CommitBatcher) processBatch(cfg sealiface.Config) ([]sealiface.CommitBa }) for _, info := range infos { - proofs = append(proofs, b.todo[info.Number].proof) + proofs = append(proofs, b.todo[info.Number].Proof) } mid, err := address.IDFromAddress(b.maddr) @@ -274,7 +276,7 @@ func (b *CommitBatcher) processBatch(cfg sealiface.Config) ([]sealiface.CommitBa params.AggregateProof, err = b.prover.AggregateSealProofs(proof5.AggregateSealVerifyProofAndInfos{ Miner: abi.ActorID(mid), - SealProof: b.todo[infos[0].Number].spt, + SealProof: b.todo[infos[0].Number].Spt, AggregateProof: arp, Infos: infos, }, proofs) @@ -362,7 +364,7 @@ func (b *CommitBatcher) processSingle(mi miner.MinerInfo, sn abi.SectorNumber, i enc := new(bytes.Buffer) params := &miner.ProveCommitSectorParams{ SectorNumber: sn, - Proof: info.proof, + Proof: info.Proof, } if err := params.MarshalCBOR(enc); err != nil { @@ -447,7 +449,7 @@ func (b *CommitBatcher) Pending(ctx context.Context) ([]abi.SectorID, error) { for _, s := range b.todo { res = append(res, abi.SectorID{ Miner: abi.ActorID(mid), - Number: s.info.Number, + Number: s.Info.Number, }) } diff --git a/extern/storage-sealing/commit_batch_test.go b/extern/storage-sealing/commit_batch_test.go new file mode 100644 index 000000000..bbf09766f --- /dev/null +++ b/extern/storage-sealing/commit_batch_test.go @@ -0,0 +1,299 @@ +package sealing_test + +import ( + "bytes" + "context" + "sort" + "sync" + "testing" + "time" + + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/require" + + "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/network" + 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" + sealing "github.com/filecoin-project/lotus/extern/storage-sealing" + "github.com/filecoin-project/lotus/extern/storage-sealing/mocks" + "github.com/filecoin-project/lotus/extern/storage-sealing/sealiface" +) + +func TestCommitBatcher(t *testing.T) { + t0123, err := address.NewFromString("t0123") + require.NoError(t, err) + + ctx := context.Background() + + as := func(ctx context.Context, mi miner.MinerInfo, use api.AddrUse, goodFunds, minFunds abi.TokenAmount) (address.Address, abi.TokenAmount, error) { + return t0123, big.Zero(), nil + } + + maxBatch := miner5.MaxAggregatedSectors + minBatch := miner5.MinAggregatedSectors + + cfg := func() (sealiface.Config, error) { + return sealiface.Config{ + MaxWaitDealsSectors: 2, + MaxSealingSectors: 0, + MaxSealingSectorsForDeals: 0, + WaitDealsDelay: time.Hour * 6, + AlwaysKeepUnsealedCopy: true, + + BatchPreCommits: true, + MinPreCommitBatch: 1, + MaxPreCommitBatch: miner5.PreCommitSectorBatchMaxSize, + PreCommitBatchWait: 24 * time.Hour, + PreCommitBatchSlack: 3 * time.Hour, + + AggregateCommits: true, + MinCommitBatch: minBatch, + MaxCommitBatch: maxBatch, + CommitBatchWait: 24 * time.Hour, + CommitBatchSlack: 1 * time.Hour, + + TerminateBatchMin: 1, + TerminateBatchMax: 100, + TerminateBatchWait: 5 * time.Minute, + }, nil + } + + type promise func(t *testing.T) + type action func(t *testing.T, s *mocks.MockCommitBatcherApi, pcb *sealing.CommitBatcher) promise + + actions := func(as ...action) action { + return func(t *testing.T, s *mocks.MockCommitBatcherApi, pcb *sealing.CommitBatcher) promise { + var ps []promise + for _, a := range as { + p := a(t, s, pcb) + if p != nil { + ps = append(ps, p) + } + } + + if len(ps) > 0 { + return func(t *testing.T) { + for _, p := range ps { + p(t) + } + } + } + return nil + } + } + + addSector := func(sn abi.SectorNumber) action { + return func(t *testing.T, s *mocks.MockCommitBatcherApi, pcb *sealing.CommitBatcher) promise { + var pcres sealiface.CommitBatchRes + var pcerr error + done := sync.Mutex{} + done.Lock() + + si := sealing.SectorInfo{ + SectorNumber: sn, + } + + s.EXPECT().ChainHead(gomock.Any()).Return(nil, abi.ChainEpoch(1), nil) + s.EXPECT().StateNetworkVersion(gomock.Any(), gomock.Any()).Return(network.Version13, nil) + s.EXPECT().StateSectorPreCommitInfo(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(&miner.SectorPreCommitOnChainInfo{ + PreCommitDeposit: big.Zero(), + }, nil) + + go func() { + defer done.Unlock() + pcres, pcerr = pcb.AddCommit(ctx, si, sealing.AggregateInput{ + Info: proof5.AggregateSealVerifyInfo{ + Number: sn, + }, + }) + }() + + return func(t *testing.T) { + done.Lock() + require.NoError(t, pcerr) + require.Empty(t, pcres.Error) + require.Contains(t, pcres.Sectors, si.SectorNumber) + } + } + } + + addSectors := func(sectors []abi.SectorNumber) action { + as := make([]action, len(sectors)) + for i, sector := range sectors { + as[i] = addSector(sector) + } + return actions(as...) + } + + waitPending := func(n int) action { + return func(t *testing.T, s *mocks.MockCommitBatcherApi, pcb *sealing.CommitBatcher) promise { + require.Eventually(t, func() bool { + p, err := pcb.Pending(ctx) + require.NoError(t, err) + return len(p) == n + }, time.Second*5, 10*time.Millisecond) + + return nil + } + } + + expectSend := func(expect []abi.SectorNumber) action { + return func(t *testing.T, s *mocks.MockCommitBatcherApi, pcb *sealing.CommitBatcher) promise { + s.EXPECT().StateMinerInfo(gomock.Any(), gomock.Any(), gomock.Any()).Return(miner.MinerInfo{Owner: t0123, Worker: t0123}, nil) + + ti := len(expect) + batch := false + if ti >= minBatch { + batch = true + ti = 1 + } + s.EXPECT().ChainHead(gomock.Any()).Return(nil, abi.ChainEpoch(1), nil) + s.EXPECT().StateSectorPreCommitInfo(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(&miner.SectorPreCommitOnChainInfo{ + PreCommitDeposit: big.Zero(), + }, nil).Times(len(expect)) + s.EXPECT().StateMinerInitialPledgeCollateral(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(big.Zero(), nil).Times(len(expect)) + if batch { + s.EXPECT().StateNetworkVersion(gomock.Any(), gomock.Any()).Return(network.Version13, nil) + s.EXPECT().ChainBaseFee(gomock.Any(), gomock.Any()).Return(big.NewInt(2000), nil) + } + + s.EXPECT().SendMsg(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), funMatcher(func(i interface{}) bool { + b := i.([]byte) + if batch { + var params miner5.ProveCommitAggregateParams + require.NoError(t, params.UnmarshalCBOR(bytes.NewReader(b))) + for _, number := range expect { + set, err := params.SectorNumbers.IsSet(uint64(number)) + require.NoError(t, err) + require.True(t, set) + } + } else { + var params miner5.ProveCommitSectorParams + require.NoError(t, params.UnmarshalCBOR(bytes.NewReader(b))) + } + return true + })).Times(ti) + return nil + } + } + + flush := func(expect []abi.SectorNumber) action { + return func(t *testing.T, s *mocks.MockCommitBatcherApi, pcb *sealing.CommitBatcher) promise { + _ = expectSend(expect)(t, s, pcb) + + batch := len(expect) >= minBatch + + r, err := pcb.Flush(ctx) + require.NoError(t, err) + if batch { + require.Len(t, r, 1) + require.Empty(t, r[0].Error) + sort.Slice(r[0].Sectors, func(i, j int) bool { + return r[0].Sectors[i] < r[0].Sectors[j] + }) + require.Equal(t, expect, r[0].Sectors) + } else { + require.Len(t, r, len(expect)) + for _, res := range r { + require.Len(t, res.Sectors, 1) + require.Empty(t, res.Error) + } + sort.Slice(r, func(i, j int) bool { + return r[i].Sectors[0] < r[j].Sectors[0] + }) + for i, res := range r { + require.Equal(t, abi.SectorNumber(i), res.Sectors[0]) + } + } + + return nil + } + } + + getSectors := func(n int) []abi.SectorNumber { + out := make([]abi.SectorNumber, n) + for i := range out { + out[i] = abi.SectorNumber(i) + } + return out + } + + tcs := map[string]struct { + actions []action + }{ + "addSingle": { + actions: []action{ + addSector(0), + waitPending(1), + flush([]abi.SectorNumber{0}), + }, + }, + "addTwo": { + actions: []action{ + addSectors(getSectors(2)), + waitPending(2), + flush(getSectors(2)), + }, + }, + "addAte": { + actions: []action{ + addSectors(getSectors(8)), + waitPending(8), + flush(getSectors(8)), + }, + }, + "addMax": { + actions: []action{ + expectSend(getSectors(maxBatch)), + addSectors(getSectors(maxBatch)), + }, + }, + } + + for name, tc := range tcs { + tc := tc + + t.Run(name, func(t *testing.T) { + // create go mock controller here + mockCtrl := gomock.NewController(t) + // when test is done, assert expectations on all mock objects. + defer mockCtrl.Finish() + + // create them mocks + pcapi := mocks.NewMockCommitBatcherApi(mockCtrl) + + pcb := sealing.NewCommitBatcher(ctx, t0123, pcapi, as, fc, cfg, &fakeProver{}) + + var promises []promise + + for _, a := range tc.actions { + p := a(t, pcapi, pcb) + if p != nil { + promises = append(promises, p) + } + } + + for _, p := range promises { + p(t) + } + + err := pcb.Stop(ctx) + require.NoError(t, err) + }) + } +} + +type fakeProver struct{} + +func (f fakeProver) AggregateSealProofs(aggregateInfo proof5.AggregateSealVerifyProofAndInfos, proofs [][]byte) ([]byte, error) { + return []byte("Trust me, I'm a proof"), nil +} + +var _ ffiwrapper.Prover = &fakeProver{} diff --git a/extern/storage-sealing/mocks/mock_commit_batcher.go b/extern/storage-sealing/mocks/mock_commit_batcher.go new file mode 100644 index 000000000..c4746e0eb --- /dev/null +++ b/extern/storage-sealing/mocks/mock_commit_batcher.go @@ -0,0 +1,149 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/filecoin-project/lotus/extern/storage-sealing (interfaces: CommitBatcherApi) + +// Package mocks is a generated GoMock package. +package mocks + +import ( + context "context" + reflect "reflect" + + address "github.com/filecoin-project/go-address" + abi "github.com/filecoin-project/go-state-types/abi" + big "github.com/filecoin-project/go-state-types/big" + network "github.com/filecoin-project/go-state-types/network" + miner "github.com/filecoin-project/lotus/chain/actors/builtin/miner" + sealing "github.com/filecoin-project/lotus/extern/storage-sealing" + miner0 "github.com/filecoin-project/specs-actors/actors/builtin/miner" + gomock "github.com/golang/mock/gomock" + cid "github.com/ipfs/go-cid" +) + +// MockCommitBatcherApi is a mock of CommitBatcherApi interface. +type MockCommitBatcherApi struct { + ctrl *gomock.Controller + recorder *MockCommitBatcherApiMockRecorder +} + +// MockCommitBatcherApiMockRecorder is the mock recorder for MockCommitBatcherApi. +type MockCommitBatcherApiMockRecorder struct { + mock *MockCommitBatcherApi +} + +// NewMockCommitBatcherApi creates a new mock instance. +func NewMockCommitBatcherApi(ctrl *gomock.Controller) *MockCommitBatcherApi { + mock := &MockCommitBatcherApi{ctrl: ctrl} + mock.recorder = &MockCommitBatcherApiMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockCommitBatcherApi) EXPECT() *MockCommitBatcherApiMockRecorder { + return m.recorder +} + +// ChainBaseFee mocks base method. +func (m *MockCommitBatcherApi) ChainBaseFee(arg0 context.Context, arg1 sealing.TipSetToken) (big.Int, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ChainBaseFee", arg0, arg1) + ret0, _ := ret[0].(big.Int) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ChainBaseFee indicates an expected call of ChainBaseFee. +func (mr *MockCommitBatcherApiMockRecorder) ChainBaseFee(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ChainBaseFee", reflect.TypeOf((*MockCommitBatcherApi)(nil).ChainBaseFee), arg0, arg1) +} + +// ChainHead mocks base method. +func (m *MockCommitBatcherApi) ChainHead(arg0 context.Context) (sealing.TipSetToken, abi.ChainEpoch, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ChainHead", arg0) + ret0, _ := ret[0].(sealing.TipSetToken) + ret1, _ := ret[1].(abi.ChainEpoch) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// ChainHead indicates an expected call of ChainHead. +func (mr *MockCommitBatcherApiMockRecorder) ChainHead(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ChainHead", reflect.TypeOf((*MockCommitBatcherApi)(nil).ChainHead), arg0) +} + +// SendMsg mocks base method. +func (m *MockCommitBatcherApi) SendMsg(arg0 context.Context, arg1, arg2 address.Address, arg3 abi.MethodNum, arg4, arg5 big.Int, arg6 []byte) (cid.Cid, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SendMsg", arg0, arg1, arg2, arg3, arg4, arg5, arg6) + ret0, _ := ret[0].(cid.Cid) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// SendMsg indicates an expected call of SendMsg. +func (mr *MockCommitBatcherApiMockRecorder) SendMsg(arg0, arg1, arg2, arg3, arg4, arg5, arg6 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendMsg", reflect.TypeOf((*MockCommitBatcherApi)(nil).SendMsg), arg0, arg1, arg2, arg3, arg4, arg5, arg6) +} + +// StateMinerInfo mocks base method. +func (m *MockCommitBatcherApi) StateMinerInfo(arg0 context.Context, arg1 address.Address, arg2 sealing.TipSetToken) (miner.MinerInfo, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "StateMinerInfo", arg0, arg1, arg2) + ret0, _ := ret[0].(miner.MinerInfo) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// StateMinerInfo indicates an expected call of StateMinerInfo. +func (mr *MockCommitBatcherApiMockRecorder) StateMinerInfo(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StateMinerInfo", reflect.TypeOf((*MockCommitBatcherApi)(nil).StateMinerInfo), arg0, arg1, arg2) +} + +// StateMinerInitialPledgeCollateral mocks base method. +func (m *MockCommitBatcherApi) StateMinerInitialPledgeCollateral(arg0 context.Context, arg1 address.Address, arg2 miner0.SectorPreCommitInfo, arg3 sealing.TipSetToken) (big.Int, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "StateMinerInitialPledgeCollateral", arg0, arg1, arg2, arg3) + ret0, _ := ret[0].(big.Int) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// StateMinerInitialPledgeCollateral indicates an expected call of StateMinerInitialPledgeCollateral. +func (mr *MockCommitBatcherApiMockRecorder) StateMinerInitialPledgeCollateral(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StateMinerInitialPledgeCollateral", reflect.TypeOf((*MockCommitBatcherApi)(nil).StateMinerInitialPledgeCollateral), arg0, arg1, arg2, arg3) +} + +// StateNetworkVersion mocks base method. +func (m *MockCommitBatcherApi) StateNetworkVersion(arg0 context.Context, arg1 sealing.TipSetToken) (network.Version, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "StateNetworkVersion", arg0, arg1) + ret0, _ := ret[0].(network.Version) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// StateNetworkVersion indicates an expected call of StateNetworkVersion. +func (mr *MockCommitBatcherApiMockRecorder) StateNetworkVersion(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StateNetworkVersion", reflect.TypeOf((*MockCommitBatcherApi)(nil).StateNetworkVersion), arg0, arg1) +} + +// StateSectorPreCommitInfo mocks base method. +func (m *MockCommitBatcherApi) StateSectorPreCommitInfo(arg0 context.Context, arg1 address.Address, arg2 abi.SectorNumber, arg3 sealing.TipSetToken) (*miner.SectorPreCommitOnChainInfo, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "StateSectorPreCommitInfo", arg0, arg1, arg2, arg3) + ret0, _ := ret[0].(*miner.SectorPreCommitOnChainInfo) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// StateSectorPreCommitInfo indicates an expected call of StateSectorPreCommitInfo. +func (mr *MockCommitBatcherApiMockRecorder) StateSectorPreCommitInfo(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StateSectorPreCommitInfo", reflect.TypeOf((*MockCommitBatcherApi)(nil).StateSectorPreCommitInfo), arg0, arg1, arg2, arg3) +} diff --git a/extern/storage-sealing/mocks/mock_precommit_batcher.go b/extern/storage-sealing/mocks/mock_precommit_batcher.go new file mode 100644 index 000000000..4a5074027 --- /dev/null +++ b/extern/storage-sealing/mocks/mock_precommit_batcher.go @@ -0,0 +1,87 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/filecoin-project/lotus/extern/storage-sealing (interfaces: PreCommitBatcherApi) + +// Package mocks is a generated GoMock package. +package mocks + +import ( + context "context" + reflect "reflect" + + address "github.com/filecoin-project/go-address" + abi "github.com/filecoin-project/go-state-types/abi" + big "github.com/filecoin-project/go-state-types/big" + miner "github.com/filecoin-project/lotus/chain/actors/builtin/miner" + sealing "github.com/filecoin-project/lotus/extern/storage-sealing" + gomock "github.com/golang/mock/gomock" + cid "github.com/ipfs/go-cid" +) + +// MockPreCommitBatcherApi is a mock of PreCommitBatcherApi interface. +type MockPreCommitBatcherApi struct { + ctrl *gomock.Controller + recorder *MockPreCommitBatcherApiMockRecorder +} + +// MockPreCommitBatcherApiMockRecorder is the mock recorder for MockPreCommitBatcherApi. +type MockPreCommitBatcherApiMockRecorder struct { + mock *MockPreCommitBatcherApi +} + +// NewMockPreCommitBatcherApi creates a new mock instance. +func NewMockPreCommitBatcherApi(ctrl *gomock.Controller) *MockPreCommitBatcherApi { + mock := &MockPreCommitBatcherApi{ctrl: ctrl} + mock.recorder = &MockPreCommitBatcherApiMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockPreCommitBatcherApi) EXPECT() *MockPreCommitBatcherApiMockRecorder { + return m.recorder +} + +// ChainHead mocks base method. +func (m *MockPreCommitBatcherApi) ChainHead(arg0 context.Context) (sealing.TipSetToken, abi.ChainEpoch, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ChainHead", arg0) + ret0, _ := ret[0].(sealing.TipSetToken) + ret1, _ := ret[1].(abi.ChainEpoch) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// ChainHead indicates an expected call of ChainHead. +func (mr *MockPreCommitBatcherApiMockRecorder) ChainHead(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ChainHead", reflect.TypeOf((*MockPreCommitBatcherApi)(nil).ChainHead), arg0) +} + +// SendMsg mocks base method. +func (m *MockPreCommitBatcherApi) SendMsg(arg0 context.Context, arg1, arg2 address.Address, arg3 abi.MethodNum, arg4, arg5 big.Int, arg6 []byte) (cid.Cid, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SendMsg", arg0, arg1, arg2, arg3, arg4, arg5, arg6) + ret0, _ := ret[0].(cid.Cid) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// SendMsg indicates an expected call of SendMsg. +func (mr *MockPreCommitBatcherApiMockRecorder) SendMsg(arg0, arg1, arg2, arg3, arg4, arg5, arg6 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendMsg", reflect.TypeOf((*MockPreCommitBatcherApi)(nil).SendMsg), arg0, arg1, arg2, arg3, arg4, arg5, arg6) +} + +// StateMinerInfo mocks base method. +func (m *MockPreCommitBatcherApi) StateMinerInfo(arg0 context.Context, arg1 address.Address, arg2 sealing.TipSetToken) (miner.MinerInfo, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "StateMinerInfo", arg0, arg1, arg2) + ret0, _ := ret[0].(miner.MinerInfo) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// StateMinerInfo indicates an expected call of StateMinerInfo. +func (mr *MockPreCommitBatcherApiMockRecorder) StateMinerInfo(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StateMinerInfo", reflect.TypeOf((*MockPreCommitBatcherApi)(nil).StateMinerInfo), arg0, arg1, arg2) +} diff --git a/extern/storage-sealing/precommit_batch.go b/extern/storage-sealing/precommit_batch.go index 9439ae14c..d85a60bc6 100644 --- a/extern/storage-sealing/precommit_batch.go +++ b/extern/storage-sealing/precommit_batch.go @@ -25,6 +25,8 @@ import ( "github.com/filecoin-project/lotus/node/config" ) +//go:generate go run github.com/golang/mock/mockgen -destination=mocks/mock_precommit_batcher.go -package=mocks . PreCommitBatcherApi + 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) @@ -243,7 +245,7 @@ func (b *PreCommitBatcher) processBatch(cfg sealiface.Config) ([]sealiface.PreCo res.Msg = &mcid - log.Infow("Sent ProveCommitAggregate message", "cid", mcid, "from", from, "sectors", len(b.todo)) + log.Infow("Sent PreCommitSectorBatch message", "cid", mcid, "from", from, "sectors", len(b.todo)) return []sealiface.PreCommitBatchRes{res}, nil } diff --git a/extern/storage-sealing/precommit_batch_test.go b/extern/storage-sealing/precommit_batch_test.go new file mode 100644 index 000000000..141742c14 --- /dev/null +++ b/extern/storage-sealing/precommit_batch_test.go @@ -0,0 +1,258 @@ +package sealing_test + +import ( + "bytes" + "context" + "sort" + "sync" + "testing" + "time" + + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/require" + + "github.com/filecoin-project/go-address" + "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/api" + "github.com/filecoin-project/lotus/chain/actors/builtin/miner" + "github.com/filecoin-project/lotus/chain/types" + sealing "github.com/filecoin-project/lotus/extern/storage-sealing" + "github.com/filecoin-project/lotus/extern/storage-sealing/mocks" + "github.com/filecoin-project/lotus/extern/storage-sealing/sealiface" + "github.com/filecoin-project/lotus/node/config" + miner0 "github.com/filecoin-project/specs-actors/actors/builtin/miner" +) + +var fc = config.MinerFeeConfig{ + MaxPreCommitGasFee: types.FIL(types.FromFil(1)), + MaxCommitGasFee: types.FIL(types.FromFil(1)), + MaxTerminateGasFee: types.FIL(types.FromFil(1)), + MaxPreCommitBatchGasFee: config.BatchFeeConfig{Base: types.FIL(types.FromFil(3)), PerSector: types.FIL(types.FromFil(1))}, + MaxCommitBatchGasFee: config.BatchFeeConfig{Base: types.FIL(types.FromFil(3)), PerSector: types.FIL(types.FromFil(1))}, +} + +func TestPrecommitBatcher(t *testing.T) { + t0123, err := address.NewFromString("t0123") + require.NoError(t, err) + + ctx := context.Background() + + as := func(ctx context.Context, mi miner.MinerInfo, use api.AddrUse, goodFunds, minFunds abi.TokenAmount) (address.Address, abi.TokenAmount, error) { + return t0123, big.Zero(), nil + } + + maxBatch := miner5.PreCommitSectorBatchMaxSize + + cfg := func() (sealiface.Config, error) { + return sealiface.Config{ + MaxWaitDealsSectors: 2, + MaxSealingSectors: 0, + MaxSealingSectorsForDeals: 0, + WaitDealsDelay: time.Hour * 6, + AlwaysKeepUnsealedCopy: true, + + BatchPreCommits: true, + MinPreCommitBatch: 1, + MaxPreCommitBatch: maxBatch, + PreCommitBatchWait: 24 * time.Hour, + PreCommitBatchSlack: 3 * time.Hour, + + AggregateCommits: true, + MinCommitBatch: miner5.MinAggregatedSectors, + MaxCommitBatch: miner5.MaxAggregatedSectors, + CommitBatchWait: 24 * time.Hour, + CommitBatchSlack: 1 * time.Hour, + + TerminateBatchMin: 1, + TerminateBatchMax: 100, + TerminateBatchWait: 5 * time.Minute, + }, nil + } + + type promise func(t *testing.T) + type action func(t *testing.T, s *mocks.MockPreCommitBatcherApi, pcb *sealing.PreCommitBatcher) promise + + actions := func(as ...action) action { + return func(t *testing.T, s *mocks.MockPreCommitBatcherApi, pcb *sealing.PreCommitBatcher) promise { + var ps []promise + for _, a := range as { + p := a(t, s, pcb) + if p != nil { + ps = append(ps, p) + } + } + + if len(ps) > 0 { + return func(t *testing.T) { + for _, p := range ps { + p(t) + } + } + } + return nil + } + } + + addSector := func(sn abi.SectorNumber) action { + return func(t *testing.T, s *mocks.MockPreCommitBatcherApi, pcb *sealing.PreCommitBatcher) promise { + var pcres sealiface.PreCommitBatchRes + var pcerr error + done := sync.Mutex{} + done.Lock() + + si := sealing.SectorInfo{ + SectorNumber: sn, + } + + s.EXPECT().ChainHead(gomock.Any()).Return(nil, abi.ChainEpoch(1), nil) + + go func() { + defer done.Unlock() + pcres, pcerr = pcb.AddPreCommit(ctx, si, big.Zero(), &miner0.SectorPreCommitInfo{ + SectorNumber: si.SectorNumber, + SealedCID: fakePieceCid(t), + DealIDs: nil, + Expiration: 0, + }) + }() + + return func(t *testing.T) { + done.Lock() + require.NoError(t, pcerr) + require.Empty(t, pcres.Error) + require.Contains(t, pcres.Sectors, si.SectorNumber) + } + } + } + + addSectors := func(sectors []abi.SectorNumber) action { + as := make([]action, len(sectors)) + for i, sector := range sectors { + as[i] = addSector(sector) + } + return actions(as...) + } + + waitPending := func(n int) action { + return func(t *testing.T, s *mocks.MockPreCommitBatcherApi, pcb *sealing.PreCommitBatcher) promise { + require.Eventually(t, func() bool { + p, err := pcb.Pending(ctx) + require.NoError(t, err) + return len(p) == n + }, time.Second*5, 10*time.Millisecond) + + return nil + } + } + + expectSend := func(expect []abi.SectorNumber) action { + return func(t *testing.T, s *mocks.MockPreCommitBatcherApi, pcb *sealing.PreCommitBatcher) promise { + s.EXPECT().StateMinerInfo(gomock.Any(), gomock.Any(), gomock.Any()).Return(miner.MinerInfo{Owner: t0123, Worker: t0123}, nil) + s.EXPECT().SendMsg(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), funMatcher(func(i interface{}) bool { + b := i.([]byte) + var params miner5.PreCommitSectorBatchParams + require.NoError(t, params.UnmarshalCBOR(bytes.NewReader(b))) + for s, number := range expect { + require.Equal(t, number, params.Sectors[s].SectorNumber) + } + return true + })) + return nil + } + } + + flush := func(expect []abi.SectorNumber) action { + return func(t *testing.T, s *mocks.MockPreCommitBatcherApi, pcb *sealing.PreCommitBatcher) promise { + _ = expectSend(expect)(t, s, pcb) + + r, err := pcb.Flush(ctx) + require.NoError(t, err) + require.Len(t, r, 1) + require.Empty(t, r[0].Error) + sort.Slice(r[0].Sectors, func(i, j int) bool { + return r[0].Sectors[i] < r[0].Sectors[j] + }) + require.Equal(t, expect, r[0].Sectors) + + return nil + } + } + + getSectors := func(n int) []abi.SectorNumber { + out := make([]abi.SectorNumber, n) + for i := range out { + out[i] = abi.SectorNumber(i) + } + return out + } + + tcs := map[string]struct { + actions []action + }{ + "addSingle": { + actions: []action{ + addSector(0), + waitPending(1), + flush([]abi.SectorNumber{0}), + }, + }, + "addTwo": { + actions: []action{ + addSectors(getSectors(2)), + waitPending(2), + flush(getSectors(2)), + }, + }, + "addMax": { + actions: []action{ + expectSend(getSectors(maxBatch)), + addSectors(getSectors(maxBatch)), + }, + }, + } + + for name, tc := range tcs { + tc := tc + + t.Run(name, func(t *testing.T) { + // create go mock controller here + mockCtrl := gomock.NewController(t) + // when test is done, assert expectations on all mock objects. + defer mockCtrl.Finish() + + // create them mocks + pcapi := mocks.NewMockPreCommitBatcherApi(mockCtrl) + + pcb := sealing.NewPreCommitBatcher(ctx, t0123, pcapi, as, fc, cfg) + + var promises []promise + + for _, a := range tc.actions { + p := a(t, pcapi, pcb) + if p != nil { + promises = append(promises, p) + } + } + + for _, p := range promises { + p(t) + } + + err := pcb.Stop(ctx) + require.NoError(t, err) + }) + } +} + +type funMatcher func(interface{}) bool + +func (funMatcher) Matches(interface{}) bool { + return true +} + +func (funMatcher) String() string { + return "fun" +} diff --git a/extern/storage-sealing/states_sealing.go b/extern/storage-sealing/states_sealing.go index c75b2af94..493cbdb98 100644 --- a/extern/storage-sealing/states_sealing.go +++ b/extern/storage-sealing/states_sealing.go @@ -590,15 +590,15 @@ func (m *Sealing) handleSubmitCommitAggregate(ctx statemachine.Context, sector S } res, err := m.commiter.AddCommit(ctx.Context(), sector, AggregateInput{ - info: proof.AggregateSealVerifyInfo{ + Info: proof.AggregateSealVerifyInfo{ Number: sector.SectorNumber, Randomness: sector.TicketValue, InteractiveRandomness: sector.SeedValue, SealedCID: *sector.CommR, UnsealedCID: *sector.CommD, }, - proof: sector.Proof, // todo: this correct?? - spt: sector.SectorType, + 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 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 26/46] 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 27/46] 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 28/46] 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 29/46] 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 30/46] 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 f392c1295c035e0a7c3d5549d3854302518ab4f5 Mon Sep 17 00:00:00 2001 From: wangchao Date: Fri, 11 Jun 2021 10:33:57 +0800 Subject: [PATCH 31/46] 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 932f3ce1d1a805e83e87adac66d3d1b0b812bd53 Mon Sep 17 00:00:00 2001 From: Rjan Date: Mon, 14 Jun 2021 13:13:25 +0200 Subject: [PATCH 32/46] Update chain list with correct help instructions Fixes #6293, changes the help text from (Default: 0) to (Default: current head) --- cli/chain.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/chain.go b/cli/chain.go index 6651a7764..0fca73406 100644 --- a/cli/chain.go +++ b/cli/chain.go @@ -536,7 +536,7 @@ var ChainListCmd = &cli.Command{ Aliases: []string{"love"}, Usage: "View a segment of the chain", Flags: []cli.Flag{ - &cli.Uint64Flag{Name: "height"}, + &cli.Uint64Flag{Name: "height", DefaultText: "current head"}, &cli.IntFlag{Name: "count", Value: 30}, &cli.StringFlag{ Name: "format", 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 33/46] 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 40efafb7cdb1847b4ee2ccc70c0724781339e867 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 34/46] 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 35/46] 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 36/46] 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 37/46] 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 38/46] 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 39/46] 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 40/46] 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 9ae780902afff4fe501b10dd23aa0d4d6052e78e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 14 Jun 2021 22:08:19 +0200 Subject: [PATCH 41/46] Test multicore SDR support --- .../sector-storage/ffiwrapper/sealer_test.go | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/extern/sector-storage/ffiwrapper/sealer_test.go b/extern/sector-storage/ffiwrapper/sealer_test.go index 5d96f187f..25a37e470 100644 --- a/extern/sector-storage/ffiwrapper/sealer_test.go +++ b/extern/sector-storage/ffiwrapper/sealer_test.go @@ -31,6 +31,7 @@ import ( "github.com/filecoin-project/specs-storage/storage" ffi "github.com/filecoin-project/filecoin-ffi" + "github.com/filecoin-project/filecoin-ffi/generated" "github.com/filecoin-project/lotus/chain/actors/policy" "github.com/filecoin-project/lotus/extern/sector-storage/ffiwrapper/basicfs" @@ -38,7 +39,34 @@ import ( "github.com/filecoin-project/lotus/extern/storage-sealing/lib/nullreader" ) +var rustLogger = func() *bytes.Buffer { + os.Setenv("RUST_LOG", "info") + + var bb bytes.Buffer + r, w, err := os.Pipe() + if err != nil { + panic(err) + } + + go func() { + _, _ = io.Copy(&bb, r) + runtime.KeepAlive(w) + }() + + resp := generated.FilInitLogFd(int32(w.Fd())) + resp.Deref() + + defer generated.FilDestroyInitLogFdResponse(resp) + + if resp.StatusCode != generated.FCPResponseStatusFCPNoError { + panic(generated.RawString(resp.ErrorMsg).Copy()) + } + + return &bb +}() + func init() { + rustLogger.Reset() logging.SetLogLevel("*", "DEBUG") //nolint: errcheck } @@ -853,3 +881,59 @@ func TestAddPiece512MPadded(t *testing.T) { require.Equal(t, "baga6ea4seaqonenxyku4o7hr5xkzbqsceipf6xgli3on54beqbk6k246sbooobq", c.PieceCID.String()) } + +func TestMulticoreSDR(t *testing.T) { + if testing.Short() { + t.Skip("skipping test in short mode") + } + + getGrothParamFileAndVerifyingKeys(sectorSize) + + dir, err := ioutil.TempDir("", "sbtest") + if err != nil { + t.Fatal(err) + } + + miner := abi.ActorID(123) + + sp := &basicfs.Provider{ + Root: dir, + } + sb, err := New(sp) + if err != nil { + t.Fatalf("%+v", err) + } + + cleanup := func() { + if t.Failed() { + fmt.Printf("not removing %s\n", dir) + return + } + if err := os.RemoveAll(dir); err != nil { + t.Error(err) + } + } + defer cleanup() + + si := storage.SectorRef{ + ID: abi.SectorID{Miner: miner, Number: 1}, + ProofType: sealProofType, + } + + s := seal{ref: si} + + // check multicore + _ = os.Setenv("FIL_PROOFS_USE_MULTICORE_SDR", "1") + rustLogger.Reset() + s.precommit(t, sb, si, func() {}) + + ok := false + for _, s := range strings.Split(rustLogger.String(), "\n") { + if strings.Contains(s, "create_label::multi") { + ok = true + break + } + } + + require.True(t, ok) +} From 265afd696edc462da8a339638fb3c6059d009ecf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 15 Jun 2021 17:46:06 +0200 Subject: [PATCH 42/46] Run TestMulticoreSDR on Circle --- .circleci/config.yml | 12 ++++++++++++ extern/sector-storage/ffiwrapper/sealer_test.go | 4 ++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 5edc0cdac..d76be4bdc 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -133,6 +133,9 @@ jobs: deadline-test: type: string default: "0" + proofs-log-test: + type: string + default: "0" test-suite-name: type: string default: unit @@ -167,6 +170,7 @@ jobs: environment: LOTUS_TEST_WINDOW_POST: << parameters.winpost-test >> LOTUS_TEST_DEADLINE_TOGGLING: << parameters.deadline-test >> + TEST_RUSTPROOFS_LOGS: << parameters.proofs-log-test >> SKIP_CONFORMANCE: "1" command: | mkdir -p /tmp/test-reports/<< parameters.test-suite-name >> @@ -212,6 +216,8 @@ jobs: <<: *test test-terminate: <<: *test + check-proofs-multicore-sdr: + <<: *test test-conformance: description: | Run tests using a corpus of interoperable test vectors for Filecoin @@ -815,6 +821,12 @@ workflows: tags: only: - /^v\d+\.\d+\.\d+(-rc\d+)?$/ + - check-proofs-multicore-sdr: + codecov-upload: true + go-test-flags: "-run=TestMulticoreSDR" + test-suite-name: multicore-sdr-check + packages: "./extern/sector-storage/ffiwrapper" + proofs-log-test: "1" - test-conformance: test-suite-name: conformance packages: "./conformance" diff --git a/extern/sector-storage/ffiwrapper/sealer_test.go b/extern/sector-storage/ffiwrapper/sealer_test.go index 25a37e470..f0d27203c 100644 --- a/extern/sector-storage/ffiwrapper/sealer_test.go +++ b/extern/sector-storage/ffiwrapper/sealer_test.go @@ -883,8 +883,8 @@ func TestAddPiece512MPadded(t *testing.T) { } func TestMulticoreSDR(t *testing.T) { - if testing.Short() { - t.Skip("skipping test in short mode") + if os.Getenv("TEST_RUSTPROOFS_LOGS") != "1" { + t.Skip("skipping test without TEST_RUSTPROOFS_LOGS=1") } getGrothParamFileAndVerifyingKeys(sectorSize) From 6b0aed9317c52c556f6161634c02524212072811 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 15 Jun 2021 17:53:26 +0200 Subject: [PATCH 43/46] Setup rust logger in the test --- .../sector-storage/ffiwrapper/sealer_test.go | 55 ++++++++++--------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/extern/sector-storage/ffiwrapper/sealer_test.go b/extern/sector-storage/ffiwrapper/sealer_test.go index f0d27203c..f88fdc8d0 100644 --- a/extern/sector-storage/ffiwrapper/sealer_test.go +++ b/extern/sector-storage/ffiwrapper/sealer_test.go @@ -39,34 +39,7 @@ import ( "github.com/filecoin-project/lotus/extern/storage-sealing/lib/nullreader" ) -var rustLogger = func() *bytes.Buffer { - os.Setenv("RUST_LOG", "info") - - var bb bytes.Buffer - r, w, err := os.Pipe() - if err != nil { - panic(err) - } - - go func() { - _, _ = io.Copy(&bb, r) - runtime.KeepAlive(w) - }() - - resp := generated.FilInitLogFd(int32(w.Fd())) - resp.Deref() - - defer generated.FilDestroyInitLogFdResponse(resp) - - if resp.StatusCode != generated.FCPResponseStatusFCPNoError { - panic(generated.RawString(resp.ErrorMsg).Copy()) - } - - return &bb -}() - func init() { - rustLogger.Reset() logging.SetLogLevel("*", "DEBUG") //nolint: errcheck } @@ -882,7 +855,35 @@ func TestAddPiece512MPadded(t *testing.T) { require.Equal(t, "baga6ea4seaqonenxyku4o7hr5xkzbqsceipf6xgli3on54beqbk6k246sbooobq", c.PieceCID.String()) } +func setupLogger(t *testing.T) *bytes.Buffer { + _ = os.Setenv("RUST_LOG", "info") + + var bb bytes.Buffer + r, w, err := os.Pipe() + if err != nil { + t.Fatal(err) + } + + go func() { + _, _ = io.Copy(&bb, r) + runtime.KeepAlive(w) + }() + + resp := generated.FilInitLogFd(int32(w.Fd())) + resp.Deref() + + defer generated.FilDestroyInitLogFdResponse(resp) + + if resp.StatusCode != generated.FCPResponseStatusFCPNoError { + t.Fatal(generated.RawString(resp.ErrorMsg).Copy()) + } + + return &bb +} + func TestMulticoreSDR(t *testing.T) { + rustLogger := setupLogger(t) + if os.Getenv("TEST_RUSTPROOFS_LOGS") != "1" { t.Skip("skipping test without TEST_RUSTPROOFS_LOGS=1") } From c7c593c74e03d8ba3b591b2e4a8eaa76ff25933d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 15 Jun 2021 17:59:46 +0200 Subject: [PATCH 44/46] TestMulticoreSDR: Setup rust logger after envvar check --- extern/sector-storage/ffiwrapper/sealer_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extern/sector-storage/ffiwrapper/sealer_test.go b/extern/sector-storage/ffiwrapper/sealer_test.go index f88fdc8d0..7f0dc914f 100644 --- a/extern/sector-storage/ffiwrapper/sealer_test.go +++ b/extern/sector-storage/ffiwrapper/sealer_test.go @@ -882,12 +882,12 @@ func setupLogger(t *testing.T) *bytes.Buffer { } func TestMulticoreSDR(t *testing.T) { - rustLogger := setupLogger(t) - if os.Getenv("TEST_RUSTPROOFS_LOGS") != "1" { t.Skip("skipping test without TEST_RUSTPROOFS_LOGS=1") } + rustLogger := setupLogger(t) + getGrothParamFileAndVerifyingKeys(sectorSize) dir, err := ioutil.TempDir("", "sbtest") From bee548facee218b4d11ef9ae232d000be03c6667 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Mon, 14 Jun 2021 22:59:17 -0400 Subject: [PATCH 45/46] Add utils to use multisigs as miner owners --- api/v0api/full.go | 2 +- cmd/lotus-shed/main.go | 1 + cmd/lotus-shed/miner-multisig.go | 388 +++++++++++++++++++++++++++++ documentation/en/api-v0-methods.md | 2 +- 4 files changed, 391 insertions(+), 2 deletions(-) create mode 100644 cmd/lotus-shed/miner-multisig.go diff --git a/api/v0api/full.go b/api/v0api/full.go index 076c37013..f646aa9fd 100644 --- a/api/v0api/full.go +++ b/api/v0api/full.go @@ -629,7 +629,7 @@ type FullNode interface { // proposal. This method of approval can be used to ensure you only approve // exactly the transaction you think you are. // It takes the following params: , , , , , - // , , + // , , MsigApproveTxnHash(context.Context, address.Address, uint64, address.Address, address.Address, types.BigInt, address.Address, uint64, []byte) (cid.Cid, error) //perm:sign // MsigCancel cancels a previously-proposed multisig message diff --git a/cmd/lotus-shed/main.go b/cmd/lotus-shed/main.go index 7c4391f18..e06b63080 100644 --- a/cmd/lotus-shed/main.go +++ b/cmd/lotus-shed/main.go @@ -59,6 +59,7 @@ func main() { signaturesCmd, actorCmd, minerTypesCmd, + minerMultisigsCmd, } app := &cli.App{ diff --git a/cmd/lotus-shed/miner-multisig.go b/cmd/lotus-shed/miner-multisig.go new file mode 100644 index 000000000..d9f158090 --- /dev/null +++ b/cmd/lotus-shed/miner-multisig.go @@ -0,0 +1,388 @@ +package main + +import ( + "bytes" + "fmt" + "strconv" + + "github.com/filecoin-project/go-state-types/abi" + miner5 "github.com/filecoin-project/specs-actors/v5/actors/builtin/miner" + + msig5 "github.com/filecoin-project/specs-actors/v5/actors/builtin/multisig" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/big" + "github.com/filecoin-project/lotus/build" + "github.com/filecoin-project/lotus/chain/actors" + "github.com/filecoin-project/lotus/chain/actors/builtin/miner" + "github.com/filecoin-project/lotus/chain/types" + lcli "github.com/filecoin-project/lotus/cli" + "github.com/urfave/cli/v2" + "golang.org/x/xerrors" +) + +var minerMultisigsCmd = &cli.Command{ + Name: "miner-multisig", + Description: "a collection of utilities for using multisigs as owner addresses of miners", + Subcommands: []*cli.Command{ + mmProposeWithdrawBalance, + mmApproveWithdrawBalance, + mmProposeChangeOwner, + mmApproveChangeOwner, + }, + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "from", + Usage: "specify address to send message from", + Required: true, + }, + &cli.StringFlag{ + Name: "multisig", + Usage: "specify multisig that will receive the message", + Required: true, + }, + &cli.StringFlag{ + Name: "miner", + Usage: "specify miner being acted upon", + Required: true, + }, + }, +} + +var mmProposeWithdrawBalance = &cli.Command{ + Name: "propose-withdraw", + Usage: "Propose to withdraw FIL from the miner", + ArgsUsage: "[amount]", + Action: func(cctx *cli.Context) error { + if !cctx.Args().Present() { + return fmt.Errorf("must pass amount to withdraw") + } + + api, closer, err := lcli.GetFullNodeAPI(cctx) + if err != nil { + return err + } + defer closer() + + ctx := lcli.ReqContext(cctx) + + multisigAddr, sender, minerAddr, err := getInputs(cctx) + if err != nil { + return err + } + + val, err := types.ParseFIL(cctx.Args().First()) + if err != nil { + return err + } + + sp, err := actors.SerializeParams(&miner5.WithdrawBalanceParams{ + AmountRequested: abi.TokenAmount(val), + }) + if err != nil { + return err + } + + pcid, err := api.MsigPropose(ctx, multisigAddr, minerAddr, big.Zero(), sender, uint64(miner.Methods.WithdrawBalance), sp) + if err != nil { + return xerrors.Errorf("proposing message: %w", err) + } + + fmt.Fprintln(cctx.App.Writer, "Propose Message CID:", pcid) + + // wait for it to get mined into a block + wait, err := api.StateWaitMsg(ctx, pcid, build.MessageConfidence) + if err != nil { + return err + } + + // check it executed successfully + if wait.Receipt.ExitCode != 0 { + fmt.Fprintln(cctx.App.Writer, "Propose owner change tx failed!") + return err + } + + var retval msig5.ProposeReturn + if err := retval.UnmarshalCBOR(bytes.NewReader(wait.Receipt.Return)); err != nil { + return fmt.Errorf("failed to unmarshal propose return value: %w", err) + } + + fmt.Printf("Transaction ID: %d\n", retval.TxnID) + if retval.Applied { + fmt.Printf("Transaction was executed during propose\n") + fmt.Printf("Exit Code: %d\n", retval.Code) + fmt.Printf("Return Value: %x\n", retval.Ret) + } + + return nil + }, +} + +var mmApproveWithdrawBalance = &cli.Command{ + Name: "approve-withdraw", + Usage: "Approve to withdraw FIL from the miner", + ArgsUsage: "[amount txnId proposer]", + Action: func(cctx *cli.Context) error { + if cctx.NArg() != 3 { + return fmt.Errorf("must pass amount, txn Id, and proposer address") + } + + api, closer, err := lcli.GetFullNodeAPI(cctx) + if err != nil { + return err + } + defer closer() + + ctx := lcli.ReqContext(cctx) + + multisigAddr, sender, minerAddr, err := getInputs(cctx) + if err != nil { + return err + } + + val, err := types.ParseFIL(cctx.Args().First()) + if err != nil { + return err + } + + sp, err := actors.SerializeParams(&miner5.WithdrawBalanceParams{ + AmountRequested: abi.TokenAmount(val), + }) + if err != nil { + return err + } + + txid, err := strconv.ParseUint(cctx.Args().Get(1), 10, 64) + if err != nil { + return err + } + + proposer, err := address.NewFromString(cctx.Args().Get(2)) + if err != nil { + return err + } + + acid, err := api.MsigApproveTxnHash(ctx, multisigAddr, txid, proposer, minerAddr, big.Zero(), sender, uint64(miner.Methods.WithdrawBalance), sp) + if err != nil { + return xerrors.Errorf("approving message: %w", err) + } + + fmt.Fprintln(cctx.App.Writer, "Approve Message CID:", acid) + + // wait for it to get mined into a block + wait, err := api.StateWaitMsg(ctx, acid, build.MessageConfidence) + if err != nil { + return err + } + + // check it executed successfully + if wait.Receipt.ExitCode != 0 { + fmt.Fprintln(cctx.App.Writer, "Approve owner change tx failed!") + return err + } + + var retval msig5.ApproveReturn + if err := retval.UnmarshalCBOR(bytes.NewReader(wait.Receipt.Return)); err != nil { + return fmt.Errorf("failed to unmarshal approve return value: %w", err) + } + + if retval.Applied { + fmt.Printf("Transaction was executed with the approve\n") + fmt.Printf("Exit Code: %d\n", retval.Code) + fmt.Printf("Return Value: %x\n", retval.Ret) + } else { + fmt.Println("Transaction was approved, but not executed") + } + return nil + }, +} + +var mmProposeChangeOwner = &cli.Command{ + Name: "propose-change-owner", + Usage: "Propose an owner address change", + ArgsUsage: "[newOwner]", + Action: func(cctx *cli.Context) error { + if !cctx.Args().Present() { + return fmt.Errorf("must pass new owner address") + } + + api, closer, err := lcli.GetFullNodeAPI(cctx) + if err != nil { + return err + } + defer closer() + + ctx := lcli.ReqContext(cctx) + + multisigAddr, sender, minerAddr, err := getInputs(cctx) + if err != nil { + return err + } + + na, err := address.NewFromString(cctx.Args().First()) + if err != nil { + return err + } + + newAddr, err := api.StateLookupID(ctx, na, types.EmptyTSK) + if err != nil { + return err + } + + mi, err := api.StateMinerInfo(ctx, minerAddr, types.EmptyTSK) + if err != nil { + return err + } + + if mi.Owner == newAddr { + return fmt.Errorf("owner address already set to %s", na) + } + + sp, err := actors.SerializeParams(&newAddr) + if err != nil { + return xerrors.Errorf("serializing params: %w", err) + } + + pcid, err := api.MsigPropose(ctx, multisigAddr, minerAddr, big.Zero(), sender, uint64(miner.Methods.ChangeOwnerAddress), sp) + if err != nil { + return xerrors.Errorf("proposing message: %w", err) + } + + fmt.Fprintln(cctx.App.Writer, "Propose Message CID:", pcid) + + // wait for it to get mined into a block + wait, err := api.StateWaitMsg(ctx, pcid, build.MessageConfidence) + if err != nil { + return err + } + + // check it executed successfully + if wait.Receipt.ExitCode != 0 { + fmt.Fprintln(cctx.App.Writer, "Propose owner change tx failed!") + return err + } + + var retval msig5.ProposeReturn + if err := retval.UnmarshalCBOR(bytes.NewReader(wait.Receipt.Return)); err != nil { + return fmt.Errorf("failed to unmarshal propose return value: %w", err) + } + + fmt.Printf("Transaction ID: %d\n", retval.TxnID) + if retval.Applied { + fmt.Printf("Transaction was executed during propose\n") + fmt.Printf("Exit Code: %d\n", retval.Code) + fmt.Printf("Return Value: %x\n", retval.Ret) + } + return nil + }, +} + +var mmApproveChangeOwner = &cli.Command{ + Name: "approve-change-owner", + Usage: "Approve an owner address change", + ArgsUsage: "[newOwner txnId proposer]", + Action: func(cctx *cli.Context) error { + if cctx.NArg() != 3 { + return fmt.Errorf("must pass new owner address, txn Id, and proposer address") + } + + api, closer, err := lcli.GetFullNodeAPI(cctx) + if err != nil { + return err + } + defer closer() + + ctx := lcli.ReqContext(cctx) + + multisigAddr, sender, minerAddr, err := getInputs(cctx) + if err != nil { + return err + } + + na, err := address.NewFromString(cctx.Args().First()) + if err != nil { + return err + } + + newAddr, err := api.StateLookupID(ctx, na, types.EmptyTSK) + if err != nil { + return err + } + + txid, err := strconv.ParseUint(cctx.Args().Get(1), 10, 64) + if err != nil { + return err + } + + proposer, err := address.NewFromString(cctx.Args().Get(2)) + if err != nil { + return err + } + + mi, err := api.StateMinerInfo(ctx, minerAddr, types.EmptyTSK) + if err != nil { + return err + } + + if mi.Owner == newAddr { + return fmt.Errorf("owner address already set to %s", na) + } + + sp, err := actors.SerializeParams(&newAddr) + if err != nil { + return xerrors.Errorf("serializing params: %w", err) + } + + acid, err := api.MsigApproveTxnHash(ctx, multisigAddr, txid, proposer, minerAddr, big.Zero(), sender, uint64(miner.Methods.ChangeOwnerAddress), sp) + if err != nil { + return xerrors.Errorf("approving message: %w", err) + } + + fmt.Fprintln(cctx.App.Writer, "Approve Message CID:", acid) + + // wait for it to get mined into a block + wait, err := api.StateWaitMsg(ctx, acid, build.MessageConfidence) + if err != nil { + return err + } + + // check it executed successfully + if wait.Receipt.ExitCode != 0 { + fmt.Fprintln(cctx.App.Writer, "Approve owner change tx failed!") + return err + } + + var retval msig5.ApproveReturn + if err := retval.UnmarshalCBOR(bytes.NewReader(wait.Receipt.Return)); err != nil { + return fmt.Errorf("failed to unmarshal approve return value: %w", err) + } + + if retval.Applied { + fmt.Printf("Transaction was executed with the approve\n") + fmt.Printf("Exit Code: %d\n", retval.Code) + fmt.Printf("Return Value: %x\n", retval.Ret) + } else { + fmt.Println("Transaction was approved, but not executed") + } + return nil + }, +} + +func getInputs(cctx *cli.Context) (address.Address, address.Address, address.Address, error) { + multisigAddr, err := address.NewFromString(cctx.String("multisig")) + if err != nil { + return address.Undef, address.Undef, address.Undef, err + } + + sender, err := address.NewFromString(cctx.String("from")) + if err != nil { + return address.Undef, address.Undef, address.Undef, err + } + + minerAddr, err := address.NewFromString(cctx.String("miner")) + if err != nil { + return address.Undef, address.Undef, address.Undef, err + } + + return multisigAddr, sender, minerAddr, nil +} diff --git a/documentation/en/api-v0-methods.md b/documentation/en/api-v0-methods.md index f6da2244c..7e7216f16 100644 --- a/documentation/en/api-v0-methods.md +++ b/documentation/en/api-v0-methods.md @@ -2505,7 +2505,7 @@ using both transaction ID and a hash of the parameters used in the proposal. This method of approval can be used to ensure you only approve exactly the transaction you think you are. It takes the following params: , , , , , -, , +, , Perms: sign From 74db586fdfaecdac1ed4017fcbb25e7b362786a3 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 46/46] sealing: Fix restartSectors race --- extern/storage-sealing/fsm.go | 3 +++ extern/storage-sealing/garbage.go | 2 ++ extern/storage-sealing/input.go | 3 +++ extern/storage-sealing/sealing.go | 7 +++++++ 4 files changed, 15 insertions(+) diff --git a/extern/storage-sealing/fsm.go b/extern/storage-sealing/fsm.go index e899701cc..359c49eb3 100644 --- a/extern/storage-sealing/fsm.go +++ b/extern/storage-sealing/fsm.go @@ -522,6 +522,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) @@ -539,6 +541,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 bf66382d3..4a698ea1d 100644 --- a/extern/storage-sealing/input.go +++ b/extern/storage-sealing/input.go @@ -394,6 +394,7 @@ 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 } @@ -446,7 +447,9 @@ 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/sealing.go b/extern/storage-sealing/sealing.go index 2d53223db..2019aa131 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 @@ -162,6 +164,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{}) @@ -189,10 +192,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{}) }