Skip to content

Commit

Permalink
client/asset/zec: separate Zcash wallet (#2553)
Browse files Browse the repository at this point in the history
* Separate the Zcash wallet

Separates the Zcash wallet. Reuse redemption finding and utxo management.
Enabled shielded-split funding of orders.
  • Loading branch information
buck54321 authored Nov 19, 2023
1 parent 84ae889 commit 281e6bc
Show file tree
Hide file tree
Showing 53 changed files with 7,210 additions and 2,455 deletions.
1,972 changes: 432 additions & 1,540 deletions client/asset/btc/btc.go

Large diffs are not rendered by default.

229 changes: 114 additions & 115 deletions client/asset/btc/btc_test.go

Large diffs are not rendered by default.

70 changes: 35 additions & 35 deletions client/asset/btc/coin_selection.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ import (
// enough func will return a non-zero excess value. Otherwise, the enough func
// will always return 0, leaving only unselected UTXOs to cover any required
// reserves.
func sendEnough(amt, feeRate uint64, subtract bool, baseTxSize uint64, segwit, reportChange bool) func(inputSize, sum uint64) (bool, uint64) {
return func(inputSize, sum uint64) (bool, uint64) {
func sendEnough(amt, feeRate uint64, subtract bool, baseTxSize uint64, segwit, reportChange bool) EnoughFunc {
return func(_, inputSize, sum uint64) (bool, uint64) {
txFee := (baseTxSize + inputSize) * feeRate
req := amt
if !subtract { // add the fee to required
Expand All @@ -45,8 +45,8 @@ func sendEnough(amt, feeRate uint64, subtract bool, baseTxSize uint64, segwit, r
// enough func will return a non-zero excess value reflecting this potential
// spit tx change. Otherwise, the enough func will always return 0, leaving
// only unselected UTXOs to cover any required reserves.
func orderEnough(val, lots, feeRate, initTxSizeBase, initTxSize uint64, segwit, reportChange bool) func(inputsSize, sum uint64) (bool, uint64) {
return func(inputsSize, sum uint64) (bool, uint64) {
func orderEnough(val, lots, feeRate, initTxSizeBase, initTxSize uint64, segwit, reportChange bool) EnoughFunc {
return func(_, inputsSize, sum uint64) (bool, uint64) {
reqFunds := calc.RequiredOrderFundsAlt(val, inputsSize, lots, initTxSizeBase, initTxSize, feeRate)
if sum >= reqFunds {
excess := sum - reqFunds
Expand All @@ -62,22 +62,22 @@ func orderEnough(val, lots, feeRate, initTxSizeBase, initTxSize uint64, segwit,
// reserveEnough generates a function that can be used as the enough argument
// to the fund method. The function returns true if sum is greater than equal
// to amt.
func reserveEnough(amt uint64) func(_, sum uint64) (bool, uint64) {
return func(_, sum uint64) (bool, uint64) {
func reserveEnough(amt uint64) EnoughFunc {
return func(_, _, sum uint64) (bool, uint64) {
return sum >= amt, 0
}
}

func sumUTXOSize(set []*compositeUTXO) (tot uint64) {
func sumUTXOSize(set []*CompositeUTXO) (tot uint64) {
for _, utxo := range set {
tot += uint64(utxo.input.VBytes())
tot += uint64(utxo.Input.VBytes())
}
return tot
}

func sumUTXOs(set []*compositeUTXO) (tot uint64) {
func SumUTXOs(set []*CompositeUTXO) (tot uint64) {
for _, utxo := range set {
tot += utxo.amount
tot += utxo.Amount
}
return tot
}
Expand All @@ -88,14 +88,14 @@ func sumUTXOs(set []*compositeUTXO) (tot uint64) {
// involves two passes over the UTXOs. The first pass randomly selects
// each UTXO with 50% probability. Then, the second pass selects any
// unused UTXOs until the total value is enough.
func subsetWithLeastOverFund(enough func(uint64, uint64) (bool, uint64), maxFund uint64, utxos []*compositeUTXO) []*compositeUTXO {
func subsetWithLeastOverFund(enough EnoughFunc, maxFund uint64, utxos []*CompositeUTXO) []*CompositeUTXO {
best := uint64(1 << 62)
var bestIncluded []bool
bestNumIncluded := 0

rnd := rand.New(rand.NewSource(time.Now().Unix()))

shuffledUTXOs := make([]*compositeUTXO, len(utxos))
shuffledUTXOs := make([]*CompositeUTXO, len(utxos))
copy(shuffledUTXOs, utxos)
rnd.Shuffle(len(shuffledUTXOs), func(i, j int) {
shuffledUTXOs[i], shuffledUTXOs[j] = shuffledUTXOs[j], shuffledUTXOs[i]
Expand All @@ -120,9 +120,9 @@ func subsetWithLeastOverFund(enough func(uint64, uint64) (bool, uint64), maxFund
if use {
included[i] = true
numIncluded++
nTotal += shuffledUTXOs[i].amount
totalSize += uint64(shuffledUTXOs[i].input.VBytes())
if e, _ := enough(totalSize, nTotal); e {
nTotal += shuffledUTXOs[i].Amount
totalSize += uint64(shuffledUTXOs[i].Input.VBytes())
if e, _ := enough(uint64(numIncluded), totalSize, nTotal); e {
if (nTotal < best || (nTotal == best && numIncluded < bestNumIncluded)) && nTotal <= maxFund {
best = nTotal
if bestIncluded == nil {
Expand All @@ -132,8 +132,8 @@ func subsetWithLeastOverFund(enough func(uint64, uint64) (bool, uint64), maxFund
bestNumIncluded = numIncluded
}
included[i] = false
nTotal -= shuffledUTXOs[i].amount
totalSize -= uint64(shuffledUTXOs[i].input.VBytes())
nTotal -= shuffledUTXOs[i].Amount
totalSize -= uint64(shuffledUTXOs[i].Input.VBytes())
numIncluded--
}
}
Expand All @@ -148,7 +148,7 @@ func subsetWithLeastOverFund(enough func(uint64, uint64) (bool, uint64), maxFund
return nil
}

set := make([]*compositeUTXO, 0, len(shuffledUTXOs))
set := make([]*CompositeUTXO, 0, len(shuffledUTXOs))
for i, inc := range bestIncluded {
if inc {
set = append(set, shuffledUTXOs[i])
Expand Down Expand Up @@ -176,19 +176,19 @@ func subsetWithLeastOverFund(enough func(uint64, uint64) (bool, uint64), maxFund
//
// If the provided UTXO set has less combined value than the requested amount a
// nil slice is returned.
func leastOverFund(enough func(inputsSize, sum uint64) (bool, uint64), utxos []*compositeUTXO) []*compositeUTXO {
func leastOverFund(enough EnoughFunc, utxos []*CompositeUTXO) []*CompositeUTXO {
return leastOverFundWithLimit(enough, math.MaxUint64, utxos)
}

// leastOverFundWithLimit is the same as leastOverFund, but with an additional
// maxFund parameter. The total value of the returned UTXOs will not exceed
// maxFund.
func leastOverFundWithLimit(enough func(inputsSize, sum uint64) (bool, uint64), maxFund uint64, utxos []*compositeUTXO) []*compositeUTXO {
func leastOverFundWithLimit(enough EnoughFunc, maxFund uint64, utxos []*CompositeUTXO) []*CompositeUTXO {
// Remove the UTXOs that are larger than maxFund
var smallEnoughUTXOs []*compositeUTXO
var smallEnoughUTXOs []*CompositeUTXO
idx := sort.Search(len(utxos), func(i int) bool {
utxo := utxos[i]
return utxo.amount > maxFund
return utxo.Amount > maxFund
})
if idx == len(utxos) {
smallEnoughUTXOs = utxos
Expand All @@ -200,24 +200,24 @@ func leastOverFundWithLimit(enough func(inputsSize, sum uint64) (bool, uint64),
// of smaller ones.
idx = sort.Search(len(smallEnoughUTXOs), func(i int) bool {
utxo := smallEnoughUTXOs[i]
e, _ := enough(uint64(utxo.input.VBytes()), utxo.amount)
e, _ := enough(1, uint64(utxo.Input.VBytes()), utxo.Amount)
return e
})
var small []*compositeUTXO
var single *compositeUTXO // only return this if smaller ones would use more
var small []*CompositeUTXO
var single *CompositeUTXO // only return this if smaller ones would use more
if idx == len(smallEnoughUTXOs) { // no one is enough
small = smallEnoughUTXOs
} else {
small = smallEnoughUTXOs[:idx]
single = smallEnoughUTXOs[idx]
}

var set []*compositeUTXO
smallSetTotalValue := sumUTXOs(small)
var set []*CompositeUTXO
smallSetTotalValue := SumUTXOs(small)
smallSetTotalSize := sumUTXOSize(small)
if e, _ := enough(smallSetTotalSize, smallSetTotalValue); !e {
if e, _ := enough(uint64(len(small)), smallSetTotalSize, smallSetTotalValue); !e {
if single != nil {
return []*compositeUTXO{single}
return []*CompositeUTXO{single}
} else {
return nil
}
Expand All @@ -226,18 +226,18 @@ func leastOverFundWithLimit(enough func(inputsSize, sum uint64) (bool, uint64),
}

// Return the small UTXO subset if it is less than the single big UTXO.
if single != nil && single.amount < sumUTXOs(set) {
return []*compositeUTXO{single}
if single != nil && single.Amount < SumUTXOs(set) {
return []*CompositeUTXO{single}
}

return set
}

// utxoSetDiff performs the setdiff(set,sub) of two UTXO sets. That is, any
// UTxOSetDiff performs the setdiff(set,sub) of two UTXO sets. That is, any
// UTXOs that are both sets are removed from the first. The comparison is done
// *by pointer*, with no regard to the values of the compositeUTXO elements.
func utxoSetDiff(set, sub []*compositeUTXO) []*compositeUTXO {
var availUTXOs []*compositeUTXO
// *by pointer*, with no regard to the values of the CompositeUTXO elements.
func UTxOSetDiff(set, sub []*CompositeUTXO) []*CompositeUTXO {
var availUTXOs []*CompositeUTXO
avail:
for _, utxo := range set {
for _, kept := range sub {
Expand Down
Loading

0 comments on commit 281e6bc

Please sign in to comment.