-
Notifications
You must be signed in to change notification settings - Fork 217
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
2536: Split change outputs with asset quantities exceeding the maximum r=jonathanknowles a=jonathanknowles # Issue Number #2532 ADP-726 # Overview Although quantities of individual assets are effectively unlimited, a transaction on the blockchain can never include an asset quantity greater than `maxBound :: Word64`. This PR tweaks the coin selection algorithm to detect change bundles containing excessively large asset quantities. If such a change bundle is detected, we now split the change bundle up into smaller bundles using [equipartitioning](https://en.wiktionary.org/wiki/equipartition). # Example Let's suppose the maximum allowable token quantity is **`5`**, and we have a change map with the following quantities: ```haskell [("a", 11), ("b", 5), ("c", 12)] ``` In this case, we must divide the map into **_at least three_** smaller maps in order to not exceed the maximum allowable token quantity. Under the equipartitioning scheme, this would give us: ```haskell [("a", 3), ("b", 1), ("c", 4)] [("a", 4), ("b", 2), ("c", 4)] [("a", 4), ("b", 2), ("c", 4)] ``` Note that while the overall sum is preserved, the individual bundles are almost equal, **_but not quite_**: this is because `11` and `5` are not divisible by `3`. We must therefore accept a small loss of proportionality in the result. # Details An **_equipartition_** of a bundle **_b_** is a _partition_ into multiple bundles, where for every asset **_a_** in the set of assets contained in **_b_**, the difference between the following quantities is either _zero_ or _one_ : - The smallest quantity of asset **_a_** in the resultant bundles - The greatest quantity of asset **_a_** in the resultant bundles In order to determine the number of parts in which to split a given bundle, we choose the **_smallest_** number of parts that still allows us satisfy the goal of not exceeding the maximum allowable quantity in any given bundle. # Performance In order to avoid evaluating a partition for every single change output, we **_short circuit_** in the event that there is no token quantity greater than the maximum allowable quantity: ```haskell equipartitionTokenMapWithMaxQuantity m (TokenQuantity maxQuantity) | maxQuantity == 0 = maxQuantityZeroError | currentMaxQuantity <= maxQuantity = m :| [] | otherwise = equipartitionTokenMap m (() :| replicate extraPartCount ()) ``` # Testing ## Property tests Equipartitioning behaviour is tested by the following property tests: - `prop_equipartitionTokenBundle*` - `prop_equipartitionTokenMap*` ## Unit tests As a sanity check, this PR also provides unit tests for `performSelection` with inputs containing token quantities that are close to the maximum, and demonstrates that change bundles are correctly partitioned in the results. Co-authored-by: Jonathan Knowles <[email protected]>
- Loading branch information
Showing
2 changed files
with
736 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.