diff --git a/cmd/pool/order.go b/cmd/pool/order.go index f86da57c2..f2f6b943d 100644 --- a/cmd/pool/order.go +++ b/cmd/pool/order.go @@ -105,31 +105,29 @@ func parseCommonParams(ctx *cli.Context, blockDuration uint32) (*poolrpc.Order, } // If the minimum channel amount flag wasn't provided, use a default of - // 10%, but make sure it doesn't dip below the minimum allowed. - minOrderChanAmt := btcutil.Amount(order.BaseSupplyUnit) + // 10% and round to the nearest unit. minChanAmt := btcutil.Amount(ctx.Uint64("min_chan_amt")) if minChanAmt == 0 { - minChanAmt = btcutil.Amount(params.Amt) / 10 - if minChanAmt < minOrderChanAmt { - minChanAmt = minOrderChanAmt - } + minChanAmt = order.RoundToNextSupplyUnit( + btcutil.Amount(params.Amt) / 10, + ).ToSatoshis() } // Verify the minimum channel amount flag has been properly set. switch { - case minChanAmt%minOrderChanAmt != 0: + case minChanAmt%order.BaseSupplyUnit != 0: return nil, fmt.Errorf("minimum channel amount %v must be "+ - "a multiple of %v", minChanAmt, minOrderChanAmt) + "a multiple of %v", minChanAmt, order.BaseSupplyUnit) - case minChanAmt < minOrderChanAmt: + case minChanAmt < order.BaseSupplyUnit: return nil, fmt.Errorf("minimum channel amount %v is below "+ - "required value of %v", minChanAmt, minOrderChanAmt) + "required value of %v", minChanAmt, order.BaseSupplyUnit) case minChanAmt > btcutil.Amount(params.Amt): return nil, fmt.Errorf("minimum channel amount %v is above "+ "order amount %v", minChanAmt, btcutil.Amount(params.Amt)) } - params.MinUnitsMatch = uint32(minChanAmt / minOrderChanAmt) + params.MinUnitsMatch = uint32(minChanAmt / order.BaseSupplyUnit) var err error params.TraderKey, err = parseAccountKey(ctx, args) diff --git a/order/supplyunit.go b/order/supplyunit.go index e5e414625..da1167809 100644 --- a/order/supplyunit.go +++ b/order/supplyunit.go @@ -10,7 +10,7 @@ type SupplyUnit uint64 const ( // BaseSupplyUnit is the smallest channel that can be bought or sold in // the system. These units are expressed in satoshis. - BaseSupplyUnit SupplyUnit = 100_000 + BaseSupplyUnit btcutil.Amount = 100_000 ) // NewSupplyFromSats calculates the number of supply units that can be bought or @@ -21,6 +21,12 @@ func NewSupplyFromSats(sats btcutil.Amount) SupplyUnit { return SupplyUnit(uint64(sats) / uint64(BaseSupplyUnit)) } +// RoundToNextSupplyUnit computes and rounds to the next whole number of supply +// units from the given amount in satoshis. +func RoundToNextSupplyUnit(sats btcutil.Amount) SupplyUnit { + return SupplyUnit((sats + BaseSupplyUnit - 1) / BaseSupplyUnit) +} + // ToSatoshis maps a set number of supply units to the corresponding number of // satoshis. func (s SupplyUnit) ToSatoshis() btcutil.Amount { diff --git a/order/supplyunit_test.go b/order/supplyunit_test.go new file mode 100644 index 000000000..acb1d5603 --- /dev/null +++ b/order/supplyunit_test.go @@ -0,0 +1,34 @@ +package order_test + +import ( + "math/rand" + "reflect" + "testing" + "testing/quick" + + "github.com/btcsuite/btcutil" + "github.com/lightninglabs/pool/order" + "github.com/stretchr/testify/require" +) + +// TestRoundToNextSupplyUnit runs a quick check scenario to ensure the +// correctness of RoundToNextSupplyUnit. +func TestRoundToNextSupplyUnit(t *testing.T) { + t.Parallel() + + f := func(sats btcutil.Amount) bool { + units := order.RoundToNextSupplyUnit(sats) + if sats%order.BaseSupplyUnit == 0 { + return units == order.NewSupplyFromSats(sats) + } + return units == order.NewSupplyFromSats(sats)+1 + } + + cfg := &quick.Config{ + Values: func(v []reflect.Value, r *rand.Rand) { + v[0] = reflect.ValueOf(btcutil.Amount(r.Int63())) + }, + } + + require.NoError(t, quick.Check(f, cfg)) +}