Skip to content
Merged
Show file tree
Hide file tree
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
default: patch
---

# Fix a bug where contracts weren't renewed due to invalid signatures.
50 changes: 27 additions & 23 deletions client/v2/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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
}

Expand All @@ -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 {
Expand All @@ -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 {
Expand Down Expand Up @@ -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 {
Expand All @@ -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),
}
}
24 changes: 12 additions & 12 deletions client/v2/contracts.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down
8 changes: 4 additions & 4 deletions client/v2/rpc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{}},
},
Expand Down
2 changes: 1 addition & 1 deletion contracts/renew.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
8 changes: 4 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
)
20 changes: 10 additions & 10 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -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=
Expand All @@ -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=
Expand All @@ -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=
Expand Down
Loading