Skip to content

Commit

Permalink
Merge pull request #10 from mike76-dev/share
Browse files Browse the repository at this point in the history
Share
  • Loading branch information
mike76-dev authored Sep 10, 2023
2 parents e6c4842 + 42d0aca commit 076dea7
Show file tree
Hide file tree
Showing 8 changed files with 296 additions and 77 deletions.
4 changes: 4 additions & 0 deletions modules/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,10 @@ type HostDB interface {
type Manager interface {
Alerter

// AcceptContracts accepts a set of contracts from the renter
// and adds them to the contract set.
AcceptContracts(types.PublicKey, []ContractMetadata)

// ActiveHosts returns an array of HostDB's active hosts.
ActiveHosts() ([]HostDBEntry, error)

Expand Down
91 changes: 91 additions & 0 deletions modules/manager/contractor/contractor.go
Original file line number Diff line number Diff line change
Expand Up @@ -557,3 +557,94 @@ func (c *Contractor) UpdateSlab(slab modules.Slab) error {
}
return err
}

// AcceptContracts accepts a set of contracts from the renter
// and adds them to the contract set.
func (c *Contractor) AcceptContracts(rpk types.PublicKey, contracts []modules.ContractMetadata) {
// Create a map of existing contracts.
existingContracts := c.staticContracts.ByRenter(rpk)
existing := make(map[types.FileContractID]struct{})
for _, contract := range existingContracts {
existing[contract.ID] = struct{}{}
}

// Iterate through the set and add only missing contracts.
for _, contract := range contracts {
if _, exists := existing[contract.ID]; exists {
continue
}

// Find the contract txn.
block, ok := c.cs.BlockAtHeight(contract.StartHeight)
if !ok {
c.log.Println("ERROR: couldn't find block at height", contract.StartHeight)
continue
}

var transaction types.Transaction
var found bool
for _, txn := range block.Transactions {
if len(txn.FileContractRevisions) > 0 && txn.FileContractRevisions[0].ParentID == contract.ID {
transaction = modules.CopyTransaction(txn)
found = true
break
}
}
if !found {
c.log.Println("ERROR: couldn't find transaction", contract.ID)
continue
}

// We have no way to get some information from the data
// provided, so we have to speculate a bit.
txnFee := transaction.MinerFees[0]
rev := transaction.FileContractRevisions[0]
payout := rev.ValidRenterPayout().Add(rev.ValidHostPayout())
tax := modules.Tax(contract.StartHeight, payout)
host, ok, err := c.hdb.Host(contract.HostKey)
if err != nil || !ok {
c.log.Println("ERROR: couldn't find host for the contract", contract.ID)
continue
}
contractFee := host.Settings.ContractPrice
maxContractFee := contract.TotalCost.Sub(txnFee).Sub(tax).Sub(contract.UploadSpending).Sub(contract.DownloadSpending).Sub(contract.FundAccountSpending)
if contractFee.Cmp(maxContractFee) > 0 {
contractFee = maxContractFee
}

// Insert the contract.
rc, err := c.staticContracts.InsertContract(transaction, contract.StartHeight, contract.TotalCost, contractFee, txnFee, tax, rpk)
if err != nil {
c.log.Printf("ERROR: couldn't accept contract %s: %v\n", contract.ID, err)
continue
}

// Add a mapping from the contract's id to the public keys of the host
// and the renter.
c.mu.Lock()
c.pubKeysToContractID[rc.RenterPublicKey.String()+rc.HostPublicKey.String()] = contract.ID
c.mu.Unlock()

// Update the spendings.
// NOTE: `renterd` doesn't store contract signatures, so we have to
// pass a nil.
err = c.UpdateContract(rev, nil, contract.UploadSpending, contract.DownloadSpending, contract.FundAccountSpending)
if err != nil {
c.log.Println("ERROR: couldn't update contract spendings", err)
}

if contract.RenewedFrom != (types.FileContractID{}) {
c.renewedFrom[contract.ID] = contract.RenewedFrom
err = c.updateRenewedContract(contract.RenewedFrom, contract.ID)
if err != nil {
c.log.Println("ERROR: couldn't update renewal history:", err)
}
}
}

// Update the hostdb to include the new contracts.
err := c.hdb.UpdateContracts(c.staticContracts.ViewAll())
if err != nil {
c.log.Println("ERROR: unable to update hostdb contracts:", err)
}
}
10 changes: 10 additions & 0 deletions modules/manager/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ type hostContractor interface {
// Allowance returns the current allowance of the renter.
Allowance(types.PublicKey) modules.Allowance

// AcceptContracts accepts a set of contracts from the renter
// and adds them to the contract set.
AcceptContracts(types.PublicKey, []modules.ContractMetadata)

// Close closes the hostContractor.
Close() error

Expand Down Expand Up @@ -1079,3 +1083,9 @@ func (m *Manager) UpdateSlab(pk types.PublicKey, slab modules.Slab) error {

return m.UpdateSpendings(renter.Email, us)
}

// AcceptContracts accepts a set of contracts from the renter
// and adds them to the contract set.
func (m *Manager) AcceptContracts(rpk types.PublicKey, contracts []modules.ContractMetadata) {
m.hostContractor.AcceptContracts(rpk, contracts)
}
87 changes: 87 additions & 0 deletions modules/provider.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package modules

import (
rhpv2 "go.sia.tech/core/rhp/v2"
"go.sia.tech/core/types"
)

Expand All @@ -18,3 +19,89 @@ type Provider interface {
// SecretKey returns the provider's secret key.
SecretKey() types.PrivateKey
}

// ExtendedContract contains the contract and its metadata.
type ExtendedContract struct {
Contract rhpv2.ContractRevision
StartHeight uint64
TotalCost types.Currency
UploadSpending types.Currency
DownloadSpending types.Currency
FundAccountSpending types.Currency
RenewedFrom types.FileContractID
}

// EncodeTo implements requestBody.
func (ec ExtendedContract) EncodeTo(e *types.Encoder) {
ec.Contract.Revision.EncodeTo(e)
ec.Contract.Signatures[0].EncodeTo(e)
ec.Contract.Signatures[1].EncodeTo(e)
e.WriteUint64(ec.StartHeight)
ec.TotalCost.EncodeTo(e)
ec.UploadSpending.EncodeTo(e)
ec.DownloadSpending.EncodeTo(e)
ec.FundAccountSpending.EncodeTo(e)
ec.RenewedFrom.EncodeTo(e)
}

// DecodeFrom implements requestBody.
func (ec ExtendedContract) DecodeFrom(d *types.Decoder) {
// Nothing to do here.
}

// ExtendedContractSet is a collection of extendedContracts.
type ExtendedContractSet struct {
Contracts []ExtendedContract
}

// EncodeTo implements requestBody.
func (ecs ExtendedContractSet) EncodeTo(e *types.Encoder) {
e.WritePrefix(len(ecs.Contracts))
for _, ec := range ecs.Contracts {
ec.EncodeTo(e)
}
}

// DecodeFrom implements requestBody.
func (ecs ExtendedContractSet) DecodeFrom(d *types.Decoder) {
// Nothing to do here.
}

// ContractMetadata contains all metadata needed to re-create
// a contract.
type ContractMetadata struct {
ID types.FileContractID
HostKey types.PublicKey

StartHeight uint64
RenewedFrom types.FileContractID

UploadSpending types.Currency
DownloadSpending types.Currency
FundAccountSpending types.Currency
TotalCost types.Currency
}

// EncodeTo implements requestBody.
func (cm ContractMetadata) EncodeTo(e *types.Encoder) {
e.Write(cm.ID[:])
e.Write(cm.HostKey[:])
e.WriteUint64(cm.StartHeight)
e.Write(cm.RenewedFrom[:])
cm.UploadSpending.EncodeTo(e)
cm.DownloadSpending.EncodeTo(e)
cm.FundAccountSpending.EncodeTo(e)
cm.TotalCost.EncodeTo(e)
}

// DecodeFrom implements requestBody.
func (cm ContractMetadata) DecodeFrom(d *types.Decoder) {
d.Read(cm.ID[:])
d.Read(cm.HostKey[:])
cm.StartHeight = d.ReadUint64()
d.Read(cm.RenewedFrom[:])
cm.UploadSpending.DecodeFrom(d)
cm.DownloadSpending.DecodeFrom(d)
cm.FundAccountSpending.DecodeFrom(d)
cm.TotalCost.DecodeFrom(d)
}
7 changes: 7 additions & 0 deletions modules/provider/consts.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ const (
// update a single slab.
updateSlabTime = 15 * time.Second

// shareContractsTime defines the amount of time that the provider has to
// accept a set of contracts from the renter.
shareContractsTime = 1 * time.Minute

// defaultConnectionDeadline is the default read and write deadline which is set
// on a connection. This ensures it times out if I/O exceeds this deadline.
defaultConnectionDeadline = 5 * time.Minute
Expand Down Expand Up @@ -94,4 +98,7 @@ var (

// updateSlabSpecifier is used to update a single slab.
updateSlabSpecifier = types.NewSpecifier("UpdateSlab")

// shareContractsSpecifier is used when a renter shares their contract set.
shareContractsSpecifier = types.NewSpecifier("ShareContracts")
)
74 changes: 27 additions & 47 deletions modules/provider/encoding.go
Original file line number Diff line number Diff line change
Expand Up @@ -281,53 +281,6 @@ func (ur *updateRequest) EncodeTo(e *types.Encoder) {
ur.FundAccount.EncodeTo(e)
}

// extendedContract contains the contract and its metadata.
type extendedContract struct {
contract rhpv2.ContractRevision
startHeight uint64
totalCost types.Currency
uploadSpending types.Currency
downloadSpending types.Currency
fundAccountSpending types.Currency
renewedFrom types.FileContractID
}

// EncodeTo implements requestBody.
func (ec extendedContract) EncodeTo(e *types.Encoder) {
ec.contract.Revision.EncodeTo(e)
ec.contract.Signatures[0].EncodeTo(e)
ec.contract.Signatures[1].EncodeTo(e)
e.WriteUint64(ec.startHeight)
ec.totalCost.EncodeTo(e)
ec.uploadSpending.EncodeTo(e)
ec.downloadSpending.EncodeTo(e)
ec.fundAccountSpending.EncodeTo(e)
ec.renewedFrom.EncodeTo(e)
}

// DecodeFrom implements requestBody.
func (ec extendedContract) DecodeFrom(d *types.Decoder) {
// Nothing to do here.
}

// extendedContractSet is a collection of extendedContracts.
type extendedContractSet struct {
contracts []extendedContract
}

// EncodeTo implements requestBody.
func (ecs extendedContractSet) EncodeTo(e *types.Encoder) {
e.WritePrefix(len(ecs.contracts))
for _, ec := range ecs.contracts {
ec.EncodeTo(e)
}
}

// DecodeFrom implements requestBody.
func (ecs extendedContractSet) DecodeFrom(d *types.Decoder) {
// Nothing to do here.
}

// formContractRequest is used when forming a contract with a single
// host using the new Renter-Satellite protocol.
type formContractRequest struct {
Expand Down Expand Up @@ -635,3 +588,30 @@ func (usr *updateSlabRequest) EncodeTo(e *types.Encoder) {
e.Write(usr.PubKey[:])
usr.Slab.EncodeTo(e)
}

// shareRequest is used when the renter submits a set of contracts.
type shareRequest struct {
PubKey types.PublicKey
Contracts []modules.ContractMetadata

Signature types.Signature
}

// DecodeFrom implements requestBody.
func (sr *shareRequest) DecodeFrom(d *types.Decoder) {
d.Read(sr.PubKey[:])
sr.Contracts = make([]modules.ContractMetadata, d.ReadPrefix())
for i := 0; i < len(sr.Contracts); i++ {
sr.Contracts[i].DecodeFrom(d)
}
sr.Signature.DecodeFrom(d)
}

// EncodeTo implements requestBody.
func (sr *shareRequest) EncodeTo(e *types.Encoder) {
e.Write(sr.PubKey[:])
e.WritePrefix(len(sr.Contracts))
for _, contract := range sr.Contracts {
contract.EncodeTo(e)
}
}
5 changes: 5 additions & 0 deletions modules/provider/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,11 @@ func (p *Provider) threadedHandleConn(conn net.Conn) {
if err != nil {
err = modules.AddContext(err, "incoming RPCUpdateSlab failed")
}
case shareContractsSpecifier:
err = p.managedAcceptContracts(s)
if err != nil {
err = modules.AddContext(err, "incoming RPCShareContracts failed")
}
default:
p.log.Println("INFO: inbound connection from:", conn.RemoteAddr()) //TODO
}
Expand Down
Loading

0 comments on commit 076dea7

Please sign in to comment.