diff --git a/api/setting.go b/api/setting.go index 7e472cb66..d01acd709 100644 --- a/api/setting.go +++ b/api/setting.go @@ -40,8 +40,8 @@ var ( DefaultGougingSettings = GougingSettings{ MaxRPCPrice: types.Siacoins(1).Div64(1000), // 1mS per RPC MaxContractPrice: types.Siacoins(15), // 15 SC per contract - MaxDownloadPrice: types.Siacoins(3000), // 3000 SC per 1 TB - MaxUploadPrice: types.Siacoins(3000), // 3000 SC per 1 TB + MaxDownloadPrice: types.Siacoins(3000).Div64(1e12), // 3000 SC per 1 TB + MaxUploadPrice: types.Siacoins(3000).Div64(1e12), // 3000 SC per 1 TB MaxStoragePrice: types.Siacoins(3000).Div64(1e12).Div64(144 * 30), // 3000 SC per TB per month HostBlockHeightLeeway: 6, // 6 blocks MinPriceTableValidity: 5 * time.Minute, // 5 minutes diff --git a/bus/bus.go b/bus/bus.go index fa9e02cf8..79623615c 100644 --- a/bus/bus.go +++ b/bus/bus.go @@ -340,6 +340,7 @@ func New(ctx context.Context, masterKey [32]byte, am AlertManager, wm WebhooksMa masterKey: masterKey, accounts: store, + explorer: ibus.NewExplorer(explorerURL), s: s, cm: cm, w: w, @@ -366,15 +367,11 @@ func New(ctx context.Context, masterKey [32]byte, am AlertManager, wm WebhooksMa // create contract locker b.contractLocker = ibus.NewContractLocker() - // create explorer - e := ibus.NewExplorer(explorerURL) - b.explorer = e - // create sectors cache b.sectors = ibus.NewSectorsCache() // create pin manager - b.pinMgr = ibus.NewPinManager(b.alerts, wm, e, store, defaultPinUpdateInterval, defaultPinRateWindow, l) + b.pinMgr = ibus.NewPinManager(b.alerts, wm, b.explorer, store, defaultPinUpdateInterval, defaultPinRateWindow, l) // create chain subscriber b.cs = ibus.NewChainSubscriber(wm, cm, store, w, announcementMaxAge, l) diff --git a/internal/bus/pinmanager.go b/internal/bus/pinmanager.go index 400758c63..bd560091b 100644 --- a/internal/bus/pinmanager.go +++ b/internal/bus/pinmanager.go @@ -264,10 +264,10 @@ func (pm *pinManager) updateGougingSettings(ctx context.Context, pins api.Gougin // update max download price if pins.MaxDownload.IsPinned() { - update, err := convertCurrencyToSC(decimal.NewFromFloat(pins.MaxDownload.Value), rate) + maxDownloadCurr, err := convertCurrencyToSC(decimal.NewFromFloat(pins.MaxDownload.Value), rate) if err != nil { pm.logger.Warn("failed to convert max download price to currency") - } else if !gs.MaxDownloadPrice.Equals(update) { + } else if update := maxDownloadCurr.Div64(1e12); !gs.MaxDownloadPrice.Equals(update) { gs.MaxDownloadPrice = update pm.logger.Infow("updating max download price", "old", gs.MaxDownloadPrice, "new", update, "rate", rate) updated = true @@ -288,10 +288,10 @@ func (pm *pinManager) updateGougingSettings(ctx context.Context, pins api.Gougin // update max upload price if pins.MaxUpload.IsPinned() { - update, err := convertCurrencyToSC(decimal.NewFromFloat(pins.MaxUpload.Value), rate) + maxUploadCurr, err := convertCurrencyToSC(decimal.NewFromFloat(pins.MaxUpload.Value), rate) if err != nil { pm.logger.Warnw("failed to convert max upload price to currency", zap.Error(err)) - } else if !gs.MaxUploadPrice.Equals(update) { + } else if update := maxUploadCurr.Div64(1e12); !gs.MaxUploadPrice.Equals(update) { pm.logger.Infow("updating max upload price", "old", gs.MaxUploadPrice, "new", update, "rate", rate) gs.MaxUploadPrice = update updated = true diff --git a/internal/bus/pinmanager_test.go b/internal/bus/pinmanager_test.go index a6e81c28e..6a4ae55dd 100644 --- a/internal/bus/pinmanager_test.go +++ b/internal/bus/pinmanager_test.go @@ -150,8 +150,11 @@ func (ms *mockPinStore) gougingSettings() api.GougingSettings { func (ms *mockPinStore) updatPinnedSettings(pps api.PricePinSettings) { b, _ := json.Marshal(pps) - ms.UpdateSetting(context.Background(), api.SettingPricePinning, string(b)) - time.Sleep(2 * testUpdateInterval) + err := ms.UpdateSetting(context.Background(), api.SettingPricePinning, string(b)) + if err != nil { + panic(err) + } + time.Sleep(10 * testUpdateInterval) } func (ms *mockPinStore) Setting(ctx context.Context, key string) (string, error) { diff --git a/internal/gouging/gouging.go b/internal/gouging/gouging.go index aadfdd57f..f2fc11dec 100644 --- a/internal/gouging/gouging.go +++ b/internal/gouging/gouging.go @@ -13,8 +13,6 @@ import ( ) const ( - bytesPerTB = 1e12 - // maxBaseRPCPriceVsBandwidth is the max ratio for sane pricing between the // MinBaseRPCPrice and the MinDownloadBandwidthPrice. This ensures that 1 // million base RPC charges are at most 1% of the cost to download 4TB. This @@ -190,7 +188,11 @@ func checkPriceGougingPT(gs api.GougingSettings, cs api.ConsensusState, txnFee t } // check LatestRevisionCost - expect sane value - maxRevisionCost, overflow := gs.MaxRPCPrice.AddWithOverflow(gs.MaxDownloadPrice.Div64(bytesPerTB).Mul64(2048)) + twoKiBMax, overflow := gs.MaxDownloadPrice.Mul64WithOverflow(2048) + if overflow { + twoKiBMax = types.MaxCurrency + } + maxRevisionCost, overflow := gs.MaxRPCPrice.AddWithOverflow(twoKiBMax) if overflow { maxRevisionCost = types.MaxCurrency } @@ -292,12 +294,9 @@ func checkPruneGougingRHPv2(gs api.GougingSettings, hs *rhpv2.HostSettings) erro if overflow { return fmt.Errorf("%w: overflow detected when computing sector download price", ErrHostSettingsGouging) } - dpptb, overflow := sectorDownloadPrice.Mul64WithOverflow(uint64(bytesPerTB) / rhpv2.SectorSize) // sectors per TB - if overflow { - return fmt.Errorf("%w: overflow detected when computing download price per TiB", ErrHostSettingsGouging) - } - if !gs.MaxDownloadPrice.IsZero() && dpptb.Cmp(gs.MaxDownloadPrice) > 0 { - return fmt.Errorf("%w: cost per TiB exceeds max dl price: %v > %v", ErrHostSettingsGouging, dpptb, gs.MaxDownloadPrice) + dppb := sectorDownloadPrice.Div64(rhpv2.SectorSize) + if !gs.MaxDownloadPrice.IsZero() && dppb.Cmp(gs.MaxDownloadPrice) > 0 { + return fmt.Errorf("%w: cost per byte exceeds max dl price: %v > %v", ErrHostSettingsGouging, dppb, gs.MaxDownloadPrice) } return nil } @@ -310,12 +309,9 @@ func checkDownloadGougingRHPv3(gs api.GougingSettings, pt *rhpv3.HostPriceTable) if overflow { return fmt.Errorf("%w: overflow detected when computing sector download price", ErrPriceTableGouging) } - dpptb, overflow := sectorDownloadPrice.Mul64WithOverflow(uint64(bytesPerTB) / rhpv2.SectorSize) // sectors per TiB - if overflow { - return fmt.Errorf("%w: overflow detected when computing download price per TiB", ErrPriceTableGouging) - } - if !gs.MaxDownloadPrice.IsZero() && dpptb.Cmp(gs.MaxDownloadPrice) > 0 { - return fmt.Errorf("%w: cost per TiB exceeds max dl price: %v > %v", ErrPriceTableGouging, dpptb, gs.MaxDownloadPrice) + dppb := sectorDownloadPrice.Div64(rhpv2.SectorSize) + if !gs.MaxDownloadPrice.IsZero() && dppb.Cmp(gs.MaxDownloadPrice) > 0 { + return fmt.Errorf("%w: cost per byte exceeds max dl price: %v > %v", ErrPriceTableGouging, dppb, gs.MaxDownloadPrice) } return nil } @@ -328,12 +324,9 @@ func checkUploadGougingRHPv3(gs api.GougingSettings, pt *rhpv3.HostPriceTable) e if overflow { return fmt.Errorf("%w: overflow detected when computing sector price", ErrPriceTableGouging) } - uploadPrice, overflow := sectorUploadPricePerMonth.Mul64WithOverflow(uint64(bytesPerTB) / rhpv2.SectorSize) // sectors per TiB - if overflow { - return fmt.Errorf("%w: overflow detected when computing upload price per TiB", ErrPriceTableGouging) - } + uploadPrice := sectorUploadPricePerMonth.Div64(rhpv2.SectorSize) if !gs.MaxUploadPrice.IsZero() && uploadPrice.Cmp(gs.MaxUploadPrice) > 0 { - return fmt.Errorf("%w: cost per TiB exceeds max ul price: %v > %v", ErrPriceTableGouging, uploadPrice, gs.MaxUploadPrice) + return fmt.Errorf("%w: cost per byte exceeds max ul price: %v > %v", ErrPriceTableGouging, uploadPrice, gs.MaxUploadPrice) } return nil } diff --git a/internal/sql/migrations.go b/internal/sql/migrations.go index c17473542..5cee30e7f 100644 --- a/internal/sql/migrations.go +++ b/internal/sql/migrations.go @@ -217,6 +217,12 @@ var ( return performMigration(ctx, tx, migrationsFs, dbIdentifier, "00017_unix_ms", log) }, }, + { + ID: "00018_gouging_units", + Migrate: func(tx Tx) error { + return performMigration(ctx, tx, migrationsFs, dbIdentifier, "00018_gouging_units", log) + }, + }, } } MetricsMigrations = func(ctx context.Context, migrationsFs embed.FS, log *zap.SugaredLogger) []Migration { diff --git a/stores/sql/mysql/migrations/main/migration_00018_gouging_units.sql b/stores/sql/mysql/migrations/main/migration_00018_gouging_units.sql new file mode 100644 index 000000000..77982c509 --- /dev/null +++ b/stores/sql/mysql/migrations/main/migration_00018_gouging_units.sql @@ -0,0 +1,18 @@ +UPDATE settings +SET value = ( + -- Update settings to new values + SELECT JSON_REPLACE(value, '$.maxDownloadPrice', newMaxDownloadPrice, '$.maxUploadPrice', newMaxUploadPrice) + FROM ( + -- Convert TB to bytes by trimming the last 12 digits + SELECT + SUBSTR(maxDownloadPrice, 1, LENGTH(maxDownloadPrice)-12) AS newMaxDownloadPrice, + SUBSTR(maxUploadPrice, 1, LENGTH(maxUploadPrice)-12) AS newMaxUploadPrice + FROM ( + -- SELECT previous settings + SELECT + JSON_UNQUOTE(JSON_EXTRACT(value, '$.maxDownloadPrice')) AS maxDownloadPrice, + JSON_UNQUOTE(JSON_EXTRACT(value, '$.maxUploadPrice')) AS maxUploadPrice + ) AS _ + ) AS _ +) +WHERE settings.key = "gouging"; diff --git a/stores/sql/sqlite/migrations/main/migration_00018_gouging_units.sql b/stores/sql/sqlite/migrations/main/migration_00018_gouging_units.sql new file mode 100644 index 000000000..c5be99f11 --- /dev/null +++ b/stores/sql/sqlite/migrations/main/migration_00018_gouging_units.sql @@ -0,0 +1,18 @@ +UPDATE settings +SET value = ( + -- Update settings to new values + SELECT JSON_REPLACE(value, '$.maxDownloadPrice', newMaxDownloadPrice, '$.maxUploadPrice', newMaxUploadPrice) + FROM ( + -- Convert TB to bytes by trimming the last 12 digits + SELECT + SUBSTR(maxDownloadPrice, 1, LENGTH(maxDownloadPrice)-12) AS newMaxDownloadPrice, + SUBSTR(maxUploadPrice, 1, LENGTH(maxUploadPrice)-12) AS newMaxUploadPrice + FROM ( + -- SELECT previous settings + SELECT + JSON_EXTRACT(value, '$.maxDownloadPrice') AS maxDownloadPrice, + JSON_EXTRACT(value, '$.maxUploadPrice') AS maxUploadPrice + ) AS _ + ) AS _ +) +WHERE settings.key = "gouging";