Skip to content

Commit

Permalink
stores: add PutContract
Browse files Browse the repository at this point in the history
  • Loading branch information
peterjan committed Sep 10, 2024
1 parent 002a78e commit d1ea979
Show file tree
Hide file tree
Showing 12 changed files with 279 additions and 101 deletions.
19 changes: 5 additions & 14 deletions api/contract.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package api

import (
"errors"
"time"

rhpv2 "go.sia.tech/core/rhp/v2"
"go.sia.tech/core/types"
Expand Down Expand Up @@ -50,8 +51,9 @@ type (

// ContractMetadata contains all metadata for a contract.
ContractMetadata struct {
ID types.FileContractID `json:"id"`
HostKey types.PublicKey `json:"hostKey"`
CreatedAt time.Time `json:"createdAt"`
ID types.FileContractID `json:"id"`
HostKey types.PublicKey `json:"hostKey"`

ProofHeight uint64 `json:"proofHeight"`
RenewedFrom types.FileContractID `json:"renewedFrom"`
Expand Down Expand Up @@ -119,9 +121,9 @@ type (

// ContractAddRequest is the request type for the /contract/:id endpoint.
ContractAddRequest struct {
Revision rhpv2.ContractRevision `json:"revision"`
ContractPrice types.Currency `json:"contractPrice"`
InitialRenterFunds types.Currency `json:"initialRenterFunds"`
Revision rhpv2.ContractRevision `json:"revision"`
StartHeight uint64 `json:"startHeight"`
State string `json:"state,omitempty"`
}
Expand Down Expand Up @@ -174,17 +176,6 @@ type (
RenterFunds types.Currency `json:"renterFunds"`
}

// ContractRenewedRequest is the request type for the /contract/:id/renewed
// endpoint.
ContractRenewedRequest struct {
Contract rhpv2.ContractRevision `json:"contract"`
ContractPrice types.Currency `json:"contractPrice"`
InitialRenterFunds types.Currency `json:"initialRenterFunds"`
RenewedFrom types.FileContractID `json:"renewedFrom"`
StartHeight uint64 `json:"startHeight"`
State string `json:"state,omitempty"`
}

// ContractRootsResponse is the response type for the /contract/:id/roots
// endpoint.
ContractRootsResponse struct {
Expand Down
13 changes: 7 additions & 6 deletions bus/bus.go
Original file line number Diff line number Diff line change
Expand Up @@ -210,17 +210,18 @@ type (

// A MetadataStore stores information about contracts and objects.
MetadataStore interface {
AddContract(ctx context.Context, c api.ContractMetadata) error
AddRenewal(ctx context.Context, c api.ContractMetadata) error
AncestorContracts(ctx context.Context, fcid types.FileContractID, minStartHeight uint64) ([]api.ContractMetadata, error)
ArchiveContract(ctx context.Context, id types.FileContractID, reason string) error
ArchiveContracts(ctx context.Context, toArchive map[types.FileContractID]string) error
ArchiveAllContracts(ctx context.Context, reason string) error
Contract(ctx context.Context, id types.FileContractID) (api.ContractMetadata, error)
Contracts(ctx context.Context, opts api.ContractsOpts) ([]api.ContractMetadata, error)
ContractSets(ctx context.Context) ([]string, error)
InsertContract(ctx context.Context, c api.ContractMetadata) error
RecordContractSpending(ctx context.Context, records []api.ContractSpendingRecord) error
RemoveContractSet(ctx context.Context, name string) error
RenewContract(ctx context.Context, c api.ContractMetadata) error
PutContract(ctx context.Context, c api.ContractMetadata) error
RenewedContract(ctx context.Context, renewedFrom types.FileContractID) (api.ContractMetadata, error)
UpdateContractSet(ctx context.Context, set string, toAdd, toRemove []types.FileContractID) error

Expand Down Expand Up @@ -410,18 +411,18 @@ func (b *Bus) Handler() http.Handler {
"GET /consensus/siafundfee/:payout": b.contractTaxHandlerGET,
"GET /consensus/state": b.consensusStateHandler,

"POST /contracts": b.contractsFormHandler,
"PUT /contracts": b.contractsHandlerPUT,
"GET /contracts": b.contractsHandlerGET,
"DELETE /contracts/all": b.contractsAllHandlerDELETE,
"POST /contracts/archive": b.contractsArchiveHandlerPOST,
"POST /contracts/form": b.contractsFormHandler,
"GET /contracts/prunable": b.contractsPrunableDataHandlerGET,
"GET /contracts/renewed/:id": b.contractsRenewedIDHandlerGET,
"GET /contracts/sets": b.contractsSetsHandlerGET,
"POST /contracts/set/:set": b.contractsSetHandlerPUT,
"DELETE /contracts/set/:set": b.contractsSetHandlerDELETE,
"POST /contracts/spending": b.contractsSpendingHandlerPOST,
"GET /contract/:id": b.contractIDHandlerGET,
"POST /contract/:id": b.contractIDHandlerPOST,
"DELETE /contract/:id": b.contractIDHandlerDELETE,
"POST /contract/:id/acquire": b.contractAcquireHandlerPOST,
"GET /contract/:id/ancestors": b.contractIDAncestorsHandler,
Expand Down Expand Up @@ -532,7 +533,7 @@ func (b *Bus) Shutdown(ctx context.Context) error {
}

func (b *Bus) addContract(ctx context.Context, rev rhpv2.ContractRevision, contractPrice, initialRenterFunds types.Currency, startHeight uint64, state string) (api.ContractMetadata, error) {
if err := b.ms.AddContract(ctx, api.ContractMetadata{
if err := b.ms.InsertContract(ctx, api.ContractMetadata{
ID: rev.ID(),
HostKey: rev.HostKey(),
StartHeight: startHeight,
Expand Down Expand Up @@ -563,7 +564,7 @@ func (b *Bus) addContract(ctx context.Context, rev rhpv2.ContractRevision, contr
}

func (b *Bus) addRenewal(ctx context.Context, renewedFrom types.FileContractID, rev rhpv2.ContractRevision, contractPrice, initialRenterFunds types.Currency, startHeight uint64, state string) (api.ContractMetadata, error) {
if err := b.ms.RenewContract(ctx, api.ContractMetadata{
if err := b.ms.AddRenewal(ctx, api.ContractMetadata{
ID: rev.ID(),
HostKey: rev.HostKey(),
RenewedFrom: renewedFrom,
Expand Down
17 changes: 5 additions & 12 deletions bus/client/contracts.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,14 @@ import (
"net/url"
"time"

rhpv2 "go.sia.tech/core/rhp/v2"
"go.sia.tech/core/types"
"go.sia.tech/renterd/api"
)

// AddContract adds the provided contract to the metadata store.
func (c *Client) AddContract(ctx context.Context, revision rhpv2.ContractRevision, contractPrice, initialRenterFunds types.Currency, startHeight uint64, state string) (added api.ContractMetadata, err error) {
err = c.c.WithContext(ctx).POST(fmt.Sprintf("/contract/%s", revision.ID()), api.ContractAddRequest{
Revision: revision,
ContractPrice: contractPrice,
InitialRenterFunds: initialRenterFunds,
StartHeight: startHeight,
State: state,
}, &added)
return
// AddContract adds the provided contract to the metadata store, if the contract
// already exists it will be replaced.
func (c *Client) AddContract(ctx context.Context, contract api.ContractMetadata) error {
return c.c.WithContext(ctx).PUT("/contracts", contract)
}

// AncestorContracts returns any ancestors of a given contract.
Expand Down Expand Up @@ -128,7 +121,7 @@ func (c *Client) DeleteContractSet(ctx context.Context, set string) (err error)

// FormContract forms a contract with a host and adds it to the bus.
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) {
err = c.c.WithContext(ctx).POST("/contracts", api.ContractFormRequest{
err = c.c.WithContext(ctx).POST("/contracts/form", api.ContractFormRequest{
EndHeight: endHeight,
HostCollateral: hostCollateral,
HostKey: hostKey,
Expand Down
43 changes: 15 additions & 28 deletions bus/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -763,7 +763,7 @@ func (b *Bus) contractsHandlerGET(jc jape.Context) {
case api.ContractFilterModeActive:
case api.ContractFilterModeArchived:
default:
jc.Error(fmt.Errorf("invalid filter mode: %v", filterMode), http.StatusBadRequest)
jc.Error(fmt.Errorf("invalid filter mode: '%v'", filterMode), http.StatusBadRequest)
return
}

Expand Down Expand Up @@ -1088,36 +1088,23 @@ func (b *Bus) contractIDHandlerGET(jc jape.Context) {
}
}

func (b *Bus) contractIDHandlerPOST(jc jape.Context) {
// decode parameters
var id types.FileContractID
if jc.DecodeParam("id", &id) != nil {
return
}
var req api.ContractAddRequest
if jc.Decode(&req) != nil {
return
}

// validate the request
if req.InitialRenterFunds.IsZero() {
http.Error(jc.ResponseWriter, "InitialRenterFunds can not be zero", http.StatusBadRequest)
return
} else if req.Revision.ID() != id {
http.Error(jc.ResponseWriter, "Contract ID missmatch", http.StatusBadRequest)
return
} else if req.Revision.ID() == (types.FileContractID{}) {
http.Error(jc.ResponseWriter, "Contract ID is required", http.StatusBadRequest)
return
} else if req.Revision.HostKey() == (types.PublicKey{}) {
http.Error(jc.ResponseWriter, "HostKey is required", http.StatusBadRequest)
func (b *Bus) contractsHandlerPUT(jc jape.Context) {
// decode request
var c api.ContractMetadata
if jc.Decode(&c) != nil {
return
}

// add the contract
metadata, err := b.addContract(jc.Request.Context(), req.Revision, req.ContractPrice, req.InitialRenterFunds, req.StartHeight, req.State)
if jc.Check("couldn't add contract", err) == nil {
jc.Encode(metadata)
// upsert the contract
if jc.Check("failed to add contract", b.ms.PutContract(jc.Request.Context(), c)) == nil {
b.broadcastAction(webhooks.Event{
Module: api.ModuleContract,
Event: api.EventAdd,
Payload: api.EventContractAdd{
Added: c,
Timestamp: time.Now().UTC(),
},
})
}
}

Expand Down
2 changes: 1 addition & 1 deletion internal/test/e2e/cluster_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1517,7 +1517,7 @@ func TestUnconfirmedContractArchival(t *testing.T) {
c := contracts[0]

// add a contract to the bus
err = cluster.bs.AddContract(context.Background(), api.ContractMetadata{
err = cluster.bs.InsertContract(context.Background(), api.ContractMetadata{
ID: types.FileContractID{1},
HostKey: types.PublicKey{1},
StartHeight: cs.BlockHeight,
Expand Down
50 changes: 28 additions & 22 deletions stores/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,27 @@ func (s *SQLStore) SlabBuffers(ctx context.Context) ([]api.SlabBuffer, error) {
return buffers, nil
}

func (s *SQLStore) AddRenewal(ctx context.Context, c api.ContractMetadata) error {
return s.db.Transaction(ctx, func(tx sql.DatabaseTx) error {
// fetch renewed contract
renewed, err := tx.Contract(ctx, c.RenewedFrom)
if err != nil {
return err
}

// insert renewal by updating the renewed contract
err = tx.UpdateContract(ctx, c.RenewedFrom, c)
if err != nil {
return err
}

// reinsert renewed contract
renewed.ArchivalReason = api.ContractArchivalReasonRenewed
renewed.RenewedTo = c.ID
return tx.InsertContract(ctx, renewed)
})
}

func (s *SQLStore) AncestorContracts(ctx context.Context, id types.FileContractID, startHeight uint64) (ancestors []api.ContractMetadata, err error) {
err = s.db.Transaction(ctx, func(tx sql.DatabaseTx) error {
ancestors, err = tx.AncestorContracts(ctx, id, startHeight)
Expand Down Expand Up @@ -208,12 +229,18 @@ func (s *SQLStore) ContractSize(ctx context.Context, id types.FileContractID) (c
return cs, err
}

func (s *SQLStore) AddContract(ctx context.Context, c api.ContractMetadata) error {
func (s *SQLStore) InsertContract(ctx context.Context, c api.ContractMetadata) error {
return s.db.Transaction(ctx, func(tx sql.DatabaseTx) error {
return tx.InsertContract(ctx, c)
})
}

func (s *SQLStore) PutContract(ctx context.Context, c api.ContractMetadata) error {
return s.db.Transaction(ctx, func(tx sql.DatabaseTx) error {
return tx.PutContract(ctx, c)
})
}

func (s *SQLStore) UpdateContractSet(ctx context.Context, name string, toAdd, toRemove []types.FileContractID) error {
toAddMap := make(map[types.FileContractID]struct{})
for _, fcid := range toAdd {
Expand Down Expand Up @@ -556,27 +583,6 @@ func (s *SQLStore) RemoveObjects(ctx context.Context, bucket, prefix string) err
return nil
}

func (s *SQLStore) RenewContract(ctx context.Context, c api.ContractMetadata) error {
return s.db.Transaction(ctx, func(tx sql.DatabaseTx) error {
// fetch renewed contract
renewed, err := tx.Contract(ctx, c.RenewedFrom)
if err != nil {
return err
}

// insert renewal by updating the renewed contract
err = tx.UpdateContract(ctx, c.RenewedFrom, c)
if err != nil {
return err
}

// reinsert renewed contract
renewed.ArchivalReason = api.ContractArchivalReasonRenewed
renewed.RenewedTo = c.ID
return tx.InsertContract(ctx, renewed)
})
}

func (s *SQLStore) Slab(ctx context.Context, key object.EncryptionKey) (slab object.Slab, err error) {
err = s.db.Transaction(ctx, func(tx sql.DatabaseTx) error {
slab, err = tx.Slab(ctx, key)
Expand Down
Loading

0 comments on commit d1ea979

Please sign in to comment.