diff --git a/.changeset/fix_a_bug_where_contracts_werent_renewed_due_to_invalid_signatures.md b/.changeset/fix_a_bug_where_contracts_werent_renewed_due_to_invalid_signatures.md new file mode 100644 index 000000000..8c81cb33b --- /dev/null +++ b/.changeset/fix_a_bug_where_contracts_werent_renewed_due_to_invalid_signatures.md @@ -0,0 +1,5 @@ +--- +default: patch +--- + +# Fix a bug where contracts weren't renewed due to invalid signatures. diff --git a/client/v2/client.go b/client/v2/client.go index 3e3e362e5..f709ee898 100644 --- a/client/v2/client.go +++ b/client/v2/client.go @@ -153,9 +153,9 @@ type Client struct { tg *threadgroup.ThreadGroup hosts *Provider - mu sync.Mutex // protects the fields below - cachedPrices map[types.PublicKey]proto.HostPrices - transports map[types.PublicKey]*transport + mu sync.Mutex // protects the fields below + cachedSettings map[types.PublicKey]proto.HostSettings + transports map[types.PublicKey]*transport } func (c *Client) resetTransport(hostKey types.PublicKey) { @@ -234,14 +234,17 @@ func (c *Client) Prices(ctx context.Context, hostKey types.PublicKey) (prices pr } defer done() + var settings proto.HostSettings + var cached bool start := time.Now() err = c.rpcFn(ctx, hostKey, func(ctx context.Context, transport rhp.TransportClient) error { - prices, err = c.prices(ctx, hostKey, transport) + settings, cached, err = c.settings(ctx, hostKey, transport) return err }) - if err == nil { + if err == nil && !cached { c.hosts.AddSettingsSample(hostKey, time.Since(start)) } + prices = settings.Prices return } @@ -257,12 +260,12 @@ func (c *Client) WriteSector(ctx context.Context, accountKey types.PrivateKey, h start := time.Now() err = c.rpcFn(ctx, hostKey, func(ctx context.Context, transport rhp.TransportClient) error { - prices, err := c.prices(ctx, hostKey, transport) + settings, _, err := c.settings(ctx, hostKey, transport) if err != nil { return fmt.Errorf("failed to get host prices: %w", err) } token := proto.NewAccountToken(accountKey, hostKey) - result, err = rhp.RPCWriteSector(ctx, transport, prices, token, bytes.NewReader(data), uint64(len(data))) + result, err = rhp.RPCWriteSector(ctx, transport, settings.Prices, token, bytes.NewReader(data), uint64(len(data))) return err }) if err == nil { @@ -282,12 +285,12 @@ func (c *Client) ReadSector(ctx context.Context, accountKey types.PrivateKey, ho start := time.Now() err = c.rpcFn(ctx, hostKey, func(ctx context.Context, transport rhp.TransportClient) error { - prices, err := c.prices(ctx, hostKey, transport) + settings, _, err := c.settings(ctx, hostKey, transport) if err != nil { return fmt.Errorf("failed to get host prices: %w", err) } token := proto.NewAccountToken(accountKey, hostKey) - result, err = rhp.RPCReadSector(ctx, transport, prices, token, w, root, offset, length) + result, err = rhp.RPCReadSector(ctx, transport, settings.Prices, token, w, root, offset, length) return err }) if err == nil { @@ -389,29 +392,30 @@ func (c *Client) Close() error { return nil } -// prices fetches the host prices using an existing transport connection. +// settings fetches the host settings using an existing transport connection. // -// If the prices are cached and valid, the cached prices are returned. -func (c *Client) prices(ctx context.Context, hostKey types.PublicKey, transport rhp.TransportClient) (proto.HostPrices, error) { +// If the settings are cached and valid, the cached settings are returned with a +// boolean flag set to 'true'. +func (c *Client) settings(ctx context.Context, hostKey types.PublicKey, transport rhp.TransportClient) (proto.HostSettings, bool, error) { c.mu.Lock() - prices := c.cachedPrices[hostKey] - if prices.Validate(hostKey) == nil && time.Until(prices.ValidUntil) > 30*time.Second { + settings := c.cachedSettings[hostKey] + if settings.Prices.Validate(hostKey) == nil && time.Until(settings.Prices.ValidUntil) > 30*time.Second { c.mu.Unlock() - return prices, nil + return settings, true, nil } c.mu.Unlock() settings, err := rhp.RPCSettings(ctx, transport) if err != nil { - return proto.HostPrices{}, err + return proto.HostSettings{}, false, err } if settings.Prices.Validate(hostKey) == nil { c.mu.Lock() - c.cachedPrices[hostKey] = settings.Prices + c.cachedSettings[hostKey] = settings c.mu.Unlock() } - return settings.Prices, nil + return settings, false, nil } func shouldResetTransport(err error) bool { @@ -435,10 +439,10 @@ func shouldResetTransport(err error) bool { // New creates a new Client. func New(hosts *Provider, log *zap.Logger) *Client { return &Client{ - log: log, - tg: threadgroup.New(), - hosts: hosts, - cachedPrices: make(map[types.PublicKey]proto.HostPrices), - transports: make(map[types.PublicKey]*transport), + log: log, + tg: threadgroup.New(), + hosts: hosts, + cachedSettings: make(map[types.PublicKey]proto.HostSettings), + transports: make(map[types.PublicKey]*transport), } } diff --git a/client/v2/contracts.go b/client/v2/contracts.go index b9b31a9b4..c7a533fba 100644 --- a/client/v2/contracts.go +++ b/client/v2/contracts.go @@ -55,11 +55,11 @@ func (c *Client) FormContract(ctx context.Context, chain ChainManager, signer rh defer done() err = c.rpcFn(ctx, params.HostKey, func(ctx context.Context, transport rhp.TransportClient) error { - prices, err := c.prices(ctx, params.HostKey, transport) + settings, _, err := c.settings(ctx, params.HostKey, transport) if err != nil { return fmt.Errorf("failed to get host prices: %w", err) } - result, err = rhp.RPCFormContract(ctx, transport, chain, signer, chain.TipState(), prices, params.HostKey, params.HostAddress, params.RPCFormContractParams) + result, err = rhp.RPCFormContract(ctx, transport, chain, signer, chain.TipState(), settings.Prices, params.HostKey, params.HostAddress, params.RPCFormContractParams) return err }) return @@ -81,11 +81,11 @@ func (c *Client) RefreshContract(ctx context.Context, chain ChainManager, signer } err = c.rpcFn(ctx, revision.HostPublicKey, func(ctx context.Context, transport rhp.TransportClient) error { - prices, err := c.prices(ctx, revision.HostPublicKey, transport) + settings, _, err := c.settings(ctx, revision.HostPublicKey, transport) if err != nil { return fmt.Errorf("failed to get host prices: %w", err) } - result, err = rhp.RPCRefreshContractPartialRollover(ctx, transport, chain, signer, chain.TipState(), prices, revision, rpcParams) + result, err = rhp.RPCRefreshContractPartialRollover(ctx, transport, chain, signer, chain.TipState(), settings.Prices, settings.WalletAddress, revision, rpcParams) return err }) return @@ -108,11 +108,11 @@ func (c *Client) RenewContract(ctx context.Context, chain ChainManager, signer r } err = c.rpcFn(ctx, revision.HostPublicKey, func(ctx context.Context, transport rhp.TransportClient) error { - prices, err := c.prices(ctx, revision.HostPublicKey, transport) + settings, _, err := c.settings(ctx, revision.HostPublicKey, transport) if err != nil { return fmt.Errorf("failed to get host prices: %w", err) } - result, err = rhp.RPCRenewContract(ctx, transport, chain, signer, chain.TipState(), prices, revision, rpcParams) + result, err = rhp.RPCRenewContract(ctx, transport, chain, signer, chain.TipState(), settings.Prices, settings.WalletAddress, revision, rpcParams) return err }) return @@ -142,11 +142,11 @@ func (c *Client) SectorRoots(ctx context.Context, signer rhp.ContractSigner, cha defer done() err = c.rpcFn(ctx, contract.Revision.HostPublicKey, func(ctx context.Context, transport rhp.TransportClient) error { - prices, err := c.prices(ctx, contract.Revision.HostPublicKey, transport) + settings, _, err := c.settings(ctx, contract.Revision.HostPublicKey, transport) if err != nil { return fmt.Errorf("failed to get host prices: %w", err) } - result, err = rhp.RPCSectorRoots(ctx, transport, chain.TipState(), prices, signer, contract, offset, length) + result, err = rhp.RPCSectorRoots(ctx, transport, chain.TipState(), settings.Prices, signer, contract, offset, length) return err }) return @@ -161,11 +161,11 @@ func (c *Client) FreeSectors(ctx context.Context, signer rhp.ContractSigner, cha defer done() err = c.rpcFn(ctx, contract.Revision.HostPublicKey, func(ctx context.Context, transport rhp.TransportClient) error { - prices, err := c.prices(ctx, contract.Revision.HostPublicKey, transport) + settings, _, err := c.settings(ctx, contract.Revision.HostPublicKey, transport) if err != nil { return fmt.Errorf("failed to get host prices: %w", err) } - result, err = rhp.RPCFreeSectors(ctx, transport, signer, chain.TipState(), prices, contract, indices) + result, err = rhp.RPCFreeSectors(ctx, transport, signer, chain.TipState(), settings.Prices, contract, indices) return err }) return @@ -180,11 +180,11 @@ func (c *Client) AppendSectors(ctx context.Context, signer rhp.ContractSigner, c defer done() err = c.rpcFn(ctx, revision.Revision.HostPublicKey, func(ctx context.Context, transport rhp.TransportClient) error { - prices, err := c.prices(ctx, revision.Revision.HostPublicKey, transport) + settings, _, err := c.settings(ctx, revision.Revision.HostPublicKey, transport) if err != nil { return fmt.Errorf("failed to get host prices: %w", err) } - res, err = rhp.RPCAppendSectors(ctx, transport, signer, chain.TipState(), prices, revision, sectors) + res, err = rhp.RPCAppendSectors(ctx, transport, signer, chain.TipState(), settings.Prices, revision, sectors) return err }) return diff --git a/client/v2/rpc_test.go b/client/v2/rpc_test.go index 89e3a3f3c..8d376fbc9 100644 --- a/client/v2/rpc_test.go +++ b/client/v2/rpc_test.go @@ -36,10 +36,10 @@ func TestRPCFnErrorDecoration(t *testing.T) { customErr := errors.New("custom context error") c := &Client{ - log: zap.NewNop(), - tg: threadgroup.New(), - hosts: NewProvider(mockStore{}), - cachedPrices: make(map[types.PublicKey]proto.HostPrices), + log: zap.NewNop(), + tg: threadgroup.New(), + hosts: NewProvider(mockStore{}), + cachedSettings: make(map[types.PublicKey]proto.HostSettings), transports: map[types.PublicKey]*transport{ hostKey: {tc: mockTransportClient{}}, }, diff --git a/contracts/renew.go b/contracts/renew.go index 699d77227..466eab542 100644 --- a/contracts/renew.go +++ b/contracts/renew.go @@ -70,7 +70,7 @@ func (cm *ContractManager) renewContract(ctx context.Context, contract Contract, var res rhp.RPCRenewContractResult err = cm.rev.WithRevision(renewCtx, lc, func(rev rhp.ContractRevision) (rhp.ContractRevision, proto.Usage, error) { cappedCollateral := collateral - estimatedRenewal, _ := proto.RenewContract(rev.Revision, settings.Prices, proto.RPCRenewContractParams{ + estimatedRenewal, _ := proto.RenewContract(rev.Revision, settings.Prices, settings.WalletAddress, proto.RPCRenewContractParams{ Allowance: allowance, Collateral: cappedCollateral, ContractID: contract.ID, diff --git a/go.mod b/go.mod index c874572fa..d0be95d67 100644 --- a/go.mod +++ b/go.mod @@ -8,8 +8,8 @@ require ( github.com/klauspost/reedsolomon v1.13.3 github.com/oschwald/geoip2-golang v1.13.0 github.com/shopspring/decimal v1.4.0 - go.sia.tech/core v0.19.1 - go.sia.tech/coreutils v0.21.2 + go.sia.tech/core v0.19.2-0.20260421131916-3a5d3f254fd9 + go.sia.tech/coreutils v0.21.3-0.20260421140827-cfc04c84419e go.sia.tech/jape v0.14.1 go.sia.tech/mux v1.5.0 go.sia.tech/web/indexd v0.17.0 @@ -39,9 +39,9 @@ require ( go.etcd.io/bbolt v1.4.3 // indirect go.sia.tech/web v0.0.0-20240422221546-c1709d16b6ef // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/net v0.52.0 // indirect + golang.org/x/net v0.53.0 // indirect golang.org/x/sync v0.20.0 // indirect golang.org/x/sys v0.43.0 // indirect golang.org/x/text v0.36.0 // indirect - golang.org/x/tools v0.43.0 // indirect + golang.org/x/tools v0.44.0 // indirect ) diff --git a/go.sum b/go.sum index 1f7d9ccbf..89590d866 100644 --- a/go.sum +++ b/go.sum @@ -48,10 +48,10 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= go.etcd.io/bbolt v1.4.3 h1:dEadXpI6G79deX5prL3QRNP6JB8UxVkqo4UPnHaNXJo= go.etcd.io/bbolt v1.4.3/go.mod h1:tKQlpPaYCVFctUIgFKFnAlvbmB3tpy1vkTnDWohtc0E= -go.sia.tech/core v0.19.1 h1:rinLh0vuB4eBMVIVZ+Fmaym7B1nxw43I/v/SxYYPVeI= -go.sia.tech/core v0.19.1/go.mod h1:UYT4fwCjNx2wsY1cvGTjXVQPjCEa2LNEgFF3lbHjUVs= -go.sia.tech/coreutils v0.21.2 h1:Qxk6oarnS3LeSo3tdT2sIuRlMwvDXBfYavZECH9fUp0= -go.sia.tech/coreutils v0.21.2/go.mod h1:Pznmht8HgBHyutm0wtWcvUrqeRfs5iMzbJ7KC9Zx+nw= +go.sia.tech/core v0.19.2-0.20260421131916-3a5d3f254fd9 h1:SHUyAU/iO9ltIqkBEkUjqwoKxbdK7/aUcpcAElIXSpw= +go.sia.tech/core v0.19.2-0.20260421131916-3a5d3f254fd9/go.mod h1:FhSj+8PZLGQZSjujkAkvjhLvzuy9hRZuoBFFJoH2mto= +go.sia.tech/coreutils v0.21.3-0.20260421140827-cfc04c84419e h1:CoKRkfl+8oBcS19Jr/rGnwri4m4Nf/0MBryc91nbqRE= +go.sia.tech/coreutils v0.21.3-0.20260421140827-cfc04c84419e/go.mod h1:mi1CrCMzRDq+lmBmSzZ8T/S+DhG5559bPf453OLliaU= go.sia.tech/jape v0.14.1 h1:3QWpOzAxxcaECuv2Mc6tbkFh+olLnyUxxP5SfwgI/qY= go.sia.tech/jape v0.14.1/go.mod h1:BEygF1DcgdWBenPa75iJ1b91FuxzLmKBxpglmx+C9BY= go.sia.tech/mux v1.5.0 h1:6bQeO5y4AQPcf+UYHjhxpaNX+2V2wI5LIl0BbAg2YDA= @@ -70,10 +70,10 @@ go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI= golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q= -golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI= -golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY= -golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0= -golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw= +golang.org/x/mod v0.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM= +golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU= +golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA= +golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs= golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI= @@ -84,8 +84,8 @@ golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg= golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164= golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= -golang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s= -golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0= +golang.org/x/tools v0.44.0 h1:UP4ajHPIcuMjT1GqzDWRlalUEoY+uzoZKnhOjbIPD2c= +golang.org/x/tools v0.44.0/go.mod h1:KA0AfVErSdxRZIsOVipbv3rQhVXTnlU6UhKxHd1seDI= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=