Skip to content

Commit

Permalink
stores: use metadata for insert/updating contracts
Browse files Browse the repository at this point in the history
  • Loading branch information
peterjan committed Sep 10, 2024
1 parent 7fbdaa6 commit 002a78e
Show file tree
Hide file tree
Showing 26 changed files with 503 additions and 896 deletions.
29 changes: 17 additions & 12 deletions api/contract.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,26 +51,31 @@ type (
// ContractMetadata contains all metadata for a contract.
ContractMetadata struct {
ID types.FileContractID `json:"id"`
HostIP string `json:"hostIP"`
HostKey types.PublicKey `json:"hostKey"`

ContractPrice types.Currency `json:"contractPrice"`
InitialRenterFunds types.Currency `json:"initialRenterFunds"`

ArchivalReason string `json:"archivalReason,omitempty"`
ContractSets []string `json:"contractSets,omitempty"`
ProofHeight uint64 `json:"proofHeight"`
RenewedFrom types.FileContractID `json:"renewedFrom"`
RenewedTo types.FileContractID `json:"renewedTo,omitempty"`
RevisionHeight uint64 `json:"revisionHeight"`
RevisionNumber uint64 `json:"revisionNumber"`
SiamuxAddr string `json:"siamuxAddr,omitempty"`
Size uint64 `json:"size"`
Spending ContractSpending `json:"spending"`
StartHeight uint64 `json:"startHeight"`
State string `json:"state"`
WindowStart uint64 `json:"windowStart"`
WindowEnd uint64 `json:"windowEnd"`

// costs & spending
ContractPrice types.Currency `json:"contractPrice"`
InitialRenterFunds types.Currency `json:"initialRenterFunds"`
Spending ContractSpending `json:"spending"`

// following fields are decorated
HostIP string `json:"hostIP"`
ContractSets []string `json:"contractSets,omitempty"`
SiamuxAddr string `json:"siamuxAddr,omitempty"`

// following fields are only set on archived contracts
ArchivalReason string `json:"archivalReason,omitempty"`
RenewedTo types.FileContractID `json:"renewedTo,omitempty"`
}

// ContractPrunableData wraps a contract's size information with its id.
Expand Down Expand Up @@ -114,7 +119,7 @@ type (

// ContractAddRequest is the request type for the /contract/:id endpoint.
ContractAddRequest struct {
Contract rhpv2.ContractRevision `json:"contract"`
Revision rhpv2.ContractRevision `json:"revision"`
ContractPrice types.Currency `json:"contractPrice"`
InitialRenterFunds types.Currency `json:"initialRenterFunds"`
StartHeight uint64 `json:"startHeight"`
Expand Down Expand Up @@ -199,8 +204,8 @@ type (
}

ContractsOpts struct {
ContractSet string `json:"contractset"`
IncludeArchived bool `json:"includeArchived"`
ContractSet string `json:"contractset"`
FilterMode string `json:"filterMode"`
}
)

Expand Down
4 changes: 4 additions & 0 deletions api/host.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ import (
)

const (
ContractFilterModeAll = "all"
ContractFilterModeActive = "active"
ContractFilterModeArchived = "archived"

HostFilterModeAll = "all"
HostFilterModeAllowed = "allowed"
HostFilterModeBlocked = "blocked"
Expand Down
50 changes: 39 additions & 11 deletions bus/bus.go
Original file line number Diff line number Diff line change
Expand Up @@ -210,8 +210,7 @@ type (

// A MetadataStore stores information about contracts and objects.
MetadataStore interface {
AddContract(ctx context.Context, c rhpv2.ContractRevision, contractPrice, initialRenterFunds types.Currency, startHeight uint64, state string) (api.ContractMetadata, error)
AddRenewedContract(ctx context.Context, c rhpv2.ContractRevision, contractPrice, initialRenterFunds types.Currency, startHeight uint64, renewedFrom types.FileContractID, state string) (api.ContractMetadata, error)
AddContract(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
Expand All @@ -221,6 +220,7 @@ type (
ContractSets(ctx context.Context) ([]string, error)
RecordContractSpending(ctx context.Context, records []api.ContractSpendingRecord) error
RemoveContractSet(ctx context.Context, name string) error
RenewContract(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 @@ -429,7 +429,6 @@ func (b *Bus) Handler() http.Handler {
"POST /contract/:id/keepalive": b.contractKeepaliveHandlerPOST,
"POST /contract/:id/prune": b.contractPruneHandlerPOST,
"POST /contract/:id/renew": b.contractIDRenewHandlerPOST,
"POST /contract/:id/renewed": b.contractIDRenewedHandlerPOST,
"POST /contract/:id/release": b.contractReleaseHandlerPOST,
"GET /contract/:id/roots": b.contractIDRootsHandlerGET,
"GET /contract/:id/size": b.contractSizeHandlerGET,
Expand Down Expand Up @@ -533,7 +532,20 @@ 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) {
c, err := b.ms.AddContract(ctx, rev, contractPrice, initialRenterFunds, startHeight, state)
if err := b.ms.AddContract(ctx, api.ContractMetadata{
ID: rev.ID(),
HostKey: rev.HostKey(),
StartHeight: startHeight,
State: state,
WindowStart: rev.Revision.WindowStart,
WindowEnd: rev.Revision.WindowEnd,
ContractPrice: contractPrice,
InitialRenterFunds: initialRenterFunds,
}); err != nil {
return api.ContractMetadata{}, err
}

added, err := b.ms.Contract(ctx, rev.ID())
if err != nil {
return api.ContractMetadata{}, err
}
Expand All @@ -542,29 +554,45 @@ func (b *Bus) addContract(ctx context.Context, rev rhpv2.ContractRevision, contr
Module: api.ModuleContract,
Event: api.EventAdd,
Payload: api.EventContractAdd{
Added: c,
Added: added,
Timestamp: time.Now().UTC(),
},
})
return c, nil

return added, err
}

func (b *Bus) addRenewedContract(ctx context.Context, renewedFrom types.FileContractID, rev rhpv2.ContractRevision, contractPrice, initialRenterFunds types.Currency, startHeight uint64, state string) (api.ContractMetadata, error) {
r, err := b.ms.AddRenewedContract(ctx, rev, contractPrice, initialRenterFunds, startHeight, renewedFrom, state)
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{
ID: rev.ID(),
HostKey: rev.HostKey(),
RenewedFrom: renewedFrom,
StartHeight: startHeight,
State: state,
WindowStart: rev.Revision.WindowStart,
WindowEnd: rev.Revision.WindowEnd,
ContractPrice: contractPrice,
InitialRenterFunds: initialRenterFunds,
}); err != nil {
return api.ContractMetadata{}, fmt.Errorf("couldn't add renewal: %w", err)
}

renewal, err := b.ms.Contract(ctx, rev.ID())
if err != nil {
return api.ContractMetadata{}, err
}

b.sectors.HandleRenewal(r.ID, r.RenewedFrom)
b.sectors.HandleRenewal(renewal.ID, renewal.RenewedFrom)
b.broadcastAction(webhooks.Event{
Module: api.ModuleContract,
Event: api.EventRenew,
Payload: api.EventContractRenew{
Renewal: r,
Renewal: renewal,
Timestamp: time.Now().UTC(),
},
})
return r, nil

return renewal, err
}

func (b *Bus) broadcastContract(ctx context.Context, fcid types.FileContractID) (txnID types.TransactionID, _ error) {
Expand Down
22 changes: 6 additions & 16 deletions bus/client/contracts.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ import (
)

// AddContract adds the provided contract to the metadata store.
func (c *Client) AddContract(ctx context.Context, contract 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", contract.ID()), api.ContractAddRequest{
Contract: contract,
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,
Expand All @@ -23,19 +23,6 @@ func (c *Client) AddContract(ctx context.Context, contract rhpv2.ContractRevisio
return
}

// AddRenewedContract adds the provided contract to the metadata store.
func (c *Client) AddRenewedContract(ctx context.Context, contract rhpv2.ContractRevision, contractPrice, initialRenterFunds types.Currency, startHeight uint64, renewedFrom types.FileContractID, state string) (renewed api.ContractMetadata, err error) {
err = c.c.WithContext(ctx).POST(fmt.Sprintf("/contract/%s/renewed", contract.ID()), api.ContractRenewedRequest{
Contract: contract,
ContractPrice: contractPrice,
InitialRenterFunds: initialRenterFunds,
RenewedFrom: renewedFrom,
StartHeight: startHeight,
State: state,
}, &renewed)
return
}

// AncestorContracts returns any ancestors of a given contract.
func (c *Client) AncestorContracts(ctx context.Context, contractID types.FileContractID, minStartHeight uint64) (contracts []api.ContractMetadata, err error) {
values := url.Values{}
Expand Down Expand Up @@ -103,6 +90,9 @@ func (c *Client) Contracts(ctx context.Context, opts api.ContractsOpts) (contrac
if opts.ContractSet != "" {
values.Set("contractset", opts.ContractSet)
}
if opts.FilterMode != "" {
values.Set("filtermode", opts.FilterMode)
}
err = c.c.WithContext(ctx).GET("/contracts?"+values.Encode(), &contracts)
return
}
Expand Down
98 changes: 49 additions & 49 deletions bus/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -753,8 +753,23 @@ func (b *Bus) contractsHandlerGET(jc jape.Context) {
if jc.DecodeForm("contractset", &cs) != nil {
return
}
filterMode := api.ContractFilterModeActive
if jc.DecodeForm("filtermode", &filterMode) != nil {
return
}

switch filterMode {
case api.ContractFilterModeAll:
case api.ContractFilterModeActive:
case api.ContractFilterModeArchived:
default:
jc.Error(fmt.Errorf("invalid filter mode: %v", filterMode), http.StatusBadRequest)
return
}

contracts, err := b.ms.Contracts(jc.Request.Context(), api.ContractsOpts{
ContractSet: cs,
FilterMode: filterMode,
})
if jc.Check("couldn't load contracts", err) == nil {
jc.Encode(contracts)
Expand Down Expand Up @@ -1074,23 +1089,36 @@ func (b *Bus) contractIDHandlerGET(jc jape.Context) {
}

func (b *Bus) contractIDHandlerPOST(jc jape.Context) {
// decode parameters
var id types.FileContractID
var req api.ContractAddRequest
if jc.DecodeParam("id", &id) != nil || jc.Decode(&req) != nil {
if jc.DecodeParam("id", &id) != nil {
return
} else if req.Contract.ID() != id {
http.Error(jc.ResponseWriter, "contract ID mismatch", http.StatusBadRequest)
}
var req api.ContractAddRequest
if jc.Decode(&req) != nil {
return
} else if req.InitialRenterFunds.IsZero() {
}

// 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)
return
}

a, err := b.addContract(jc.Request.Context(), req.Contract, req.ContractPrice, req.InitialRenterFunds, req.StartHeight, req.State)
if jc.Check("couldn't store contract", err) != 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)
}
jc.Encode(a)
}

func (b *Bus) contractIDRenewHandlerPOST(jc jape.Context) {
Expand Down Expand Up @@ -1150,11 +1178,11 @@ func (b *Bus) contractIDRenewHandlerPOST(jc jape.Context) {

// send V2 transaction if we're passed the V2 hardfork allow height
var newRevision rhpv2.ContractRevision
var contractPrice, fundAmount types.Currency
var contractPrice, initialRenterFunds types.Currency
if b.isPassedV2AllowHeight() {
panic("not implemented")
} else {
newRevision, contractPrice, fundAmount, err = b.renewContract(ctx, cs, gp, c, h.Settings, rrr.RenterFunds, rrr.MinNewCollateral, rrr.MaxFundAmount, rrr.EndHeight, rrr.ExpectedNewStorage)
newRevision, contractPrice, initialRenterFunds, err = b.renewContract(ctx, cs, gp, c, h.Settings, rrr.RenterFunds, rrr.MinNewCollateral, rrr.MaxFundAmount, rrr.EndHeight, rrr.ExpectedNewStorage)
if errors.Is(err, api.ErrMaxFundAmountExceeded) {
jc.Error(err, http.StatusBadRequest)
return
Expand All @@ -1163,39 +1191,11 @@ func (b *Bus) contractIDRenewHandlerPOST(jc jape.Context) {
}
}

// add renewal contract to store
metadata, err := b.addRenewedContract(ctx, fcid, newRevision, contractPrice, fundAmount, cs.Index.Height, api.ContractStatePending)
if jc.Check("couldn't store contract", err) != nil {
return
}

// send the response
jc.Encode(metadata)
}

func (b *Bus) contractIDRenewedHandlerPOST(jc jape.Context) {
var id types.FileContractID
var req api.ContractRenewedRequest
if jc.DecodeParam("id", &id) != nil || jc.Decode(&req) != nil {
return
// add the renewal
metadata, err := b.addRenewal(ctx, fcid, newRevision, contractPrice, initialRenterFunds, cs.Index.Height, api.ContractStatePending)
if jc.Check("couldn't add renewal", err) == nil {
jc.Encode(metadata)
}
if req.Contract.ID() != id {
http.Error(jc.ResponseWriter, "contract ID mismatch", http.StatusBadRequest)
return
}
if req.InitialRenterFunds.IsZero() {
http.Error(jc.ResponseWriter, "InitialRenterFunds can not be zero", http.StatusBadRequest)
return
}
if req.State == "" {
req.State = api.ContractStatePending
}
r, err := b.addRenewedContract(jc.Request.Context(), req.RenewedFrom, req.Contract, req.ContractPrice, req.InitialRenterFunds, req.StartHeight, req.State)
if jc.Check("couldn't store contract", err) != nil {
return
}

jc.Encode(r)
}

func (b *Bus) contractIDRootsHandlerGET(jc jape.Context) {
Expand Down Expand Up @@ -2441,11 +2441,11 @@ func (b *Bus) contractsFormHandler(jc jape.Context) {
}

// send V2 transaction if we're passed the V2 hardfork allow height
var contract rhpv2.ContractRevision
var rev rhpv2.ContractRevision
if b.isPassedV2AllowHeight() {
panic("not implemented")
} else {
contract, err = b.formContract(
rev, err = b.formContract(
ctx,
settings,
rfr.RenterAddress,
Expand All @@ -2460,16 +2460,16 @@ func (b *Bus) contractsFormHandler(jc jape.Context) {
}
}

// store the contract
// add the contract
metadata, err := b.addContract(
ctx,
contract,
contract.Revision.MissedHostPayout().Sub(rfr.HostCollateral),
rev,
rev.Revision.MissedHostPayout().Sub(rfr.HostCollateral),
rfr.RenterFunds,
b.cm.Tip().Height,
api.ContractStatePending,
)
if jc.Check("couldn't store contract", err) != nil {
if jc.Check("couldn't add contract", err) != nil {
return
}

Expand Down
4 changes: 2 additions & 2 deletions internal/bus/chainsubscriber.go
Original file line number Diff line number Diff line change
Expand Up @@ -407,7 +407,7 @@ func (s *chainSubscriber) updateContract(tx sql.ChainUpdateTx, index types.Chain

// reverted renewal: 'complete' -> 'active'
if curr != nil {
if err := tx.UpdateContract(fcid, index.Height, prev.revisionNumber, prev.fileSize); err != nil {
if err := tx.UpdateContractRevision(fcid, index.Height, prev.revisionNumber, prev.fileSize); err != nil {
return fmt.Errorf("failed to revert contract: %w", err)
}
if state == api.ContractStateComplete {
Expand Down Expand Up @@ -440,7 +440,7 @@ func (s *chainSubscriber) updateContract(tx sql.ChainUpdateTx, index types.Chain
}

// handle apply
if err := tx.UpdateContract(fcid, index.Height, curr.revisionNumber, curr.fileSize); err != nil {
if err := tx.UpdateContractRevision(fcid, index.Height, curr.revisionNumber, curr.fileSize); err != nil {
return fmt.Errorf("failed to update contract %v: %w", fcid, err)
}

Expand Down
Loading

0 comments on commit 002a78e

Please sign in to comment.