From 05f7f20da63972ce578e1a2f1279cce068e4fb7e Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Wed, 4 Sep 2024 13:14:52 +0200 Subject: [PATCH 1/3] docs+rfq: add README files and conceptual documentation for RFQ --- README.md | 6 + docs/rfq-and-decimal-display.md | 404 ++++++++++++++++++++++++++++++++ rfq/README.md | 20 ++ 3 files changed, 430 insertions(+) create mode 100644 docs/rfq-and-decimal-display.md create mode 100644 rfq/README.md diff --git a/README.md b/README.md index 2ec1a67cc..ef7364357 100644 --- a/README.md +++ b/README.md @@ -139,6 +139,12 @@ updates the daemon's funds-custody material. Merely having the `lnd` seed phrase is **NOT** enough to restore assets minted or received. **WITHOUT BACKUP BEFORE DELETION, FUNDS ARE DESTROYED**. +## RFQ, asset decimal display, unit precision and price oracle + +[Everything related to the RFQ (Request For Quote) system, the asset's currency +precision (decimal display) and the RFQ price oracle can be found in this +document](./docs/rfq-and-decimal-display.md). + ## Submit feature requests The [GitHub issue tracker](https://github.com/lightninglabs/taproot-assets/issues) can be used to request specific improvements or report bugs. diff --git a/docs/rfq-and-decimal-display.md b/docs/rfq-and-decimal-display.md new file mode 100644 index 000000000..410df8d2d --- /dev/null +++ b/docs/rfq-and-decimal-display.md @@ -0,0 +1,404 @@ +# Asset Decimal Display + +Within the Taproot Assets Protocol, an asset's unit is an integer (`uint64`). +That means, the protocol cannot represent fractions of an asset. +Therefore, any asset that represents a fiat currency would need to issue assets +equivalent to at least the smallest unit in use. +For example, an asset that represents the US-Dollar would need to be minted in a +way that one asset unit would represent one USD **cent**. +Or in other words, 100 units of such an asset would represent 1 US-Dollar. +Beyond the smallest unit, additional breathing room should be added to ensure +minimal precision loss during conversion arithmetic (see next chapter). + +Because wallet software user interfaces aren't expected to know what +"resolution" or precision any asset in the wild represents, a new field called +`decimal_display` was added to the JSON metadata of new assets minted with +`tapd v0.4.0-alpha` and later (this field is encoded in the metadata field as a +JSON field, therefore it is only compatible with assets that have a JSON +metadata field). + +An issuer can specify `tapcli assets mint --decimal_display X` to specify the +number of decimal places the comma should be shifted to the left when displaying +a sum of asset units. + +For the example above, a USD asset would choose `--decimal_display 2` to +indicate that 100 units ($10^2$) should be displayed as `$1.00` in the wallet +UI. Or another example: 1234 USD cent asset units would be displayed as `$12.34` +in the UI. + +An asset's decimal display can be viewed with the `tapcli assets list` command +(for an asset that's owned): +```shell +$ tapcli assets list + +{ + "assets": [ + { + "version": "ASSET_VERSION_V0", + "asset_genesis": { + ... + }, + "amount": "10000000", + ... + "decimal_display": { + "decimal_display": 3 + } + } + ] +} +``` + +If the `decimal_display` field is missing or showing as +`"decimal_display": null`, it means the asset is using a value of 0 (which means +no shift in decimal places). + +For an asset that isn't owned by the local node (but its issuance information +was synced from a universe server), the decimal display value can be retrieved +from the asset's metadata: + +```shell +$ tapcli assets meta --asset_id xyz + +{ + "data": "7b22646563696d616c5f646973706c6179223a357d", + "type": "META_TYPE_JSON", + "meta_hash": "2ae3dc4e0430e7e19134adb516d9a59237efa0c580479c5e983ca0c1b6777c65" +} + +$ tapcli assets meta --asset_id xyz | jq -r '.data' | xxd -p -r + +{"decimal_display":5} +``` + +## Precision requirement for assets in the Lightning Network + +Due to the non-divisible (integer) nature of Taproot Asset units, the smallest +asset amount that can be transferred with a Lightning Network HTLC is one asset +unit (partial units or zero units aren't possible). + +If one such asset unit represents significantly more than a couple of +milli-satoshi or even full satoshi, then in some cases, due to integer division +and rounding up, the user might end up spending noticeably more assets than +necessary when paying an invoice. + +**Example 1: Paying a 1 satoshi invoice**: + +While writing this article, one USD cent is roughly equivalent to 19 satoshi. + +So if a user with USD cent assets in their wallet attempted to pay an invoice +denominated over 1 satoshi, they would need to send a full USD cent to satisfy +the invoice (again, only full asset units can be transported over an HTLC). + +Even if one cent isn't much, the overpayment would still be roughly `19x`. + +**Example 2: Paying an invoice with MPP**: + +Multi-Part Payments (MPP) allow a single payment to be split up into multiple +parts/paths, resulting in multiple HTLCs per payment. + +Assuming a `decimal_display` value of `2` (1 unit represents 1 USD cent), if a +user wants to pay an invoice over `1,000,000` satoshi, that would be +equivalent to `$526.315789 USD` or `52,631.5789` cents +(`1 million satoshi / 19 satoshi`, showing extra decimal places to demonstrate +loss of precision). +If the user were to pay this amount in a single HTLC, they would send `52,632` +asset units (need to round up to satisfy integer asset amount and invoice +minimum amount requirements), overpaying by `0.4211` cents. + +If the user's wallet decided to split up the payment into 16 parts for example, +then each part would correspond to `3,289.4737` cents. To satisfy the integer +asset amount and invoice minimum amount requirement, each of the 16 HTLCs would +send out `3290` cents. That's a full `8.4211` cents of overpayment. + +## What precision should I choose when minting an asset for the Lightning Network? + +To address the issue of rounding up when splitting payments or representing +small satoshi amounts as asset units, an issuer of assets should use a high +enough value for `decimal_display` when minting. + +**But what is a good value for `decimal_display`?** + +We recommend to use a `decimal_display` value of `6` for currencies which +use a smaller subunit with two decimal places (such as cents for USD or EUR, +penny for GBP and so on). + +For currencies without smaller units (for example JPY or VND), a +`decimal_display` value of `4` is recommended. + +## What if I made an asset with the wrong amount of precision? + +The `decimal_display` value is stored in the `asset_meta` field of the +`genesis_asset` that creates a particular `group_key` (or `asset_id`). As a +result, the value of the `asset_meta` actually determined the original +`asset_id` and `group_key` used, therefore these values are strongly bound. + +The only way to "fix" the `decimal_display` value is to burn all the existing +assets, creating new assets with the proper decimal display value. It's possible +to do this in a _single atomic transaction_ using the gRPC/REST interface. + +Such a transaction would: +1. Burn referenced asset inputs +2. Create new asset units under a new group key + +# RFQ + +The RFQ system is responsible for acquiring real-time price quotes for +converting between asset units and satoshi (both directions) or between +different asset types. + +It's important to note that the direction of "inbound/in/buy" and +"outbound/out/sell" is always seen from the point of view of the **wallet end +user**. So what is outbound for the end user would be inbound for the RFQ peer +(edge node) and vice versa. + +There are two main user stories, as seen from the point of view of the wallet +end user: + +1. Sending out assets: The user wants to pay a Lightning Network invoice that + is denominated in satoshi. The user only has assets in their wallet, they + (or their wallet software) want to find out how many asset units they need to + send in order to satisfy the invoice amount in satoshi. +2. Receiving assets: The user wants to get paid in a specific asset. The user + only knows about the asset, so they (or their wallet software) want to find + out what the asset amount corresponds to in satoshi. + +**NOTE**: All arithmetic conversions in the section below always use +_fixed point_ arithmetic. A `scale` (equivalent to a decimal display, but just +for computations) of either `11`, or the `decimal_display` value (which ever is +greater is used). + +## Sell Order (Paying Invoices) + +The sell order covers the first user story: The user wants to pay a +satoshi-denominated invoice with assets. + +The end result is that the user uses the pre-image referenced by the payment +hash in the invoice to atomically sell some of their assets units in their +channel to the RFQ per (edge node), ensuring the payment is only complete is the +receiver receives their funds. +Note that from the PoV of the edge node, they're effectively paid a routing fee +to buy asset units (more asset unit inbound) by also sending out BTC outbound +(less BTC outbound). + +Formal definition: +- Use case: sending assets as a payment, selling `buxx` for `msat` +- User query: `Q = how many out_asset units for in_asset amount?` (how many + `buxx` do I need to sell/send to pay this payment denominated in `msat`?) +- `out_asset`: `buxx` (user sells `buxx` asset to RFQ peer, sending that value + to the edge node) +- `in_asset`: `msat` (user "receives" `msat` from RFQ peer, which are then + routed to the network) +- `max_amount`: `in_asset` (what is the maximum amount of `msat` the RFQ peer + has to forward to the network? Equal to invoice amount plus user-defined max + routing fee limit) +- `price_out_asset`: `out_asset_units_per_btc` (`buxx per BTC`) +- `price_in_asset`: `in_asset_units_per_btc` (`msat per BTC`) + +### Calculating asset units to send + +In this case, we have an invoice denominated in mSAT, and want to convert to +asset units `U`. Given the total amount of mSAT to send (`X`), the number of +assets units per BTC (`Y`), and the total amount of mSAT in 1 BTC (`M`), we can +convert from mSAT to asset units as follows: +* U = (X / M) * Y +* where + * `U` is the result, the number of asset units to send + * `X` is the invoice amount in mSAT + * `M` is the number of mSAT in a BTC (100,000,000,000), specified by + `price_in_asset` + * `Y` is the number of asset units per BTC, specified by `price_out_asset` + +## Buy Order (Receiving via an Invoice) + +The buy order covers the second user story: The user wants to get paid, they +create an invoice specifying the number of asset units they want to receive, +which is then mapped to a normal, satoshi-denominated invoice. +The end result is that the user uses the satoshis sent by the sender through the +normal LN network to _buy_ enough asset units to satisfy their invoice, using +the edge node and the atomic exchange of the pre-image. + +Formal definition: +- Use case: receiving assets through an invoice, selling `msat` for `buxx` +- User query: `Q = how many out_asset units for in_asset amount?` (how many + `msat` should I denominate my invoice with to receive a given amount of + `buxx`?) +- `out_asset`: `msat` (user sells sats to RFQ peer, which are routed to them by + the network) +- `in_asset`: `buxx` (user buys `buxx` from RFQ peer) +- `max_amount`: `in_asset` (what is the maximum number of `buxx` the RFQ peer + has to sell? Equal to the amount in the user query) +- `price_out_asset`: `out_asset_units_per_btc` (`msat per BTC`) +- `price_in_asset`: `in_asset_units_per_btc` (`buxx per BTC`) + +### Calculating satoshi to receive + +For the receiving case, we perform the opposite computation that we did for +sending: we want to receive `U` asset units, given a rate of (`Y`) units per +BTC, we can compute the amount of satoshis that must be paid (`X`) into the edge +node as: + +* `X = (U / Y) * M` +* where + * `X` is the result, the number of mSAT to receive + * `U` is the desired number of asset units to receive + * `Y` is the number of asset units per BTC, specified by `price_out_asset` + * `M` is the number of mSAT in a BTC (100,000,000,000), specified by + `price_in_asset` + +## Examples + +See `TestFindDecimalDisplayBoundaries` and `TestUsdToJpy` in +`rfq/convert_test.go` for how these examples are constructed. + +**Case 1**: Buying/selling USD against BTC. + +```text +In Asset: USD with decimal display = 6 (1_000_000 asset units = 1 USD) +Out Asset: satoshi / milli-satoshi + +Example 1: +---------- + +What is price rate when 1 BTC = 20,000.00 USD? + +decimalDisplay: 6 1000000 units = 1 USD, 1 BTC = 20000000000 units +Max issuable units: can represent 922337203 BTC +Min payable invoice amount: 5 mSAT +Max MPP rounding error: 80 mSAT (@16 shards) +Satoshi per USD: 5000 +Satoshi per Asset Unit: 0.00500 +Asset Units per Satoshi: 200 +Price In Asset: 20000000000 +Price Out Asset: 100000000000 + + +Example 2: +---------- + +What is price rate when 1 BTC = 1,000,000.00 USD? + +decimalDisplay: 6 1000000 units = 1 USD, 1 BTC = 1000000000000 units +Max issuable units: can represent 18446744 BTC +Min payable invoice amount: 1 mSAT +Max MPP rounding error: 1 mSAT (@16 shards) +Satoshi per USD: 100 +Satoshi per Asset Unit: 0.00010 +Asset Units per Satoshi: 10000 +Price In Asset: 1000000000000 +Price Out Asset: 100000000000 + + +Example 3: +---------- + +What is price rate when 1 BTC = 10,000,000.00 USD? + +decimalDisplay: 6 1000000 units = 1 USD, 1 BTC = 10000000000000 units +Max issuable units: can represent 1844674 BTC +Min payable invoice amount: 1 mSAT +Max MPP rounding error: 0 mSAT (@16 shards) +Satoshi per USD: 10 +Satoshi per Asset Unit: 0.00001 +Asset Units per Satoshi: 100000 +Price In Asset: 10000000000000 +Price Out Asset: 100000000000 +``` + +**Case 2**: Buying/selling USD against JPY. + +```text +In Asset: USD with decimal display = 6 (1_000_000 asset units = 1 USD) +Out Asset: JPY with decimal display = 4 (10_000 asset units = 1 JPY) + +Assumption: 1 USD = 142 JPY + +Example 1: +---------- + +What is price rate when 1 BTC = 20,000.00 USD (1 BTC = 2,840,000 JPY)? + +Satoshi per USD: 5000 +Satoshi per USD Asset Unit: 0.00500 +USD Asset Units per Satoshi: 200 +Satoshi per JPY: 35 +Satoshi per JPY Asset Unit: 0.35211 +JPY Asset Units per Satoshi: 284 +Price In Asset: 20000000000 +Price Out Asset: 28400000000 + 1 USD in JPY: 142 + + +Example 2: +---------- + +What is price rate when 1 BTC = 1,000,000.00 USD (1 BTC = 142,000,000 JPY)? + +Satoshi per USD: 100 +Satoshi per USD Asset Unit: 0.00010 +USD Asset Units per Satoshi: 10000 +Satoshi per JPY: 0 +Satoshi per JPY Asset Unit: 0.00704 +JPY Asset Units per Satoshi: 14199 +Price In Asset: 1000000000000 +Price Out Asset: 1420000000000 +500 USD in JPY: 71000 +``` + +# Price Oracle + +The price oracle is an important component in the RFQ system, as it provides the +values for the exchange rates mentioned above. + +Both parties of an asset channel (the wallet end user and the edge node) might +use a price oracle, but its role is different for those parties: +* The **Price Oracle for the edge node** is responsible for putting a price tag + on the service that is offered by the edge node, which is an atomic swap + between two types of assets (often one of them being BTC). In other words, + the oracle is responsible for calculating a concrete exchange rate for a + specific atomic swap. The inputs to that calculation are variables such as + the official exchange rate between the asset and BTC ("official" market rate, + potentially obtained from a third party exchange API), the size/volume of the + swap and the requested validity duration (expiry). The output of the + calculation is again an exchange rate, but one that is adjusted to include a + spread vs. the input exchange rate. The spread is what allows the edge node + to be compensated for offering the swap service, including potential exchange + rate fluctuation risks. It is expected that the spread is adjusted by the + price oracle implementation based on the size and validity duration of the + swap, because those values directly correlate with the exchange rate risk. +* The **Price Oracle for the wallet end user** on the other hand is simply + tasked with validating exchange rates offered to them by the edge node, to + make sure they aren't proposing absurd rates (by accident or on purpose). + +Due to the fundamentally different roles of the price oracle for both parties, +it is expected that the actual implementation for the price oracle is also +different among the parties. + +## Edge node + +Given that an edge node might want to implement their specific business logic +and rules, no default implementation for a price oracle for edge nodes is +provided. Edge node operators need to implement the RPC interface defined in +`taprpc/priceoraclerpc` and point their `tapd` to use their custom +implementation with the +`experimental.rfq.priceoracleaddress=rfqrpc://:` configuration +value. +An example implementation of a price oracle server implementing that RPC +interface with Golang can be found in +[`docs/examples/basic-price-oracle`](examples/basic-price-oracle). + +## Wallet end user + +The wallet end user's price oracle implementation can be quite simple. All it +needs to do is to query an exchange provider's API for the current exchange +rate of an asset. Then the maximum deviation from that "official" market rate +that is accepted from edge nodes can be configured using the +`experimental.rfq.acceptpricedeviationppm=` configuration value (which is in +parts per million and the default value is `50000` which is equal to `5%`). + +Because the API endpoints of public exchange platforms aren't standardized, +there also isn't a default implementation of an end user price oracle available. +It is expected that third party developers (or at some point even the exchange +platforms themselves) will offer a gRPC (`rfqrpc`) compatible price oracle +endpoint that can directly be plugged into the +`experimental.rfq.priceoracleaddress=rfqrpc://:` configuration +value on the wallet end user side. diff --git a/rfq/README.md b/rfq/README.md new file mode 100644 index 000000000..395b35a2c --- /dev/null +++ b/rfq/README.md @@ -0,0 +1,20 @@ +# RFQ + +This package contains most code related to the business logic of the Request +For Quotes subsystem. + +The high-level/conceptual explanation of [how RFQ (and related concepts such +as the decimal display value and price oracles) works, can be found +in this separate document](../docs/rfq-and-decimal-display.md). + +The actual [wire messages are located in the `rfqmsg`](../rfqmsg) package. + +The [gRPC definitions of the RFQ methods can be found in the `taprpc/rfqrpc` +package](../taprpc/rfqrpc). + +The [gRPC definitions of the price oracle methods can be found in the +`taprpc/priceoraclerpc` package](../taprpc/priceoraclerpc). + +[An example implementation of a price oracle server implementing that RPC +interface with Golang can be found in +`docs/examples/basic-price-oracle`](../docs/examples/basic-price-oracle). From ef39272d69597015d9d66ace99782fda984d8a8f Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 20 Aug 2024 20:17:02 -0700 Subject: [PATCH 2/3] build: add rapid as a dep --- go.mod | 1 + go.sum | 2 ++ 2 files changed, 3 insertions(+) diff --git a/go.mod b/go.mod index e9ed5aa33..7d3185edd 100644 --- a/go.mod +++ b/go.mod @@ -201,6 +201,7 @@ require ( modernc.org/strutil v1.2.0 // indirect modernc.org/token v1.1.0 // indirect nhooyr.io/websocket v1.8.7 // indirect + pgregory.net/rapid v1.1.0 // indirect sigs.k8s.io/yaml v1.2.0 // indirect ) diff --git a/go.sum b/go.sum index d9159df35..7fae92d46 100644 --- a/go.sum +++ b/go.sum @@ -1169,6 +1169,8 @@ modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g= nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= +pgregory.net/rapid v1.1.0 h1:CMa0sjHSru3puNx+J0MIAuiiEV4N0qj8/cMWGBBCsjw= +pgregory.net/rapid v1.1.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= From b4208ac0584fd4b92bd201e40f20b91875cc6158 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Wed, 4 Sep 2024 13:38:01 +0200 Subject: [PATCH 3/3] rfqmath: implement fixed point based arithmetic for RFQ --- docs/rfq-and-decimal-display.md | 2 +- go.mod | 2 +- rfq/README.md | 3 + rfqmath/README.md | 8 + rfqmath/arithmetic.go | 213 ++++++ rfqmath/arithmetic_test.go | 291 ++++++++ rfqmath/convert.go | 95 +++ rfqmath/convert_test.go | 650 ++++++++++++++++++ rfqmath/fixed_point.go | 116 ++++ rfqmath/fixed_point_test.go | 178 +++++ ...Addition_big_int-20240820164933-66012.fail | 4 + ...Addition_big_int-20240820170908-69698.fail | 12 + ...ition_go_uint_64-20240820164933-66012.fail | 4 + ...Division_big_int-20240820172818-75296.fail | 9 + ...ision_go_uint_64-20240820172818-75296.fail | 9 + ...ision_go_uint_64-20240820172845-75480.fail | 9 + ...ision_go_uint_64-20240820173239-76566.fail | 9 + ...ision_go_uint_64-20240820173647-77930.fail | 9 + ...sat_msat_to_units-20240820192921-7922.fail | 20 + ...sat_msat_to_units-20240820193036-8326.fail | 20 + ...sat_msat_to_units-20240820193141-8834.fail | 20 + ...sat_msat_to_units-20240820193235-9334.fail | 20 + ...sat_msat_to_units-20240820193356-9930.fail | 20 + ...at_msat_to_units-20240820193746-11254.fail | 20 + ...at_msat_to_units-20240820193828-11692.fail | 20 + ...at_msat_to_units-20240820194002-12447.fail | 20 + ...at_msat_to_units-20240820194225-13596.fail | 21 + ...dtrip_conversion-20240820200712-26765.fail | 22 + ...dtrip_conversion-20240820200825-27273.fail | 22 + ...trip_conversion-20240913124134-254478.fail | 20 + ...at_units_to_msat-20240820195405-19290.fail | 20 + ...at_units_to_msat-20240820195451-19659.fail | 20 + ...at_units_to_msat-20240820195640-20028.fail | 20 + ...at_units_to_msat-20240820195835-21008.fail | 20 + ...at_units_to_msat-20240820195942-21560.fail | 20 + ...at_units_to_msat-20240820200033-22032.fail | 20 + ...at_units_to_msat-20240820200108-22231.fail | 20 + ...at_units_to_msat-20240820200151-22503.fail | 20 + ...at_units_to_msat-20240820200210-22739.fail | 20 + ...at_units_to_msat-20240820200252-23305.fail | 20 + ...at_units_to_msat-20240820200301-23438.fail | 20 + ...at_units_to_msat-20240820200331-23865.fail | 20 + ...at_units_to_msat-20240820200353-24465.fail | 20 + ...ision_go_uint_64-20240820172016-73584.fail | 9 + ...edPoint_division-20240820184638-95557.fail | 21 + ...edPoint_division-20240820185145-96965.fail | 21 + ...edPoint_division-20240820185548-97845.fail | 21 + ...xedPoint_division-20240820190729-1824.fail | 20 + ...xedPoint_division-20240820190932-2693.fail | 20 + ...xedPoint_division-20240820191700-4540.fail | 20 + ...xedPoint_division-20240820191737-4783.fail | 20 + ...xedPoint_division-20240820191814-4986.fail | 20 + ...int_from_uint64-20240913124134-254478.fail | 19 + ...t_multiplication-20240820183255-91468.fail | 13 + ...t_multiplication-20240820183614-92659.fail | 13 + ...dPoint_scale_to-20240913124134-254478.fail | 22 + ...romFloat_big_int-20240820174201-79554.fail | 6 + ...Float_go_uint_64-20240820174201-79554.fail | 6 + 58 files changed, 2377 insertions(+), 2 deletions(-) create mode 100644 rfqmath/README.md create mode 100644 rfqmath/arithmetic.go create mode 100644 rfqmath/arithmetic_test.go create mode 100644 rfqmath/convert.go create mode 100644 rfqmath/convert_test.go create mode 100644 rfqmath/fixed_point.go create mode 100644 rfqmath/fixed_point_test.go create mode 100644 rfqmath/testdata/rapid/TestAddition_big_int/TestAddition_big_int-20240820164933-66012.fail create mode 100644 rfqmath/testdata/rapid/TestAddition_big_int/TestAddition_big_int-20240820170908-69698.fail create mode 100644 rfqmath/testdata/rapid/TestAddition_go_uint_64/TestAddition_go_uint_64-20240820164933-66012.fail create mode 100644 rfqmath/testdata/rapid/TestArithmeticDivision_big_int/TestArithmeticDivision_big_int-20240820172818-75296.fail create mode 100644 rfqmath/testdata/rapid/TestArithmeticDivision_go_uint_64/TestArithmeticDivision_go_uint_64-20240820172818-75296.fail create mode 100644 rfqmath/testdata/rapid/TestArithmeticDivision_go_uint_64/TestArithmeticDivision_go_uint_64-20240820172845-75480.fail create mode 100644 rfqmath/testdata/rapid/TestArithmeticDivision_go_uint_64/TestArithmeticDivision_go_uint_64-20240820173239-76566.fail create mode 100644 rfqmath/testdata/rapid/TestArithmeticDivision_go_uint_64/TestArithmeticDivision_go_uint_64-20240820173647-77930.fail create mode 100644 rfqmath/testdata/rapid/TestConversionMsat_msat_to_units/TestConversionMsat_msat_to_units-20240820192921-7922.fail create mode 100644 rfqmath/testdata/rapid/TestConversionMsat_msat_to_units/TestConversionMsat_msat_to_units-20240820193036-8326.fail create mode 100644 rfqmath/testdata/rapid/TestConversionMsat_msat_to_units/TestConversionMsat_msat_to_units-20240820193141-8834.fail create mode 100644 rfqmath/testdata/rapid/TestConversionMsat_msat_to_units/TestConversionMsat_msat_to_units-20240820193235-9334.fail create mode 100644 rfqmath/testdata/rapid/TestConversionMsat_msat_to_units/TestConversionMsat_msat_to_units-20240820193356-9930.fail create mode 100644 rfqmath/testdata/rapid/TestConversionMsat_msat_to_units/TestConversionMsat_msat_to_units-20240820193746-11254.fail create mode 100644 rfqmath/testdata/rapid/TestConversionMsat_msat_to_units/TestConversionMsat_msat_to_units-20240820193828-11692.fail create mode 100644 rfqmath/testdata/rapid/TestConversionMsat_msat_to_units/TestConversionMsat_msat_to_units-20240820194002-12447.fail create mode 100644 rfqmath/testdata/rapid/TestConversionMsat_msat_to_units/TestConversionMsat_msat_to_units-20240820194225-13596.fail create mode 100644 rfqmath/testdata/rapid/TestConversionMsat_roundtrip_conversion/TestConversionMsat_roundtrip_conversion-20240820200712-26765.fail create mode 100644 rfqmath/testdata/rapid/TestConversionMsat_roundtrip_conversion/TestConversionMsat_roundtrip_conversion-20240820200825-27273.fail create mode 100644 rfqmath/testdata/rapid/TestConversionMsat_roundtrip_conversion/TestConversionMsat_roundtrip_conversion-20240913124134-254478.fail create mode 100644 rfqmath/testdata/rapid/TestConversionMsat_units_to_msat/TestConversionMsat_units_to_msat-20240820195405-19290.fail create mode 100644 rfqmath/testdata/rapid/TestConversionMsat_units_to_msat/TestConversionMsat_units_to_msat-20240820195451-19659.fail create mode 100644 rfqmath/testdata/rapid/TestConversionMsat_units_to_msat/TestConversionMsat_units_to_msat-20240820195640-20028.fail create mode 100644 rfqmath/testdata/rapid/TestConversionMsat_units_to_msat/TestConversionMsat_units_to_msat-20240820195835-21008.fail create mode 100644 rfqmath/testdata/rapid/TestConversionMsat_units_to_msat/TestConversionMsat_units_to_msat-20240820195942-21560.fail create mode 100644 rfqmath/testdata/rapid/TestConversionMsat_units_to_msat/TestConversionMsat_units_to_msat-20240820200033-22032.fail create mode 100644 rfqmath/testdata/rapid/TestConversionMsat_units_to_msat/TestConversionMsat_units_to_msat-20240820200108-22231.fail create mode 100644 rfqmath/testdata/rapid/TestConversionMsat_units_to_msat/TestConversionMsat_units_to_msat-20240820200151-22503.fail create mode 100644 rfqmath/testdata/rapid/TestConversionMsat_units_to_msat/TestConversionMsat_units_to_msat-20240820200210-22739.fail create mode 100644 rfqmath/testdata/rapid/TestConversionMsat_units_to_msat/TestConversionMsat_units_to_msat-20240820200252-23305.fail create mode 100644 rfqmath/testdata/rapid/TestConversionMsat_units_to_msat/TestConversionMsat_units_to_msat-20240820200301-23438.fail create mode 100644 rfqmath/testdata/rapid/TestConversionMsat_units_to_msat/TestConversionMsat_units_to_msat-20240820200331-23865.fail create mode 100644 rfqmath/testdata/rapid/TestConversionMsat_units_to_msat/TestConversionMsat_units_to_msat-20240820200353-24465.fail create mode 100644 rfqmath/testdata/rapid/TestDivision_go_uint_64/TestDivision_go_uint_64-20240820172016-73584.fail create mode 100644 rfqmath/testdata/rapid/TestFixedPoint_division/TestFixedPoint_division-20240820184638-95557.fail create mode 100644 rfqmath/testdata/rapid/TestFixedPoint_division/TestFixedPoint_division-20240820185145-96965.fail create mode 100644 rfqmath/testdata/rapid/TestFixedPoint_division/TestFixedPoint_division-20240820185548-97845.fail create mode 100644 rfqmath/testdata/rapid/TestFixedPoint_division/TestFixedPoint_division-20240820190729-1824.fail create mode 100644 rfqmath/testdata/rapid/TestFixedPoint_division/TestFixedPoint_division-20240820190932-2693.fail create mode 100644 rfqmath/testdata/rapid/TestFixedPoint_division/TestFixedPoint_division-20240820191700-4540.fail create mode 100644 rfqmath/testdata/rapid/TestFixedPoint_division/TestFixedPoint_division-20240820191737-4783.fail create mode 100644 rfqmath/testdata/rapid/TestFixedPoint_division/TestFixedPoint_division-20240820191814-4986.fail create mode 100644 rfqmath/testdata/rapid/TestFixedPoint_from_uint64/TestFixedPoint_from_uint64-20240913124134-254478.fail create mode 100644 rfqmath/testdata/rapid/TestFixedPoint_multiplication/TestFixedPoint_multiplication-20240820183255-91468.fail create mode 100644 rfqmath/testdata/rapid/TestFixedPoint_multiplication/TestFixedPoint_multiplication-20240820183614-92659.fail create mode 100644 rfqmath/testdata/rapid/TestFixedPoint_scale_to/TestFixedPoint_scale_to-20240913124134-254478.fail create mode 100644 rfqmath/testdata/rapid/TestToFromFloat_big_int/TestToFromFloat_big_int-20240820174201-79554.fail create mode 100644 rfqmath/testdata/rapid/TestToFromFloat_go_uint_64/TestToFromFloat_go_uint_64-20240820174201-79554.fail diff --git a/docs/rfq-and-decimal-display.md b/docs/rfq-and-decimal-display.md index 410df8d2d..927233620 100644 --- a/docs/rfq-and-decimal-display.md +++ b/docs/rfq-and-decimal-display.md @@ -248,7 +248,7 @@ node as: ## Examples See `TestFindDecimalDisplayBoundaries` and `TestUsdToJpy` in -`rfq/convert_test.go` for how these examples are constructed. +`rfqmath/convert_test.go` for how these examples are constructed. **Case 1**: Buying/selling USD against BTC. diff --git a/go.mod b/go.mod index 7d3185edd..e417a2c61 100644 --- a/go.mod +++ b/go.mod @@ -50,6 +50,7 @@ require ( gopkg.in/macaroon-bakery.v2 v2.1.0 gopkg.in/macaroon.v2 v2.1.0 modernc.org/sqlite v1.30.0 + pgregory.net/rapid v1.1.0 ) require ( @@ -201,7 +202,6 @@ require ( modernc.org/strutil v1.2.0 // indirect modernc.org/token v1.1.0 // indirect nhooyr.io/websocket v1.8.7 // indirect - pgregory.net/rapid v1.1.0 // indirect sigs.k8s.io/yaml v1.2.0 // indirect ) diff --git a/rfq/README.md b/rfq/README.md index 395b35a2c..3a8929569 100644 --- a/rfq/README.md +++ b/rfq/README.md @@ -7,6 +7,9 @@ The high-level/conceptual explanation of [how RFQ (and related concepts such as the decimal display value and price oracles) works, can be found in this separate document](../docs/rfq-and-decimal-display.md). +The implementation of the [RFQ fixed point arithmetic can be found in the +`rfqmath` package](../rfqmath). + The actual [wire messages are located in the `rfqmsg`](../rfqmsg) package. The [gRPC definitions of the RFQ methods can be found in the `taprpc/rfqrpc` diff --git a/rfqmath/README.md b/rfqmath/README.md new file mode 100644 index 000000000..6bb969c7d --- /dev/null +++ b/rfqmath/README.md @@ -0,0 +1,8 @@ +# RFQ math + +This package contains code related to fixed point arithmetics used in RFQ +exchange rate calculations. + +The high-level/conceptual explanation of [how RFQ (and related concepts such +as the decimal display value and price oracles) works, can be found +in this separate document](../docs/rfq-and-decimal-display.md). diff --git a/rfqmath/arithmetic.go b/rfqmath/arithmetic.go new file mode 100644 index 000000000..edfacccc6 --- /dev/null +++ b/rfqmath/arithmetic.go @@ -0,0 +1,213 @@ +package rfqmath + +import ( + "math/big" + + "golang.org/x/exp/constraints" +) + +// Arithmetic defines the basic arithmetic operations. The structure of the +// interfaces allows for chaining the arithmetic operations. +type Arithmetic[N any] interface { + // Add returns the sum of the two numbers. + Add(N) N + + // Mul returns the product of the two numbers. + Mul(N) N + + // Sub returns the difference of the two numbers. + Sub(N) N + + // Div returns the division of the two numbers. + Div(N) N +} + +// Int is an interface that represents an integer types and the operations we +// care about w.r.t that type. +type Int[N any] interface { + // Arithmetic asserts that the target type of this interface satisfies + // the Arithmetic interface. This lets us get around limitations + // regarding recursive types in Go. + Arithmetic[N] + + // Equals returns true if the two integers are equal. + Equals(other N) bool + + // ToFloat converts the integer to a float. + ToFloat() float64 + + // FromFloat converts a float to the integer type. + FromFloat(float64) N + + // ToUint64 converts the integer to a uint64. + ToUint64() uint64 + + // FromUint64 converts a uint64 to the integer type. + FromUint64(uint64) N +} + +// NewInt creates a new integer of the target type. +func NewInt[N Int[N]]() N { + var n N + return n +} + +// GoInt is a concrete implementation of the Int interface for the set of +// built-in integer types. It ends up mapping the integers to a uint64 +// internally for operations. +type GoInt[T constraints.Unsigned] struct { + value T +} + +// NewGoInt creates a new GoInt from the given integer. +func NewGoInt[T constraints.Unsigned](value T) GoInt[T] { + return GoInt[T]{ + value: value, + } +} + +// Add returns the sum of the two integers. +func (b GoInt[T]) Add(other GoInt[T]) GoInt[T] { + return GoInt[T]{ + value: b.value + other.value, + } +} + +// Mul returns the product of the two integers. +func (b GoInt[T]) Mul(other GoInt[T]) GoInt[T] { + return GoInt[T]{ + value: b.value * other.value, + } +} + +// Sub returns the difference of the two integers. +func (b GoInt[T]) Sub(other GoInt[T]) GoInt[T] { + return GoInt[T]{ + value: b.value - other.value, + } +} + +// Div returns the division of the two integers. +func (b GoInt[T]) Div(other GoInt[T]) GoInt[T] { + return GoInt[T]{ + value: b.value / other.value, + } +} + +// ToFloat converts the integer to a float. +func (b GoInt[T]) ToFloat() float64 { + return float64(b.value) +} + +// FromFloat converts a float to the integer type. +func (b GoInt[T]) FromFloat(f float64) GoInt[T] { + b.value = T(f) + return b +} + +// ToUint64 converts the integer to a uint64. +func (b GoInt[T]) ToUint64() uint64 { + return uint64(b.value) +} + +// FromUint64 converts a uint64 to the integer type. +func (b GoInt[T]) FromUint64(u uint64) GoInt[T] { + b.value = T(u) + return b +} + +// Equals returns true if the two integers are equal. +func (b GoInt[T]) Equals(other GoInt[T]) bool { + return b.value == other.value +} + +// A compile-time constraint to ensure that the GoInt type implements the Int +// interface. +var _ Int[GoInt[uint]] = GoInt[uint]{} + +// BigInt is a concrete implementation of the Int interface using Go's big +// integer type. +type BigInt struct { + value *big.Int +} + +// NewBigInt creates a new BigInt from the given integer. +func NewBigInt(value *big.Int) BigInt { + return BigInt{ + value: value, + } +} + +// copyInt returns a copy of the internal big.Int. This is used to ensure we +// don't mutate the underlying bit.Int during arithmetic operations. +func (b BigInt) copyInt() *big.Int { + return new(big.Int).Set(b.value) +} + +// Add returns the sum of the two integers. +func (b BigInt) Add(other BigInt) BigInt { + return BigInt{ + value: b.copyInt().Add(b.value, other.value), + } +} + +// Mul returns the product of the two integers. +func (b BigInt) Mul(other BigInt) BigInt { + return BigInt{ + value: b.copyInt().Mul(b.value, other.value), + } +} + +// Sub returns the difference of the two integers. +func (b BigInt) Sub(other BigInt) BigInt { + return BigInt{ + value: b.copyInt().Sub(b.value, other.value), + } +} + +// Div returns the division of the two integers. +func (b BigInt) Div(other BigInt) BigInt { + return BigInt{ + value: b.copyInt().Div(b.value, other.value), + } +} + +// ToFloat converts the integer to a float. +func (b BigInt) ToFloat() float64 { + floatVal, _ := b.value.Float64() + return floatVal +} + +// FromFloat converts a float to the integer type. +func (b BigInt) FromFloat(f float64) BigInt { + if b.value == nil { + b.value = new(big.Int) + } + + b.value.SetInt64(int64(f)) + return b +} + +// FromUint64 converts a uint64 to the integer type. +func (b BigInt) FromUint64(u uint64) BigInt { + if b.value == nil { + b.value = new(big.Int) + } + + b.value.SetUint64(u) + return b +} + +// ToUint64 converts the integer to a uint64. +func (b BigInt) ToUint64() uint64 { + return b.value.Uint64() +} + +// Equals returns true if the two integers are equal. +func (b BigInt) Equals(other BigInt) bool { + return b.value.Cmp(other.value) == 0 +} + +// A compile-time constraint to ensure that the BigInt type implements the Int +// interface. +var _ Int[BigInt] = BigInt{} diff --git a/rfqmath/arithmetic_test.go b/rfqmath/arithmetic_test.go new file mode 100644 index 000000000..6ae67a35c --- /dev/null +++ b/rfqmath/arithmetic_test.go @@ -0,0 +1,291 @@ +package rfqmath + +import ( + "math" + "math/big" + "testing" + + "github.com/stretchr/testify/require" + "pgregory.net/rapid" +) + +func testAddition[N Int[N]](t *rapid.T) { + zero := NewInt[N]().FromUint64(0) + + gen := rapid.Custom(func(t *rapid.T) N { + return NewInt[N]().FromUint64( + rapid.Uint64Range(0, math.MaxUint64).Draw(t, "n"), + ) + }) + + a := gen.Draw(t, "a") + b := gen.Draw(t, "b") + + // Adding zero to a value should not change it. + if c := a.Add(zero); !c.Equals(a) { + t.Errorf("a + 0 = %v, expected %v", c, a) + } + + // A+B should be equal to B+A. + if c := a.Add(b); !c.Equals(b.Add(a)) { + t.Errorf("a + b = %v, b + a = %v", c, b.Add(a)) + } + + // (A + B) + C should be equal to A + (B + C) (associativity). + c := gen.Draw(t, "c") + if d, e := a.Add(b).Add(c), a.Add(b.Add(c)); !d.Equals(e) { + t.Errorf("associativity: (a + b) + c = %v, a + (b + c) = %v", + d, e) + } +} + +// TestArithmeticAddition tests some basic invariants around addition of the +// integer type. +func TestArithmeticAddition(t *testing.T) { + t.Parallel() + + t.Run("go_uint_64", func(t *testing.T) { + rapid.Check(t, testAddition[GoInt[uint64]]) + }) + t.Run("big_int", func(t *testing.T) { + rapid.Check(t, testAddition[BigInt]) + }) +} + +func testMultiplication[N Int[N]](t *rapid.T) { + one := NewInt[N]().FromUint64(1) + + gen := rapid.Custom(func(t *rapid.T) N { + return NewInt[N]().FromUint64( + rapid.Uint64Range(0, math.MaxUint64).Draw(t, "n"), + ) + }) + + a := gen.Draw(t, "a") + b := gen.Draw(t, "b") + + // Multiplying by one should not change the value. + if c := a.Mul(one); !c.Equals(a) { + t.Errorf("a * 1 = %v, expected %v", c, a) + } + + // A*B should be equal to B*A (commutativity). + if c := a.Mul(b); !c.Equals(b.Mul(a)) { + t.Errorf("a * b = %v, b * a = %v", c, b.Mul(a)) + } +} + +// TestArithmeticMultiplication tests some basic invariants around +// multiplication of the integer type. +func TestArithmeticMultiplication(t *testing.T) { + t.Parallel() + + t.Run("go_uint_64", func(t *testing.T) { + rapid.Check(t, testMultiplication[GoInt[uint64]]) + }) + t.Run("big_int", func(t *testing.T) { + rapid.Check(t, testMultiplication[BigInt]) + }) +} + +func testSubtraction[N Int[N]](t *rapid.T) { + zero := NewInt[N]().FromUint64(0) + + gen := rapid.Custom(func(t *rapid.T) N { + return NewInt[N]().FromUint64( + rapid.Uint64Range(0, math.MaxUint64).Draw(t, "n"), + ) + }) + + a := gen.Draw(t, "a") + + // Subtracting zero should not change the value> + if c := a.Sub(zero); !c.Equals(a) { + t.Errorf("a - 0 = %v, expected %v", c, a) + } + + // a - a should equal zero> + if c := a.Sub(a); !c.Equals(zero) { + t.Errorf("a - a = %v, expected 0", c) + } +} + +// TestArithmeticSubtraction tests some basic invariants around subtraction of +// the integer type. +func TestArithmeticSubtraction(t *testing.T) { + t.Parallel() + + t.Run("go_uint_64", func(t *testing.T) { + rapid.Check(t, testSubtraction[GoInt[uint64]]) + }) + t.Run("big_int", func(t *testing.T) { + rapid.Check(t, testSubtraction[BigInt]) + }) +} + +// testDivision tests the division operation +func testDivision[N Int[N]](t *rapid.T) { + one := NewInt[N]().FromUint64(1) + zero := NewInt[N]().FromUint64(0) + + gen := rapid.Custom(func(t *rapid.T) N { + return NewInt[N]().FromUint64( + rapid.Uint64Range(1, math.MaxUint64).Draw(t, "n"), + ) + }) + + a := gen.Draw(t, "a") + b := gen.Draw(t, "b") + + _, isUint64 := any(a).(GoInt[uint64]) + + // Dividing by one should not change the value. + if c := a.Div(one); !c.Equals(a) { + t.Errorf("a / 1 = %v, expected %v", c, a) + } + + // a / a should equal one. + if c := a.Div(a); !c.Equals(one) { + t.Errorf("a / a = %v, expected 1", c) + } + + // (a * b) / b should equal a (for non-zero b). + if !b.Equals(zero) { + product := a.Mul(b) + + // If this is the uint64 implementation, and the product + // overflows, then we can skip this check. + // + // TODO(roasbeef): predicate it all on checked arithmetic + switch { + case isUint64 && a.ToUint64() > math.MaxUint64/b.ToUint64(): + fallthrough + case isUint64 && product.ToUint64() < a.ToUint64(): + break + default: + quotient := product.Div(b) + if !quotient.Equals(a) { + t.Errorf("(a * b) / b: (%v * %v) / %v = %v, "+ + "expected %v", a, b, b, quotient, a) + } + } + } + + // a / 1 should equal a + divByOne := a.Div(one) + if !divByOne.Equals(a) { + t.Errorf("division by one: %v / 1 = %v, expected %v", a, + divByOne, a) + } + + // 0 / a should equal 0 (for non-zero a). + if !a.Equals(zero) { + zeroDiv := zero.Div(a) + if !zeroDiv.Equals(zero) { + t.Errorf("zero divided by a: 0 / %v = %v, expected 0", + a, zeroDiv) + } + } +} + +// TestArithmeticDivision tests some basic invariants around division of the +// integer type. +func TestArithmeticDivision(t *testing.T) { + t.Parallel() + + t.Run("go_uint_64", func(t *testing.T) { + rapid.Check(t, testDivision[GoInt[uint64]]) + }) + t.Run("big_int", func(t *testing.T) { + rapid.Check(t, testDivision[BigInt]) + }) +} + +func testToFromFloat[N Int[N]](t *rapid.T) { + gen := rapid.Custom(func(t *rapid.T) N { + return NewInt[N]().FromUint64( + rapid.Uint64Range(0, math.MaxUint64).Draw(t, "n"), + ) + }) + + a := gen.Draw(t, "a") + + // For this test, we can only support values less than + // 9007199254740991, or 2^53-1. + if a.ToUint64() > 9007199254740991 { + return + } + + // Converting to float and back should preserve the value. + float := a.ToFloat() + b := NewInt[N]().FromFloat(float) + + if !a.Equals(b) { + t.Errorf("toFloat/fromFloat conversion failed: original %v, "+ + "got %v", a, b) + } +} + +// TestArithmeticToFromFloat tests the conversion to and from float for the +// integer type. +func TestArithmeticToFromFloat(t *testing.T) { + t.Parallel() + + t.Run("go_uint_64", func(t *testing.T) { + rapid.Check(t, testToFromFloat[GoInt[uint64]]) + }) + t.Run("big_int", func(t *testing.T) { + rapid.Check(t, testToFromFloat[BigInt]) + }) +} + +// testToFromUint64 tests the conversion between uint64 and Int +func testToFromUint64[N Int[N]](t *rapid.T) { + gen := rapid.Uint64().Draw(t, "n") + + a := NewInt[N]().FromUint64(gen) + + // Converting to uint64 and back should preserve the value. + uint64Val := a.ToUint64() + b := NewInt[N]().FromUint64(uint64Val) + if !a.Equals(b) { + t.Errorf("toUint64/fromUint64 conversion failed: original %v, "+ + "got %v", a, b) + } +} + +// TestArithmeticToFromUint64 tests the conversion to and from uint64 for the +// integer type. +func TestArithmeticToFromUint64(t *testing.T) { + t.Parallel() + + t.Run("go_uint_64", func(t *testing.T) { + rapid.Check(t, testToFromUint64[GoInt[uint64]]) + }) + t.Run("big_int", func(t *testing.T) { + rapid.Check(t, testToFromUint64[BigInt]) + }) +} + +// TestArithmeticGoIntConstructor tests the NewGoInt constructor for the GoInt +// type. +func TestArithmeticGoIntConstructor(t *testing.T) { + t.Parallel() + + // We should be able to create a new GoInt from a uint64. + val := uint64(123) + a := NewGoInt[uint64](val) + require.Equal(t, val, a.value) +} + +// TestArithmeticIntConstructor tests the NewInt constructor for the BigInt. +func TestArithmeticIntConstructor(t *testing.T) { + t.Parallel() + + // We should be able to create a new BigInt from a uint64. + val := uint64(123) + a := NewBigInt(new(big.Int).SetUint64(val)) + if !a.Equals(NewInt[BigInt]().FromUint64(val)) { + t.Fatalf("expected %v, got %v", val, a) + } +} diff --git a/rfqmath/convert.go b/rfqmath/convert.go new file mode 100644 index 000000000..21d753d59 --- /dev/null +++ b/rfqmath/convert.go @@ -0,0 +1,95 @@ +package rfqmath + +import ( + "math" + + "github.com/btcsuite/btcd/btcutil" + "github.com/lightningnetwork/lnd/lnwire" +) + +// defaultArithmeticScale is the default scale used for arithmetic operations. +// This is used to ensure that we don't lose precision when doing arithmetic +// operations. +const defaultArithmeticScale = 11 + +// MilliSatoshiToUnits converts the given milli-satoshi amount to units using +// the given price in units per bitcoin as a fixed point in the asset's desired +// resolution (scale equal to decimal display). +// +// Given the amount of mSat (X), and the number of units per BTC (Y), we can +// compute the total amount of units (U) as follows: +// - U = (X / M) * Y +// - where M is the number of mSAT in a BTC (100,000,000,000). +func MilliSatoshiToUnits[N Int[N]](milliSat lnwire.MilliSatoshi, + unitsPerBtc FixedPoint[N]) FixedPoint[N] { + + // We take the max of the target arithmetic scale and the given unit's + // scale, which is expected to be the asset's decimal display value. + arithmeticScale := uint8(math.Max( + float64(defaultArithmeticScale), float64(unitsPerBtc.Scale), + )) + + // Before we do any computation, we'll scale everything up to our + // arithmetic scale. + mSatFixed := FixedPointFromUint64[N]( + uint64(milliSat), arithmeticScale, + ) + scaledUnitsPerBtc := unitsPerBtc.ScaleTo(arithmeticScale) + + // Next, we'll convert the amount of mSAT to BTC. We do this by + // dividing by the number of mSAT in a BTC. + oneBtcInMilliSat := FixedPointFromUint64[N]( + uint64(btcutil.SatoshiPerBitcoin*1_000), arithmeticScale, + ) + amtBTC := mSatFixed.Div(oneBtcInMilliSat) + + // Now that we have the amount of BTC as input, and the amount of units + // per BTC, we multiply the two to get the total amount of units. + amtUnits := amtBTC.Mul(scaledUnitsPerBtc) + + // The final response will need to scale back down to the original + // amount of units that were passed in. + scaledAmt := amtUnits.ScaleTo(unitsPerBtc.Scale) + + return scaledAmt +} + +// UnitsToMilliSatoshi converts the given number of asset units to a +// milli-satoshi amount, using the given price in units per bitcoin as a fixed +// point in the asset's desired resolution (scale equal to decimal display). +// +// Given the amount of asset units (U), and the number of units per BTC (Y), we +// compute the total amount of mSAT (X) as follows: +// - X = (U / Y) * M +// - where M is the number of mSAT in a BTC (100,000,000,000). +func UnitsToMilliSatoshi[N Int[N]](assetUnits, + unitsPerBtc FixedPoint[N]) lnwire.MilliSatoshi { + + // We take the max of the target arithmetic scale and the given unit's + // scale, which is expected to be the asset's decimal display value. + arithmeticScale := uint8(math.Max( + float64(defaultArithmeticScale), float64(unitsPerBtc.Scale), + )) + + // Before we do the computation, we'll scale everything up to our + // arithmetic scale. + assetUnits = assetUnits.ScaleTo(arithmeticScale) + unitsPerBtc = unitsPerBtc.ScaleTo(arithmeticScale) + + // We have the number of units, and the number of units per BTC, so we + // can arrive at the number of BTC via: BTC = units / (units/BTC). + amtBTC := assetUnits.Div(unitsPerBtc) + + // Now that we have the amount of BTC, we can map to mSat by + // multiplying by the number of mSAT in a BTC. + oneBtcInMilliSat := FixedPointFromUint64[N]( + uint64(btcutil.SatoshiPerBitcoin*1_000), arithmeticScale, + ) + + amtMsat := amtBTC.Mul(oneBtcInMilliSat) + + // We did the computation in terms of the scaled integers, so no we'll + // go back to a normal mSAT value scaling down to zero (no decimals) + // along the way. + return lnwire.MilliSatoshi(amtMsat.ScaleTo(0).ToUint64()) +} diff --git a/rfqmath/convert_test.go b/rfqmath/convert_test.go new file mode 100644 index 000000000..a63686a57 --- /dev/null +++ b/rfqmath/convert_test.go @@ -0,0 +1,650 @@ +package rfqmath + +import ( + "fmt" + "math" + "testing" + + "github.com/btcsuite/btcd/btcutil" + "github.com/davecgh/go-spew/spew" + "github.com/lightningnetwork/lnd/lnwire" + "github.com/stretchr/testify/require" + "pgregory.net/rapid" +) + +var ( + btcPricesCents = []uint64{ + 1_000_00, + 3_456_78, + 5_000_00, + + 10_000_00, + 20_000_00, + 34_567_89, + 50_000_00, + 50_702_12, + + 100_000_00, + 345_678_90, + 500_000_00, + + 1_000_000_00, + 3_456_789_01, + 5_000_000_00, + + 10_000_000_00, + 34_567_890_12, + 50_000_000_00, + } + + maxDecimalDisplay = 8 + + invoiceAmountsMsat = []uint64{ + 1, + 2, + 3, + 5, + + 10, + 34, + 50, + + 100, + 345, + 500, + + 1_000, + 3_456, + 5_000, + + 10_000, + 34_567, + 50_000, + + 100_000, + 345_678, + 500_000, + + 1_000_000, + 3_456_789, + 5_000_000, + + 10_000_000, + 20_000_000, + 34_567_890, + 50_000_000, + + 100_000_000, + 345_678_901, + 500_000_000, + + 1_000_000_000, + 3_456_789_012, + 5_000_000_000, + + 10_000_000_000, + 34_567_890_123, + 50_000_000_000, + + 100_000_000_000, + 345_678_901_234, + 500_000_000_000, + } +) + +// newBig creates a new BigInt from the given int64. +func newBig(n uint64) BigInt { + return NewInt[BigInt]().FromUint64(n) +} + +// TestConvertFindDecimalDisplayBoundaries tests the maximum number of units +// that can be represented with a given decimal display, the smallest payable +// invoice amount, and the maximum MPP rounding error. The values are printed +// out on standard output. +func TestConvertFindDecimalDisplayBoundaries(t *testing.T) { + limitsBitInt := calcLimits[BigInt] + for _, btcPriceCents := range btcPricesCents { + fmt.Printf("-------------\nBTC price: %d USD\n-------------\n", + btcPriceCents/100) + for decDisp := 2; decDisp <= maxDecimalDisplay; decDisp++ { + unitsPerUsd := uint64(math.Pow10(decDisp)) + + priceCents := FixedPoint[BigInt]{ + Coefficient: new(BigInt).FromUint64( + btcPriceCents, + ), + Scale: 2, + } + priceScaled := priceCents.ScaleTo(uint8(decDisp)) + + numShards := float64(16) + maxUnits, smallestAmount, mSatPerUnit := limitsBitInt( + btcPriceCents, decDisp, + ) + + maxRoundMSat := uint64(mSatPerUnit * numShards) + + oneUsd := FixedPoint[BigInt]{ + Coefficient: new(BigInt).FromUint64(1), + Scale: 0, + }.ScaleTo(uint8(decDisp)) + + mSatPerUsd := UnitsToMilliSatoshi(oneUsd, priceScaled) + unitsPerSat := MilliSatoshiToUnits(1000, priceScaled) + + fmt.Printf("decimalDisplay: %d\t\t\t%v units = 1 USD, "+ + "1 BTC = %v units\n"+ + "Max issuable units:\t\t\tcan represent %v "+ + "BTC\n"+ + "Min payable invoice amount:\t%d mSAT\n"+ + "Max MPP rounding error:\t\t%d mSAT (@%.0f "+ + "shards)\n"+ + "Satoshi per USD:\t\t\t%d\n"+ + "Satoshi per Asset Unit: \t%.5f\n"+ + "Asset Units per Satoshi: \t%v\n"+ + "Price In Asset: \t\t\t%v\n"+ + "Price Out Asset: \t\t\t%v\n\n", + decDisp, unitsPerUsd, priceScaled, + maxUnits, smallestAmount, maxRoundMSat, + numShards, mSatPerUsd/1000, mSatPerUnit/1000, + unitsPerSat, priceScaled, + uint64(btcutil.SatoshiPerBitcoin*1000)) + } + } +} + +// calcLimits calculates the maximum number of units that can be represented +// with a given decimal display, the smallest payable invoice amount, and the +// maximum MPP rounding error for a given BTC price in cents, decimal display +// value and number of MPP shards. +func calcLimits[N Int[N]](btcPriceCent uint64, decDisplay int) (uint64, uint64, + float64) { + + msatScale := defaultArithmeticScale + + // In the unit test, the price is always given as cents per BTC. + var v N + priceCents := FixedPoint[N]{ + Coefficient: v.FromUint64(btcPriceCent), + Scale: 2, + } + + // priceScaled is the number of units per USD at the given price. + priceScaled := priceCents.ScaleTo(uint8(decDisplay)) + + // priceScaledF is the same as priceScaled, but in float64 format. + priceScaledF := float64(btcPriceCent) * math.Pow10(decDisplay-2) + + // mSatPerUnitF is the number of mSAT per asset unit at the given + // price. + mSatPerUnitF := math.Pow10(msatScale) / priceScaledF + + // maxUnits is the maximum number of BTC that can be represented with + // assets given the decimal display (assuming a uint64 is used). + maxUnits := NewInt[N]().FromUint64( + math.MaxUint64, + ).Div(priceScaled.Coefficient) + + smallestAmount := uint64(0) + for _, invoiceAmount := range invoiceAmountsMsat { + invAmt := NewInt[N]().FromUint64(invoiceAmount) + + unitsForInvoice := invAmt.Mul( + priceScaled.Coefficient, + ).ToUint64() / uint64(math.Pow10(msatScale)) + + if unitsForInvoice > 0 && smallestAmount == 0 { + smallestAmount = invoiceAmount + } + } + + return maxUnits.ToUint64(), smallestAmount, mSatPerUnitF +} + +// TestConvertFixedPointFromUint64 tests the FixedPointFromUint64 scales up +// properly based on the passed scale and integer value. +func TestConvertFixedPointFromUint64(t *testing.T) { + t.Parallel() + + testCases := []struct { + name string + value uint64 + scale uint8 + expectedOut FixedPoint[BigInt] + }{ + { + name: "scale 0", + value: 1, + scale: 0, + expectedOut: FixedPoint[BigInt]{ + Coefficient: newBig(1), + Scale: 0, + }, + }, + { + name: "scale 2", + value: 1, + scale: 2, + expectedOut: FixedPoint[BigInt]{ + Coefficient: newBig(100), + Scale: 2, + }, + }, + { + name: "scale 6", + value: 1, + scale: 6, + expectedOut: FixedPoint[BigInt]{ + Coefficient: newBig(1_000_000), + Scale: 6, + }, + }, + { + name: "scale 8", + value: 1, + scale: 8, + expectedOut: FixedPoint[BigInt]{ + Coefficient: newBig(100_000_000), + Scale: 8, + }, + }, + { + name: "scale 8 msat in BTC", + value: btcutil.SatoshiPerBitcoin * 1000, + scale: 8, + expectedOut: FixedPoint[BigInt]{ + Coefficient: newBig(uint64( + 10_000_000_000_000_000_000, + )), + Scale: 8, + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + out := FixedPointFromUint64[BigInt]( + tc.value, tc.scale, + ) + + require.Equal(t, tc.expectedOut, out) + }) + } +} + +// TestConvertScaleTo tests the ScaleTo method of the FixedPoint type. +func TestConvertScaleTo(t *testing.T) { + t.Parallel() + + testCases := []struct { + name string + in FixedPoint[BigInt] + scaleTo uint8 + expectedOut FixedPoint[BigInt] + }{ + { + name: "scale from 0 to 12", + in: FixedPoint[BigInt]{ + Coefficient: newBig(1), + Scale: 0, + }, + scaleTo: 12, + expectedOut: FixedPoint[BigInt]{ + Coefficient: newBig(1_000_000_000_000), + Scale: 12, + }, + }, + { + name: "scale from 0 to 4", + in: FixedPoint[BigInt]{ + Coefficient: newBig(9), + Scale: 0, + }, + scaleTo: 4, + expectedOut: FixedPoint[BigInt]{ + Coefficient: newBig(90_000), + Scale: 4, + }, + }, + { + name: "scale from 2 to 4", + in: FixedPoint[BigInt]{ + Coefficient: newBig(123_456), + Scale: 2, + }, + scaleTo: 4, + expectedOut: FixedPoint[BigInt]{ + Coefficient: newBig(12_345_600), + Scale: 4, + }, + }, + { + name: "scale from 4 to 2, no precision loss", + in: FixedPoint[BigInt]{ + Coefficient: newBig(12_345_600), + Scale: 4, + }, + scaleTo: 2, + expectedOut: FixedPoint[BigInt]{ + Coefficient: newBig(123_456), + Scale: 2, + }, + }, + { + name: "scale from 6 to 2, with precision loss", + in: FixedPoint[BigInt]{ + Coefficient: newBig(12_345_600), + Scale: 6, + }, + scaleTo: 2, + expectedOut: FixedPoint[BigInt]{ + Coefficient: newBig(1_234), + Scale: 2, + }, + }, + { + name: "scale from 6 to 2, with full loss of value", + in: FixedPoint[BigInt]{ + Coefficient: newBig(12), + Scale: 6, + }, + scaleTo: 2, + expectedOut: FixedPoint[BigInt]{ + Coefficient: newBig(0), + Scale: 2, + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + out := tc.in.ScaleTo(tc.scaleTo) + require.True( + t, tc.expectedOut.Equals(out), + "expected %v, got %v", + spew.Sdump(tc.expectedOut), spew.Sdump(out), + ) + }) + } +} + +// TestConvertMilliSatoshiToUnits tests the MilliSatoshiToUnits function. +func TestConvertMilliSatoshiToUnits(t *testing.T) { + t.Parallel() + + testCases := []struct { + invoiceAmount lnwire.MilliSatoshi + price FixedPoint[BigInt] + expectedUnits uint64 + }{ + { + // 5k USD per BTC @ decimal display 2. + invoiceAmount: 200_000, + price: FixedPoint[BigInt]{ + Coefficient: newBig(5_000_00), + Scale: 2, + }, + expectedUnits: 1, + }, + { + // 5k USD per BTC @ decimal display 6. + invoiceAmount: 200_000, + price: FixedPoint[BigInt]{ + Coefficient: newBig(5_000_00), + Scale: 2, + }.ScaleTo(6), + expectedUnits: 10_000, + }, + { + // 50k USD per BTC @ decimal display 6. + invoiceAmount: 1_973, + price: FixedPoint[BigInt]{ + Coefficient: newBig(50_702_00), + Scale: 2, + }.ScaleTo(6), + expectedUnits: 1000, + }, + { + // 50M USD per BTC @ decimal display 6. + invoiceAmount: 123_456_789, + price: FixedPoint[BigInt]{ + Coefficient: newBig(50_702_000_00), + Scale: 2, + }.ScaleTo(6), + expectedUnits: 62595061158, + }, + { + // 50k USD per BTC @ decimal display 6. + invoiceAmount: 5_070, + price: FixedPoint[BigInt]{ + Coefficient: newBig(50_702_12), + Scale: 2, + }.ScaleTo(6), + expectedUnits: 2_570, + }, + { + // 7.341M JPY per BTC @ decimal display 6. + invoiceAmount: 5_000, + price: FixedPoint[BigInt]{ + Coefficient: newBig(7_341_847), + Scale: 0, + }.ScaleTo(6), + expectedUnits: 367_092, + }, + { + // 7.341M JPY per BTC @ decimal display 2. + invoiceAmount: 5_000, + price: FixedPoint[BigInt]{ + Coefficient: newBig(7_341_847), + Scale: 0, + }.ScaleTo(4), + expectedUnits: 3_670, + }, + } + + for _, tc := range testCases { + name := fmt.Sprintf("milliSat=%d,price=%s", tc.invoiceAmount, + tc.price.String()) + + t.Run(name, func(t *testing.T) { + units := MilliSatoshiToUnits(tc.invoiceAmount, tc.price) + require.Equal(t, tc.expectedUnits, units.ToUint64()) + + mSat := UnitsToMilliSatoshi(units, tc.price) + + diff := tc.invoiceAmount - mSat + require.LessOrEqual(t, diff, uint64(2), "mSAT diff") + }) + } +} + +// TestConvertUsdToJpy tests the conversion of USD to JPY using a BTC price in +// USD and a BTC price in JPY, both expressed as a FixedPoint. +func TestConvertUsdToJpy(t *testing.T) { + t.Parallel() + + testCases := []struct { + name string + usdPrice FixedPoint[BigInt] + jpyPrice FixedPoint[BigInt] + usdAmount uint64 + expectedJpy uint64 + }{ + { + name: "1 USD to JPY @ 2.840M JPY/BTC, 20k USD/BTC", + usdPrice: FixedPoint[BigInt]{ + Coefficient: newBig(20_000_00), + Scale: 2, + }.ScaleTo(6), + jpyPrice: FixedPoint[BigInt]{ + Coefficient: newBig(2_840_000), + Scale: 0, + }.ScaleTo(4), + usdAmount: 1, + expectedJpy: 142, + }, + { + name: "100 USD to JPY @ 7.341M JPY/BTC, 50'702 USD/BTC", + usdPrice: FixedPoint[BigInt]{ + Coefficient: newBig(50_702_12), + Scale: 2, + }.ScaleTo(6), + jpyPrice: FixedPoint[BigInt]{ + Coefficient: newBig(7_341_847), + Scale: 0, + }.ScaleTo(4), + usdAmount: 100, + expectedJpy: 14_480, + }, + { + name: "500 USD to JPY @ 142M JPY/BTC, 1M USD/BTC", + usdPrice: FixedPoint[BigInt]{ + Coefficient: newBig(1_000_000_00), + Scale: 2, + }.ScaleTo(6), + jpyPrice: FixedPoint[BigInt]{ + Coefficient: newBig(142_000_000), + Scale: 0, + }.ScaleTo(4), + usdAmount: 500, + expectedJpy: 71_000, + }, + } + + for _, tc := range testCases { + // Easy way to scale up the USD amount to 6 decimal display. + dollarUnits := FixedPoint[BigInt]{ + Coefficient: newBig(tc.usdAmount), + Scale: 0, + }.ScaleTo(6) + + // Convert the USD to mSAT. + hundredUsdAsMilliSatoshi := UnitsToMilliSatoshi( + dollarUnits, tc.usdPrice, + ) + + // Convert the mSAT to JPY. + usdAmountAsJpy := MilliSatoshiToUnits( + hundredUsdAsMilliSatoshi, tc.jpyPrice, + ) + + // Go from decimal display of 4 to 0 (full JPY). + fullJpy := usdAmountAsJpy.ToUint64() / 10_000 + + require.Equal(t, tc.expectedJpy, fullJpy) + + oneUsd := FixedPoint[BigInt]{ + Coefficient: newBig(1), + Scale: 0, + }.ScaleTo(6) + oneJpy := FixedPoint[BigInt]{ + Coefficient: newBig(1), + Scale: 0, + }.ScaleTo(4) + + _, _, mSatPerUsdUnit := calcLimits[BigInt]( + tc.usdPrice.ScaleTo(2).ToUint64(), 6, + ) + mSatPerUsd := UnitsToMilliSatoshi(oneUsd, tc.usdPrice) + usdUnitsPerSat := MilliSatoshiToUnits(1000, tc.usdPrice) + + _, _, mSatPerJpyUnit := calcLimits[BigInt]( + tc.jpyPrice.ScaleTo(0).ToUint64(), 4, + ) + mSatPerJpy := UnitsToMilliSatoshi(oneJpy, tc.jpyPrice) + jpyUnitsPerSat := MilliSatoshiToUnits(1000, tc.jpyPrice) + + fmt.Printf("Satoshi per USD:\t\t\t\t%d\n"+ + "Satoshi per USD Asset Unit: \t%.5f\n"+ + "USD Asset Units per Satoshi: \t%v\n"+ + "Satoshi per JPY:\t\t\t\t%d\n"+ + "Satoshi per JPY Asset Unit: \t%.5f\n"+ + "JPY Asset Units per Satoshi: \t%v\n"+ + "Price In Asset: \t\t\t\t%v\n"+ + "Price Out Asset: \t\t\t\t%v\n"+ + "%3d USD in JPY: \t\t\t\t%d\n\n", + mSatPerUsd/1000, mSatPerUsdUnit/1000, usdUnitsPerSat, + mSatPerJpy/1000, mSatPerJpyUnit/1000, jpyUnitsPerSat, + tc.usdPrice.Coefficient, tc.jpyPrice.Coefficient, + tc.usdAmount, fullJpy) + } +} + +func testMilliSatoshiToUnits[N Int[N]](t *rapid.T) { + unitsPerBtc := rapid.Uint64Range(1, 100_000_000).Draw(t, "unitsPerBtc") + scale := uint8(rapid.IntRange(2, 9).Draw(t, "scale")) + + msat := lnwire.MilliSatoshi( + rapid.Uint64Range(1, math.MaxUint32).Draw(t, "msat"), + ) + unitsPerBtcFP := FixedPointFromUint64[N](unitsPerBtc, scale) + + result := MilliSatoshiToUnits(msat, unitsPerBtcFP) + + // The result should have the same scale as unitsPerBtc. + require.Equal(t, scale, result.Scale) + + // If we recompute the value using pure floats, then we should be + // within a margin of error related to the scale: U = (X / M) * Y. + // + // TODO(roasbeef): make delta a function of scale + xByMFloat := float64(msat) / float64(btcutil.SatoshiPerBitcoin*1000) + unitsFloat := xByMFloat * float64(unitsPerBtc) + require.InDelta(t, unitsFloat, result.ToFloat64(), 0.012) +} + +func testUnitsToMilliSatoshi[N Int[N]](t *rapid.T) { + units := rapid.Uint64Range(1, 1_000_000_000).Draw(t, "units") + unitsPerBtc := rapid.Uint64Range( + 1_000, 100_000_000, + ).Draw(t, "unitsPerBtc") + scale := uint8(rapid.IntRange(5, 9).Draw(t, "scale")) + + unitsFP := FixedPointFromUint64[N](units, scale) + unitsPerBtcFP := FixedPointFromUint64[N](unitsPerBtc, scale) + + result := UnitsToMilliSatoshi(unitsFP, unitsPerBtcFP) + + // If we recompute the value using pure floats, then we should be + // within a margin of error related to the scale: X = (U / Y) * M. + // + // TODO(roasbeef): make delta a function of scale + uByYFloat := float64(units) / float64(unitsPerBtc) + msatFloat := uByYFloat * float64(btcutil.SatoshiPerBitcoin*1000) + require.InEpsilon(t, uint64(msatFloat), uint64(result), 0.01) +} + +func testRoundTripConversion[N Int[N]](t *rapid.T) { + unitsPerBtc := rapid.Uint64Range( + 1_000, 100_000_000, + ).Draw(t, "unitsPerBtc") + scale := uint8(rapid.IntRange(9, 10).Draw(t, "scale")) + + msat := lnwire.MilliSatoshi( + rapid.Uint64Range(1, math.MaxUint32).Draw(t, "msat"), + ) + unitsPerBtcFP := FixedPointFromUint64[N](unitsPerBtc, scale) + + units := MilliSatoshiToUnits(msat, unitsPerBtcFP) + msatResult := UnitsToMilliSatoshi(units, unitsPerBtcFP) + + // TODO(roasbeef): should it also round up to the nearest sat on the + // other end? + // * can end up in cases where we have 0.9 msat + + // The round trip conversion should preserve the value. + require.InDelta(t, uint64(msat), uint64(msatResult), 1) +} + +// TestConversionMsat tests key invariant properties of the conversion +// functions. +func TestConversionMsat(t *testing.T) { + t.Parallel() + + t.Run("msat_to_units", rapid.MakeCheck(testMilliSatoshiToUnits[BigInt])) + t.Run("units_to_msat", rapid.MakeCheck(testUnitsToMilliSatoshi[BigInt])) + t.Run( + "roundtrip_conversion", + rapid.MakeCheck(testRoundTripConversion[BigInt]), + ) +} diff --git a/rfqmath/fixed_point.go b/rfqmath/fixed_point.go new file mode 100644 index 000000000..3fdc48757 --- /dev/null +++ b/rfqmath/fixed_point.go @@ -0,0 +1,116 @@ +package rfqmath + +import ( + "fmt" + "math" + "strconv" +) + +// FixedPoint is used to represent fixed point arithmetic for currency related +// calculations. A fixed point consists of a value, and a scale. The value is +// the integer representation of the number. The scale is used to represent the +// fractional/decimal component. +type FixedPoint[T Int[T]] struct { + // Coefficient is the value of the FixedPoint integer. + Coefficient T + + // Scale is used to represent the fractional component. This always + // represents a power of 10. Eg: a scale value of 2 (two decimal + // places) maps to a multiplication by 100. + Scale uint8 +} + +// String returns the string version of the fixed point value. +func (f FixedPoint[T]) String() string { + coefficient := f.Coefficient.ToFloat() / math.Pow10(int(f.Scale)) + return fmt.Sprintf("%.*f", f.Scale, coefficient) +} + +// ScaleTo returns a new FixedPoint that is scaled up or down to the given +// scale. +func (f FixedPoint[T]) ScaleTo(newScale uint8) FixedPoint[T] { + // Scale diff is the difference between the current scale and the new + // scale. If this is negative, we need to scale down. + scaleDiff := int32(newScale) - int32(f.Scale) + + absoluteScale := int(math.Abs(float64(scaleDiff))) + scaleMultiplier := NewInt[T]().FromFloat(math.Pow10(absoluteScale)) + + // We'll explicitly handle the scale down vs scale up case. + var newCoefficient T + switch { + // No change in scale. + case scaleDiff == 0: + newCoefficient = f.Coefficient + + // Larger scale, so we'll multiply by 10^scaleDiff. + case scaleDiff > 0: + newCoefficient = f.Coefficient.Mul(scaleMultiplier) + + // Smaller scale, so we'll divide by 10^scaleDiff. + case scaleDiff < 0: + newCoefficient = f.Coefficient.Div(scaleMultiplier) + } + + return FixedPoint[T]{ + Coefficient: newCoefficient, + Scale: newScale, + } +} + +// ToUint64 returns a new FixedPoint that is scaled down from the existing scale +// and mapped to a uint64 representing the amount of units. This should be used +// to go from FixedPoint to an amount of "units". +func (f FixedPoint[T]) ToUint64() uint64 { + return f.Coefficient.ToUint64() +} + +// ToFloat64 returns a float64 representation of the FixedPoint value. +func (f FixedPoint[T]) ToFloat64() float64 { + floatStr := f.String() + float, _ := strconv.ParseFloat(floatStr, 64) + return float +} + +// Mul returns a new FixedPoint that is the result of multiplying the existing +// int by the passed one. +func (f FixedPoint[T]) Mul(other FixedPoint[T]) FixedPoint[T] { + multiplier := NewInt[T]().FromFloat(math.Pow10(int(f.Scale))) + + result := f.Coefficient.Mul(other.Coefficient).Div(multiplier) + + return FixedPoint[T]{ + Coefficient: result, + Scale: f.Scale, + } +} + +// Div returns a new FixedPoint that is the result of dividing the existing int +// by the passed one. +func (f FixedPoint[T]) Div(other FixedPoint[T]) FixedPoint[T] { + multiplier := NewInt[T]().FromFloat(math.Pow10(int(f.Scale))) + + result := f.Coefficient.Mul(multiplier).Div(other.Coefficient) + + return FixedPoint[T]{ + Coefficient: result, + Scale: f.Scale, + } +} + +// Equals returns true if the two FixedPoint values are equal. +func (f FixedPoint[T]) Equals(other FixedPoint[T]) bool { + return f.Coefficient.Equals(other.Coefficient) && f.Scale == other.Scale +} + +// FixedPointFromUint64 creates a new FixedPoint from the given integer and +// scale. Note that the input here should be *unscaled*. +func FixedPointFromUint64[N Int[N]](value uint64, scale uint8) FixedPoint[N] { + scaleN := NewInt[N]().FromFloat(math.Pow10(int(scale))) + coefficientN := NewInt[N]().FromUint64(value) + + return FixedPoint[N]{ + Coefficient: scaleN.Mul(coefficientN), + Scale: scale, + } +} diff --git a/rfqmath/fixed_point_test.go b/rfqmath/fixed_point_test.go new file mode 100644 index 000000000..6e79e4c03 --- /dev/null +++ b/rfqmath/fixed_point_test.go @@ -0,0 +1,178 @@ +package rfqmath + +import ( + "math" + "testing" + + "github.com/stretchr/testify/require" + "pgregory.net/rapid" +) + +func testScaleTo[N Int[N]](t *rapid.T) { + coefficient := rapid.Uint64().Draw(t, "coefficient") + scale := uint8(rapid.IntRange(0, 18).Draw(t, "scale")) + newScale := uint8(rapid.IntRange(0, 18).Draw(t, "newScale")) + + fp := FixedPointFromUint64[N](coefficient, scale) + scaledFp := fp.ScaleTo(newScale) + + // Scaling to the same scale should not change the coefficient. + if scale == newScale { + require.True( + t, fp.Equals(scaledFp), "scaling to same scale "+ + "changed coefficient: %v to %v", fp, scaledFp, + ) + } + + // Scaling up then down should approximate the original coefficient. + if newScale > scale { + backScaled := scaledFp.ScaleTo(scale) + require.True( + t, fp.Equals(backScaled), + "scaling up then down didn't return to original: %v "+ + "to %v to %v", fp, scaledFp, backScaled, + ) + } + + // The internal scale should reflect the new scale. + require.Equal( + t, newScale, scaledFp.Scale, "scaled FixedPoint has "+ + "incorrect scale: got %d, want %d", scaledFp.Scale, + newScale, + ) +} + +func testFixedPointMultiplication[N Int[N]](t *rapid.T) { + a := rapid.Uint64().Draw(t, "a") + b := rapid.Uint64().Draw(t, "b") + scale := uint8(rapid.IntRange(0, 9).Draw(t, "scale")) + + fpA := FixedPointFromUint64[N](a, scale) + fpB := FixedPointFromUint64[N](b, scale) + result := fpA.Mul(fpB) + + // a * b should equal b * a. + require.True( + t, result.Equals(fpB.Mul(fpA)), + "multiplication not commutative: %v * %v != %v * %v", fpA, + fpB, fpB, fpA, + ) + + // a * 1 should equal a. + one := FixedPointFromUint64[N](1, scale) + require.True( + t, fpA.Equals(fpA.Mul(one)), + "multiplication by 1 changed value: %v * 1 = %v", fpA, + fpA.Mul(one), + ) + + // The result should have the same scale as the operands. + require.Equal( + t, scale, result.Scale, "multiplication changed scale: %v * "+ + "%v has scale %d, expected %d", + fpA, fpB, result.Scale, scale, + ) + + // (a * b) / b should be equal to a, for non zero values of b. + if !fpB.Equals(FixedPointFromUint64[N](0, scale)) { + divided := result.Div(fpB) + require.True( + t, fpA.Equals(divided), "precision loss in "+ + "multiplication: (%v * %v) / %v = %v, "+ + "expected approx %v", + fpA, fpB, fpB, divided, fpA) + } +} + +func testFixedPointDivision[N Int[N]](t *rapid.T) { + // Generate a random a, b, and scale. We make sure b is not 0 to avoid + // division by zero. + a := rapid.Uint64().Draw(t, "a") + b := rapid.Uint64Range(1, math.MaxUint32).Draw(t, "b") + scale := uint8(rapid.IntRange(1, 9).Draw(t, "scale")) + + fpA := FixedPointFromUint64[N](a, scale) + fpB := FixedPointFromUint64[N](b, scale) + result := fpA.Div(fpB) + + // If a and b are the same, then the result should be 1. + if a == b { + require.True( + t, + result.Equals(FixedPointFromUint64[N](1, scale)), + "division of equal values is not 1: %v / %v = %v", fpA, + fpB, result, + ) + } + + // TODO(roasbeef): property against (a * b) / b = a + // * need programmatic check for allowed precision loss based on scale + // * require.InDelta + + // Property: a / 1 should equal a + one := FixedPointFromUint64[N](1, scale) + require.True( + t, fpA.Equals(fpA.Div(one)), "division by 1 changed "+ + "value: %v / 1 = %v", fpA, fpA.Div(one), + ) +} + +func testEquality[N Int[N]](t *rapid.T) { + coefficient := rapid.Uint64().Draw(t, "coefficient") + scale := uint8(rapid.IntRange(0, 18).Draw(t, "scale")) + + fp1 := FixedPointFromUint64[N](coefficient, scale) + fp2 := FixedPointFromUint64[N](coefficient, scale) + + // Two FixedPoints with the same coefficient and scale should be equal. + require.True( + t, fp1.Equals(fp2), "equal FixedPoints not considered equal: "+ + "%v and %v", fp1, fp2, + ) + + // Two FixedPoints with different values should not be equal. + fp3 := FixedPointFromUint64[N](coefficient+1, scale) + require.False( + t, fp1.Equals(fp3), "different FixedPoints considered equal: "+ + "%v and %v", fp1, fp3, + ) + + // Two FixedPoints with different scales should not be equal. + fp4 := FixedPointFromUint64[N](coefficient, scale+1) + require.False( + t, fp1.Equals(fp4), "different FixedPoints considered equal: "+ + "%v and %v", fp1, fp4, + ) +} + +func testFromUint64[N Int[N]](t *rapid.T) { + coefficient := rapid.Uint64().Draw(t, "coefficient") + scale := uint8(rapid.IntRange(0, 18).Draw(t, "scale")) + + fp := FixedPointFromUint64[N](coefficient, scale) + + // The created FixedPoint should have the correct scale. + require.Equal(t, scale, fp.Scale) + + // Scaling back to 0 should give the original coefficient. + scaledBack := fp.ScaleTo(0) + require.Equal(t, coefficient, scaledBack.Coefficient.ToUint64()) +} + +// TestFixedPoint runs a series of property-based tests on the FixedPoint type +// exercising key invariant properties. +func TestFixedPoint(t *testing.T) { + t.Parallel() + + t.Run("scale_to", rapid.MakeCheck(testScaleTo[BigInt])) + + t.Run("multiplication", func(t *testing.T) { + rapid.Check(t, testFixedPointMultiplication[BigInt]) + }) + + t.Run("division", rapid.MakeCheck(testFixedPointDivision[BigInt])) + + t.Run("equality", rapid.MakeCheck(testEquality[BigInt])) + + t.Run("from_uint64", rapid.MakeCheck(testFromUint64[BigInt])) +} diff --git a/rfqmath/testdata/rapid/TestAddition_big_int/TestAddition_big_int-20240820164933-66012.fail b/rfqmath/testdata/rapid/TestAddition_big_int/TestAddition_big_int-20240820164933-66012.fail new file mode 100644 index 000000000..a4e1b8365 --- /dev/null +++ b/rfqmath/testdata/rapid/TestAddition_big_int/TestAddition_big_int-20240820164933-66012.fail @@ -0,0 +1,4 @@ +# +v0.4.8#11122783583065558934 +0x10000000000000 +0x0 \ No newline at end of file diff --git a/rfqmath/testdata/rapid/TestAddition_big_int/TestAddition_big_int-20240820170908-69698.fail b/rfqmath/testdata/rapid/TestAddition_big_int/TestAddition_big_int-20240820170908-69698.fail new file mode 100644 index 000000000..414f32709 --- /dev/null +++ b/rfqmath/testdata/rapid/TestAddition_big_int/TestAddition_big_int-20240820170908-69698.fail @@ -0,0 +1,12 @@ +# 2024/08/20 17:09:08.916662 [TestAddition/big_int] [rapid] draw a: rfq.BigInt{value:(*big.Int)(0x14000590fc0)} +# 2024/08/20 17:09:08.916668 [TestAddition/big_int] [rapid] draw b: rfq.BigInt{value:(*big.Int)(0x14000591020)} +# 2024/08/20 17:09:08.916670 [TestAddition/big_int] [rapid] draw c: rfq.BigInt{value:(*big.Int)(0x14000591100)} +# 2024/08/20 17:09:08.916673 [TestAddition/big_int] increment: a + 1 = {0x14000591200}, expected {0x14000590fc0} +# +v0.4.8#8843793317408812300 +0x1fcb25faecfd25 +0xffffffffffffffff +0x1360cf102c1ff4 +0xfd21 +0x102a03ee9e647e +0xf39 \ No newline at end of file diff --git a/rfqmath/testdata/rapid/TestAddition_go_uint_64/TestAddition_go_uint_64-20240820164933-66012.fail b/rfqmath/testdata/rapid/TestAddition_go_uint_64/TestAddition_go_uint_64-20240820164933-66012.fail new file mode 100644 index 000000000..733caa299 --- /dev/null +++ b/rfqmath/testdata/rapid/TestAddition_go_uint_64/TestAddition_go_uint_64-20240820164933-66012.fail @@ -0,0 +1,4 @@ +# +v0.4.8#3692229848789265198 +0x0 +0x0 \ No newline at end of file diff --git a/rfqmath/testdata/rapid/TestArithmeticDivision_big_int/TestArithmeticDivision_big_int-20240820172818-75296.fail b/rfqmath/testdata/rapid/TestArithmeticDivision_big_int/TestArithmeticDivision_big_int-20240820172818-75296.fail new file mode 100644 index 000000000..a5382fbe9 --- /dev/null +++ b/rfqmath/testdata/rapid/TestArithmeticDivision_big_int/TestArithmeticDivision_big_int-20240820172818-75296.fail @@ -0,0 +1,9 @@ +# 2024/08/20 17:28:18.801605 [TestArithmeticDivision/big_int] [rapid] draw a: rfq.BigInt{value:(*big.Int)(0x140004106e0)} +# 2024/08/20 17:28:18.801608 [TestArithmeticDivision/big_int] [rapid] draw b: rfq.BigInt{value:(*big.Int)(0x14000410780)} +# 2024/08/20 17:28:18.801610 [TestArithmeticDivision/big_int] average not between values: (2 + 1) / 2 = 1, expected to be between 2 and 1 +# +v0.4.8#5821315167184086184 +0x0 +0x1 +0x0 +0x0 \ No newline at end of file diff --git a/rfqmath/testdata/rapid/TestArithmeticDivision_go_uint_64/TestArithmeticDivision_go_uint_64-20240820172818-75296.fail b/rfqmath/testdata/rapid/TestArithmeticDivision_go_uint_64/TestArithmeticDivision_go_uint_64-20240820172818-75296.fail new file mode 100644 index 000000000..67ba1b59d --- /dev/null +++ b/rfqmath/testdata/rapid/TestArithmeticDivision_go_uint_64/TestArithmeticDivision_go_uint_64-20240820172818-75296.fail @@ -0,0 +1,9 @@ +# 2024/08/20 17:28:18.799517 [TestArithmeticDivision/go_uint_64] [rapid] draw a: rfq.GoInt[uint64]{value:0x2} +# 2024/08/20 17:28:18.799524 [TestArithmeticDivision/go_uint_64] [rapid] draw b: rfq.GoInt[uint64]{value:0x1} +# 2024/08/20 17:28:18.799525 [TestArithmeticDivision/go_uint_64] average not between values: ({2} + {1}) / 2 = {1}, expected to be between {2} and {1} +# +v0.4.8#4179823434397146909 +0x0 +0x1 +0x0 +0x0 \ No newline at end of file diff --git a/rfqmath/testdata/rapid/TestArithmeticDivision_go_uint_64/TestArithmeticDivision_go_uint_64-20240820172845-75480.fail b/rfqmath/testdata/rapid/TestArithmeticDivision_go_uint_64/TestArithmeticDivision_go_uint_64-20240820172845-75480.fail new file mode 100644 index 000000000..b02427b6d --- /dev/null +++ b/rfqmath/testdata/rapid/TestArithmeticDivision_go_uint_64/TestArithmeticDivision_go_uint_64-20240820172845-75480.fail @@ -0,0 +1,9 @@ +# 2024/08/20 17:28:45.600826 [TestArithmeticDivision/go_uint_64] [rapid] draw a: rfq.GoInt[uint64]{value:0x1d837c} +# 2024/08/20 17:28:45.600834 [TestArithmeticDivision/go_uint_64] [rapid] draw b: rfq.GoInt[uint64]{value:0x8ac88e8c738} +# 2024/08/20 17:28:45.600837 [TestArithmeticDivision/go_uint_64] (a * b) / b: ({1934204} * {9537124353848}) / {9537124353848} = {0}, expected {1934204} +# +v0.4.8#16398140998316487451 +0x167b3fb87fd147 +0x1d837b +0x1da3b5b2f4b95c +0x8ac88e8c737 \ No newline at end of file diff --git a/rfqmath/testdata/rapid/TestArithmeticDivision_go_uint_64/TestArithmeticDivision_go_uint_64-20240820173239-76566.fail b/rfqmath/testdata/rapid/TestArithmeticDivision_go_uint_64/TestArithmeticDivision_go_uint_64-20240820173239-76566.fail new file mode 100644 index 000000000..2a9be4c30 --- /dev/null +++ b/rfqmath/testdata/rapid/TestArithmeticDivision_go_uint_64/TestArithmeticDivision_go_uint_64-20240820173239-76566.fail @@ -0,0 +1,9 @@ +# 2024/08/20 17:32:39.628206 [TestArithmeticDivision/go_uint_64] [rapid] draw a: rfq.GoInt[uint64]{value:0x2} +# 2024/08/20 17:32:39.628214 [TestArithmeticDivision/go_uint_64] [rapid] draw b: rfq.GoInt[uint64]{value:0xffffffffffffffff} +# 2024/08/20 17:32:39.628215 [TestArithmeticDivision/go_uint_64] (a * b) / b: ({2} * {18446744073709551615}) / {18446744073709551615} = {0}, expected {2} +# +v0.4.8#17898137883115097820 +0x0 +0x1 +0x1f4c4038d2fde5 +0x0 \ No newline at end of file diff --git a/rfqmath/testdata/rapid/TestArithmeticDivision_go_uint_64/TestArithmeticDivision_go_uint_64-20240820173647-77930.fail b/rfqmath/testdata/rapid/TestArithmeticDivision_go_uint_64/TestArithmeticDivision_go_uint_64-20240820173647-77930.fail new file mode 100644 index 000000000..0dcb55bee --- /dev/null +++ b/rfqmath/testdata/rapid/TestArithmeticDivision_go_uint_64/TestArithmeticDivision_go_uint_64-20240820173647-77930.fail @@ -0,0 +1,9 @@ +# 2024/08/20 17:36:47.125887 [TestArithmeticDivision/go_uint_64] [rapid] draw a: rfq.GoInt[uint64]{value:0xd5d8fd} +# 2024/08/20 17:36:47.125894 [TestArithmeticDivision/go_uint_64] [rapid] draw b: rfq.GoInt[uint64]{value:0x1327612a2f7} +# 2024/08/20 17:36:47.125900 [TestArithmeticDivision/go_uint_64] (a * b) / b: ({14014717} * {1316240925431}) / {1316240925431} = {0}, expected {14014717} +# +v0.4.8#1574078808614341535 +0x181076fcb778c1 +0xd5d8fc +0x1d2b2d20f6a995 +0x1327612a2f6 \ No newline at end of file diff --git a/rfqmath/testdata/rapid/TestConversionMsat_msat_to_units/TestConversionMsat_msat_to_units-20240820192921-7922.fail b/rfqmath/testdata/rapid/TestConversionMsat_msat_to_units/TestConversionMsat_msat_to_units-20240820192921-7922.fail new file mode 100644 index 000000000..c64876500 --- /dev/null +++ b/rfqmath/testdata/rapid/TestConversionMsat_msat_to_units/TestConversionMsat_msat_to_units-20240820192921-7922.fail @@ -0,0 +1,20 @@ +# 2024/08/20 19:29:21.826824 [TestConversionMsat/msat_to_units] [rapid] draw unitsPerBtc: 0x5c8038 +# 2024/08/20 19:29:21.826828 [TestConversionMsat/msat_to_units] [rapid] draw scale: 3 +# 2024/08/20 19:29:21.826830 [TestConversionMsat/msat_to_units] [rapid] draw msat: 0x4954713993f4 +# 2024/08/20 19:29:21.826866 [TestConversionMsat/msat_to_units] +# Error Trace: /Users/roasbeef/gocode/src/github.com/lightninglabs/taproot-assets/rfq/convert_test.go:582 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:368 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:377 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:203 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:138 +# Error: Max difference between 4.887719949437e+09 and 4.887719949436e+09 allowed is 0.001, but difference was 0.0010004043579101562 +# Test: TestConversionMsat/msat_to_units +# +v0.4.8#10172577110180847590 +0x17917e6c82f04d +0x5c8037 +0x0 +0x38e38e38e38e4 +0x3 +0x1e083305a49553 +0x4954713993f4 \ No newline at end of file diff --git a/rfqmath/testdata/rapid/TestConversionMsat_msat_to_units/TestConversionMsat_msat_to_units-20240820193036-8326.fail b/rfqmath/testdata/rapid/TestConversionMsat_msat_to_units/TestConversionMsat_msat_to_units-20240820193036-8326.fail new file mode 100644 index 000000000..9e3d0f6a7 --- /dev/null +++ b/rfqmath/testdata/rapid/TestConversionMsat_msat_to_units/TestConversionMsat_msat_to_units-20240820193036-8326.fail @@ -0,0 +1,20 @@ +# 2024/08/20 19:30:36.699355 [TestConversionMsat/msat_to_units] [rapid] draw unitsPerBtc: 0xe00027600000 +# 2024/08/20 19:30:36.699359 [TestConversionMsat/msat_to_units] [rapid] draw scale: 1 +# 2024/08/20 19:30:36.699361 [TestConversionMsat/msat_to_units] [rapid] draw msat: 0x30000118405 +# 2024/08/20 19:30:36.699814 [TestConversionMsat/msat_to_units] +# Error Trace: /Users/roasbeef/gocode/src/github.com/lightninglabs/taproot-assets/rfq/convert_test.go:582 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:368 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:377 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:203 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:138 +# Error: Max difference between 8.124006125226344e+15 and 8.124006125226346e+15 allowed is 1, but difference was -2 +# Test: TestConversionMsat/msat_to_units +# +v0.4.8#5100515850280364727 +0x1e25d5aaf5413f +0xe000275fffff +0x0 +0x0 +0x1 +0x1d55d01f064540 +0x30000118405 \ No newline at end of file diff --git a/rfqmath/testdata/rapid/TestConversionMsat_msat_to_units/TestConversionMsat_msat_to_units-20240820193141-8834.fail b/rfqmath/testdata/rapid/TestConversionMsat_msat_to_units/TestConversionMsat_msat_to_units-20240820193141-8834.fail new file mode 100644 index 000000000..a11b221f0 --- /dev/null +++ b/rfqmath/testdata/rapid/TestConversionMsat_msat_to_units/TestConversionMsat_msat_to_units-20240820193141-8834.fail @@ -0,0 +1,20 @@ +# 2024/08/20 19:31:41.534378 [TestConversionMsat/msat_to_units] [rapid] draw unitsPerBtc: 0x1 +# 2024/08/20 19:31:41.534382 [TestConversionMsat/msat_to_units] [rapid] draw scale: 0 +# 2024/08/20 19:31:41.534383 [TestConversionMsat/msat_to_units] [rapid] draw msat: 0x2540be401 +# 2024/08/20 19:31:41.534541 [TestConversionMsat/msat_to_units] +# Error Trace: /Users/roasbeef/gocode/src/github.com/lightninglabs/taproot-assets/rfq/convert_test.go:582 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:368 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:377 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:203 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:138 +# Error: Max difference between 0.10000000001 and 0 allowed is 0.1, but difference was 0.10000000001 +# Test: TestConversionMsat/msat_to_units +# +v0.4.8#13359388164245503914 +0x0 +0x0 +0x0 +0x0 +0x0 +0x1bac0321a696f7 +0x2540be401 \ No newline at end of file diff --git a/rfqmath/testdata/rapid/TestConversionMsat_msat_to_units/TestConversionMsat_msat_to_units-20240820193235-9334.fail b/rfqmath/testdata/rapid/TestConversionMsat_msat_to_units/TestConversionMsat_msat_to_units-20240820193235-9334.fail new file mode 100644 index 000000000..839d63182 --- /dev/null +++ b/rfqmath/testdata/rapid/TestConversionMsat_msat_to_units/TestConversionMsat_msat_to_units-20240820193235-9334.fail @@ -0,0 +1,20 @@ +# 2024/08/20 19:32:35.136247 [TestConversionMsat/msat_to_units] [rapid] draw unitsPerBtc: 0x1 +# 2024/08/20 19:32:35.136251 [TestConversionMsat/msat_to_units] [rapid] draw scale: 9 +# 2024/08/20 19:32:35.136252 [TestConversionMsat/msat_to_units] [rapid] draw msat: 0x100000986bff9e9 +# 2024/08/20 19:32:35.136279 [TestConversionMsat/msat_to_units] +# Error Trace: /Users/roasbeef/gocode/src/github.com/lightninglabs/taproot-assets/rfq/convert_test.go:582 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:368 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:377 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:203 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:138 +# Error: Max difference between 720576.349533619 and 720576.349533618 allowed is 1e-09, but difference was 1.0477378964424133e-09 +# Test: TestConversionMsat/msat_to_units +# +v0.4.8#7103084821872933262 +0x0 +0x0 +0x0 +0x1f2b5a0e61c3a6 +0x0 +0x1eed3adebb7e56 +0x100000986bff9e9 \ No newline at end of file diff --git a/rfqmath/testdata/rapid/TestConversionMsat_msat_to_units/TestConversionMsat_msat_to_units-20240820193356-9930.fail b/rfqmath/testdata/rapid/TestConversionMsat_msat_to_units/TestConversionMsat_msat_to_units-20240820193356-9930.fail new file mode 100644 index 000000000..b3d5ead06 --- /dev/null +++ b/rfqmath/testdata/rapid/TestConversionMsat_msat_to_units/TestConversionMsat_msat_to_units-20240820193356-9930.fail @@ -0,0 +1,20 @@ +# 2024/08/20 19:33:56.071466 [TestConversionMsat/msat_to_units] [rapid] draw unitsPerBtc: 0xf4240 +# 2024/08/20 19:33:56.071470 [TestConversionMsat/msat_to_units] [rapid] draw scale: 2 +# 2024/08/20 19:33:56.071471 [TestConversionMsat/msat_to_units] [rapid] draw msat: 0x2000001416facf0 +# 2024/08/20 19:33:56.071499 [TestConversionMsat/msat_to_units] +# Error Trace: /Users/roasbeef/gocode/src/github.com/lightninglabs/taproot-assets/rfq/convert_test.go:582 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:368 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:377 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:203 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:138 +# Error: Max difference between 1.44115193468661e+12 and 1.4411519346866e+12 allowed is 0.01, but difference was 0.010009765625 +# Test: TestConversionMsat/msat_to_units +# +v0.4.8#17681317893978739450 +0x1ef68112abdf0d +0x0 +0x0 +0x0 +0x0 +0x1efd6495652b9d +0x2000001416facf0 \ No newline at end of file diff --git a/rfqmath/testdata/rapid/TestConversionMsat_msat_to_units/TestConversionMsat_msat_to_units-20240820193746-11254.fail b/rfqmath/testdata/rapid/TestConversionMsat_msat_to_units/TestConversionMsat_msat_to_units-20240820193746-11254.fail new file mode 100644 index 000000000..ff80a3126 --- /dev/null +++ b/rfqmath/testdata/rapid/TestConversionMsat_msat_to_units/TestConversionMsat_msat_to_units-20240820193746-11254.fail @@ -0,0 +1,20 @@ +# 2024/08/20 19:37:46.787847 [TestConversionMsat/msat_to_units] [rapid] draw unitsPerBtc: 0x1000008 +# 2024/08/20 19:37:46.787850 [TestConversionMsat/msat_to_units] [rapid] draw scale: 2 +# 2024/08/20 19:37:46.787851 [TestConversionMsat/msat_to_units] [rapid] draw msat: 0xbb01341ee1f7fc +# 2024/08/20 19:37:46.787875 [TestConversionMsat/msat_to_units] +# Error Trace: /Users/roasbeef/gocode/src/github.com/lightninglabs/taproot-assets/rfq/convert_test.go:582 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:368 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:377 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:203 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:138 +# Error: Max difference between 8.831051558254441e+12 and 8.83105155825443e+12 allowed is 0.011, but difference was 0.01171875 +# Test: TestConversionMsat/msat_to_units +# +v0.4.8#5447221931303694455 +0x1887f7847071a7 +0x1000007 +0x0 +0x0 +0x0 +0x1edc0e8ca7363c +0xbb01341ee1f7fc \ No newline at end of file diff --git a/rfqmath/testdata/rapid/TestConversionMsat_msat_to_units/TestConversionMsat_msat_to_units-20240820193828-11692.fail b/rfqmath/testdata/rapid/TestConversionMsat_msat_to_units/TestConversionMsat_msat_to_units-20240820193828-11692.fail new file mode 100644 index 000000000..0c7dc7015 --- /dev/null +++ b/rfqmath/testdata/rapid/TestConversionMsat_msat_to_units/TestConversionMsat_msat_to_units-20240820193828-11692.fail @@ -0,0 +1,20 @@ +# 2024/08/20 19:38:28.394587 [TestConversionMsat/msat_to_units] [rapid] draw unitsPerBtc: 0x5d216 +# 2024/08/20 19:38:28.394593 [TestConversionMsat/msat_to_units] [rapid] draw scale: 5 +# 2024/08/20 19:38:28.394596 [TestConversionMsat/msat_to_units] [rapid] draw msat: 0xffffffffffffffff +# 2024/08/20 19:38:28.394638 [TestConversionMsat/msat_to_units] +# Error Trace: /Users/roasbeef/gocode/src/github.com/lightninglabs/taproot-assets/rfq/convert_test.go:582 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:368 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:377 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:203 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:138 +# Error: Max difference between 7.036731887845392e+13 and 7.036731887845394e+13 allowed is 0.012, but difference was -0.015625 +# Test: TestConversionMsat/msat_to_units +# +v0.4.8#15100101661602011294 +0x154122ef484b41 +0x5d215 +0x0 +0x38e38e38e38e4 +0x3 +0x1f4c4038d2fde5 +0x0 \ No newline at end of file diff --git a/rfqmath/testdata/rapid/TestConversionMsat_msat_to_units/TestConversionMsat_msat_to_units-20240820194002-12447.fail b/rfqmath/testdata/rapid/TestConversionMsat_msat_to_units/TestConversionMsat_msat_to_units-20240820194002-12447.fail new file mode 100644 index 000000000..927395367 --- /dev/null +++ b/rfqmath/testdata/rapid/TestConversionMsat_msat_to_units/TestConversionMsat_msat_to_units-20240820194002-12447.fail @@ -0,0 +1,20 @@ +# 2024/08/20 19:40:02.856056 [TestConversionMsat/msat_to_units] [rapid] draw unitsPerBtc: 0x1 +# 2024/08/20 19:40:02.856059 [TestConversionMsat/msat_to_units] [rapid] draw scale: 2 +# 2024/08/20 19:40:02.856061 [TestConversionMsat/msat_to_units] [rapid] draw msat: 0x0 +# 2024/08/20 19:40:02.856085 [TestConversionMsat/msat_to_units] +# Error Trace: /Users/roasbeef/gocode/src/github.com/lightninglabs/taproot-assets/rfq/convert_test.go:582 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:368 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:377 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:203 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:138 +# Error: expected value must have a value other than zero to calculate the relative error +# Test: TestConversionMsat/msat_to_units +# +v0.4.8#5012443549421810556 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 \ No newline at end of file diff --git a/rfqmath/testdata/rapid/TestConversionMsat_msat_to_units/TestConversionMsat_msat_to_units-20240820194225-13596.fail b/rfqmath/testdata/rapid/TestConversionMsat_msat_to_units/TestConversionMsat_msat_to_units-20240820194225-13596.fail new file mode 100644 index 000000000..d062bb801 --- /dev/null +++ b/rfqmath/testdata/rapid/TestConversionMsat_msat_to_units/TestConversionMsat_msat_to_units-20240820194225-13596.fail @@ -0,0 +1,21 @@ +# 2024/08/20 19:42:25.968272 [TestConversionMsat/msat_to_units] [rapid] draw unitsPerBtc: 0x1 +# 2024/08/20 19:42:25.968276 [TestConversionMsat/msat_to_units] [rapid] draw scale: 3 +# 2024/08/20 19:42:25.968277 [TestConversionMsat/msat_to_units] [rapid] draw msat: 0x1 +# 2024/08/20 19:42:25.968307 [TestConversionMsat/msat_to_units] +# Error Trace: /Users/roasbeef/gocode/src/github.com/lightninglabs/taproot-assets/rfq/convert_test.go:584 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:368 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:377 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:203 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:138 +# Error: Relative error is too high: 0.1 (expected) +# < 1 (actual) +# Test: TestConversionMsat/msat_to_units +# +v0.4.8#1404303672777295698 +0x0 +0x0 +0x0 +0x0 +0x1 +0x0 +0x0 \ No newline at end of file diff --git a/rfqmath/testdata/rapid/TestConversionMsat_roundtrip_conversion/TestConversionMsat_roundtrip_conversion-20240820200712-26765.fail b/rfqmath/testdata/rapid/TestConversionMsat_roundtrip_conversion/TestConversionMsat_roundtrip_conversion-20240820200712-26765.fail new file mode 100644 index 000000000..d7160c3f1 --- /dev/null +++ b/rfqmath/testdata/rapid/TestConversionMsat_roundtrip_conversion/TestConversionMsat_roundtrip_conversion-20240820200712-26765.fail @@ -0,0 +1,22 @@ +# 2024/08/20 20:07:12.567913 [TestConversionMsat/roundtrip_conversion] [rapid] draw unitsPerBtc: 0x3e8 +# 2024/08/20 20:07:12.567918 [TestConversionMsat/roundtrip_conversion] [rapid] draw scale: 2 +# 2024/08/20 20:07:12.567919 [TestConversionMsat/roundtrip_conversion] [rapid] draw msat: 0x1 +# 2024/08/20 20:07:12.567948 [TestConversionMsat/roundtrip_conversion] +# Error Trace: /Users/roasbeef/gocode/src/github.com/lightninglabs/taproot-assets/rfq/convert_test.go:624 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:368 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:377 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:203 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:138 +# Error: Not equal: +# expected: 0x1 +# actual : 0x0 +# Test: TestConversionMsat/roundtrip_conversion +# +v0.4.8#2761056324836857054 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 \ No newline at end of file diff --git a/rfqmath/testdata/rapid/TestConversionMsat_roundtrip_conversion/TestConversionMsat_roundtrip_conversion-20240820200825-27273.fail b/rfqmath/testdata/rapid/TestConversionMsat_roundtrip_conversion/TestConversionMsat_roundtrip_conversion-20240820200825-27273.fail new file mode 100644 index 000000000..3bd416909 --- /dev/null +++ b/rfqmath/testdata/rapid/TestConversionMsat_roundtrip_conversion/TestConversionMsat_roundtrip_conversion-20240820200825-27273.fail @@ -0,0 +1,22 @@ +# 2024/08/20 20:08:25.616801 [TestConversionMsat/roundtrip_conversion] [rapid] draw unitsPerBtc: 0x3e9 +# 2024/08/20 20:08:25.616805 [TestConversionMsat/roundtrip_conversion] [rapid] draw scale: 8 +# 2024/08/20 20:08:25.616806 [TestConversionMsat/roundtrip_conversion] [rapid] draw msat: 0x1 +# 2024/08/20 20:08:25.616835 [TestConversionMsat/roundtrip_conversion] +# Error Trace: /Users/roasbeef/gocode/src/github.com/lightninglabs/taproot-assets/rfq/convert_test.go:624 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:368 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:377 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:203 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:138 +# Error: Not equal: +# expected: 0x1 +# actual : 0x0 +# Test: TestConversionMsat/roundtrip_conversion +# +v0.4.8#3682528489092645867 +0x0 +0x1 +0x0 +0x0 +0x0 +0x0 +0x0 \ No newline at end of file diff --git a/rfqmath/testdata/rapid/TestConversionMsat_roundtrip_conversion/TestConversionMsat_roundtrip_conversion-20240913124134-254478.fail b/rfqmath/testdata/rapid/TestConversionMsat_roundtrip_conversion/TestConversionMsat_roundtrip_conversion-20240913124134-254478.fail new file mode 100644 index 000000000..27552d21b --- /dev/null +++ b/rfqmath/testdata/rapid/TestConversionMsat_roundtrip_conversion/TestConversionMsat_roundtrip_conversion-20240913124134-254478.fail @@ -0,0 +1,20 @@ +# 2024/09/13 12:41:34.065837 [TestConversionMsat/roundtrip_conversion] [rapid] draw unitsPerBtc: 0x3e8 +# 2024/09/13 12:41:34.065842 [TestConversionMsat/roundtrip_conversion] [rapid] draw scale: 9 +# 2024/09/13 12:41:34.065843 [TestConversionMsat/roundtrip_conversion] [rapid] draw msat: 0x2 +# 2024/09/13 12:41:34.065885 [TestConversionMsat/roundtrip_conversion] +# Error Trace: /home/guggero/projects/go/src/github.com/lightninglabs/taproot-assets/rfqmath/convert_test.go:633 +# /home/guggero/projects/go/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:368 +# /home/guggero/projects/go/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:377 +# /home/guggero/projects/go/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:203 +# /home/guggero/projects/go/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:138 +# Error: Max difference between 2 and 0 allowed is 1, but difference was 2 +# Test: TestConversionMsat/roundtrip_conversion +# +v0.4.8#51941873740363090 +0x0 +0x0 +0x0 +0x0 +0x0 +0x0 +0x1 \ No newline at end of file diff --git a/rfqmath/testdata/rapid/TestConversionMsat_units_to_msat/TestConversionMsat_units_to_msat-20240820195405-19290.fail b/rfqmath/testdata/rapid/TestConversionMsat_units_to_msat/TestConversionMsat_units_to_msat-20240820195405-19290.fail new file mode 100644 index 000000000..5d19cee52 --- /dev/null +++ b/rfqmath/testdata/rapid/TestConversionMsat_units_to_msat/TestConversionMsat_units_to_msat-20240820195405-19290.fail @@ -0,0 +1,20 @@ +# 2024/08/20 19:54:05.179175 [TestConversionMsat/units_to_msat] [rapid] draw units: 0x1 +# 2024/08/20 19:54:05.179178 [TestConversionMsat/units_to_msat] [rapid] draw unitsPerBtc: 0x3 +# 2024/08/20 19:54:05.179180 [TestConversionMsat/units_to_msat] [rapid] draw scale: 0 +# 2024/08/20 19:54:05.179206 [TestConversionMsat/units_to_msat] +# Error Trace: /Users/roasbeef/gocode/src/github.com/lightninglabs/taproot-assets/rfq/convert_test.go:600 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:368 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:377 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:203 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:138 +# Error: Max difference between 3.3333333333333332e+10 and 3.3333333333e+10 allowed is 0.012, but difference was 0.3333320617675781 +# Test: TestConversionMsat/units_to_msat +# +v0.4.8#8750460502005251008 +0x0 +0x1 +0x2bb512bb512bc +0x2 +0x0 +0x0 +0x0 \ No newline at end of file diff --git a/rfqmath/testdata/rapid/TestConversionMsat_units_to_msat/TestConversionMsat_units_to_msat-20240820195451-19659.fail b/rfqmath/testdata/rapid/TestConversionMsat_units_to_msat/TestConversionMsat_units_to_msat-20240820195451-19659.fail new file mode 100644 index 000000000..42965bf9f --- /dev/null +++ b/rfqmath/testdata/rapid/TestConversionMsat_units_to_msat/TestConversionMsat_units_to_msat-20240820195451-19659.fail @@ -0,0 +1,20 @@ +# 2024/08/20 19:54:51.137064 [TestConversionMsat/units_to_msat] [rapid] draw units: 0x80155 +# 2024/08/20 19:54:51.137069 [TestConversionMsat/units_to_msat] [rapid] draw unitsPerBtc: 0x3 +# 2024/08/20 19:54:51.137070 [TestConversionMsat/units_to_msat] [rapid] draw scale: 2 +# 2024/08/20 19:54:51.137098 [TestConversionMsat/units_to_msat] +# Error Trace: /Users/roasbeef/gocode/src/github.com/lightninglabs/taproot-assets/rfq/convert_test.go:600 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:368 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:377 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:203 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:138 +# Error: Max difference between 1.7487633333333334e+16 and 1.7487633333333332e+16 allowed is 1, but difference was 2 +# Test: TestConversionMsat/units_to_msat +# +v0.4.8#14775267594850882245 +0x15e2f3b407ce5b +0x80155 +0x2bb512bb512bc +0x2 +0x0 +0x0 +0x0 \ No newline at end of file diff --git a/rfqmath/testdata/rapid/TestConversionMsat_units_to_msat/TestConversionMsat_units_to_msat-20240820195640-20028.fail b/rfqmath/testdata/rapid/TestConversionMsat_units_to_msat/TestConversionMsat_units_to_msat-20240820195640-20028.fail new file mode 100644 index 000000000..187b7ae26 --- /dev/null +++ b/rfqmath/testdata/rapid/TestConversionMsat_units_to_msat/TestConversionMsat_units_to_msat-20240820195640-20028.fail @@ -0,0 +1,20 @@ +# 2024/08/20 19:56:40.109591 [TestConversionMsat/units_to_msat] [rapid] draw units: 0xafebff1 +# 2024/08/20 19:56:40.109595 [TestConversionMsat/units_to_msat] [rapid] draw unitsPerBtc: 0x1 +# 2024/08/20 19:56:40.109596 [TestConversionMsat/units_to_msat] [rapid] draw scale: 2 +# 2024/08/20 19:56:40.109630 [TestConversionMsat/units_to_msat] +# Error Trace: /Users/roasbeef/gocode/src/github.com/lightninglabs/taproot-assets/rfq/convert_test.go:600 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:368 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:377 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:203 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:138 +# Error: Max difference between 1.84467441e+19 and 2.6290448384e+10 allowed is 1, but difference was 1.844674407370955e+19 +# Test: TestConversionMsat/units_to_msat +# +v0.4.8#13149110153814018824 +0x1bf2bf33ad203a +0xafebff0 +0x0 +0x0 +0x0 +0x0 +0x0 \ No newline at end of file diff --git a/rfqmath/testdata/rapid/TestConversionMsat_units_to_msat/TestConversionMsat_units_to_msat-20240820195835-21008.fail b/rfqmath/testdata/rapid/TestConversionMsat_units_to_msat/TestConversionMsat_units_to_msat-20240820195835-21008.fail new file mode 100644 index 000000000..dde0f4269 --- /dev/null +++ b/rfqmath/testdata/rapid/TestConversionMsat_units_to_msat/TestConversionMsat_units_to_msat-20240820195835-21008.fail @@ -0,0 +1,20 @@ +# 2024/08/20 19:58:35.191108 [TestConversionMsat/units_to_msat] [rapid] draw units: 0x42200 +# 2024/08/20 19:58:35.191112 [TestConversionMsat/units_to_msat] [rapid] draw unitsPerBtc: 0x3 +# 2024/08/20 19:58:35.191113 [TestConversionMsat/units_to_msat] [rapid] draw scale: 2 +# 2024/08/20 19:58:35.191144 [TestConversionMsat/units_to_msat] +# Error Trace: /Users/roasbeef/gocode/src/github.com/lightninglabs/taproot-assets/rfq/convert_test.go:600 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:368 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:377 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:203 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:138 +# Error: Max difference between 9.028266666666668e+15 and 9.028266666666666e+15 allowed is 1, but difference was 2 +# Test: TestConversionMsat/units_to_msat +# +v0.4.8#6802039471366465389 +0x184cb6b1e3fbc4 +0x421ff +0x2bb512bb512bc +0x2 +0x0 +0x0 +0x0 \ No newline at end of file diff --git a/rfqmath/testdata/rapid/TestConversionMsat_units_to_msat/TestConversionMsat_units_to_msat-20240820195942-21560.fail b/rfqmath/testdata/rapid/TestConversionMsat_units_to_msat/TestConversionMsat_units_to_msat-20240820195942-21560.fail new file mode 100644 index 000000000..6a5acefb1 --- /dev/null +++ b/rfqmath/testdata/rapid/TestConversionMsat_units_to_msat/TestConversionMsat_units_to_msat-20240820195942-21560.fail @@ -0,0 +1,20 @@ +# 2024/08/20 19:59:42.177148 [TestConversionMsat/units_to_msat] [rapid] draw units: 0x84040 +# 2024/08/20 19:59:42.177151 [TestConversionMsat/units_to_msat] [rapid] draw unitsPerBtc: 0x3 +# 2024/08/20 19:59:42.177152 [TestConversionMsat/units_to_msat] [rapid] draw scale: 2 +# 2024/08/20 19:59:42.177172 [TestConversionMsat/units_to_msat] +# Error Trace: /Users/roasbeef/gocode/src/github.com/lightninglabs/taproot-assets/rfq/convert_test.go:600 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:368 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:377 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:203 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:138 +# Error: Max difference between 1.8024533333333336e+16 and 1.8024533333333332e+16 allowed is 2, but difference was 4 +# Test: TestConversionMsat/units_to_msat +# +v0.4.8#12024211361469033735 +0x18e2b3ecb40738 +0x8403f +0x2bb512bb512bc +0x2 +0x0 +0x0 +0x0 \ No newline at end of file diff --git a/rfqmath/testdata/rapid/TestConversionMsat_units_to_msat/TestConversionMsat_units_to_msat-20240820200033-22032.fail b/rfqmath/testdata/rapid/TestConversionMsat_units_to_msat/TestConversionMsat_units_to_msat-20240820200033-22032.fail new file mode 100644 index 000000000..bf5c0651d --- /dev/null +++ b/rfqmath/testdata/rapid/TestConversionMsat_units_to_msat/TestConversionMsat_units_to_msat-20240820200033-22032.fail @@ -0,0 +1,20 @@ +# 2024/08/20 20:00:33.807631 [TestConversionMsat/units_to_msat] [rapid] draw units: 0x400000 +# 2024/08/20 20:00:33.807634 [TestConversionMsat/units_to_msat] [rapid] draw unitsPerBtc: 0xb +# 2024/08/20 20:00:33.807635 [TestConversionMsat/units_to_msat] [rapid] draw scale: 2 +# 2024/08/20 20:00:33.807664 [TestConversionMsat/units_to_msat] +# Error Trace: /Users/roasbeef/gocode/src/github.com/lightninglabs/taproot-assets/rfq/convert_test.go:600 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:368 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:377 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:203 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:138 +# Error: Max difference between 38130036363636368 and 38130036363636363 allowed is 4, but difference was 8 +# Test: TestConversionMsat/units_to_msat +# +v0.4.8#6951556080559585419 +0x19ed5016d8f2ed +0x3fffff +0x783f437ca4dff +0xa +0x0 +0x0 +0x0 \ No newline at end of file diff --git a/rfqmath/testdata/rapid/TestConversionMsat_units_to_msat/TestConversionMsat_units_to_msat-20240820200108-22231.fail b/rfqmath/testdata/rapid/TestConversionMsat_units_to_msat/TestConversionMsat_units_to_msat-20240820200108-22231.fail new file mode 100644 index 000000000..f279904cd --- /dev/null +++ b/rfqmath/testdata/rapid/TestConversionMsat_units_to_msat/TestConversionMsat_units_to_msat-20240820200108-22231.fail @@ -0,0 +1,20 @@ +# 2024/08/20 20:01:08.580518 [TestConversionMsat/units_to_msat] [rapid] draw units: 0x41f881 +# 2024/08/20 20:01:08.580521 [TestConversionMsat/units_to_msat] [rapid] draw unitsPerBtc: 0x3 +# 2024/08/20 20:01:08.580523 [TestConversionMsat/units_to_msat] [rapid] draw scale: 2 +# 2024/08/20 20:01:08.580553 [TestConversionMsat/units_to_msat] +# Error Trace: /Users/roasbeef/gocode/src/github.com/lightninglabs/taproot-assets/rfq/convert_test.go:600 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:368 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:377 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:203 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:138 +# Error: Max difference between 144115233333333312 and 144115233333333333 allowed is 16, but difference was -32 +# Test: TestConversionMsat/units_to_msat +# +v0.4.8#1498180290190086233 +0x1a639ab0ef6602 +0x41f880 +0x2bb512bb512bc +0x2 +0x0 +0x0 +0x0 \ No newline at end of file diff --git a/rfqmath/testdata/rapid/TestConversionMsat_units_to_msat/TestConversionMsat_units_to_msat-20240820200151-22503.fail b/rfqmath/testdata/rapid/TestConversionMsat_units_to_msat/TestConversionMsat_units_to_msat-20240820200151-22503.fail new file mode 100644 index 000000000..354121bc9 --- /dev/null +++ b/rfqmath/testdata/rapid/TestConversionMsat_units_to_msat/TestConversionMsat_units_to_msat-20240820200151-22503.fail @@ -0,0 +1,20 @@ +# 2024/08/20 20:01:51.157848 [TestConversionMsat/units_to_msat] [rapid] draw units: 0x41f881 +# 2024/08/20 20:01:51.157851 [TestConversionMsat/units_to_msat] [rapid] draw unitsPerBtc: 0x3 +# 2024/08/20 20:01:51.157852 [TestConversionMsat/units_to_msat] [rapid] draw scale: 2 +# 2024/08/20 20:01:51.157874 [TestConversionMsat/units_to_msat] +# Error Trace: /Users/roasbeef/gocode/src/github.com/lightninglabs/taproot-assets/rfq/convert_test.go:600 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:368 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:377 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:203 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:138 +# Error: Max difference between 144115233333333312 and 144115233333333333 allowed is 16, but difference was -32 +# Test: TestConversionMsat/units_to_msat +# +v0.4.8#15231506120755130323 +0x1ab8d0d9f677d7 +0x41f880 +0x2bb512bb512bc +0x2 +0x0 +0x0 +0x0 \ No newline at end of file diff --git a/rfqmath/testdata/rapid/TestConversionMsat_units_to_msat/TestConversionMsat_units_to_msat-20240820200210-22739.fail b/rfqmath/testdata/rapid/TestConversionMsat_units_to_msat/TestConversionMsat_units_to_msat-20240820200210-22739.fail new file mode 100644 index 000000000..33516037d --- /dev/null +++ b/rfqmath/testdata/rapid/TestConversionMsat_units_to_msat/TestConversionMsat_units_to_msat-20240820200210-22739.fail @@ -0,0 +1,20 @@ +# 2024/08/20 20:02:10.767864 [TestConversionMsat/units_to_msat] [rapid] draw units: 0xc00002 +# 2024/08/20 20:02:10.767868 [TestConversionMsat/units_to_msat] [rapid] draw unitsPerBtc: 0x5 +# 2024/08/20 20:02:10.767868 [TestConversionMsat/units_to_msat] [rapid] draw scale: 2 +# 2024/08/20 20:02:10.767892 [TestConversionMsat/units_to_msat] +# Error Trace: /Users/roasbeef/gocode/src/github.com/lightninglabs/taproot-assets/rfq/convert_test.go:600 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:368 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:377 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:203 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:138 +# Error: Max difference between 251658279999999968 and 251658280000000000 allowed is 16, but difference was -32 +# Test: TestConversionMsat/units_to_msat +# +v0.4.8#719842723362755795 +0x1b912bb77a3349 +0xc00001 +0x53aefb476c954 +0x4 +0x0 +0x0 +0x0 \ No newline at end of file diff --git a/rfqmath/testdata/rapid/TestConversionMsat_units_to_msat/TestConversionMsat_units_to_msat-20240820200252-23305.fail b/rfqmath/testdata/rapid/TestConversionMsat_units_to_msat/TestConversionMsat_units_to_msat-20240820200252-23305.fail new file mode 100644 index 000000000..7d0c5d64a --- /dev/null +++ b/rfqmath/testdata/rapid/TestConversionMsat_units_to_msat/TestConversionMsat_units_to_msat-20240820200252-23305.fail @@ -0,0 +1,20 @@ +# 2024/08/20 20:02:52.411047 [TestConversionMsat/units_to_msat] [rapid] draw units: 0x4614f5d +# 2024/08/20 20:02:52.411051 [TestConversionMsat/units_to_msat] [rapid] draw unitsPerBtc: 0x3e9 +# 2024/08/20 20:02:52.411052 [TestConversionMsat/units_to_msat] [rapid] draw scale: 2 +# 2024/08/20 20:02:52.411077 [TestConversionMsat/units_to_msat] +# Error Trace: /Users/roasbeef/gocode/src/github.com/lightninglabs/taproot-assets/rfq/convert_test.go:600 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:368 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:377 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:203 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:138 +# Error: Max difference between 7341276023976025 and 7341276023976023 allowed is 1, but difference was 2 +# Test: TestConversionMsat/units_to_msat +# +v0.4.8#3064334297326474484 +0x1c9315bc7646d7 +0x4614f5c +0x0 +0x1 +0x0 +0x0 +0x0 \ No newline at end of file diff --git a/rfqmath/testdata/rapid/TestConversionMsat_units_to_msat/TestConversionMsat_units_to_msat-20240820200301-23438.fail b/rfqmath/testdata/rapid/TestConversionMsat_units_to_msat/TestConversionMsat_units_to_msat-20240820200301-23438.fail new file mode 100644 index 000000000..26d96ac62 --- /dev/null +++ b/rfqmath/testdata/rapid/TestConversionMsat_units_to_msat/TestConversionMsat_units_to_msat-20240820200301-23438.fail @@ -0,0 +1,20 @@ +# 2024/08/20 20:03:01.125907 [TestConversionMsat/units_to_msat] [rapid] draw units: 0x10000012 +# 2024/08/20 20:03:01.125911 [TestConversionMsat/units_to_msat] [rapid] draw unitsPerBtc: 0x3e9 +# 2024/08/20 20:03:01.125912 [TestConversionMsat/units_to_msat] [rapid] draw scale: 2 +# 2024/08/20 20:03:01.125938 [TestConversionMsat/units_to_msat] +# Error Trace: /Users/roasbeef/gocode/src/github.com/lightninglabs/taproot-assets/rfq/convert_test.go:600 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:368 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:377 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:203 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:138 +# Error: Max difference between 26816730669330672 and 26816730669330669 allowed is 2, but difference was 4 +# Test: TestConversionMsat/units_to_msat +# +v0.4.8#11773713363038861905 +0x1d1d921bcfc604 +0x10000011 +0x0 +0x1 +0x0 +0x0 +0x0 \ No newline at end of file diff --git a/rfqmath/testdata/rapid/TestConversionMsat_units_to_msat/TestConversionMsat_units_to_msat-20240820200331-23865.fail b/rfqmath/testdata/rapid/TestConversionMsat_units_to_msat/TestConversionMsat_units_to_msat-20240820200331-23865.fail new file mode 100644 index 000000000..40c37d851 --- /dev/null +++ b/rfqmath/testdata/rapid/TestConversionMsat_units_to_msat/TestConversionMsat_units_to_msat-20240820200331-23865.fail @@ -0,0 +1,20 @@ +# 2024/08/20 20:03:31.186104 [TestConversionMsat/units_to_msat] [rapid] draw units: 0x1580004c +# 2024/08/20 20:03:31.186112 [TestConversionMsat/units_to_msat] [rapid] draw unitsPerBtc: 0x3e9 +# 2024/08/20 20:03:31.186120 [TestConversionMsat/units_to_msat] [rapid] draw scale: 2 +# 2024/08/20 20:03:31.186174 [TestConversionMsat/units_to_msat] +# Error Trace: /Users/roasbeef/gocode/src/github.com/lightninglabs/taproot-assets/rfq/convert_test.go:604 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:368 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:377 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:203 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:138 +# Error: Max difference between 36034987012987016 and 36034987012987012 allowed is 4, but difference was 8 +# Test: TestConversionMsat/units_to_msat +# +v0.4.8#1698680413443471488 +0x1d1d921bcfc604 +0x1580004b +0x0 +0x1 +0x0 +0x0 +0x0 \ No newline at end of file diff --git a/rfqmath/testdata/rapid/TestConversionMsat_units_to_msat/TestConversionMsat_units_to_msat-20240820200353-24465.fail b/rfqmath/testdata/rapid/TestConversionMsat_units_to_msat/TestConversionMsat_units_to_msat-20240820200353-24465.fail new file mode 100644 index 000000000..29fe987a4 --- /dev/null +++ b/rfqmath/testdata/rapid/TestConversionMsat_units_to_msat/TestConversionMsat_units_to_msat-20240820200353-24465.fail @@ -0,0 +1,20 @@ +# 2024/08/20 20:03:53.597708 [TestConversionMsat/units_to_msat] [rapid] draw units: 0x30000001 +# 2024/08/20 20:03:53.597712 [TestConversionMsat/units_to_msat] [rapid] draw unitsPerBtc: 0x3e9 +# 2024/08/20 20:03:53.597715 [TestConversionMsat/units_to_msat] [rapid] draw scale: 2 +# 2024/08/20 20:03:53.597752 [TestConversionMsat/units_to_msat] +# Error Trace: /Users/roasbeef/gocode/src/github.com/lightninglabs/taproot-assets/rfq/convert_test.go:604 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:368 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:377 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:203 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:138 +# Error: Max difference between 80450186713286704 and 80450186713286713 allowed is 10, but difference was -16 +# Test: TestConversionMsat/units_to_msat +# +v0.4.8#3425578523902412339 +0x1d5a61ef5b4646 +0x30000000 +0x0 +0x1 +0x0 +0x0 +0x0 \ No newline at end of file diff --git a/rfqmath/testdata/rapid/TestDivision_go_uint_64/TestDivision_go_uint_64-20240820172016-73584.fail b/rfqmath/testdata/rapid/TestDivision_go_uint_64/TestDivision_go_uint_64-20240820172016-73584.fail new file mode 100644 index 000000000..58fc1716c --- /dev/null +++ b/rfqmath/testdata/rapid/TestDivision_go_uint_64/TestDivision_go_uint_64-20240820172016-73584.fail @@ -0,0 +1,9 @@ +# 2024/08/20 17:20:16.916448 [TestDivision/go_uint_64] [rapid] draw a: rfq.GoInt[uint64]{value:0x6e1ca9} +# 2024/08/20 17:20:16.916456 [TestDivision/go_uint_64] [rapid] draw b: rfq.GoInt[uint64]{value:0x2532d12fe10} +# 2024/08/20 17:20:16.916457 [TestDivision/go_uint_64] (a * b) / b: ({7216297} * {2556261760528}) / {2556261760528} = {0}, expected {7216297} +# +v0.4.8#2181140046571239304 +0x17917e6c82f04d +0x6e1ca8 +0x1d55d01f064540 +0x2532d12fe0f \ No newline at end of file diff --git a/rfqmath/testdata/rapid/TestFixedPoint_division/TestFixedPoint_division-20240820184638-95557.fail b/rfqmath/testdata/rapid/TestFixedPoint_division/TestFixedPoint_division-20240820184638-95557.fail new file mode 100644 index 000000000..2e33e9925 --- /dev/null +++ b/rfqmath/testdata/rapid/TestFixedPoint_division/TestFixedPoint_division-20240820184638-95557.fail @@ -0,0 +1,21 @@ +# 2024/08/20 18:46:38.429436 [TestFixedPoint/division] [rapid] draw a: 0x1 +# 2024/08/20 18:46:38.429440 [TestFixedPoint/division] [rapid] draw b: 0x2 +# 2024/08/20 18:46:38.429442 [TestFixedPoint/division] [rapid] draw scale: 0 +# 2024/08/20 18:46:38.429467 [TestFixedPoint/division] +# Error Trace: /Users/roasbeef/gocode/src/github.com/lightninglabs/taproot-assets/rfq/fixed_point_test.go:101 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:368 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:377 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:203 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:138 +# Error: Should be true +# Test: TestFixedPoint/division +# Messages: division and multiplication didn't return to original: 1 / 2 * 2 = 0 +# +v0.4.8#11577463865337840042 +0x0 +0x1 +0x0 +0x1 +0x0 +0x0 +0x0 \ No newline at end of file diff --git a/rfqmath/testdata/rapid/TestFixedPoint_division/TestFixedPoint_division-20240820185145-96965.fail b/rfqmath/testdata/rapid/TestFixedPoint_division/TestFixedPoint_division-20240820185145-96965.fail new file mode 100644 index 000000000..8e7a3772e --- /dev/null +++ b/rfqmath/testdata/rapid/TestFixedPoint_division/TestFixedPoint_division-20240820185145-96965.fail @@ -0,0 +1,21 @@ +# 2024/08/20 18:51:45.264116 [TestFixedPoint/division] [rapid] draw a: 0x1 +# 2024/08/20 18:51:45.264121 [TestFixedPoint/division] [rapid] draw b: 0x3 +# 2024/08/20 18:51:45.264124 [TestFixedPoint/division] [rapid] draw scale: 2 +# 2024/08/20 18:51:45.264169 [TestFixedPoint/division] +# Error Trace: /Users/roasbeef/gocode/src/github.com/lightninglabs/taproot-assets/rfq/fixed_point_test.go:122 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:368 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:377 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:203 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:138 +# Error: Should be true +# Test: TestFixedPoint/division +# Messages: division and multiplication didn't return to original: 1.00 / 3.00 * 3.00 = 0.99 +# +v0.4.8#16705493221449357884 +0x0 +0x1 +0x1e1e1e1e1e1e2 +0x2 +0x0 +0x0 +0x0 \ No newline at end of file diff --git a/rfqmath/testdata/rapid/TestFixedPoint_division/TestFixedPoint_division-20240820185548-97845.fail b/rfqmath/testdata/rapid/TestFixedPoint_division/TestFixedPoint_division-20240820185548-97845.fail new file mode 100644 index 000000000..ce91a2da5 --- /dev/null +++ b/rfqmath/testdata/rapid/TestFixedPoint_division/TestFixedPoint_division-20240820185548-97845.fail @@ -0,0 +1,21 @@ +# 2024/08/20 18:55:48.508732 [TestFixedPoint/division] [rapid] draw a: 0x1 +# 2024/08/20 18:55:48.508735 [TestFixedPoint/division] [rapid] draw b: 0x3 +# 2024/08/20 18:55:48.508736 [TestFixedPoint/division] [rapid] draw scale: 1 +# 2024/08/20 18:55:48.508766 [TestFixedPoint/division] +# Error Trace: /Users/roasbeef/gocode/src/github.com/lightninglabs/taproot-assets/rfq/fixed_point_test.go:122 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:368 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:377 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:203 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:138 +# Error: Should be true +# Test: TestFixedPoint/division +# Messages: division and multiplication didn't return to original: 1.0 / 3.0 * 3.0 = 0.9 +# +v0.4.8#3797222840912657358 +0x0 +0x1 +0x293205e293206 +0x2 +0x0 +0x0 +0x0 \ No newline at end of file diff --git a/rfqmath/testdata/rapid/TestFixedPoint_division/TestFixedPoint_division-20240820190729-1824.fail b/rfqmath/testdata/rapid/TestFixedPoint_division/TestFixedPoint_division-20240820190729-1824.fail new file mode 100644 index 000000000..ab3d3d19b --- /dev/null +++ b/rfqmath/testdata/rapid/TestFixedPoint_division/TestFixedPoint_division-20240820190729-1824.fail @@ -0,0 +1,20 @@ +# 2024/08/20 19:07:29.990414 [TestFixedPoint/division] [rapid] draw a: 0x1 +# 2024/08/20 19:07:29.990418 [TestFixedPoint/division] [rapid] draw b: 0x3 +# 2024/08/20 19:07:29.990419 [TestFixedPoint/division] [rapid] draw scale: 2 +# 2024/08/20 19:07:29.990456 [TestFixedPoint/division] +# Error Trace: /Users/roasbeef/gocode/src/github.com/lightninglabs/taproot-assets/rfq/fixed_point_test.go:122 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:368 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:377 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:203 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:138 +# Error: Max difference between 1 and 0.99 allowed is 0.01, but difference was 0.010000000000000009 +# Test: TestFixedPoint/division +# +v0.4.8#11033356856993482064 +0x0 +0x1 +0x293205e293206 +0x2 +0x0 +0x0 +0x1 \ No newline at end of file diff --git a/rfqmath/testdata/rapid/TestFixedPoint_division/TestFixedPoint_division-20240820190932-2693.fail b/rfqmath/testdata/rapid/TestFixedPoint_division/TestFixedPoint_division-20240820190932-2693.fail new file mode 100644 index 000000000..f2f7c5960 --- /dev/null +++ b/rfqmath/testdata/rapid/TestFixedPoint_division/TestFixedPoint_division-20240820190932-2693.fail @@ -0,0 +1,20 @@ +# 2024/08/20 19:09:32.582066 [TestFixedPoint/division] [rapid] draw a: 0x1 +# 2024/08/20 19:09:32.582069 [TestFixedPoint/division] [rapid] draw b: 0x11 +# 2024/08/20 19:09:32.582071 [TestFixedPoint/division] [rapid] draw scale: 2 +# 2024/08/20 19:09:32.582100 [TestFixedPoint/division] +# Error Trace: /Users/roasbeef/gocode/src/github.com/lightninglabs/taproot-assets/rfq/fixed_point_test.go:122 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:368 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:377 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:203 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:138 +# Error: Max difference between 1 and 0.85 allowed is 0.1, but difference was 0.15000000000000002 +# Test: TestFixedPoint/division +# +v0.4.8#12578498951298446328 +0x0 +0x1 +0x91f0816bb7983 +0x10 +0x0 +0x0 +0x1 \ No newline at end of file diff --git a/rfqmath/testdata/rapid/TestFixedPoint_division/TestFixedPoint_division-20240820191700-4540.fail b/rfqmath/testdata/rapid/TestFixedPoint_division/TestFixedPoint_division-20240820191700-4540.fail new file mode 100644 index 000000000..f7796d5c4 --- /dev/null +++ b/rfqmath/testdata/rapid/TestFixedPoint_division/TestFixedPoint_division-20240820191700-4540.fail @@ -0,0 +1,20 @@ +# 2024/08/20 19:17:00.846753 [TestFixedPoint/division] [rapid] draw a: 0x1 +# 2024/08/20 19:17:00.846757 [TestFixedPoint/division] [rapid] draw b: 0x22 +# 2024/08/20 19:17:00.846758 [TestFixedPoint/division] [rapid] draw scale: 2 +# 2024/08/20 19:17:00.846792 [TestFixedPoint/division] +# Error Trace: /Users/roasbeef/gocode/src/github.com/lightninglabs/taproot-assets/rfq/fixed_point_test.go:118 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:368 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:377 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:203 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:138 +# Error: Max difference between 1 and 0.68 allowed is 0.2, but difference was 0.31999999999999995 +# Test: TestFixedPoint/division +# +v0.4.8#13344510664195743955 +0x0 +0x1 +0xaf6482c718a38 +0x21 +0x0 +0x0 +0x1 \ No newline at end of file diff --git a/rfqmath/testdata/rapid/TestFixedPoint_division/TestFixedPoint_division-20240820191737-4783.fail b/rfqmath/testdata/rapid/TestFixedPoint_division/TestFixedPoint_division-20240820191737-4783.fail new file mode 100644 index 000000000..45eb4f240 --- /dev/null +++ b/rfqmath/testdata/rapid/TestFixedPoint_division/TestFixedPoint_division-20240820191737-4783.fail @@ -0,0 +1,20 @@ +# 2024/08/20 19:17:37.445373 [TestFixedPoint/division] [rapid] draw a: 0x1 +# 2024/08/20 19:17:37.445376 [TestFixedPoint/division] [rapid] draw b: 0x43 +# 2024/08/20 19:17:37.445377 [TestFixedPoint/division] [rapid] draw scale: 3 +# 2024/08/20 19:17:37.445411 [TestFixedPoint/division] +# Error Trace: /Users/roasbeef/gocode/src/github.com/lightninglabs/taproot-assets/rfq/fixed_point_test.go:118 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:368 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:377 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:203 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:138 +# Error: Max difference between 1 and 0.938 allowed is 0.05, but difference was 0.062000000000000055 +# Test: TestFixedPoint/division +# +v0.4.8#11228210291612621324 +0x0 +0x1 +0xca79d95bdbce4 +0x42 +0x0 +0x38e38e38e38e4 +0x2 \ No newline at end of file diff --git a/rfqmath/testdata/rapid/TestFixedPoint_division/TestFixedPoint_division-20240820191814-4986.fail b/rfqmath/testdata/rapid/TestFixedPoint_division/TestFixedPoint_division-20240820191814-4986.fail new file mode 100644 index 000000000..5a98f727c --- /dev/null +++ b/rfqmath/testdata/rapid/TestFixedPoint_division/TestFixedPoint_division-20240820191814-4986.fail @@ -0,0 +1,20 @@ +# 2024/08/20 19:18:14.866068 [TestFixedPoint/division] [rapid] draw a: 0x6 +# 2024/08/20 19:18:14.866073 [TestFixedPoint/division] [rapid] draw b: 0x259 +# 2024/08/20 19:18:14.866074 [TestFixedPoint/division] [rapid] draw scale: 2 +# 2024/08/20 19:18:14.866113 [TestFixedPoint/division] +# Error Trace: /Users/roasbeef/gocode/src/github.com/lightninglabs/taproot-assets/rfq/fixed_point_test.go:118 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:368 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:377 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:203 +# /Users/roasbeef/gocode/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:138 +# Error: Max difference between 6 and 0 allowed is 5, but difference was 6 +# Test: TestFixedPoint/division +# +v0.4.8#9555057467400453217 +0x3a76b2ef2b67b +0x6 +0x10f56a9b66714c +0x258 +0x0 +0x0 +0x1 \ No newline at end of file diff --git a/rfqmath/testdata/rapid/TestFixedPoint_from_uint64/TestFixedPoint_from_uint64-20240913124134-254478.fail b/rfqmath/testdata/rapid/TestFixedPoint_from_uint64/TestFixedPoint_from_uint64-20240913124134-254478.fail new file mode 100644 index 000000000..185e6985e --- /dev/null +++ b/rfqmath/testdata/rapid/TestFixedPoint_from_uint64/TestFixedPoint_from_uint64-20240913124134-254478.fail @@ -0,0 +1,19 @@ +# 2024/09/13 12:41:34.066731 [TestFixedPoint/from_uint64] [rapid] draw value: 0x1 +# 2024/09/13 12:41:34.066735 [TestFixedPoint/from_uint64] [rapid] draw scale: 1 +# 2024/09/13 12:41:34.066762 [TestFixedPoint/from_uint64] +# Error Trace: /home/guggero/projects/go/src/github.com/lightninglabs/taproot-assets/rfqmath/fixed_point_test.go:159 +# /home/guggero/projects/go/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:368 +# /home/guggero/projects/go/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:377 +# /home/guggero/projects/go/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:203 +# /home/guggero/projects/go/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:138 +# Error: Not equal: +# expected: 0x1 +# actual : 0x0 +# Test: TestFixedPoint/from_uint64 +# +v0.4.8#7214229959836792886 +0x0 +0x1 +0x0 +0x0 +0x1 \ No newline at end of file diff --git a/rfqmath/testdata/rapid/TestFixedPoint_multiplication/TestFixedPoint_multiplication-20240820183255-91468.fail b/rfqmath/testdata/rapid/TestFixedPoint_multiplication/TestFixedPoint_multiplication-20240820183255-91468.fail new file mode 100644 index 000000000..98888f722 --- /dev/null +++ b/rfqmath/testdata/rapid/TestFixedPoint_multiplication/TestFixedPoint_multiplication-20240820183255-91468.fail @@ -0,0 +1,13 @@ +# 2024/08/20 18:32:55.871867 [TestFixedPoint/multiplication] [rapid] draw a: 0xffffffffffffffff +# 2024/08/20 18:32:55.871935 [TestFixedPoint/multiplication] [rapid] draw b: 0x1 +# 2024/08/20 18:32:55.871938 [TestFixedPoint/multiplication] [rapid] draw scale: 0 +# 2024/08/20 18:32:55.871944 [TestFixedPoint/multiplication] Precision loss in multiplication: (18446744073709551616 * 1) / 1 = 18446744073709551616, expected approx 18446744073709551616 +# +v0.4.8#10594727827758254320 +0x1f4c4038d2fde5 +0x0 +0x0 +0x1 +0x0 +0x0 +0x0 \ No newline at end of file diff --git a/rfqmath/testdata/rapid/TestFixedPoint_multiplication/TestFixedPoint_multiplication-20240820183614-92659.fail b/rfqmath/testdata/rapid/TestFixedPoint_multiplication/TestFixedPoint_multiplication-20240820183614-92659.fail new file mode 100644 index 000000000..f78f72766 --- /dev/null +++ b/rfqmath/testdata/rapid/TestFixedPoint_multiplication/TestFixedPoint_multiplication-20240820183614-92659.fail @@ -0,0 +1,13 @@ +# 2024/08/20 18:36:14.294423 [TestFixedPoint/multiplication] [rapid] draw a: 0x0 +# 2024/08/20 18:36:14.294428 [TestFixedPoint/multiplication] [rapid] draw b: 0x0 +# 2024/08/20 18:36:14.294429 [TestFixedPoint/multiplication] [rapid] draw scale: 2 +# 2024/08/20 18:36:14.294435 [TestFixedPoint/multiplication] Fractional multiplication incorrect: 0.5 * 0.5 = 25.0, expected 0.25 +# +v0.4.8#17258131092389388373 +0x0 +0x0 +0x0 +0x0 +0x0 +0x38e38e38e38e4 +0x2 \ No newline at end of file diff --git a/rfqmath/testdata/rapid/TestFixedPoint_scale_to/TestFixedPoint_scale_to-20240913124134-254478.fail b/rfqmath/testdata/rapid/TestFixedPoint_scale_to/TestFixedPoint_scale_to-20240913124134-254478.fail new file mode 100644 index 000000000..ba6aeb7c0 --- /dev/null +++ b/rfqmath/testdata/rapid/TestFixedPoint_scale_to/TestFixedPoint_scale_to-20240913124134-254478.fail @@ -0,0 +1,22 @@ +# 2024/09/13 12:41:34.062877 [TestFixedPoint/scale_to] [rapid] draw value: 0x1 +# 2024/09/13 12:41:34.062881 [TestFixedPoint/scale_to] [rapid] draw scale: 0 +# 2024/09/13 12:41:34.062882 [TestFixedPoint/scale_to] [rapid] draw newScale: 1 +# 2024/09/13 12:41:34.062913 [TestFixedPoint/scale_to] +# Error Trace: /home/guggero/projects/go/src/github.com/lightninglabs/taproot-assets/rfqmath/fixed_point_test.go:30 +# /home/guggero/projects/go/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:368 +# /home/guggero/projects/go/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:377 +# /home/guggero/projects/go/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:203 +# /home/guggero/projects/go/pkg/mod/pgregory.net/rapid@v1.1.0/engine.go:138 +# Error: Should be true +# Test: TestFixedPoint/scale_to +# Messages: scaling up then down didn't return to original: 1 to 1.0 to -92233720368547758080 +# +v0.4.8#14913217214437782375 +0x0 +0x1 +0x0 +0x0 +0x0 +0x0 +0x0 +0x1 \ No newline at end of file diff --git a/rfqmath/testdata/rapid/TestToFromFloat_big_int/TestToFromFloat_big_int-20240820174201-79554.fail b/rfqmath/testdata/rapid/TestToFromFloat_big_int/TestToFromFloat_big_int-20240820174201-79554.fail new file mode 100644 index 000000000..a3acdae78 --- /dev/null +++ b/rfqmath/testdata/rapid/TestToFromFloat_big_int/TestToFromFloat_big_int-20240820174201-79554.fail @@ -0,0 +1,6 @@ +# 2024/08/20 17:42:01.537632 [TestToFromFloat/big_int] [rapid] draw a: rfq.BigInt{value:(*big.Int)(0x1400038ca60)} +# 2024/08/20 17:42:01.537635 [TestToFromFloat/big_int] toFloat/fromFloat conversion failed: original 9007199254740993, got 9007199254740992 +# +v0.4.8#9170116783179485040 +0x1eb66c6cc8c439 +0x20000000000001 \ No newline at end of file diff --git a/rfqmath/testdata/rapid/TestToFromFloat_go_uint_64/TestToFromFloat_go_uint_64-20240820174201-79554.fail b/rfqmath/testdata/rapid/TestToFromFloat_go_uint_64/TestToFromFloat_go_uint_64-20240820174201-79554.fail new file mode 100644 index 000000000..564b023db --- /dev/null +++ b/rfqmath/testdata/rapid/TestToFromFloat_go_uint_64/TestToFromFloat_go_uint_64-20240820174201-79554.fail @@ -0,0 +1,6 @@ +# 2024/08/20 17:42:01.534951 [TestToFromFloat/go_uint_64] [rapid] draw a: rfq.GoInt[uint64]{value:0x20000000000001} +# 2024/08/20 17:42:01.534957 [TestToFromFloat/go_uint_64] toFloat/fromFloat conversion failed: original {9007199254740993}, got {9007199254740992} +# +v0.4.8#8285709902570533030 +0x1eb66c6cc8c439 +0x20000000000001 \ No newline at end of file