Skip to content
This repository has been archived by the owner on Nov 2, 2018. It is now read-only.

Commit

Permalink
Merge pull request #1464 from NebulousLabs/renew-ids
Browse files Browse the repository at this point in the history
Retain a mapping of old IDs -> renewed IDs
  • Loading branch information
David Vorick committed Oct 25, 2016
2 parents 5ef90ee + 3fc2c9f commit 970176c
Show file tree
Hide file tree
Showing 8 changed files with 80 additions and 15 deletions.
11 changes: 10 additions & 1 deletion modules/renter/contractor/contractor.go
Expand Up @@ -46,7 +46,7 @@ type Contractor struct {
downloaders map[types.FileContractID]*hostDownloader
editors map[types.FileContractID]*hostEditor
lastChange modules.ConsensusChangeID
renewHeight types.BlockHeight // height at which to renew contracts
renewedIDs map[types.FileContractID]types.FileContractID
renewing map[types.FileContractID]bool // prevent revising during renewal
revising map[types.FileContractID]bool // prevent overlapping revisions

Expand Down Expand Up @@ -95,6 +95,14 @@ func (c *Contractor) Contracts() (cs []modules.RenterContract) {
return
}

// resolveID returns the ID of the most recent renewal of id.
func (c *Contractor) resolveID(id types.FileContractID) types.FileContractID {
if newID, ok := c.renewedIDs[id]; ok && newID != id {
return c.resolveID(newID)
}
return id
}

// New returns a new Contractor.
func New(cs consensusSet, wallet walletShim, tpool transactionPool, hdb hostDB, persistDir string) (*Contractor, error) {
// Check for nil inputs.
Expand Down Expand Up @@ -138,6 +146,7 @@ func newContractor(cs consensusSet, w wallet, tp transactionPool, hdb hostDB, p
contracts: make(map[types.FileContractID]modules.RenterContract),
downloaders: make(map[types.FileContractID]*hostDownloader),
editors: make(map[types.FileContractID]*hostEditor),
renewedIDs: make(map[types.FileContractID]types.FileContractID),
renewing: make(map[types.FileContractID]bool),
revising: make(map[types.FileContractID]bool),
}
Expand Down
28 changes: 28 additions & 0 deletions modules/renter/contractor/contractor_test.go
Expand Up @@ -135,6 +135,34 @@ func TestContracts(t *testing.T) {
}
}

// TestResolveID tests the resolveID method.
func TestResolveID(t *testing.T) {
c := &Contractor{
renewedIDs: map[types.FileContractID]types.FileContractID{
{1}: {2},
{2}: {3},
{3}: {4},
{5}: {6},
},
}
tests := []struct {
id types.FileContractID
resolved types.FileContractID
}{
{types.FileContractID{0}, types.FileContractID{0}},
{types.FileContractID{1}, types.FileContractID{4}},
{types.FileContractID{2}, types.FileContractID{4}},
{types.FileContractID{3}, types.FileContractID{4}},
{types.FileContractID{4}, types.FileContractID{4}},
{types.FileContractID{5}, types.FileContractID{6}},
}
for _, test := range tests {
if r := c.resolveID(test.id); r != test.resolved {
t.Errorf("expected %v -> %v, got %v", test.id, test.resolved, r)
}
}
}

// TestAllowance tests the Allowance method.
func TestAllowance(t *testing.T) {
c := &Contractor{
Expand Down
12 changes: 5 additions & 7 deletions modules/renter/contractor/doc.go
Expand Up @@ -22,13 +22,11 @@ same storage capacity, and they should all end at the same height. Hosts are
selected from the HostDB; there is no support for manually specifying hosts.
Contracts are automatically renewed by the contractor at a safe threshold
before they are set to expire. The contractor maintains a renewHeight variable
that indicates when its current set of contracts will expire. When contracts
are renewed, they are renewed with the current allowance, which may differ
from the allowance that was used to form the initial contracts. In general,
this means that allowance modifications only take effect upon the next
"contract cycle" (the exception being "sufficiently greater" modifications, as
defined above).
before they are set to expire. When contracts are renewed, they are renewed
with the current allowance, which may differ from the allowance that was used
to form the initial contracts. In general, this means that allowance
modifications only take effect upon the next "contract cycle" (the exception
being "sufficiently greater" modifications, as defined above).
As an example, imagine that the user first sets an allowance that will cover
10 contracts of 10 sectors each for 100 blocks. The contractor will
Expand Down
1 change: 1 addition & 0 deletions modules/renter/contractor/downloader.go
Expand Up @@ -122,6 +122,7 @@ func (hd *hostDownloader) Close() error {
// from a host.
func (c *Contractor) Downloader(id types.FileContractID) (_ Downloader, err error) {
c.mu.RLock()
id = c.resolveID(id)
cachedDownloader, haveDownloader := c.downloaders[id]
height := c.blockHeight
contract, haveContract := c.contracts[id]
Expand Down
1 change: 1 addition & 0 deletions modules/renter/contractor/editor.go
Expand Up @@ -179,6 +179,7 @@ func (he *hostEditor) Modify(oldRoot, newRoot crypto.Hash, offset uint64, newDat
// delete sectors on a host.
func (c *Contractor) Editor(id types.FileContractID) (_ Editor, err error) {
c.mu.RLock()
id = c.resolveID(id)
cachedEditor, haveEditor := c.editors[id]
height := c.blockHeight
contract, haveContract := c.contracts[id]
Expand Down
20 changes: 14 additions & 6 deletions modules/renter/contractor/persist.go
Expand Up @@ -12,26 +12,29 @@ type contractorPersist struct {
BlockHeight types.BlockHeight
CachedRevisions []cachedRevision
Contracts []modules.RenterContract
LastChange modules.ConsensusChangeID
RenewHeight types.BlockHeight
FinancialMetrics modules.RenterFinancialMetrics
LastChange modules.ConsensusChangeID
RenewedIDs map[string]string
}

// persistData returns the data in the Contractor that will be saved to disk.
func (c *Contractor) persistData() contractorPersist {
data := contractorPersist{
Allowance: c.allowance,
BlockHeight: c.blockHeight,
LastChange: c.lastChange,
RenewHeight: c.renewHeight,
FinancialMetrics: c.financialMetrics,
LastChange: c.lastChange,
RenewedIDs: make(map[string]string),
}
for _, rev := range c.cachedRevisions {
data.CachedRevisions = append(data.CachedRevisions, rev)
}
for _, contract := range c.contracts {
data.Contracts = append(data.Contracts, contract)
}
for oldID, newID := range c.renewedIDs {
data.RenewedIDs[oldID.String()] = newID.String()
}
return data
}

Expand All @@ -50,9 +53,14 @@ func (c *Contractor) load() error {
for _, contract := range data.Contracts {
c.contracts[contract.ID] = contract
}
c.lastChange = data.LastChange
c.renewHeight = data.RenewHeight
c.financialMetrics = data.FinancialMetrics
c.lastChange = data.LastChange
for oldString, newString := range data.RenewedIDs {
var oldHash, newHash crypto.Hash
oldHash.LoadString(oldString)
newHash.LoadString(newString)
c.renewedIDs[types.FileContractID(oldHash)] = types.FileContractID(newHash)
}
return nil
}

Expand Down
21 changes: 20 additions & 1 deletion modules/renter/contractor/persist_test.go
Expand Up @@ -20,7 +20,8 @@ func (m memPersist) load(data *contractorPersist) error { *data = contractor
func TestSaveLoad(t *testing.T) {
// create contractor with mocked persist dependency
c := &Contractor{
contracts: make(map[types.FileContractID]modules.RenterContract),
contracts: make(map[types.FileContractID]modules.RenterContract),
renewedIDs: make(map[types.FileContractID]types.FileContractID),
}
c.persist = new(memPersist)

Expand All @@ -30,6 +31,12 @@ func TestSaveLoad(t *testing.T) {
{1}: {NetAddress: "bar"},
{2}: {NetAddress: "baz"},
}
c.renewedIDs = map[types.FileContractID]types.FileContractID{
{0}: {1},
{1}: {2},
{2}: {3},
}

// save and reload
err := c.save()
if err != nil {
Expand All @@ -46,6 +53,12 @@ func TestSaveLoad(t *testing.T) {
if !ok0 || !ok1 || !ok2 {
t.Fatal("contracts were not restored properly:", c.contracts)
}
_, ok0 = c.renewedIDs[types.FileContractID{0}]
_, ok1 = c.renewedIDs[types.FileContractID{1}]
_, ok2 = c.renewedIDs[types.FileContractID{2}]
if !ok0 || !ok1 || !ok2 {
t.Fatal("renewed IDs were not restored properly:", c.renewedIDs)
}

// use stdPersist instead of mock
c.persist = newPersist(build.TempDir("contractor", "TestSaveLoad"))
Expand All @@ -67,4 +80,10 @@ func TestSaveLoad(t *testing.T) {
if !ok0 || !ok1 || !ok2 {
t.Fatal("contracts were not restored properly:", c.contracts)
}
_, ok0 = c.renewedIDs[types.FileContractID{0}]
_, ok1 = c.renewedIDs[types.FileContractID{1}]
_, ok2 = c.renewedIDs[types.FileContractID{2}]
if !ok0 || !ok1 || !ok2 {
t.Fatal("renewed IDs were not restored properly:", c.renewedIDs)
}
}
1 change: 1 addition & 0 deletions modules/renter/contractor/renew.go
Expand Up @@ -145,6 +145,7 @@ func (c *Contractor) managedRenewContracts() error {
for id, contract := range newContracts {
delete(c.contracts, id)
c.contracts[contract.ID] = contract
c.renewedIDs[id] = contract.ID
}
err = c.saveSync()
c.mu.Unlock()
Expand Down

0 comments on commit 970176c

Please sign in to comment.