Skip to content

Commit ad107d0

Browse files
Merge v2 -> dev (#1480)
This PR merges the `v2` branch into the `dev` branch effectively turning `dev` into the main development branch for `v2`.
2 parents 766b227 + 6668d90 commit ad107d0

File tree

20 files changed

+357
-254
lines changed

20 files changed

+357
-254
lines changed

api/contract.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,16 @@ type (
144144
TotalCost types.Currency `json:"totalCost"`
145145
}
146146

147+
// ContractFormRequest is the request type for the POST /contracts endpoint.
148+
ContractFormRequest struct {
149+
EndHeight uint64 `json:"endHeight"`
150+
HostCollateral types.Currency `json:"hostCollateral"`
151+
HostKey types.PublicKey `json:"hostKey"`
152+
HostIP string `json:"hostIP"`
153+
RenterFunds types.Currency `json:"renterFunds"`
154+
RenterAddress types.Address `json:"renterAddress"`
155+
}
156+
147157
// ContractKeepaliveRequest is the request type for the /contract/:id/keepalive
148158
// endpoint.
149159
ContractKeepaliveRequest struct {

api/wallet.go

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55
"net/url"
66
"time"
77

8-
rhpv2 "go.sia.tech/core/rhp/v2"
98
rhpv3 "go.sia.tech/core/rhp/v3"
109
"go.sia.tech/core/types"
1110
)
@@ -45,18 +44,6 @@ type (
4544
DependsOn []types.Transaction `json:"dependsOn"`
4645
}
4746

48-
// WalletPrepareFormRequest is the request type for the /wallet/prepare/form
49-
// endpoint.
50-
WalletPrepareFormRequest struct {
51-
EndHeight uint64 `json:"endHeight"`
52-
HostCollateral types.Currency `json:"hostCollateral"`
53-
HostKey types.PublicKey `json:"hostKey"`
54-
HostSettings rhpv2.HostSettings `json:"hostSettings"`
55-
RenterAddress types.Address `json:"renterAddress"`
56-
RenterFunds types.Currency `json:"renterFunds"`
57-
RenterKey types.PublicKey `json:"renterKey"`
58-
}
59-
6047
// WalletPrepareRenewRequest is the request type for the /wallet/prepare/renew
6148
// endpoint.
6249
WalletPrepareRenewRequest struct {

api/worker.go

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -80,16 +80,6 @@ type (
8080
Error string `json:"error,omitempty"`
8181
}
8282

83-
// RHPFormRequest is the request type for the /rhp/form endpoint.
84-
RHPFormRequest struct {
85-
EndHeight uint64 `json:"endHeight"`
86-
HostCollateral types.Currency `json:"hostCollateral"`
87-
HostKey types.PublicKey `json:"hostKey"`
88-
HostIP string `json:"hostIP"`
89-
RenterFunds types.Currency `json:"renterFunds"`
90-
RenterAddress types.Address `json:"renterAddress"`
91-
}
92-
9383
// RHPFormResponse is the response type for the /rhp/form endpoint.
9484
RHPFormResponse struct {
9585
ContractID types.FileContractID `json:"contractID"`

autopilot/autopilot.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,13 @@ type Bus interface {
4242
ConsensusState(ctx context.Context) (api.ConsensusState, error)
4343

4444
// contracts
45-
AddContract(ctx context.Context, c rhpv2.ContractRevision, contractPrice, totalCost types.Currency, startHeight uint64, state string) (api.ContractMetadata, error)
4645
AddRenewedContract(ctx context.Context, c rhpv2.ContractRevision, contractPrice, totalCost types.Currency, startHeight uint64, renewedFrom types.FileContractID, state string) (api.ContractMetadata, error)
4746
AncestorContracts(ctx context.Context, id types.FileContractID, minStartHeight uint64) ([]api.ArchivedContract, error)
4847
ArchiveContracts(ctx context.Context, toArchive map[types.FileContractID]string) error
4948
Contract(ctx context.Context, id types.FileContractID) (api.ContractMetadata, error)
5049
Contracts(ctx context.Context, opts api.ContractsOpts) (contracts []api.ContractMetadata, err error)
5150
FileContractTax(ctx context.Context, payout types.Currency) (types.Currency, error)
51+
FormContract(ctx context.Context, renterAddress types.Address, renterFunds types.Currency, hostKey types.PublicKey, hostIP string, hostCollateral types.Currency, endHeight uint64) (api.ContractMetadata, error)
5252
SetContractSet(ctx context.Context, set string, contracts []types.FileContractID) error
5353
PrunableData(ctx context.Context) (prunableData api.ContractsPrunableDataResponse, err error)
5454

autopilot/contractor/contractor.go

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -81,14 +81,14 @@ const (
8181
)
8282

8383
type Bus interface {
84-
AddContract(ctx context.Context, c rhpv2.ContractRevision, contractPrice, totalCost types.Currency, startHeight uint64, state string) (api.ContractMetadata, error)
8584
AddRenewedContract(ctx context.Context, c rhpv2.ContractRevision, contractPrice, totalCost types.Currency, startHeight uint64, renewedFrom types.FileContractID, state string) (api.ContractMetadata, error)
8685
AncestorContracts(ctx context.Context, id types.FileContractID, minStartHeight uint64) ([]api.ArchivedContract, error)
8786
ArchiveContracts(ctx context.Context, toArchive map[types.FileContractID]string) error
8887
ConsensusState(ctx context.Context) (api.ConsensusState, error)
8988
Contract(ctx context.Context, id types.FileContractID) (api.ContractMetadata, error)
9089
Contracts(ctx context.Context, opts api.ContractsOpts) (contracts []api.ContractMetadata, err error)
9190
FileContractTax(ctx context.Context, payout types.Currency) (types.Currency, error)
91+
FormContract(ctx context.Context, renterAddress types.Address, renterFunds types.Currency, hostKey types.PublicKey, hostIP string, hostCollateral types.Currency, endHeight uint64) (api.ContractMetadata, error)
9292
Host(ctx context.Context, hostKey types.PublicKey) (api.Host, error)
9393
RecordContractSetChurnMetric(ctx context.Context, metrics ...api.ContractSetChurnMetric) error
9494
SearchHosts(ctx context.Context, opts api.SearchHostOptions) ([]api.Host, error)
@@ -99,7 +99,6 @@ type Bus interface {
9999
type Worker interface {
100100
Contracts(ctx context.Context, hostTimeout time.Duration) (api.ContractsResponse, error)
101101
RHPBroadcast(ctx context.Context, fcid types.FileContractID) (err error)
102-
RHPForm(ctx context.Context, endHeight uint64, hk types.PublicKey, hostIP string, renterAddress types.Address, renterFunds types.Currency, hostCollateral types.Currency) (rhpv2.ContractRevision, []types.Transaction, error)
103102
RHPPriceTable(ctx context.Context, hostKey types.PublicKey, siamuxAddr string, timeout time.Duration) (api.HostPriceTable, error)
104103
RHPRenew(ctx context.Context, fcid types.FileContractID, endHeight uint64, hk types.PublicKey, hostIP string, hostAddress, renterAddress types.Address, renterFunds, minNewCollateral, maxFundAmount types.Currency, expectedNewStorage, windowSize uint64) (api.RHPRenewResponse, error)
105104
RHPScan(ctx context.Context, hostKey types.PublicKey, hostIP string, timeout time.Duration) (api.RHPScanResponse, error)
@@ -228,7 +227,7 @@ func (c *Contractor) formContract(ctx *mCtx, w Worker, host api.Host, minInitial
228227
hostCollateral := rhpv2.ContractFormationCollateral(ctx.Period(), expectedStorage, scan.Settings)
229228

230229
// form contract
231-
contract, _, err := w.RHPForm(ctx, endHeight, hk, host.NetAddress, ctx.state.Address, renterFunds, hostCollateral)
230+
contract, err := c.bus.FormContract(ctx, ctx.state.Address, renterFunds, hk, host.NetAddress, hostCollateral, endHeight)
232231
if err != nil {
233232
// TODO: keep track of consecutive failures and break at some point
234233
logger.Errorw(fmt.Sprintf("contract formation failed, err: %v", err), "hk", hk)
@@ -241,20 +240,12 @@ func (c *Contractor) formContract(ctx *mCtx, w Worker, host api.Host, minInitial
241240
// update the budget
242241
*budget = budget.Sub(renterFunds)
243242

244-
// persist contract in store
245-
contractPrice := contract.Revision.MissedHostPayout().Sub(hostCollateral)
246-
formedContract, err := c.bus.AddContract(ctx, contract, contractPrice, renterFunds, cs.BlockHeight, api.ContractStatePending)
247-
if err != nil {
248-
logger.Errorw(fmt.Sprintf("contract formation failed, err: %v", err), "hk", hk)
249-
return api.ContractMetadata{}, true, err
250-
}
251-
252243
logger.Infow("formation succeeded",
253-
"fcid", formedContract.ID,
244+
"fcid", contract.ID,
254245
"renterFunds", renterFunds.String(),
255246
"collateral", hostCollateral.String(),
256247
)
257-
return formedContract, true, nil
248+
return contract, true, nil
258249
}
259250

260251
func (c *Contractor) initialContractFunding(settings rhpv2.HostSettings, txnFee, minFunding, maxFunding types.Currency) types.Currency {

autopilot/workerpool.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55
"sync"
66
"time"
77

8-
rhpv2 "go.sia.tech/core/rhp/v2"
98
rhpv3 "go.sia.tech/core/rhp/v3"
109
"go.sia.tech/core/types"
1110
"go.sia.tech/renterd/api"
@@ -20,7 +19,6 @@ type Worker interface {
2019
MigrateSlab(ctx context.Context, s object.Slab, set string) (api.MigrateSlabResponse, error)
2120

2221
RHPBroadcast(ctx context.Context, fcid types.FileContractID) (err error)
23-
RHPForm(ctx context.Context, endHeight uint64, hk types.PublicKey, hostIP string, renterAddress types.Address, renterFunds types.Currency, hostCollateral types.Currency) (rhpv2.ContractRevision, []types.Transaction, error)
2422
RHPFund(ctx context.Context, contractID types.FileContractID, hostKey types.PublicKey, hostIP, siamuxAddr string, balance types.Currency) (err error)
2523
RHPPriceTable(ctx context.Context, hostKey types.PublicKey, siamuxAddr string, timeout time.Duration) (api.HostPriceTable, error)
2624
RHPPruneContract(ctx context.Context, fcid types.FileContractID, timeout time.Duration) (pruned, remaining uint64, err error)

bus/bus.go

Lines changed: 96 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"errors"
1010
"fmt"
1111
"math/big"
12+
"net"
1213
"net/http"
1314
"strings"
1415
"time"
@@ -26,10 +27,13 @@ import (
2627
"go.sia.tech/renterd/api"
2728
"go.sia.tech/renterd/bus/client"
2829
ibus "go.sia.tech/renterd/internal/bus"
30+
"go.sia.tech/renterd/internal/rhp"
31+
rhp2 "go.sia.tech/renterd/internal/rhp/v2"
2932
"go.sia.tech/renterd/object"
3033
"go.sia.tech/renterd/stores/sql"
3134
"go.sia.tech/renterd/webhooks"
3235
"go.uber.org/zap"
36+
"golang.org/x/crypto/blake2b"
3337
)
3438

3539
const (
@@ -303,6 +307,7 @@ type (
303307

304308
type Bus struct {
305309
startTime time.Time
310+
masterKey [32]byte
306311

307312
accountsMgr AccountManager
308313
alerts alerts.Alerter
@@ -320,6 +325,8 @@ type Bus struct {
320325
mtrcs MetricsStore
321326
ss SettingStore
322327

328+
rhp2 *rhp2.Client
329+
323330
contractLocker ContractLocker
324331
sectors UploadingSectorsCache
325332
walletMetricsRecorder WalletMetricsRecorder
@@ -328,10 +335,13 @@ type Bus struct {
328335
}
329336

330337
// New returns a new Bus
331-
func New(ctx context.Context, am AlertManager, wm WebhooksManager, cm ChainManager, s Syncer, w Wallet, store Store, announcementMaxAge time.Duration, l *zap.Logger) (_ *Bus, err error) {
338+
func New(ctx context.Context, masterKey [32]byte, am AlertManager, wm WebhooksManager, cm ChainManager, s Syncer, w Wallet, store Store, announcementMaxAge time.Duration, l *zap.Logger) (_ *Bus, err error) {
332339
l = l.Named("bus")
333340

334341
b := &Bus{
342+
startTime: time.Now(),
343+
masterKey: masterKey,
344+
335345
s: s,
336346
cm: cm,
337347
w: w,
@@ -346,7 +356,7 @@ func New(ctx context.Context, am AlertManager, wm WebhooksManager, cm ChainManag
346356
webhooksMgr: wm,
347357
logger: l.Sugar(),
348358

349-
startTime: time.Now(),
359+
rhp2: rhp2.New(rhp.NewFallbackDialer(store, net.Dialer{}, l), l),
350360
}
351361

352362
// init settings
@@ -411,6 +421,7 @@ func (b *Bus) Handler() http.Handler {
411421
"GET /consensus/siafundfee/:payout": b.contractTaxHandlerGET,
412422
"GET /consensus/state": b.consensusStateHandler,
413423

424+
"POST /contracts": b.contractsFormHandler,
414425
"GET /contracts": b.contractsHandlerGET,
415426
"DELETE /contracts/all": b.contractsAllHandlerDELETE,
416427
"POST /contracts/archive": b.contractsArchiveHandlerPOST,
@@ -507,7 +518,6 @@ func (b *Bus) Handler() http.Handler {
507518
"POST /wallet/fund": b.walletFundHandler,
508519
"GET /wallet/outputs": b.walletOutputsHandler,
509520
"GET /wallet/pending": b.walletPendingHandler,
510-
"POST /wallet/prepare/form": b.walletPrepareFormHandler,
511521
"POST /wallet/prepare/renew": b.walletPrepareRenewHandler,
512522
"POST /wallet/redistribute": b.walletRedistributeHandler,
513523
"POST /wallet/send": b.walletSendSiacoinsHandler,
@@ -532,6 +542,71 @@ func (b *Bus) Shutdown(ctx context.Context) error {
532542
)
533543
}
534544

545+
func (b *Bus) addContract(ctx context.Context, rev rhpv2.ContractRevision, contractPrice, totalCost types.Currency, startHeight uint64, state string) (api.ContractMetadata, error) {
546+
c, err := b.ms.AddContract(ctx, rev, contractPrice, totalCost, startHeight, state)
547+
if err != nil {
548+
return api.ContractMetadata{}, err
549+
}
550+
551+
b.broadcastAction(webhooks.Event{
552+
Module: api.ModuleContract,
553+
Event: api.EventAdd,
554+
Payload: api.EventContractAdd{
555+
Added: c,
556+
Timestamp: time.Now().UTC(),
557+
},
558+
})
559+
return c, nil
560+
}
561+
562+
func (b *Bus) isPassedV2AllowHeight() bool {
563+
cs := b.cm.TipState()
564+
return cs.Index.Height >= cs.Network.HardforkV2.AllowHeight
565+
}
566+
567+
func (b *Bus) formContract(ctx context.Context, hostSettings rhpv2.HostSettings, renterAddress types.Address, renterFunds, hostCollateral types.Currency, hostKey types.PublicKey, hostIP string, endHeight uint64) (rhpv2.ContractRevision, error) {
568+
// derive the renter key
569+
renterKey := b.deriveRenterKey(hostKey)
570+
571+
// prepare the transaction
572+
cs := b.cm.TipState()
573+
fc := rhpv2.PrepareContractFormation(renterKey.PublicKey(), hostKey, renterFunds, hostCollateral, endHeight, hostSettings, renterAddress)
574+
txn := types.Transaction{FileContracts: []types.FileContract{fc}}
575+
576+
// calculate the miner fee
577+
fee := b.cm.RecommendedFee().Mul64(cs.TransactionWeight(txn))
578+
txn.MinerFees = []types.Currency{fee}
579+
580+
// fund the transaction
581+
cost := rhpv2.ContractFormationCost(cs, fc, hostSettings.ContractPrice).Add(fee)
582+
toSign, err := b.w.FundTransaction(&txn, cost, true)
583+
if err != nil {
584+
return rhpv2.ContractRevision{}, fmt.Errorf("couldn't fund transaction: %w", err)
585+
}
586+
587+
// sign the transaction
588+
b.w.SignTransaction(&txn, toSign, wallet.ExplicitCoveredFields(txn))
589+
590+
// form the contract
591+
contract, txnSet, err := b.rhp2.FormContract(ctx, hostKey, hostIP, renterKey, append(b.cm.UnconfirmedParents(txn), txn))
592+
if err != nil {
593+
b.w.ReleaseInputs([]types.Transaction{txn}, nil)
594+
return rhpv2.ContractRevision{}, err
595+
}
596+
597+
// add transaction set to the pool
598+
_, err = b.cm.AddPoolTransactions(txnSet)
599+
if err != nil {
600+
b.w.ReleaseInputs([]types.Transaction{txn}, nil)
601+
return rhpv2.ContractRevision{}, fmt.Errorf("couldn't add transaction set to the pool: %w", err)
602+
}
603+
604+
// broadcast the transaction set
605+
go b.s.BroadcastTransactionSet(txnSet)
606+
607+
return contract, nil
608+
}
609+
535610
// initSettings loads the default settings if the setting is not already set and
536611
// ensures the settings are valid
537612
func (b *Bus) initSettings(ctx context.Context) error {
@@ -645,3 +720,21 @@ func (b *Bus) initSettings(ctx context.Context) error {
645720

646721
return nil
647722
}
723+
724+
func (b *Bus) deriveRenterKey(hostKey types.PublicKey) types.PrivateKey {
725+
seed := blake2b.Sum256(append(b.deriveSubKey("renterkey"), hostKey[:]...))
726+
pk := types.NewPrivateKeyFromSeed(seed[:])
727+
for i := range seed {
728+
seed[i] = 0
729+
}
730+
return pk
731+
}
732+
733+
func (b *Bus) deriveSubKey(purpose string) types.PrivateKey {
734+
seed := blake2b.Sum256(append(b.masterKey[:], []byte(purpose)...))
735+
pk := types.NewPrivateKeyFromSeed(seed[:])
736+
for i := range seed {
737+
seed[i] = 0
738+
}
739+
return pk
740+
}

bus/client/contracts.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,19 @@ func (c *Client) DeleteContractSet(ctx context.Context, set string) (err error)
130130
return
131131
}
132132

133+
// FormContract forms a contract with a host and adds it to the bus.
134+
func (c *Client) FormContract(ctx context.Context, renterAddress types.Address, renterFunds types.Currency, hostKey types.PublicKey, hostIP string, hostCollateral types.Currency, endHeight uint64) (contract api.ContractMetadata, err error) {
135+
err = c.c.WithContext(ctx).POST("/contracts", api.ContractFormRequest{
136+
EndHeight: endHeight,
137+
HostCollateral: hostCollateral,
138+
HostKey: hostKey,
139+
HostIP: hostIP,
140+
RenterFunds: renterFunds,
141+
RenterAddress: renterAddress,
142+
}, &contract)
143+
return
144+
}
145+
133146
// KeepaliveContract extends the duration on an already acquired lock on a
134147
// contract.
135148
func (c *Client) KeepaliveContract(ctx context.Context, contractID types.FileContractID, lockID uint64, d time.Duration) (err error) {

bus/client/wallet.go

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import (
66
"net/http"
77
"net/url"
88

9-
rhpv2 "go.sia.tech/core/rhp/v2"
109
rhpv3 "go.sia.tech/core/rhp/v3"
1110
"go.sia.tech/core/types"
1211
"go.sia.tech/renterd/api"
@@ -64,21 +63,6 @@ func (c *Client) WalletPending(ctx context.Context) (resp []types.Transaction, e
6463
return
6564
}
6665

67-
// WalletPrepareForm funds and signs a contract transaction.
68-
func (c *Client) WalletPrepareForm(ctx context.Context, renterAddress types.Address, renterKey types.PublicKey, renterFunds, hostCollateral types.Currency, hostKey types.PublicKey, hostSettings rhpv2.HostSettings, endHeight uint64) (txns []types.Transaction, err error) {
69-
req := api.WalletPrepareFormRequest{
70-
EndHeight: endHeight,
71-
HostCollateral: hostCollateral,
72-
HostKey: hostKey,
73-
HostSettings: hostSettings,
74-
RenterAddress: renterAddress,
75-
RenterFunds: renterFunds,
76-
RenterKey: renterKey,
77-
}
78-
err = c.c.WithContext(ctx).POST("/wallet/prepare/form", req, &txns)
79-
return
80-
}
81-
8266
// WalletPrepareRenew funds and signs a contract renewal transaction.
8367
func (c *Client) WalletPrepareRenew(ctx context.Context, revision types.FileContractRevision, hostAddress, renterAddress types.Address, renterKey types.PrivateKey, renterFunds, minNewCollateral, maxFundAmount types.Currency, pt rhpv3.HostPriceTable, endHeight, windowSize, expectedStorage uint64) (api.WalletPrepareRenewResponse, error) {
8468
req := api.WalletPrepareRenewRequest{

0 commit comments

Comments
 (0)