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:
Jakub Sztandera
2025-05-01 15:41:20 +02:00
committed by GitHub
parent fb24904a90
commit 262d6f27ad
24 changed files with 333 additions and 714 deletions

View File

@ -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

View File

@ -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

View 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
}
}

View 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
}
}

View File

@ -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": {

View 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
}
}

View 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
}
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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() {

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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")
}

View File

@ -1 +0,0 @@
00000000000000000000000000000000000000000000000000000000004c4b40000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000002a38554c172da3010bde72b18ce190f360d90de82a16966424a214d0f9d1e16790d9ac8922bcb4dd24cfebd2bd9c63698810323ebbd5dbd7dbbd2fb45afd75f429e61ff732f0691e1a5dbd1ca28a6c413ea8c2b49d8d8eddf496e38883b991990ccc60cdcfe542993190de93c556c47db5703f773e0039a17a59f1f20b101fd980b648acbbe03e7afa9e08c9ba57a414db0cc85288eda4aa5711e56402dae14e18047d8086c86852a49b83188f74a3d6f803d13e8173a42306cf723bd117c2b1394c622a5ce4aea6dba89edfe3b7dd0e70c8501fa1cb5581530a5ecdfe2982a50b2481794f0f71c04376f8eb5c885e154a32bc22f090b78b5fa608710ad542ea3cc7a5682e10eb8a40ea42ac3e81ee5d6ec5c11d5e12bdc68051183cc58092a8ea7e0fa7724f398d910eb7bc393b475aa49996dafe79f245111ad430744fc289a1ad61e2e517365530d0787f2be70e97c22f0ba692dbcb54cab5cb555ce90713b91a584aafdbf1ca357d76497a3eb6a1d7841bd9c8caaf5d01bfbd5fa9337d9878ebc605cadc7de955bfd2e457c255f1aa3d52ee59fd562748e7b2742d486c79c81c1f92bdb81dc626d4d48054ab3c23f3966e69127a8725374fac0a835eabfa88f88a323e2824b9ee4c95209baa386a240743a4fadebe0f941472f97f9669d6f1aa25542c391d926cca5b53f2a6b6e4eef39d2ed8240d82265ce98e6a9b12dcde318f5ba30d10f2625f5897c8dc8bea80c39a4d556db838f4d3e79c430a8cd70b11d578da0eaaddbdfd7fd8352e0339e3145ddc1c825c968de1bcfa3dff2fc2790d9e7788d8bd6e84dd0d1423b099421496f5cc1938ee681b64f6569dd0224fdebe6dd941197dbc312da16ef4f2cd9ed9c67c82ee1b4aae70cb9f2638589a2aa0fc897f5fc09ace7e1544ef2a7406a66dbf2e0aa6566086c87d1e1b475c558772f3efe030000000000000000000000000000000000000000000000000000000000

View File

@ -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",

View File

@ -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
}

View File

@ -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)),
)

View File

@ -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),
),

View File

@ -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,