Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix evaluation endpoint returning 0 usable hosts due to gouging on period #1181

Merged
merged 3 commits into from
Apr 22, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 1 addition & 5 deletions autopilot/autopilot.go
Original file line number Diff line number Diff line change
@@ -195,10 +195,6 @@ func (ap *Autopilot) configHandlerPOST(jc jape.Context) {
if jc.Check("failed to get recommended fee", err) != nil {
return
}
cfg, err := ap.Config(ctx)
if jc.Check("failed to get autopilot config", err) != nil {
return
}

// fetch hosts
hosts, err := ap.bus.SearchHosts(ctx, api.SearchHostOptions{Limit: -1, FilterMode: api.HostFilterModeAllowed})
@@ -207,7 +203,7 @@ func (ap *Autopilot) configHandlerPOST(jc jape.Context) {
}

// evaluate the config
jc.Encode(contractor.EvaluateConfig(reqCfg, cs, fee, cfg.CurrentPeriod, rs, gs, hosts))
jc.Encode(contractor.EvaluateConfig(reqCfg, cs, fee, rs, gs, hosts))
}

func (ap *Autopilot) Run() error {
19 changes: 10 additions & 9 deletions autopilot/contractor/evaluate.go
Original file line number Diff line number Diff line change
@@ -6,8 +6,8 @@ import (
"go.sia.tech/renterd/worker"
)

func countUsableHosts(cfg api.AutopilotConfig, cs api.ConsensusState, fee types.Currency, currentPeriod uint64, rs api.RedundancySettings, gs api.GougingSettings, hosts []api.Host) (usables uint64) {
gc := worker.NewGougingChecker(gs, cs, fee, currentPeriod, cfg.Contracts.RenewWindow)
func countUsableHosts(cfg api.AutopilotConfig, cs api.ConsensusState, fee types.Currency, period uint64, rs api.RedundancySettings, gs api.GougingSettings, hosts []api.Host) (usables uint64) {
gc := worker.NewGougingChecker(gs, cs, fee, period, cfg.Contracts.RenewWindow)
for _, host := range hosts {
hc := checkHost(cfg, rs, gc, host, minValidScore)
if hc.Usability.IsUsable() {
@@ -20,8 +20,9 @@ func countUsableHosts(cfg api.AutopilotConfig, cs api.ConsensusState, fee types.
// EvaluateConfig evaluates the given configuration and if the gouging settings
// are too strict for the number of contracts required by 'cfg', it will provide
// a recommendation on how to loosen it.
func EvaluateConfig(cfg api.AutopilotConfig, cs api.ConsensusState, fee types.Currency, currentPeriod uint64, rs api.RedundancySettings, gs api.GougingSettings, hosts []api.Host) (resp api.ConfigEvaluationResponse) {
gc := worker.NewGougingChecker(gs, cs, fee, currentPeriod, cfg.Contracts.RenewWindow)
func EvaluateConfig(cfg api.AutopilotConfig, cs api.ConsensusState, fee types.Currency, rs api.RedundancySettings, gs api.GougingSettings, hosts []api.Host) (resp api.ConfigEvaluationResponse) {
period := cfg.Contracts.Period
gc := worker.NewGougingChecker(gs, cs, fee, period, cfg.Contracts.RenewWindow)

resp.Hosts = uint64(len(hosts))
for _, host := range hosts {
@@ -88,35 +89,35 @@ func EvaluateConfig(cfg api.AutopilotConfig, cs api.ConsensusState, fee types.Cu
// MaxRPCPrice
tmpGS := maxGS()
tmpGS.MaxRPCPrice = gs.MaxRPCPrice
if optimiseGougingSetting(&tmpGS, &tmpGS.MaxRPCPrice, cfg, cs, fee, currentPeriod, rs, hosts) {
if optimiseGougingSetting(&tmpGS, &tmpGS.MaxRPCPrice, cfg, cs, fee, period, rs, hosts) {
optimisedGS.MaxRPCPrice = tmpGS.MaxRPCPrice
success = true
}
// MaxContractPrice
tmpGS = maxGS()
tmpGS.MaxContractPrice = gs.MaxContractPrice
if optimiseGougingSetting(&tmpGS, &tmpGS.MaxContractPrice, cfg, cs, fee, currentPeriod, rs, hosts) {
if optimiseGougingSetting(&tmpGS, &tmpGS.MaxContractPrice, cfg, cs, fee, period, rs, hosts) {
optimisedGS.MaxContractPrice = tmpGS.MaxContractPrice
success = true
}
// MaxDownloadPrice
tmpGS = maxGS()
tmpGS.MaxDownloadPrice = gs.MaxDownloadPrice
if optimiseGougingSetting(&tmpGS, &tmpGS.MaxDownloadPrice, cfg, cs, fee, currentPeriod, rs, hosts) {
if optimiseGougingSetting(&tmpGS, &tmpGS.MaxDownloadPrice, cfg, cs, fee, period, rs, hosts) {
optimisedGS.MaxDownloadPrice = tmpGS.MaxDownloadPrice
success = true
}
// MaxUploadPrice
tmpGS = maxGS()
tmpGS.MaxUploadPrice = gs.MaxUploadPrice
if optimiseGougingSetting(&tmpGS, &tmpGS.MaxUploadPrice, cfg, cs, fee, currentPeriod, rs, hosts) {
if optimiseGougingSetting(&tmpGS, &tmpGS.MaxUploadPrice, cfg, cs, fee, period, rs, hosts) {
optimisedGS.MaxUploadPrice = tmpGS.MaxUploadPrice
success = true
}
// MaxStoragePrice
tmpGS = maxGS()
tmpGS.MaxStoragePrice = gs.MaxStoragePrice
if optimiseGougingSetting(&tmpGS, &tmpGS.MaxStoragePrice, cfg, cs, fee, currentPeriod, rs, hosts) {
if optimiseGougingSetting(&tmpGS, &tmpGS.MaxStoragePrice, cfg, cs, fee, period, rs, hosts) {
optimisedGS.MaxStoragePrice = tmpGS.MaxStoragePrice
success = true
}
11 changes: 4 additions & 7 deletions internal/test/e2e/cluster.go
Original file line number Diff line number Diff line change
@@ -689,18 +689,15 @@ func (c *TestCluster) AddHost(h *Host) {
c.hosts = append(c.hosts, h)

// Fund host from bus.
res, err := c.Bus.Wallet(context.Background())
c.tt.OK(err)

fundAmt := res.Confirmed.Div64(2).Div64(uint64(len(c.hosts))) // 50% of bus balance
fundAmt := types.Siacoins(100e3)
var scos []types.SiacoinOutput
for i := 0; i < 10; i++ {
scos = append(scos, types.SiacoinOutput{
Value: fundAmt.Div64(10),
Value: fundAmt,
Address: h.WalletAddress(),
})
}
c.tt.OK(c.Bus.SendSiacoins(context.Background(), scos, true))
c.tt.OK(c.Bus.SendSiacoins(context.Background(), scos, false))

// Mine transaction.
c.MineBlocks(1)
@@ -720,7 +717,7 @@ func (c *TestCluster) AddHost(h *Host) {
c.tt.Helper()

c.MineBlocks(1)
_, err = c.Bus.Host(context.Background(), h.PublicKey())
_, err := c.Bus.Host(context.Background(), h.PublicKey())
if err != nil {
return err
}
19 changes: 18 additions & 1 deletion internal/test/e2e/gouging_test.go
Original file line number Diff line number Diff line change
@@ -23,7 +23,6 @@ func TestGouging(t *testing.T) {

// create a new test cluster
cluster := newTestCluster(t, testClusterOptions{
hosts: int(test.AutopilotConfig.Contracts.Amount),
logger: newTestLoggerCustom(zapcore.ErrorLevel),
})
defer cluster.Shutdown()
@@ -33,6 +32,13 @@ func TestGouging(t *testing.T) {
w := cluster.Worker
tt := cluster.tt

// mine enough blocks for the current period to become > period
cluster.MineBlocks(int(cfg.Period) * 2)

// add hosts
tt.OKAll(cluster.AddHostsBlocking(int(test.AutopilotConfig.Contracts.Amount)))
cluster.WaitForAccounts()

// build a hosts map
hostsMap := make(map[string]*Host)
for _, h := range cluster.hosts {
@@ -99,11 +105,22 @@ func TestGouging(t *testing.T) {
tt.OK(err)
if resp.Recommendation == nil {
t.Fatal("expected recommendation")
} else if resp.Unusable.Gouging.Gouging != 3 {
t.Fatalf("expected 3 gouging errors, got %v", resp.Unusable.Gouging)
}

// set optimised settings
tt.OK(b.UpdateSetting(context.Background(), api.SettingGouging, resp.Recommendation.GougingSettings))

// evaluate optimised settings
resp, err = cluster.Autopilot.EvaluateConfig(context.Background(), test.AutopilotConfig, resp.Recommendation.GougingSettings, test.RedundancySettings)
tt.OK(err)
if resp.Recommendation != nil {
t.Fatal("expected no recommendation")
} else if resp.Usable != 3 {
t.Fatalf("expected 3 usable hosts, got %v", resp.Usable)
}

// upload some data - should work now once contract maintenance is done
tt.Retry(30, time.Second, func() error {
_, err := w.UploadObject(context.Background(), bytes.NewReader(data), api.DefaultBucketName, path, api.UploadObjectOptions{})