From 763cde7b055634f30da2d1bd78a3fa0f8e089bd3 Mon Sep 17 00:00:00 2001 From: Chris Schinnerl Date: Fri, 13 Dec 2024 14:11:38 +0100 Subject: [PATCH] extend test to check if number of contracts is right and if contracts are on right hosts --- autopilot/contractor/contractor.go | 16 +++++++---- autopilot/contractor/hostfilter.go | 7 ++++- bus/bus.go | 7 +++++ bus/routes.go | 7 +++++ go.mod | 2 +- go.sum | 4 +-- internal/test/e2e/cluster.go | 2 +- internal/test/e2e/cluster_test.go | 44 ++++++++++++++++++++++++++---- 8 files changed, 73 insertions(+), 16 deletions(-) diff --git a/autopilot/contractor/contractor.go b/autopilot/contractor/contractor.go index e8c1dbd26..023f5d103 100644 --- a/autopilot/contractor/contractor.go +++ b/autopilot/contractor/contractor.go @@ -463,7 +463,9 @@ func activeContracts(ctx context.Context, bus Bus, logger *zap.SugaredLogger) ([ // fetch active contracts logger.Info("fetching active contracts") start := time.Now() - metadatas, err := bus.Contracts(ctx, api.ContractsOpts{FilterMode: api.ContractFilterModeActive}) + metadatas, err := bus.Contracts(ctx, api.ContractsOpts{ + FilterMode: api.ContractFilterModeActive, + }) if err != nil { return nil, err } @@ -902,7 +904,9 @@ func performContractFormations(ctx *mCtx, bus Bus, cr contractReviser, hf hostFi wanted := int(ctx.WantedContracts()) // fetch all active contracts - contracts, err := bus.Contracts(ctx, api.ContractsOpts{}) + contracts, err := bus.Contracts(ctx, api.ContractsOpts{ + FilterMode: api.ContractFilterModeActive, + }) if err != nil { return 0, fmt.Errorf("failed to fetch contracts: %w", err) } @@ -1052,7 +1056,9 @@ func performHostChecks(ctx *mCtx, bus Bus, logger *zap.SugaredLogger) error { func performPostMaintenanceTasks(ctx *mCtx, bus Bus, alerter alerts.Alerter, cc contractChecker, rb revisionBroadcaster, logger *zap.SugaredLogger) error { // fetch some contract and host info - allContracts, err := bus.Contracts(ctx, api.ContractsOpts{}) + allContracts, err := bus.Contracts(ctx, api.ContractsOpts{ + FilterMode: api.ContractFilterModeActive, + }) if err != nil { return fmt.Errorf("failed to fetch all contracts: %w", err) } @@ -1117,7 +1123,7 @@ func performV2ContractMigration(ctx *mCtx, bus Bus, cr contractReviser, logger * } contracts, err := bus.Contracts(ctx, api.ContractsOpts{ - FilterMode: api.ContractFilterModeAll, // TODO: change to usable + FilterMode: api.ContractFilterModeActive, }) if err != nil { logger.With(zap.Error(err)).Error("failed to fetch contracts for migration") @@ -1149,7 +1155,7 @@ func performV2ContractMigration(ctx *mCtx, bus Bus, cr contractReviser, logger * } // form a new contract with the same host - contract, _, err := cr.formContract(ctx, bus, host, InitialContractFunding, logger) + _, _, err = cr.formContract(ctx, bus, host, InitialContractFunding, logger) if err != nil { logger.Errorf("failed to form a v2 contract with the host") continue diff --git a/autopilot/contractor/hostfilter.go b/autopilot/contractor/hostfilter.go index b9dd6064d..d2d349479 100644 --- a/autopilot/contractor/hostfilter.go +++ b/autopilot/contractor/hostfilter.go @@ -8,6 +8,7 @@ import ( "go.sia.tech/core/types" "go.sia.tech/renterd/api" "go.sia.tech/renterd/internal/gouging" + rhp4 "go.sia.tech/renterd/internal/rhp/v4" ) const ( @@ -165,7 +166,11 @@ func checkHost(gc gouging.Checker, sh scoredHost, minScore float64, period uint6 // calculate remaining host info fields if !h.IsAnnounced() { ub.NotAnnounced = true - } else if !h.Scanned { + } else if !h.Scanned || + // NOTE: a v2 host might have been scanned before the v2 height so strictly + // speaking it is scanned but since it hasn't been scanned since, the + // settings aren't set so we treat it as not scanned + (h.IsV2() && h.V2Settings == (rhp4.HostSettings{})) { ub.NotCompletingScan = true } else { // online check diff --git a/bus/bus.go b/bus/bus.go index dbb3ebaa0..82024d260 100644 --- a/bus/bus.go +++ b/bus/bus.go @@ -796,6 +796,13 @@ func (b *Bus) renewContractV1(ctx context.Context, cs consensus.State, gp api.Go // derive the renter key renterKey := b.masterKey.DeriveContractKey(c.HostKey) + // cap v1 renewals to the v2 require height since the host won't allow us to + // form contracts beyond that + v2ReqHeight := b.cm.TipState().Network.HardforkV2.RequireHeight + if endHeight >= v2ReqHeight { + endHeight = v2ReqHeight - 1 + } + // fetch the revision rev, err := b.rhp3Client.Revision(ctx, c.ID, c.HostKey, hs.SiamuxAddr()) if err != nil { diff --git a/bus/routes.go b/bus/routes.go index 2fe14b41a..fcb6deed7 100644 --- a/bus/routes.go +++ b/bus/routes.go @@ -2320,6 +2320,13 @@ func (b *Bus) contractsFormHandler(jc jape.Context) { return } + // cap v1 formations to the v2 require height since the host won't allow + // us to form contracts beyond that + v2ReqHeight := b.cm.TipState().Network.HardforkV2.RequireHeight + if rfr.EndHeight >= v2ReqHeight { + rfr.EndHeight = v2ReqHeight - 1 + } + // check gouging breakdown := gc.CheckSettings(settings) if breakdown.Gouging() { diff --git a/go.mod b/go.mod index a2820e7f2..f24081a06 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( go.sia.tech/core v0.7.2-0.20241210224920-0534a5928ddb go.sia.tech/coreutils v0.7.1-0.20241211045514-6881993d8806 go.sia.tech/gofakes3 v0.0.5 - go.sia.tech/hostd v1.1.3-0.20241212081824-0f6d95b852db + go.sia.tech/hostd v1.1.3-0.20241212215223-9e3440475bed go.sia.tech/jape v0.12.1 go.sia.tech/mux v1.3.0 go.sia.tech/web/renterd v0.69.0 diff --git a/go.sum b/go.sum index c5157f74d..361877a3d 100644 --- a/go.sum +++ b/go.sum @@ -61,8 +61,8 @@ go.sia.tech/coreutils v0.7.1-0.20241211045514-6881993d8806 h1:zmLtpmFQPKMukYMiQB go.sia.tech/coreutils v0.7.1-0.20241211045514-6881993d8806/go.mod h1:6z3oHrQqcLoFEAT/l6XnvOivEGXgIfWBKcq0OqsouWA= go.sia.tech/gofakes3 v0.0.5 h1:vFhVBUFbKE9ZplvLE2w4TQxFMQyF8qvgxV4TaTph+Vw= go.sia.tech/gofakes3 v0.0.5/go.mod h1:LXEzwGw+OHysWLmagleCttX93cJZlT9rBu/icOZjQ54= -go.sia.tech/hostd v1.1.3-0.20241212081824-0f6d95b852db h1:ey3ezMYHPzY+FZ4yL8xsAWnCJWI2J9z4rtpmRa8dj0A= -go.sia.tech/hostd v1.1.3-0.20241212081824-0f6d95b852db/go.mod h1:6wTgoXKmsLQT22lUcHI4/dUcb3mhXFR+9zYWIki8Qho= +go.sia.tech/hostd v1.1.3-0.20241212215223-9e3440475bed h1:C42AxWwwoP13EhZsdWwR17Rc9S7gXI4JnRN0AyZRxc8= +go.sia.tech/hostd v1.1.3-0.20241212215223-9e3440475bed/go.mod h1:6wTgoXKmsLQT22lUcHI4/dUcb3mhXFR+9zYWIki8Qho= go.sia.tech/jape v0.12.1 h1:xr+o9V8FO8ScRqbSaqYf9bjj1UJ2eipZuNcI1nYousU= go.sia.tech/jape v0.12.1/go.mod h1:wU+h6Wh5olDjkPXjF0tbZ1GDgoZ6VTi4naFw91yyWC4= go.sia.tech/mux v1.3.0 h1:hgR34IEkqvfBKUJkAzGi31OADeW2y7D6Bmy/Jcbop9c= diff --git a/internal/test/e2e/cluster.go b/internal/test/e2e/cluster.go index eca33da30..bee8ce48e 100644 --- a/internal/test/e2e/cluster.go +++ b/internal/test/e2e/cluster.go @@ -630,7 +630,7 @@ func announceHosts(hosts []*Host) error { for _, host := range hosts { settings := defaultHostSettings settings.NetAddress = host.rhp4Listener.Addr().(*net.TCPAddr).IP.String() - if err := host.settings.UpdateSettings(settings); err != nil { + if err := host.UpdateSettings(settings); err != nil { return err } if err := host.settings.Announce(); err != nil { diff --git a/internal/test/e2e/cluster_test.go b/internal/test/e2e/cluster_test.go index b902bffa9..ee3bdef13 100644 --- a/internal/test/e2e/cluster_test.go +++ b/internal/test/e2e/cluster_test.go @@ -35,7 +35,6 @@ import ( "go.sia.tech/renterd/internal/utils" "go.sia.tech/renterd/object" "go.sia.tech/renterd/stores/sql/sqlite" - "go.uber.org/zap" "lukechampine.com/frand" ) @@ -2859,6 +2858,7 @@ func TestContractFundsReturnWhenHostOffline(t *testing.T) { }) } +// TODO: upload and download func TestV1ToV2Transition(t *testing.T) { // create a chain manager with a custom network that starts before the v2 // allow height @@ -2879,13 +2879,11 @@ func TestV1ToV2Transition(t *testing.T) { // create a test cluster nHosts := 3 - l, _ := zap.NewDevelopment() cluster := newTestCluster(t, testClusterOptions{ autopilotConfig: &apCfg, hosts: 0, // add hosts manually later cm: cm, uploadPacking: false, // disable to make sure we don't accidentally serve data from disk - logger: l, }) defer cluster.Shutdown() tt := cluster.tt @@ -2929,9 +2927,43 @@ func TestV1ToV2Transition(t *testing.T) { cluster.MineBlocks(1) time.Sleep(100 * time.Millisecond) } + time.Sleep(time.Second) - // check the contracts again - contracts, err = cluster.Bus.Contracts(context.Background(), api.ContractsOpts{FilterMode: api.ContractFilterModeAll}) + // check that we have 1 archived contract for every contract we had before + archivedContracts, err := cluster.Bus.Contracts(context.Background(), api.ContractsOpts{FilterMode: api.ContractFilterModeArchived}) tt.OK(err) - fmt.Println("contracts", len(contracts)) + if len(archivedContracts) != nHosts-1 { + t.Fatalf("expected %v archived contracts, got %v", 2*(nHosts-1), len(archivedContracts)) + } + + // they should be on nHosts-1 unique hosts + usedHosts := make(map[types.PublicKey]struct{}) + for _, c := range archivedContracts { + if c.ArchivalReason != "migrated to v2" { + t.Fatalf("expected archival reason to be 'migrated to v2', got %v", c.ArchivalReason) + } else if c.V2 { + t.Fatalf("expected contract to be v1, got v2") + } + usedHosts[c.HostKey] = struct{}{} + } + if len(usedHosts) != nHosts-1 { + t.Fatalf("expected %v unique hosts, got %v", nHosts-1, len(usedHosts)) + } + + // we should have the same number of active contracts + activeContracts, err := cluster.Bus.Contracts(context.Background(), api.ContractsOpts{FilterMode: api.ContractFilterModeActive}) + tt.OK(err) + if len(activeContracts) != nHosts-1 { + t.Fatalf("expected %v active contracts, got %v", nHosts-1, len(activeContracts)) + } + + // they should be on the same hosts as before + for _, c := range activeContracts { + if _, ok := usedHosts[c.HostKey]; !ok { + t.Fatal("host not found in used hosts") + } else if !c.V2 { + t.Fatal("expected contract to be v2, got v1", c.ID, c.ArchivalReason) + } + delete(usedHosts, c.HostKey) + } }