mirror of
https://github.com/lightninglabs/loop.git
synced 2026-08-13 12:33:03 +02:00
assets: reject malformed RFQ asset rates
Validate the rate pointer and decimal coefficient before converting asset units. Return errors for nil, malformed, non-positive, and oversized-scale rates instead of allowing nil dereferences or division-by-zero panics. Add regression tests for each case.
This commit is contained in:
parent
dc39d63d8d
commit
cfbade1239
2 changed files with 62 additions and 2 deletions
|
|
@ -5,6 +5,7 @@ import (
|
|||
"encoding/hex"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
|
|
@ -12,7 +13,6 @@ import (
|
|||
|
||||
"github.com/btcsuite/btcd/btcutil"
|
||||
"github.com/lightninglabs/taproot-assets/rfqmath"
|
||||
"github.com/lightninglabs/taproot-assets/rpcutils"
|
||||
"github.com/lightninglabs/taproot-assets/taprpc"
|
||||
"github.com/lightninglabs/taproot-assets/taprpc/priceoraclerpc"
|
||||
"github.com/lightninglabs/taproot-assets/taprpc/rfqrpc"
|
||||
|
|
@ -275,7 +275,7 @@ func (c *TapdClient) GetAssetPrice(ctx context.Context, assetID string,
|
|||
func getSatsFromAssetAmt(assetAmt uint64, assetRate *rfqrpc.FixedPoint) (
|
||||
btcutil.Amount, error) {
|
||||
|
||||
rateFP, err := rpcutils.UnmarshalRfqFixedPoint(assetRate)
|
||||
rateFP, err := unmarshalAssetRate(assetRate)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("cannot unmarshal asset rate: %w", err)
|
||||
}
|
||||
|
|
@ -287,6 +287,33 @@ func getSatsFromAssetAmt(assetAmt uint64, assetRate *rfqrpc.FixedPoint) (
|
|||
return msatAmt.ToSatoshis(), nil
|
||||
}
|
||||
|
||||
// unmarshalAssetRate validates and converts an RPC asset rate to the fixed
|
||||
// point representation used for RFQ arithmetic.
|
||||
func unmarshalAssetRate(assetRate *rfqrpc.FixedPoint) (
|
||||
*rfqmath.BigIntFixedPoint, error) {
|
||||
|
||||
if assetRate == nil {
|
||||
return nil, fmt.Errorf("asset rate cannot be nil")
|
||||
}
|
||||
if assetRate.Scale > math.MaxUint8 {
|
||||
return nil, fmt.Errorf("scale value overflow: %v", assetRate.Scale)
|
||||
}
|
||||
|
||||
coefficient, ok := new(big.Int).SetString(assetRate.Coefficient, 10)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid asset rate coefficient: %q",
|
||||
assetRate.Coefficient)
|
||||
}
|
||||
if coefficient.Sign() <= 0 {
|
||||
return nil, fmt.Errorf("asset rate coefficient must be positive")
|
||||
}
|
||||
|
||||
return &rfqmath.BigIntFixedPoint{
|
||||
Coefficient: rfqmath.NewBigInt(coefficient),
|
||||
Scale: uint8(assetRate.Scale),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// getPaymentMaxAmount returns the milisat amount we are willing to pay for the
|
||||
// payment.
|
||||
func getPaymentMaxAmount(satAmount btcutil.Amount, feeLimitMultiplier float64) (
|
||||
|
|
|
|||
|
|
@ -308,6 +308,39 @@ func TestGetSatsFromAssetAmt(t *testing.T) {
|
|||
expected: btcutil.Amount(0),
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
assetAmt: 1000,
|
||||
assetRate: nil,
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
assetAmt: 1000,
|
||||
assetRate: &rfqrpc.FixedPoint{
|
||||
Coefficient: "not-a-number", Scale: 0,
|
||||
},
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
assetAmt: 1000,
|
||||
assetRate: &rfqrpc.FixedPoint{
|
||||
Coefficient: "0", Scale: 0,
|
||||
},
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
assetAmt: 1000,
|
||||
assetRate: &rfqrpc.FixedPoint{
|
||||
Coefficient: "-1", Scale: 0,
|
||||
},
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
assetAmt: 1000,
|
||||
assetRate: &rfqrpc.FixedPoint{
|
||||
Coefficient: "1", Scale: 256,
|
||||
},
|
||||
expectError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue