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

Wallet implicit defrag #2692

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 6 additions & 0 deletions modules/wallet/consts.go
Expand Up @@ -15,6 +15,12 @@ const (
// defragThreshold is the number of outputs a wallet is allowed before it is
// defragmented.
defragThreshold = 50

// naturalDefragThreshold is the number of outputs a wallet is allowed
// before it starts defragging using its normal spending behavior. This
// threshold is a bit lower than the defragThreshold to avoid starting the
// thread.
naturalDefragThreshold = defragThreshold - 10
)

var (
Expand Down
50 changes: 50 additions & 0 deletions modules/wallet/defrag_test.go
Expand Up @@ -277,3 +277,53 @@ func TestDefragInterrupted(t *testing.T) {
}

}

// TestDefragWalletSendCoins mines many blocks and checks that the wallet's outputs are
// consolidated after spending some coins.
func TestDefragWalletSendCoins(t *testing.T) {
if testing.Short() {
t.SkipNow()
}
t.Parallel()
wt, err := createWalletTester(t.Name(), &ProductionDependencies{})
if err != nil {
t.Fatal(err)
}
defer wt.closeWt()

// mine defragThreshold blocks, resulting in defragThreshold outputs
for i := 0; i < naturalDefragThreshold; i++ {
_, err := wt.miner.AddBlock()
if err != nil {
t.Fatal(err)
}
}

// send coins to trigger a defrag
uc, err := wt.wallet.NextAddress()
if err != nil {
t.Fatal(err)
}
_, err = wt.wallet.SendSiacoins(types.SiacoinPrecision, uc.UnlockHash())
if err != nil {
t.Fatal(err)
}

// allow some time for the defrag transaction to occur, then mine another block
time.Sleep(time.Second * 5)

_, err = wt.miner.AddBlock()
if err != nil {
t.Fatal(err)
}

// defrag should keep the outputs below the threshold
wt.wallet.mu.Lock()
// force a sync because bucket stats may not be reliable until commit
wt.wallet.syncDB()
siacoinOutputs := wt.wallet.dbTx.Bucket(bucketSiacoinOutputs).Stats().KeyN
wt.wallet.mu.Unlock()
if siacoinOutputs > defragThreshold {
t.Fatalf("defrag should result in fewer than defragThreshold outputs, got %v wanted %v\n", siacoinOutputs, defragThreshold)
}
}
18 changes: 16 additions & 2 deletions modules/wallet/transactionbuilder.go
Expand Up @@ -149,7 +149,16 @@ func (tb *transactionBuilder) FundSiacoins(amount types.Currency) error {
so.outputs = append(so.outputs, sco)
}
}
sort.Sort(sort.Reverse(so))

// If we have too man unspent transactions we might as well do some
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Too man

// defragging since we create a setup transaction anyway
defrag := tb.wallet.defragDisabled && len(so.ids) >= naturalDefragThreshold
if defrag {
// If we defrag we start with the smallest output
sort.Sort(so)
} else {
sort.Sort(sort.Reverse(so))
}

// Create and fund a parent transaction that will add the correct amount of
// siacoins to the transaction.
Expand Down Expand Up @@ -183,7 +192,12 @@ func (tb *transactionBuilder) FundSiacoins(amount types.Currency) error {
// Add the output to the total fund
fund = fund.Add(sco.Value)
potentialFund = potentialFund.Add(sco.Value)
if fund.Cmp(amount) >= 0 {
if defrag && len(spentScoids) >= defragBatchSize {
// If we defrag we don't stop once we have gathered enough money
// but if we reach the batch size.
break
} else if !defrag && fund.Cmp(amount) >= 0 {
// If we don't defrag we stop once we have gathered enough money.
break
}
}
Expand Down