mirror of
https://github.com/filecoin-project/lotus.git
synced 2025-05-17 07:08:26 +08:00
feat(f3): remove dynamic manifests and specify full static manifests (#13074)
* feat(f3): allow specifying full static manifests Signed-off-by: Jakub Sztandera <oss@kubuxu.com> * fix interop ManifestServerID Signed-off-by: Jakub Sztandera <oss@kubuxu.com> * cleanup F3BoostrapEpoch Signed-off-by: Jakub Sztandera <oss@kubuxu.com> * fix butterfly Signed-off-by: Jakub Sztandera <oss@kubuxu.com> * Rip out dynamic manifest handling Signed-off-by: Jakub Sztandera <oss@kubuxu.com> * Fix itests Signed-off-by: Jakub Sztandera <oss@kubuxu.com> * fix lint Signed-off-by: Jakub Sztandera <oss@kubuxu.com> * Disable non-relevant linter Signed-off-by: Jakub Sztandera <oss@kubuxu.com> * fix tests Signed-off-by: Jakub Sztandera <oss@kubuxu.com> * Address review Signed-off-by: Jakub Sztandera <oss@kubuxu.com> * add CHANGELOG Signed-off-by: Jakub Sztandera <oss@kubuxu.com> * Update build/buildconstants/f3manifest_butterfly.json Co-authored-by: Masih H. Derkani <m@derkani.org> * Update build/buildconstants/f3manifest_interop.json Co-authored-by: Masih H. Derkani <m@derkani.org> * Update build/buildconstants/f3manifest_2k.json Co-authored-by: Masih H. Derkani <m@derkani.org> --------- Signed-off-by: Jakub Sztandera <oss@kubuxu.com> Co-authored-by: Masih H. Derkani <m@derkani.org>
This commit is contained in:
@ -10,7 +10,6 @@ linters:
|
||||
- gosec
|
||||
- unconvert
|
||||
- staticcheck
|
||||
- exportloopref
|
||||
- unused
|
||||
|
||||
# We don't want to skip builtin/
|
||||
@ -51,7 +50,6 @@ issues:
|
||||
|
||||
exclude-use-default: false
|
||||
exclude-rules:
|
||||
|
||||
- path: .*_test.go
|
||||
linters:
|
||||
- gosec
|
||||
|
@ -19,6 +19,7 @@
|
||||
- feat: expose `/v2` APIs through Lotus Gateway ([filecoin-project/lotus#13075](https://github.com/filecoin-project/lotus/pull/13075))
|
||||
- chore: upgrade to go-f3 `v0.8.4` ([filecoin-project/lotus#13084](https://github.com/filecoin-project/lotus/pull/13084))
|
||||
- fix(f3): limit the concurrency of F3 power table calculation ([filecoin-project/lotus#13085](https://github.com/filecoin-project/lotus/pull/13085))
|
||||
- feat(f3): remove dynnamic manifest functionality and use static manifest ([filecoin-project/lotus#13074](https://github.com/filecoin-project/lotus/pull/13074))
|
||||
|
||||
See https://github.com/filecoin-project/lotus/blob/release/v1.33.0/CHANGELOG.md
|
||||
|
||||
|
61
build/buildconstants/f3manifest_2k.json
Normal file
61
build/buildconstants/f3manifest_2k.json
Normal file
@ -0,0 +1,61 @@
|
||||
{
|
||||
"Pause": false,
|
||||
"ProtocolVersion": 7,
|
||||
"InitialInstance": 0,
|
||||
"BootstrapEpoch": 1000,
|
||||
"NetworkName": "2k",
|
||||
"ExplicitPower": null,
|
||||
"IgnoreECPower": false,
|
||||
"InitialPowerTable": null,
|
||||
"CommitteeLookback": 10,
|
||||
"CatchUpAlignment": 15000000000,
|
||||
"Gpbft": {
|
||||
"Delta": 6000000000,
|
||||
"DeltaBackOffExponent": 2,
|
||||
"QualityDeltaMultiplier": 1,
|
||||
"MaxLookaheadRounds": 5,
|
||||
"ChainProposedLength": 100,
|
||||
"RebroadcastBackoffBase": 6000000000,
|
||||
"RebroadcastBackoffExponent": 1.3,
|
||||
"RebroadcastBackoffSpread": 0.1,
|
||||
"RebroadcastBackoffMax": 60000000000
|
||||
},
|
||||
"EC": {
|
||||
"Period": 4000000000,
|
||||
"Finality": 900,
|
||||
"DelayMultiplier": 2,
|
||||
"BaseDecisionBackoffTable": [1.3, 1.69, 2.2, 2.86, 3.71, 4.83, 6.27, 7.5],
|
||||
"HeadLookback": 4,
|
||||
"Finalize": true
|
||||
},
|
||||
"CertificateExchange": {
|
||||
"ClientRequestTimeout": 10000000000,
|
||||
"ServerRequestTimeout": 60000000000,
|
||||
"MinimumPollInterval": 30000000000,
|
||||
"MaximumPollInterval": 120000000000
|
||||
},
|
||||
"PubSub": {
|
||||
"CompressionEnabled": true,
|
||||
"ChainCompressionEnabled": true,
|
||||
"GMessageSubscriptionBufferSize": 128,
|
||||
"ValidatedMessageBufferSize": 128
|
||||
},
|
||||
"ChainExchange": {
|
||||
"SubscriptionBufferSize": 32,
|
||||
"MaxChainLength": 100,
|
||||
"MaxInstanceLookahead": 10,
|
||||
"MaxDiscoveredChainsPerInstance": 1000,
|
||||
"MaxWantedChainsPerInstance": 1000,
|
||||
"RebroadcastInterval": 2000000000,
|
||||
"MaxTimestampAge": 8000000000
|
||||
},
|
||||
"PartialMessageManager": {
|
||||
"PendingDiscoveredChainsBufferSize": 100,
|
||||
"PendingPartialMessagesBufferSize": 100,
|
||||
"PendingChainBroadcastsBufferSize": 100,
|
||||
"PendingInstanceRemovalBufferSize": 10,
|
||||
"CompletedMessagesBufferSize": 100,
|
||||
"MaxBufferedMessagesPerInstance": 25000,
|
||||
"MaxCachedValidatedMessagesPerInstance": 25000
|
||||
}
|
||||
}
|
61
build/buildconstants/f3manifest_butterfly.json
Normal file
61
build/buildconstants/f3manifest_butterfly.json
Normal file
@ -0,0 +1,61 @@
|
||||
{
|
||||
"Pause": false,
|
||||
"ProtocolVersion": 7,
|
||||
"InitialInstance": 0,
|
||||
"BootstrapEpoch": 1000,
|
||||
"NetworkName": "butterflynet",
|
||||
"ExplicitPower": null,
|
||||
"IgnoreECPower": false,
|
||||
"InitialPowerTable": null,
|
||||
"CommitteeLookback": 10,
|
||||
"CatchUpAlignment": 15000000000,
|
||||
"Gpbft": {
|
||||
"Delta": 6000000000,
|
||||
"DeltaBackOffExponent": 2,
|
||||
"QualityDeltaMultiplier": 1,
|
||||
"MaxLookaheadRounds": 5,
|
||||
"ChainProposedLength": 100,
|
||||
"RebroadcastBackoffBase": 6000000000,
|
||||
"RebroadcastBackoffExponent": 1.3,
|
||||
"RebroadcastBackoffSpread": 0.1,
|
||||
"RebroadcastBackoffMax": 60000000000
|
||||
},
|
||||
"EC": {
|
||||
"Period": 30000000000,
|
||||
"Finality": 900,
|
||||
"DelayMultiplier": 2,
|
||||
"BaseDecisionBackoffTable": [1.3, 1.69, 2.2, 2.86, 3.71, 4.83, 6.27, 7.5],
|
||||
"HeadLookback": 4,
|
||||
"Finalize": true
|
||||
},
|
||||
"CertificateExchange": {
|
||||
"ClientRequestTimeout": 10000000000,
|
||||
"ServerRequestTimeout": 60000000000,
|
||||
"MinimumPollInterval": 30000000000,
|
||||
"MaximumPollInterval": 120000000000
|
||||
},
|
||||
"PubSub": {
|
||||
"CompressionEnabled": true,
|
||||
"ChainCompressionEnabled": true,
|
||||
"GMessageSubscriptionBufferSize": 128,
|
||||
"ValidatedMessageBufferSize": 128
|
||||
},
|
||||
"ChainExchange": {
|
||||
"SubscriptionBufferSize": 32,
|
||||
"MaxChainLength": 100,
|
||||
"MaxInstanceLookahead": 10,
|
||||
"MaxDiscoveredChainsPerInstance": 1000,
|
||||
"MaxWantedChainsPerInstance": 1000,
|
||||
"RebroadcastInterval": 2000000000,
|
||||
"MaxTimestampAge": 8000000000
|
||||
},
|
||||
"PartialMessageManager": {
|
||||
"PendingDiscoveredChainsBufferSize": 100,
|
||||
"PendingPartialMessagesBufferSize": 100,
|
||||
"PendingChainBroadcastsBufferSize": 100,
|
||||
"PendingInstanceRemovalBufferSize": 10,
|
||||
"CompletedMessagesBufferSize": 100,
|
||||
"MaxBufferedMessagesPerInstance": 25000,
|
||||
"MaxCachedValidatedMessagesPerInstance": 25000
|
||||
}
|
||||
}
|
@ -2,11 +2,13 @@
|
||||
"Pause": false,
|
||||
"ProtocolVersion": 7,
|
||||
"InitialInstance": 0,
|
||||
"BootstrapEpoch": 5000000,
|
||||
"NetworkName": "filecoin",
|
||||
"BootstrapEpoch": 2081674,
|
||||
"NetworkName": "calibrationnet",
|
||||
"ExplicitPower": null,
|
||||
"IgnoreECPower": false,
|
||||
"InitialPowerTable": null,
|
||||
"InitialPowerTable": {
|
||||
"/": "bafy2bzaceab236vmmb3n4q4tkvua2n4dphcbzzxerxuey3mot4g3cov5j3r2c"
|
||||
},
|
||||
"CommitteeLookback": 10,
|
||||
"CatchUpAlignment": 15000000000,
|
||||
"Gpbft": {
|
||||
@ -34,7 +36,7 @@
|
||||
6.27,
|
||||
7.5
|
||||
],
|
||||
"HeadLookback": 0,
|
||||
"HeadLookback": 4,
|
||||
"Finalize": true
|
||||
},
|
||||
"CertificateExchange": {
|
61
build/buildconstants/f3manifest_interop.json
Normal file
61
build/buildconstants/f3manifest_interop.json
Normal file
@ -0,0 +1,61 @@
|
||||
{
|
||||
"Pause": false,
|
||||
"ProtocolVersion": 7,
|
||||
"InitialInstance": 0,
|
||||
"BootstrapEpoch": 1000,
|
||||
"NetworkName": "interopnet",
|
||||
"ExplicitPower": null,
|
||||
"IgnoreECPower": false,
|
||||
"InitialPowerTable": null,
|
||||
"CommitteeLookback": 10,
|
||||
"CatchUpAlignment": 15000000000,
|
||||
"Gpbft": {
|
||||
"Delta": 6000000000,
|
||||
"DeltaBackOffExponent": 2,
|
||||
"QualityDeltaMultiplier": 1,
|
||||
"MaxLookaheadRounds": 5,
|
||||
"ChainProposedLength": 100,
|
||||
"RebroadcastBackoffBase": 6000000000,
|
||||
"RebroadcastBackoffExponent": 1.3,
|
||||
"RebroadcastBackoffSpread": 0.1,
|
||||
"RebroadcastBackoffMax": 60000000000
|
||||
},
|
||||
"EC": {
|
||||
"Period": 30000000000,
|
||||
"Finality": 900,
|
||||
"DelayMultiplier": 2,
|
||||
"BaseDecisionBackoffTable": [1.3, 1.69, 2.2, 2.86, 3.71, 4.83, 6.27, 7.5],
|
||||
"HeadLookback": 4,
|
||||
"Finalize": true
|
||||
},
|
||||
"CertificateExchange": {
|
||||
"ClientRequestTimeout": 10000000000,
|
||||
"ServerRequestTimeout": 60000000000,
|
||||
"MinimumPollInterval": 30000000000,
|
||||
"MaximumPollInterval": 120000000000
|
||||
},
|
||||
"PubSub": {
|
||||
"CompressionEnabled": true,
|
||||
"ChainCompressionEnabled": true,
|
||||
"GMessageSubscriptionBufferSize": 128,
|
||||
"ValidatedMessageBufferSize": 128
|
||||
},
|
||||
"ChainExchange": {
|
||||
"SubscriptionBufferSize": 32,
|
||||
"MaxChainLength": 100,
|
||||
"MaxInstanceLookahead": 10,
|
||||
"MaxDiscoveredChainsPerInstance": 1000,
|
||||
"MaxWantedChainsPerInstance": 1000,
|
||||
"RebroadcastInterval": 2000000000,
|
||||
"MaxTimestampAge": 8000000000
|
||||
},
|
||||
"PartialMessageManager": {
|
||||
"PendingDiscoveredChainsBufferSize": 100,
|
||||
"PendingPartialMessagesBufferSize": 100,
|
||||
"PendingChainBroadcastsBufferSize": 100,
|
||||
"PendingInstanceRemovalBufferSize": 10,
|
||||
"CompletedMessagesBufferSize": 100,
|
||||
"MaxBufferedMessagesPerInstance": 25000,
|
||||
"MaxCachedValidatedMessagesPerInstance": 25000
|
||||
}
|
||||
}
|
63
build/buildconstants/f3manifest_mainnet.json
Normal file
63
build/buildconstants/f3manifest_mainnet.json
Normal file
@ -0,0 +1,63 @@
|
||||
{
|
||||
"Pause": false,
|
||||
"ProtocolVersion": 7,
|
||||
"InitialInstance": 0,
|
||||
"BootstrapEpoch": 4920480,
|
||||
"NetworkName": "filecoin",
|
||||
"ExplicitPower": null,
|
||||
"IgnoreECPower": false,
|
||||
"InitialPowerTable": {
|
||||
"/": "bafy2bzacecklgxd2eksmodvhgurqvorkg3wamgqkrunir3al2gchv2cikgmbu"
|
||||
},
|
||||
"CommitteeLookback": 10,
|
||||
"CatchUpAlignment": 15000000000,
|
||||
"Gpbft": {
|
||||
"Delta": 6000000000,
|
||||
"DeltaBackOffExponent": 2,
|
||||
"QualityDeltaMultiplier": 8,
|
||||
"MaxLookaheadRounds": 5,
|
||||
"ChainProposedLength": 20,
|
||||
"RebroadcastBackoffBase": 6000000000,
|
||||
"RebroadcastBackoffExponent": 1.3,
|
||||
"RebroadcastBackoffSpread": 0.1,
|
||||
"RebroadcastBackoffMax": 60000000000
|
||||
},
|
||||
"EC": {
|
||||
"Period": 30000000000,
|
||||
"Finality": 900,
|
||||
"DelayMultiplier": 2,
|
||||
"BaseDecisionBackoffTable": [1.3, 1.69, 2.2, 2.86, 3.71, 4.83, 6.27, 7.5],
|
||||
"HeadLookback": 4,
|
||||
"Finalize": true
|
||||
},
|
||||
"CertificateExchange": {
|
||||
"ClientRequestTimeout": 10000000000,
|
||||
"ServerRequestTimeout": 60000000000,
|
||||
"MinimumPollInterval": 30000000000,
|
||||
"MaximumPollInterval": 120000000000
|
||||
},
|
||||
"PubSub": {
|
||||
"CompressionEnabled": true,
|
||||
"ChainCompressionEnabled": true,
|
||||
"GMessageSubscriptionBufferSize": 768,
|
||||
"ValidatedMessageBufferSize": 1024
|
||||
},
|
||||
"ChainExchange": {
|
||||
"SubscriptionBufferSize": 64,
|
||||
"MaxChainLength": 20,
|
||||
"MaxInstanceLookahead": 10,
|
||||
"MaxDiscoveredChainsPerInstance": 1000,
|
||||
"MaxWantedChainsPerInstance": 1000,
|
||||
"RebroadcastInterval": 2000000000,
|
||||
"MaxTimestampAge": 16000000000
|
||||
},
|
||||
"PartialMessageManager": {
|
||||
"PendingDiscoveredChainsBufferSize": 100,
|
||||
"PendingPartialMessagesBufferSize": 100,
|
||||
"PendingChainBroadcastsBufferSize": 100,
|
||||
"PendingInstanceRemovalBufferSize": 10,
|
||||
"CompletedMessagesBufferSize": 100,
|
||||
"MaxBufferedMessagesPerInstance": 25000,
|
||||
"MaxCachedValidatedMessagesPerInstance": 25000
|
||||
}
|
||||
}
|
@ -4,6 +4,7 @@
|
||||
package buildconstants
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
@ -180,7 +181,6 @@ func init() {
|
||||
}
|
||||
|
||||
F3Enabled = getBoolean("LOTUS_F3_ENABLED", F3Enabled)
|
||||
F3BootstrapEpoch = getUpgradeHeight("LOTUS_F3_BOOTSTRAP_EPOCH", F3BootstrapEpoch)
|
||||
|
||||
BuildType |= Build2k
|
||||
|
||||
@ -211,11 +211,5 @@ var WhitelistedBlock = cid.Undef
|
||||
|
||||
var F3Enabled = true
|
||||
|
||||
var F3ManifestServerID = MustParseID("12D3KooWHcNBkqXEBrsjoveQvj6zDF3vK5S9tAfqyYaQF1LGSJwG")
|
||||
|
||||
// The initial F3 power table CID.
|
||||
var F3InitialPowerTableCID cid.Cid = cid.Undef
|
||||
|
||||
var F3BootstrapEpoch abi.ChainEpoch = 1000
|
||||
|
||||
var F3ParamsAddress = ""
|
||||
//go:embed f3manifest_2k.json
|
||||
var F3ManifestBytes []byte
|
||||
|
@ -4,6 +4,8 @@
|
||||
package buildconstants
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
|
||||
"github.com/ipfs/go-cid"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
@ -109,10 +111,5 @@ var WhitelistedBlock = cid.Undef
|
||||
|
||||
const F3Enabled = true
|
||||
|
||||
var F3ManifestServerID = MustParseID("12D3KooWJr9jy4ngtJNR7JC1xgLFra3DjEtyxskRYWvBK9TC3Yn6")
|
||||
|
||||
// The initial F3 power table CID.
|
||||
var F3InitialPowerTableCID cid.Cid = cid.Undef
|
||||
|
||||
const F3BootstrapEpoch abi.ChainEpoch = -1
|
||||
const F3ParamsAddress = "0x9fd3B2D38EE4C920c9954DA752eDF810887501c1"
|
||||
//go:embed f3manifest_butterfly.json
|
||||
var F3ManifestBytes []byte
|
||||
|
@ -4,6 +4,7 @@
|
||||
package buildconstants
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
@ -167,12 +168,5 @@ var WhitelistedBlock = cid.Undef
|
||||
|
||||
const F3Enabled = true
|
||||
|
||||
var F3ManifestServerID = MustParseID("12D3KooWS9vD9uwm8u2uPyJV32QBAhKAmPYwmziAgr3Xzk2FU1Mr")
|
||||
|
||||
// The initial F3 power table CID.
|
||||
var F3InitialPowerTableCID cid.Cid = cid.MustParse("bafy2bzaceab236vmmb3n4q4tkvua2n4dphcbzzxerxuey3mot4g3cov5j3r2c")
|
||||
|
||||
// Calibnet F3 activation epoch is 2024-10-24T13:30:00Z - Epoch 2081674
|
||||
const F3BootstrapEpoch abi.ChainEpoch = UpgradeTuktukHeight + 2880
|
||||
|
||||
var F3ParamsAddress = ""
|
||||
//go:embed f3manifest_calibnet.json
|
||||
var F3ManifestBytes []byte
|
||||
|
@ -149,11 +149,5 @@ var WhitelistedBlock = cid.Undef
|
||||
|
||||
const F3Enabled = true
|
||||
|
||||
var F3ManifestServerID = MustParseID("12D3KooWQJ2rdVnG4okDUB6yHQhAjNutGNemcM7XzqC9Eo4z9Jce")
|
||||
|
||||
// The initial F3 power table CID.
|
||||
var F3InitialPowerTableCID cid.Cid = cid.Undef
|
||||
|
||||
const F3BootstrapEpoch abi.ChainEpoch = 1000
|
||||
|
||||
var F3ParamsAddress = ""
|
||||
//go:embed f3manifest_interop.json
|
||||
var F3ManifestBytes []byte
|
||||
|
@ -4,6 +4,7 @@
|
||||
package buildconstants
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"math"
|
||||
"os"
|
||||
"strconv"
|
||||
@ -190,13 +191,7 @@ const Eip155ChainId = 314
|
||||
// WhitelistedBlock skips checks on message validity in this block to sidestep the zero-bls signature
|
||||
var WhitelistedBlock = cid.MustParse("bafy2bzaceapyg2uyzk7vueh3xccxkuwbz3nxewjyguoxvhx77malc2lzn2ybi")
|
||||
|
||||
// The F3 manifest server ID, if any.
|
||||
var F3ManifestServerID = MustParseID("12D3KooWENMwUF9YxvQxar7uBWJtZkA6amvK4xWmKXfSiHUo2Qq7")
|
||||
|
||||
// The initial F3 power table CID.
|
||||
var F3InitialPowerTableCID = cid.MustParse("bafy2bzacecklgxd2eksmodvhgurqvorkg3wamgqkrunir3al2gchv2cikgmbu")
|
||||
|
||||
const F3Enabled = true
|
||||
const F3BootstrapEpoch abi.ChainEpoch = -1
|
||||
|
||||
var F3ParamsAddress = "0xA19080A1Bcb82Bb61bcb9691EC94653Eb5315716"
|
||||
//go:embed f3manifest_mainnet.json
|
||||
var F3ManifestBytes []byte
|
||||
|
@ -6,8 +6,6 @@ package buildconstants
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/ipfs/go-cid"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/go-state-types/abi"
|
||||
builtin2 "github.com/filecoin-project/specs-actors/v2/actors/builtin"
|
||||
@ -67,16 +65,6 @@ func init() {
|
||||
if os.Getenv("LOTUS_ADDRESS_TYPE") == AddressMainnetEnvVar {
|
||||
SetAddressNetwork(address.Mainnet)
|
||||
}
|
||||
|
||||
if ptCid := os.Getenv("F3_INITIAL_POWERTABLE_CID"); ptCid != "" {
|
||||
if k, err := cid.Parse(ptCid); err != nil {
|
||||
log.Errorf("failed to parse F3_INITIAL_POWERTABLE_CID %q: %s", ptCid, err)
|
||||
} else if F3InitialPowerTableCID.Defined() && k != F3InitialPowerTableCID {
|
||||
log.Errorf("ignoring F3_INITIAL_POWERTABLE_CID as lotus has a hard-coded initial F3 power table")
|
||||
} else {
|
||||
F3InitialPowerTableCID = k
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sync
|
||||
|
@ -8,10 +8,10 @@
|
||||
package buildconstants
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"math/big"
|
||||
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/libp2p/go-libp2p/core/peer"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/go-state-types/abi"
|
||||
@ -125,14 +125,12 @@ var (
|
||||
|
||||
ZeroAddress = MustParseAddress("f3yaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaby2smx7a")
|
||||
|
||||
WhitelistedBlock = cid.Undef
|
||||
BootstrappersFile = ""
|
||||
GenesisFile = ""
|
||||
F3Enabled = false
|
||||
F3ManifestServerID peer.ID = ""
|
||||
F3BootstrapEpoch abi.ChainEpoch = -1
|
||||
F3InitialPowerTableCID = cid.Undef
|
||||
F3ParamsAddress = ""
|
||||
WhitelistedBlock = cid.Undef
|
||||
BootstrappersFile = ""
|
||||
GenesisFile = ""
|
||||
|
||||
F3Enabled = false
|
||||
F3ManifestBytes []byte
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
@ -1,12 +1,17 @@
|
||||
package buildconstants
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"math/big"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/ipfs/go-cid"
|
||||
logging "github.com/ipfs/go-log/v2"
|
||||
"github.com/libp2p/go-libp2p/core/peer"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/go-f3/manifest"
|
||||
"github.com/filecoin-project/go-state-types/abi"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/actors/policy"
|
||||
@ -47,3 +52,36 @@ func wholeFIL(whole uint64) *big.Int {
|
||||
bigWhole := big.NewInt(int64(whole))
|
||||
return bigWhole.Mul(bigWhole, big.NewInt(int64(FilecoinPrecision)))
|
||||
}
|
||||
|
||||
func F3Manifest() *manifest.Manifest {
|
||||
if F3ManifestBytes == nil {
|
||||
return nil
|
||||
}
|
||||
var manif manifest.Manifest
|
||||
|
||||
if err := json.Unmarshal(F3ManifestBytes, &manif); err != nil {
|
||||
log.Panicf("failed to unmarshal F3 manifest: %s", err)
|
||||
}
|
||||
if err := manif.Validate(); err != nil {
|
||||
log.Panicf("invalid F3 manifest: %s", err)
|
||||
}
|
||||
|
||||
if ptCid := os.Getenv("F3_INITIAL_POWERTABLE_CID"); ptCid != "" {
|
||||
if k, err := cid.Parse(ptCid); err != nil {
|
||||
log.Errorf("failed to parse F3_INITIAL_POWERTABLE_CID %q: %s", ptCid, err)
|
||||
} else if manif.InitialPowerTable.Defined() && k != manif.InitialPowerTable {
|
||||
log.Errorf("ignoring F3_INITIAL_POWERTABLE_CID as lotus has a hard-coded initial F3 power table")
|
||||
} else {
|
||||
manif.InitialPowerTable = k
|
||||
}
|
||||
}
|
||||
if !manif.InitialPowerTable.Defined() {
|
||||
log.Warn("initial power table is not specified, it will be populated automatically assuming this is testing network")
|
||||
}
|
||||
|
||||
// EC Period sanity check
|
||||
if manif.EC.Period != time.Duration(BlockDelaySecs)*time.Second {
|
||||
log.Panicf("static manifest EC period is %v, expected %v", manif.EC.Period, time.Duration(BlockDelaySecs)*time.Second)
|
||||
}
|
||||
return &manif
|
||||
}
|
||||
|
@ -1,18 +1,15 @@
|
||||
package lf3
|
||||
|
||||
import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/libp2p/go-libp2p/core/peer"
|
||||
|
||||
"github.com/filecoin-project/go-f3/gpbft"
|
||||
"github.com/filecoin-project/go-f3/manifest"
|
||||
"github.com/filecoin-project/go-state-types/abi"
|
||||
|
||||
"github.com/filecoin-project/lotus/build/buildconstants"
|
||||
"github.com/filecoin-project/lotus/chain/actors/policy"
|
||||
"github.com/filecoin-project/lotus/node/modules/dtypes"
|
||||
)
|
||||
|
||||
@ -24,21 +21,6 @@ type Config struct {
|
||||
// StaticManifest this instance's default manifest absent any dynamic manifests. Also see
|
||||
// PrioritizeStaticManifest.
|
||||
StaticManifest *manifest.Manifest
|
||||
// DynamicManifestProvider is the peer ID of the peer authorized to send us dynamic manifest
|
||||
// updates. Dynamic manifest updates can be used for testing but will not be used to affect
|
||||
// finality.
|
||||
DynamicManifestProvider peer.ID
|
||||
// PrioritizeStaticManifest means that, once we get within one finality of the static
|
||||
// manifest's bootstrap epoch we'll switch to it and ignore any further dynamic manifest
|
||||
// updates. This exists to enable bootstrapping F3.
|
||||
PrioritizeStaticManifest bool
|
||||
// TESTINGAllowDynamicFinalize allow dynamic manifests to finalize tipsets. DO NOT ENABLE
|
||||
// THIS IN PRODUCTION!
|
||||
AllowDynamicFinalize bool
|
||||
|
||||
// ContractAddress specifies the address of the contract carring F3 parameters
|
||||
ContractAddress string
|
||||
ContractPollInterval time.Duration
|
||||
}
|
||||
|
||||
// NewManifest constructs a sane F3 manifest based on the passed parameters. This function does not
|
||||
@ -85,34 +67,10 @@ func NewConfig(nn dtypes.NetworkName) *Config {
|
||||
if nn == "testnetnet" {
|
||||
nn = "filecoin"
|
||||
}
|
||||
pollInterval := 15 * time.Minute
|
||||
if envVar := os.Getenv("LOTUS_F3_POLL_INTERVAL"); len(envVar) != 0 {
|
||||
d, err := time.ParseDuration(envVar)
|
||||
if err != nil {
|
||||
log.Errorf("invalid duration in LOTUS_F3_POLL_INTERVAL, defaulting to %v", pollInterval)
|
||||
} else {
|
||||
pollInterval = d
|
||||
}
|
||||
|
||||
}
|
||||
c := &Config{
|
||||
BaseNetworkName: gpbft.NetworkName(nn),
|
||||
PrioritizeStaticManifest: true,
|
||||
DynamicManifestProvider: buildconstants.F3ManifestServerID,
|
||||
AllowDynamicFinalize: false,
|
||||
ContractPollInterval: pollInterval,
|
||||
}
|
||||
if buildconstants.F3BootstrapEpoch >= 0 {
|
||||
c.StaticManifest = NewManifest(
|
||||
c.BaseNetworkName,
|
||||
policy.ChainFinality,
|
||||
buildconstants.F3BootstrapEpoch,
|
||||
time.Duration(buildconstants.BlockDelaySecs)*time.Second,
|
||||
buildconstants.F3InitialPowerTableCID,
|
||||
)
|
||||
}
|
||||
if buildconstants.F3ParamsAddress != "" {
|
||||
c.ContractAddress = buildconstants.F3ParamsAddress
|
||||
BaseNetworkName: gpbft.NetworkName(nn),
|
||||
StaticManifest: buildconstants.F3Manifest(),
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
@ -1,356 +1,25 @@
|
||||
package lf3
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/flate"
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ipfs/go-datastore"
|
||||
"github.com/ipfs/go-datastore/namespace"
|
||||
pubsub "github.com/libp2p/go-libp2p-pubsub"
|
||||
"golang.org/x/sync/errgroup"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/go-f3/ec"
|
||||
"github.com/filecoin-project/go-f3/gpbft"
|
||||
"github.com/filecoin-project/go-f3/manifest"
|
||||
"github.com/filecoin-project/go-state-types/abi"
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/build/buildconstants"
|
||||
"github.com/filecoin-project/lotus/chain/store"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/chain/types/ethtypes"
|
||||
"github.com/filecoin-project/lotus/lib/must"
|
||||
"github.com/filecoin-project/lotus/node/modules/dtypes"
|
||||
"github.com/filecoin-project/lotus/node/modules/helpers"
|
||||
)
|
||||
|
||||
type headGetter store.ChainStore
|
||||
|
||||
func (hg *headGetter) GetHead(context.Context) (ec.TipSet, error) {
|
||||
head := (*store.ChainStore)(hg).GetHeaviestTipSet()
|
||||
if head == nil {
|
||||
return nil, xerrors.New("no heaviest tipset")
|
||||
func NewManifestProvider(mctx helpers.MetricsCtx, config *Config) (prov manifest.ManifestProvider, err error) {
|
||||
if config.StaticManifest == nil {
|
||||
return manifest.NoopManifestProvider{}, nil
|
||||
}
|
||||
return &f3TipSet{TipSet: head}, nil
|
||||
}
|
||||
|
||||
// Determines the max. number of configuration changes
|
||||
// that are allowed for the dynamic manifest.
|
||||
// If the manifest changes more than this number, the F3
|
||||
// message topic will be filtered
|
||||
var MaxDynamicManifestChangesAllowed = 1000
|
||||
|
||||
func NewManifestProvider(mctx helpers.MetricsCtx, config *Config, cs *store.ChainStore, ps *pubsub.PubSub, mds dtypes.MetadataDS, stateCaller StateCaller) (prov manifest.ManifestProvider, err error) {
|
||||
var primaryManifest manifest.ManifestProvider
|
||||
|
||||
// Check if static manifest activation is disabled
|
||||
staticDisabled := false
|
||||
if config.StaticManifest != nil && build.IsF3EpochActivationDisabled(config.StaticManifest.BootstrapEpoch) {
|
||||
log.Warnf("F3 activation disabled by environment configuration for bootstrap epoch %d", config.StaticManifest.BootstrapEpoch)
|
||||
staticDisabled = true
|
||||
return manifest.NoopManifestProvider{}, nil
|
||||
}
|
||||
|
||||
// Check if contract manifest activation is disabled
|
||||
contractDisabled := false
|
||||
if config.ContractAddress != "" && build.IsF3ContractActivationDisabled(config.ContractAddress) {
|
||||
log.Warnf("F3 activation disabled by environment configuration for contract %s", config.ContractAddress)
|
||||
contractDisabled = true
|
||||
}
|
||||
|
||||
if config.StaticManifest != nil && !staticDisabled {
|
||||
log.Infof("using static manifest as primary")
|
||||
primaryManifest, err = manifest.NewStaticManifestProvider(config.StaticManifest)
|
||||
} else if config.ContractAddress != "" && !contractDisabled {
|
||||
log.Infow("using contract manifest as primary", "address", config.ContractAddress)
|
||||
primaryManifest, err = NewContractManifestProvider(mctx, config, stateCaller)
|
||||
}
|
||||
|
||||
smp, err := manifest.NewStaticManifestProvider(config.StaticManifest)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating primary manifest: %w", err)
|
||||
return nil, xerrors.Errorf("creating static manifest provider: %w", err)
|
||||
}
|
||||
|
||||
if config.DynamicManifestProvider == "" || !build.IsF3PassiveTestingEnabled() {
|
||||
if config.StaticManifest == nil && config.ContractAddress == "" {
|
||||
return manifest.NoopManifestProvider{}, nil
|
||||
}
|
||||
return primaryManifest, nil
|
||||
}
|
||||
|
||||
opts := []manifest.DynamicManifestProviderOption{
|
||||
manifest.DynamicManifestProviderWithDatastore(
|
||||
namespace.Wrap(mds, datastore.NewKey("/f3-dynamic-manifest")),
|
||||
),
|
||||
}
|
||||
|
||||
if config.AllowDynamicFinalize {
|
||||
log.Error("dynamic F3 manifests are allowed to finalize tipsets, do not enable this in production!")
|
||||
}
|
||||
|
||||
networkNameBase := config.BaseNetworkName + "/"
|
||||
filter := func(m *manifest.Manifest) error {
|
||||
if m.EC.Finalize {
|
||||
if !config.AllowDynamicFinalize {
|
||||
return fmt.Errorf("refusing dynamic manifest that finalizes tipsets")
|
||||
}
|
||||
log.Error("WARNING: loading a dynamic F3 manifest that will finalize new tipsets")
|
||||
}
|
||||
if !strings.HasPrefix(string(m.NetworkName), string(networkNameBase)) {
|
||||
return fmt.Errorf(
|
||||
"refusing dynamic manifest with network name %q, must start with %q",
|
||||
m.NetworkName,
|
||||
networkNameBase,
|
||||
)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
opts = append(opts,
|
||||
manifest.DynamicManifestProviderWithFilter(filter),
|
||||
)
|
||||
|
||||
prov, err = manifest.NewDynamicManifestProvider(ps, config.DynamicManifestProvider, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if config.PrioritizeStaticManifest && primaryManifest != nil {
|
||||
prov, err = manifest.NewFusingManifestProvider(mctx,
|
||||
(*headGetter)(cs), prov, primaryManifest)
|
||||
}
|
||||
return prov, err
|
||||
}
|
||||
|
||||
type StateCaller interface {
|
||||
StateCall(ctx context.Context, msg *types.Message, tsk types.TipSetKey) (res *api.InvocResult, err error)
|
||||
}
|
||||
|
||||
type ContractManifestProvider struct {
|
||||
address string
|
||||
networkName gpbft.NetworkName
|
||||
stateCaller StateCaller
|
||||
pollInterval time.Duration
|
||||
|
||||
manifestChanges chan *manifest.Manifest
|
||||
|
||||
errgrp *errgroup.Group
|
||||
runningCtx context.Context
|
||||
cancel context.CancelFunc
|
||||
}
|
||||
|
||||
func NewContractManifestProvider(mctx helpers.MetricsCtx, config *Config, stateCaller StateCaller) (*ContractManifestProvider, error) {
|
||||
ctx, cancel := context.WithCancel(context.WithoutCancel(mctx))
|
||||
errgrp, ctx := errgroup.WithContext(ctx)
|
||||
return &ContractManifestProvider{
|
||||
stateCaller: stateCaller,
|
||||
address: config.ContractAddress,
|
||||
networkName: config.BaseNetworkName,
|
||||
pollInterval: config.ContractPollInterval,
|
||||
|
||||
manifestChanges: make(chan *manifest.Manifest, 1),
|
||||
|
||||
errgrp: errgrp,
|
||||
runningCtx: ctx,
|
||||
cancel: cancel,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (cmp *ContractManifestProvider) Start(context.Context) error {
|
||||
// no address, nothing to do
|
||||
if len(cmp.address) == 0 {
|
||||
// send nil so fusing knows we have nothing
|
||||
log.Infof("contract manifest provider, address unknown, exiting")
|
||||
cmp.manifestChanges <- nil
|
||||
return nil
|
||||
}
|
||||
|
||||
var knownManifest *manifest.Manifest
|
||||
knownManifest, err := cmp.fetchManifest(cmp.runningCtx)
|
||||
if err != nil {
|
||||
log.Warnw("got error while fetching manifest from contract", "error", err)
|
||||
}
|
||||
cmp.manifestChanges <- knownManifest
|
||||
|
||||
cmp.errgrp.Go(func() error {
|
||||
t := time.NewTicker(cmp.pollInterval)
|
||||
defer t.Stop()
|
||||
|
||||
loop:
|
||||
for cmp.runningCtx.Err() == nil {
|
||||
select {
|
||||
case <-t.C:
|
||||
m, err := cmp.fetchManifest(cmp.runningCtx)
|
||||
if err != nil {
|
||||
log.Warnw("got error while fetching manifest from contract", "error", err)
|
||||
continue loop
|
||||
}
|
||||
|
||||
if knownManifest.Equal(m) {
|
||||
continue loop
|
||||
}
|
||||
|
||||
c, err := m.Cid()
|
||||
if err != nil {
|
||||
log.Errorf("got error while computing manifest CID")
|
||||
}
|
||||
|
||||
if m != nil {
|
||||
log.Infow("new manifest from contract", "enabled", true,
|
||||
"bootstrapEpoch", m.BootstrapEpoch,
|
||||
"manifestCID", c)
|
||||
} else {
|
||||
log.Info("new manifest from contract", "enabled", false)
|
||||
}
|
||||
cmp.manifestChanges <- m
|
||||
knownManifest = m
|
||||
case <-cmp.runningCtx.Done():
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func decompressManifest(compressedManifest []byte) (*manifest.Manifest, error) {
|
||||
reader := io.LimitReader(flate.NewReader(bytes.NewReader(compressedManifest)), 1<<20)
|
||||
var m manifest.Manifest
|
||||
err := json.NewDecoder(reader).Decode(&m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &m, nil
|
||||
}
|
||||
|
||||
func (cmp *ContractManifestProvider) fetchManifest(ctx context.Context) (*manifest.Manifest, error) {
|
||||
ethReturn, err := cmp.callContract(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("calling contract at %s: %w", cmp.address, err)
|
||||
}
|
||||
if len(ethReturn) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
activationEpoch, compressedManifest, err := parseContractReturn(ethReturn)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parsing contract information: %w", err)
|
||||
}
|
||||
|
||||
if activationEpoch == math.MaxUint64 || len(compressedManifest) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
m, err := decompressManifest(compressedManifest)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("got error while decoding manifest: %w", err)
|
||||
}
|
||||
|
||||
if m.BootstrapEpoch < 0 || uint64(m.BootstrapEpoch) != activationEpoch {
|
||||
return nil, fmt.Errorf("bootstrap epoch does not match: %d != %d", m.BootstrapEpoch, activationEpoch)
|
||||
}
|
||||
|
||||
if !m.InitialPowerTable.Defined() && buildconstants.F3InitialPowerTableCID.Defined() {
|
||||
m.InitialPowerTable = buildconstants.F3InitialPowerTableCID
|
||||
}
|
||||
|
||||
if err := m.Validate(); err != nil {
|
||||
return nil, fmt.Errorf("manifest does not validate: %w", err)
|
||||
}
|
||||
|
||||
if m.NetworkName != cmp.networkName {
|
||||
return nil, fmt.Errorf("network name does not match, expected: %s, got: %s",
|
||||
cmp.networkName, m.NetworkName)
|
||||
}
|
||||
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func parseContractReturn(retBytes []byte) (uint64, []byte, error) {
|
||||
// 3*32 because there should be 3 slots minimum
|
||||
if len(retBytes) < 3*32 {
|
||||
return 0, nil, fmt.Errorf("no activation information")
|
||||
}
|
||||
|
||||
var slot []byte
|
||||
// split off first slot
|
||||
slot, retBytes = retBytes[:32], retBytes[32:]
|
||||
// it is uint64 so we want the last 8 bytes
|
||||
slot = slot[24:32]
|
||||
activationEpoch := binary.BigEndian.Uint64(slot)
|
||||
|
||||
// next slot is the offest to variable length bytes
|
||||
// it is always the same 0x00000...0040
|
||||
slot, retBytes = retBytes[:32], retBytes[32:]
|
||||
for i := 0; i < 31; i++ {
|
||||
if slot[i] != 0 {
|
||||
return 0, nil, fmt.Errorf("wrong value for offest (padding): slot[%d] = 0x%x != 0x00", i, slot[i])
|
||||
}
|
||||
}
|
||||
if slot[31] != 0x40 {
|
||||
return 0, nil, fmt.Errorf("wrong value for offest : slot[31] = 0x%x != 0x40", slot[31])
|
||||
}
|
||||
|
||||
// finally after that there are manifest bytes
|
||||
// starts with length in a full slot, slot no 3
|
||||
slot, retBytes = retBytes[:32], retBytes[32:]
|
||||
slot = slot[24:32]
|
||||
pLen := binary.BigEndian.Uint64(slot)
|
||||
if pLen > 4<<10 {
|
||||
return 0, nil, fmt.Errorf("too long declared payload: %d > %d", pLen, 4<<10)
|
||||
}
|
||||
payloadLength := int(pLen)
|
||||
|
||||
if payloadLength > len(retBytes) {
|
||||
return 0, nil, fmt.Errorf("not enough remaining bytes: %d > %d", payloadLength, retBytes)
|
||||
}
|
||||
|
||||
return activationEpoch, retBytes[:payloadLength], nil
|
||||
}
|
||||
|
||||
func (cmp *ContractManifestProvider) callContract(ctx context.Context) ([]byte, error) {
|
||||
address, err := ethtypes.ParseEthAddress(cmp.address)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("trying to parse contract address: %s: %w", cmp.address, err)
|
||||
}
|
||||
|
||||
ethCall := ethtypes.EthCall{
|
||||
To: &address,
|
||||
Data: must.One(ethtypes.DecodeHexString("0x2587660d")), // method ID of activationInformation()
|
||||
}
|
||||
|
||||
fMessage, err := ethCall.ToFilecoinMessage()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("converting to filecoin message: %w", err)
|
||||
}
|
||||
|
||||
msgRes, err := cmp.stateCaller.StateCall(ctx, fMessage, types.EmptyTSK)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("state call error: %w", err)
|
||||
}
|
||||
if msgRes.MsgRct.ExitCode != 0 {
|
||||
return nil, fmt.Errorf("message returned exit code %v: %v", msgRes.MsgRct.ExitCode, msgRes.Error)
|
||||
}
|
||||
|
||||
var ethReturn abi.CborBytes
|
||||
err = ethReturn.UnmarshalCBOR(bytes.NewReader(msgRes.MsgRct.Return))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not decode return value: %w", err)
|
||||
}
|
||||
return []byte(ethReturn), nil
|
||||
}
|
||||
|
||||
func (cmp *ContractManifestProvider) Stop(context.Context) error {
|
||||
cmp.cancel()
|
||||
return cmp.errgrp.Wait()
|
||||
}
|
||||
|
||||
func (cmp *ContractManifestProvider) ManifestUpdates() <-chan *manifest.Manifest {
|
||||
return cmp.manifestChanges
|
||||
return smp, err
|
||||
}
|
||||
|
@ -1,45 +0,0 @@
|
||||
package lf3
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/filecoin-project/go-f3/manifest"
|
||||
)
|
||||
|
||||
//go:embed testdata/contract_manifest_golden.json
|
||||
var manifestJSONBytes []byte
|
||||
|
||||
//go:embed testdata/contract_return.hex
|
||||
var hexReturn string
|
||||
|
||||
func TestContractManifest_ParseEthData(t *testing.T) {
|
||||
var manifestGolden manifest.Manifest
|
||||
err := json.Unmarshal(manifestJSONBytes, &manifestGolden)
|
||||
require.NoError(t, manifestGolden.Validate(), "golden manifest is not valid")
|
||||
|
||||
require.NoErrorf(t, err, "did manifest format change?")
|
||||
decodedHex, err := hex.DecodeString(strings.Trim(hexReturn, "\n"))
|
||||
require.NoError(t, err, "failed to decode hex string")
|
||||
|
||||
activationEpoch, compressedManifest, err := parseContractReturn(decodedHex)
|
||||
require.NoError(t, err, "parseContractReturn failed")
|
||||
|
||||
require.Equal(t, uint64(5000000), activationEpoch, "activationEpoch mismatch")
|
||||
|
||||
manifest, err := decompressManifest(compressedManifest)
|
||||
require.NoError(t, err, "decompressManifest failed")
|
||||
|
||||
require.NoError(t, manifest.Validate(), "manifest is not valid")
|
||||
|
||||
manifestJSON, err := json.MarshalIndent(manifest, "", " ")
|
||||
require.NoError(t, err, "failed to marshal manifest")
|
||||
|
||||
require.JSONEq(t, string(manifestJSONBytes), string(manifestJSON), "manifest JSON mismatch")
|
||||
|
||||
}
|
1
chain/lf3/testdata/contract_return.hex
vendored
1
chain/lf3/testdata/contract_return.hex
vendored
@ -1 +0,0 @@
|
||||
00000000000000000000000000000000000000000000000000000000004c4b40000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000002a38554c172da3010bde72b18ce190f360d90de82a16966424a214d0f9d1e16790d9ac8922bcb4dd24cfebd2bd9c63698810323ebbd5dbd7dbbd2fb45afd75f429e61ff732f0691e1a5dbd1ca28a6c413ea8c2b49d8d8eddf496e38883b991990ccc60cdcfe542993190de93c556c47db5703f773e0039a17a59f1f20b101fd980b648acbbe03e7afa9e08c9ba57a414db0cc85288eda4aa5711e56402dae14e18047d8086c86852a49b83188f74a3d6f803d13e8173a42306cf723bd117c2b1394c622a5ce4aea6dba89edfe3b7dd0e70c8501fa1cb5581530a5ecdfe2982a50b2481794f0f71c04376f8eb5c885e154a32bc22f090b78b5fa608710ad542ea3cc7a5682e10eb8a40ea42ac3e81ee5d6ec5c11d5e12bdc68051183cc58092a8ea7e0fa7724f398d910eb7bc393b475aa49996dafe79f245111ad430744fc289a1ad61e2e517365530d0787f2be70e97c22f0ba692dbcb54cab5cb555ce90713b91a584aafdbf1ca357d76497a3eb6a1d7841bd9c8caaf5d01bfbd5fa9337d9878ebc605cadc7de955bfd2e457c255f1aa3d52ee59fd562748e7b2742d486c79c81c1f92bdb81dc626d4d48054ab3c23f3966e69127a8725374fac0a835eabfa88f88a323e2824b9ee4c95209baa386a240743a4fadebe0f941472f97f9669d6f1aa25542c391d926cca5b53f2a6b6e4eef39d2ed8240d82265ce98e6a9b12dcde318f5ba30d10f2625f5897c8dc8bea80c39a4d556db838f4d3e79c430a8cd70b11d578da0eaaddbdfd7fd8352e0339e3145ddc1c825c968de1bcfa3dff2fc2790d9e7788d8bd6e84dd0d1423b099421496f5cc1938ee681b64f6569dd0224fdebe6dd941197dbc312da16ef4f2cd9ed9c67c82ee1b4aae70cb9f2638589a2aa0fc897f5fc09ace7e1544ef2a7406a66dbf2e0aa6566086c87d1e1b475c558772f3efe030000000000000000000000000000000000000000000000000000000000
|
@ -25,7 +25,6 @@ import (
|
||||
"github.com/filecoin-project/go-f3/manifest"
|
||||
"github.com/filecoin-project/go-state-types/abi"
|
||||
|
||||
"github.com/filecoin-project/lotus/chain/lf3"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/chain/types/ethtypes"
|
||||
lcli "github.com/filecoin-project/lotus/cli"
|
||||
@ -40,7 +39,6 @@ var f3Cmd = &cli.Command{
|
||||
Subcommands: []*cli.Command{
|
||||
f3ClearStateCmd,
|
||||
f3GenExplicitPower,
|
||||
f3CheckActivation,
|
||||
f3CheckActivationRaw,
|
||||
},
|
||||
}
|
||||
@ -74,54 +72,6 @@ func loadF3IDList(path string) ([]gpbft.ActorID, error) {
|
||||
return ids, nil
|
||||
}
|
||||
|
||||
var f3CheckActivation = &cli.Command{
|
||||
Name: "check-activation",
|
||||
Usage: "queries f3 parameters contract using chain module",
|
||||
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "contract",
|
||||
Usage: "address contract to query",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "networkname",
|
||||
Usage: "name of the network to be used",
|
||||
Value: "filecoin",
|
||||
},
|
||||
},
|
||||
Action: func(cctx *cli.Context) error {
|
||||
config := lf3.Config{
|
||||
BaseNetworkName: gpbft.NetworkName(cctx.String("networkname")),
|
||||
ContractAddress: cctx.String("contract"),
|
||||
ContractPollInterval: 15 * time.Second,
|
||||
}
|
||||
api, closer, err := cliutil.GetFullNodeAPIV1(cctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("getting api: %w", err)
|
||||
}
|
||||
defer closer()
|
||||
ctx := cliutil.ReqContext(cctx)
|
||||
prov, err := lf3.NewManifestProvider(ctx, &config, nil, nil, nil, api)
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating manifest proivder: %w", err)
|
||||
}
|
||||
|
||||
err = prov.Start(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("starting manifest provider: %w", err)
|
||||
}
|
||||
for {
|
||||
select {
|
||||
case m := <-prov.ManifestUpdates():
|
||||
log.Infof("new manifest: %+v\n", m)
|
||||
case <-ctx.Done():
|
||||
_ = prov.Stop(context.Background())
|
||||
return nil
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
var f3CheckActivationRaw = &cli.Command{
|
||||
Name: "check-activation-raw",
|
||||
Usage: "queries f3 parameters contract using raw logic",
|
||||
|
@ -8,9 +8,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/libp2p/go-libp2p"
|
||||
pubsub "github.com/libp2p/go-libp2p-pubsub"
|
||||
"github.com/libp2p/go-libp2p/core/host"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
@ -23,8 +20,8 @@ import (
|
||||
|
||||
"github.com/filecoin-project/lotus/api"
|
||||
lotus_api "github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/build/buildconstants"
|
||||
"github.com/filecoin-project/lotus/chain/lf3"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
"github.com/filecoin-project/lotus/itests/kit"
|
||||
"github.com/filecoin-project/lotus/node"
|
||||
"github.com/filecoin-project/lotus/node/modules"
|
||||
@ -37,10 +34,8 @@ const (
|
||||
)
|
||||
|
||||
type testEnv struct {
|
||||
nodes []*kit.TestFullNode
|
||||
miners []*kit.TestMiner
|
||||
// observer currently not use but may come handy to test certificate exchanges
|
||||
ms *manifest.ManifestSender
|
||||
nodes []*kit.TestFullNode
|
||||
miners []*kit.TestMiner
|
||||
m *manifest.Manifest
|
||||
t *testing.T
|
||||
testCtx context.Context
|
||||
@ -66,6 +61,9 @@ func TestF3_Enabled(t *testing.T) {
|
||||
// 2. Not-yet-ready state (F3 enabled but not yet operational)
|
||||
func TestF3_InactiveModes(t *testing.T) {
|
||||
kit.QuietMiningLogs()
|
||||
oldMani := buildconstants.F3ManifestBytes
|
||||
defer func() { buildconstants.F3ManifestBytes = oldMani }()
|
||||
buildconstants.F3ManifestBytes = nil
|
||||
|
||||
testCases := []struct {
|
||||
mode string
|
||||
@ -185,71 +183,6 @@ func TestF3_InactiveModes(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestF3_Rebootstrap tests F3 can be rebootsrapped by changing the manifest
|
||||
// without disrupting miner participation.
|
||||
func TestF3_Rebootstrap(t *testing.T) {
|
||||
kit.QuietMiningLogs()
|
||||
|
||||
const blocktime = 100 * time.Millisecond
|
||||
e := setup(t, blocktime)
|
||||
e.waitTillAllMinersParticipate(10 * time.Second)
|
||||
n := e.nodes[0]
|
||||
|
||||
newInstance := uint64(2)
|
||||
e.waitTillF3Instance(newInstance, 20*time.Second)
|
||||
e.requireAllMinersParticipate()
|
||||
|
||||
prevCert, err := n.F3GetCertificate(e.testCtx, newInstance)
|
||||
require.NoError(t, err)
|
||||
|
||||
cpy := *e.m
|
||||
cpy.BootstrapEpoch = 25
|
||||
cpy.NetworkName = BaseNetworkName + "/2"
|
||||
e.ms.UpdateManifest(&cpy)
|
||||
|
||||
e.waitTillManifestChange(&cpy, 20*time.Second)
|
||||
e.waitTillAllMinersParticipate(10 * time.Second)
|
||||
e.waitTillF3Rebootstrap(20 * time.Second)
|
||||
e.waitTillF3Instance(prevCert.GPBFTInstance+1, 20*time.Second)
|
||||
e.requireAllMinersParticipate()
|
||||
}
|
||||
|
||||
// TestF3_PauseAndRebootstrap tests that F3 pause, then resume, then and
|
||||
// rebootstrap works as expected, and all miners continue to participate in F3
|
||||
// regardless.
|
||||
func TestF3_PauseAndRebootstrap(t *testing.T) {
|
||||
kit.QuietMiningLogs()
|
||||
|
||||
const blocktime = 100 * time.Millisecond
|
||||
e := setup(t, blocktime)
|
||||
e.waitTillAllMinersParticipate(10 * time.Second)
|
||||
|
||||
newInstance := uint64(2)
|
||||
e.waitTillF3Instance(newInstance, 20*time.Second)
|
||||
e.requireAllMinersParticipate()
|
||||
|
||||
origManifest := *e.m
|
||||
pausedManifest := origManifest
|
||||
pausedManifest.Pause = true
|
||||
e.ms.UpdateManifest(&pausedManifest)
|
||||
e.waitTillF3Pauses(30 * time.Second)
|
||||
e.requireAllMinersParticipate() // Pause should not affect participation leasing.
|
||||
|
||||
e.ms.UpdateManifest(&origManifest)
|
||||
e.waitTillF3Runs(30 * time.Second)
|
||||
e.waitTillAllMinersParticipate(10 * time.Second)
|
||||
|
||||
cpy := *e.m
|
||||
cpy.NetworkName = BaseNetworkName + "/2"
|
||||
cpy.BootstrapEpoch = 25
|
||||
e.ms.UpdateManifest(&cpy)
|
||||
|
||||
e.waitTillManifestChange(&cpy, 20*time.Second)
|
||||
e.waitTillAllMinersParticipate(10 * time.Second)
|
||||
e.waitTillF3Rebootstrap(20 * time.Second)
|
||||
e.requireAllMinersParticipate()
|
||||
}
|
||||
|
||||
// Tests that pause/resume and rebootstrapping F3 works
|
||||
func TestF3_Bootstrap(t *testing.T) {
|
||||
kit.QuietMiningLogs()
|
||||
@ -260,29 +193,13 @@ func TestF3_Bootstrap(t *testing.T) {
|
||||
)
|
||||
|
||||
staticManif := newTestManifest(BaseNetworkName, bootstrapEpoch, blocktime)
|
||||
dynamicManif := *staticManif
|
||||
dynamicManif.BootstrapEpoch = 5
|
||||
dynamicManif.EC.Finalize = false
|
||||
dynamicManif.NetworkName = BaseNetworkName + "/1"
|
||||
|
||||
e := setupWithStaticManifest(t, staticManif, true)
|
||||
|
||||
e.ms.UpdateManifest(&dynamicManif)
|
||||
e.waitTillManifestChange(&dynamicManif, 20*time.Second)
|
||||
e.waitTillAllMinersParticipate(10 * time.Second)
|
||||
e.waitTillF3Instance(2, 20*time.Second)
|
||||
|
||||
e.waitTillManifestChange(staticManif, 20*time.Second)
|
||||
e.waitTillAllMinersParticipate(10 * time.Second)
|
||||
e.waitTillF3Instance(2, 20*time.Second)
|
||||
|
||||
// Try to switch back, we should ignore the manifest update.
|
||||
e.ms.UpdateManifest(&dynamicManif)
|
||||
for _, n := range e.nodes {
|
||||
m, err := n.F3GetManifest(e.testCtx)
|
||||
require.NoError(e.t, err)
|
||||
require.True(t, m.Equal(staticManif))
|
||||
}
|
||||
e.requireAllMinersParticipate()
|
||||
}
|
||||
|
||||
@ -305,36 +222,6 @@ func TestF3_JsonRPCErrorsPassThrough(t *testing.T) {
|
||||
require.Zero(t, ticket)
|
||||
}
|
||||
|
||||
func (e *testEnv) waitTillF3Rebootstrap(timeout time.Duration) {
|
||||
e.waitFor(func(n *kit.TestFullNode) bool {
|
||||
// the prev epoch yet, check if we already bootstrapped and from
|
||||
// the right epoch
|
||||
cert, err := n.F3GetCertificate(e.testCtx, 0)
|
||||
if err != nil || cert == nil {
|
||||
return false
|
||||
}
|
||||
m, err := n.F3GetManifest(e.testCtx)
|
||||
require.NoError(e.t, err)
|
||||
|
||||
// Find the first non-null block at or before the target height, that's the bootstrap block.
|
||||
targetEpoch := m.BootstrapEpoch - m.EC.Finality
|
||||
ts, err := n.ChainGetTipSetByHeight(e.testCtx, abi.ChainEpoch(targetEpoch), types.EmptyTSK)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return cert.ECChain.Base().Epoch == int64(ts.Height())
|
||||
}, timeout)
|
||||
}
|
||||
|
||||
func (e *testEnv) waitTillF3Pauses(timeout time.Duration) {
|
||||
e.waitFor(func(n *kit.TestFullNode) bool {
|
||||
r, err := n.F3IsRunning(e.testCtx)
|
||||
require.NoError(e.t, err)
|
||||
return !r
|
||||
}, timeout)
|
||||
}
|
||||
|
||||
func (e *testEnv) waitTillF3Runs(timeout time.Duration) {
|
||||
e.waitFor(func(n *kit.TestFullNode) bool {
|
||||
r, err := n.F3IsRunning(e.testCtx)
|
||||
@ -440,7 +327,7 @@ func (e *testEnv) waitFor(f func(n *kit.TestFullNode) bool, timeout time.Duratio
|
||||
// and the second full-node is an observer that is not directly connected to
|
||||
// a miner. The last return value is the manifest sender for the network.
|
||||
func setup(t *testing.T, blocktime time.Duration, opts ...kit.NodeOpt) *testEnv {
|
||||
return setupWithStaticManifest(t, newTestManifest(BaseNetworkName+"/1", DefaultBootstrapEpoch, blocktime), false, opts...)
|
||||
return setupWithStaticManifest(t, newTestManifest(BaseNetworkName, DefaultBootstrapEpoch, blocktime), false, opts...)
|
||||
}
|
||||
|
||||
func newTestManifest(networkName gpbft.NetworkName, bootstrapEpoch int64, blocktime time.Duration) *manifest.Manifest {
|
||||
@ -494,16 +381,9 @@ func setupWithStaticManifest(t *testing.T, manif *manifest.Manifest, testBootstr
|
||||
require.NoError(t, errgrp.Wait())
|
||||
})
|
||||
|
||||
// create manifest host first to get the manifest ID to setup F3
|
||||
manifestServerHost, err := libp2p.New(libp2p.ListenAddrStrings("/ip4/127.0.0.1/udp/0/quic-v1"))
|
||||
require.NoError(t, err)
|
||||
|
||||
cfg := &lf3.Config{
|
||||
BaseNetworkName: BaseNetworkName,
|
||||
StaticManifest: manif,
|
||||
DynamicManifestProvider: manifestServerHost.ID(),
|
||||
PrioritizeStaticManifest: testBootstrap,
|
||||
AllowDynamicFinalize: !testBootstrap,
|
||||
BaseNetworkName: BaseNetworkName,
|
||||
StaticManifest: manif,
|
||||
}
|
||||
|
||||
nodeOpts := []kit.NodeOpt{kit.WithAllSubsystems(), kit.F3Config(cfg)}
|
||||
@ -539,27 +419,5 @@ func setupWithStaticManifest(t *testing.T, manif *manifest.Manifest, testBootstr
|
||||
e.nodes = []*kit.TestFullNode{&n1, &n2, &n3}
|
||||
e.miners = []*kit.TestMiner{&m1, &m2, &m3, &m4}
|
||||
|
||||
// create manifest sender and connect to full-nodes
|
||||
e.ms = e.newManifestSender(ctx, t, manifestServerHost, blocktime)
|
||||
for _, n := range e.nodes {
|
||||
err = n.NetConnect(ctx, e.ms.PeerInfo())
|
||||
require.NoError(t, err)
|
||||
}
|
||||
errgrp.Go(func() error {
|
||||
defer func() {
|
||||
require.NoError(t, manifestServerHost.Close())
|
||||
}()
|
||||
return e.ms.Run(ctx)
|
||||
})
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *testEnv) newManifestSender(ctx context.Context, t *testing.T, h host.Host, senderTimeout time.Duration) *manifest.ManifestSender {
|
||||
ps, err := pubsub.NewGossipSub(ctx, h)
|
||||
require.NoError(t, err)
|
||||
|
||||
ms, err := manifest.NewManifestSender(ctx, h, ps, e.m, senderTimeout)
|
||||
require.NoError(t, err)
|
||||
return ms
|
||||
}
|
||||
|
@ -265,8 +265,6 @@ func F3Backend(backend lf3.F3Backend) NodeOpt {
|
||||
func F3Disabled() NodeOpt {
|
||||
return ConstructorOpts(
|
||||
node.Unset(new(*lf3.Config)),
|
||||
node.Unset(new(*lf3.ContractManifestProvider)),
|
||||
node.Unset(new(lf3.StateCaller)),
|
||||
node.Unset(new(manifest.ManifestProvider)),
|
||||
node.Unset(new(lf3.F3Backend)),
|
||||
)
|
||||
|
@ -188,8 +188,6 @@ var ChainNode = Options(
|
||||
|
||||
If(build.IsF3Enabled(),
|
||||
Override(new(*lf3.Config), lf3.NewConfig),
|
||||
Override(new(*lf3.ContractManifestProvider), lf3.NewContractManifestProvider),
|
||||
Override(new(lf3.StateCaller), From(new(full.StateModule))),
|
||||
Override(new(manifest.ManifestProvider), lf3.NewManifestProvider),
|
||||
Override(new(lf3.F3Backend), lf3.New),
|
||||
),
|
||||
|
@ -3,7 +3,6 @@ package lp2p
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
@ -368,21 +367,11 @@ func GossipSub(in GossipIn) (service *pubsub.PubSub, err error) {
|
||||
allowTopics = append(allowTopics, drandTopics...)
|
||||
|
||||
if in.F3Config != nil {
|
||||
if in.F3Config.StaticManifest != nil || in.F3Config.ContractAddress != "" {
|
||||
if in.F3Config.StaticManifest != nil {
|
||||
gpbftTopic := manifest.PubSubTopicFromNetworkName(in.F3Config.BaseNetworkName)
|
||||
chainexTopic := manifest.ChainExchangeTopicFromNetworkName(in.F3Config.BaseNetworkName)
|
||||
allowTopics = append(allowTopics, gpbftTopic, chainexTopic)
|
||||
}
|
||||
if in.F3Config.DynamicManifestProvider != "" {
|
||||
gpbftTopicPrefix := manifest.PubSubTopicFromNetworkName(in.F3Config.BaseNetworkName)
|
||||
chainexTopicPrefix := manifest.ChainExchangeTopicFromNetworkName(in.F3Config.BaseNetworkName)
|
||||
allowTopics = append(allowTopics, manifest.ManifestPubSubTopicName)
|
||||
for i := range lf3.MaxDynamicManifestChangesAllowed {
|
||||
gpbftTopic := fmt.Sprintf("%s/%d", gpbftTopicPrefix, i)
|
||||
chainexTopic := fmt.Sprintf("%s/%d", chainexTopicPrefix, i)
|
||||
allowTopics = append(allowTopics, gpbftTopic, chainexTopic)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
options = append(options,
|
||||
|
Reference in New Issue
Block a user