CHIP-0053: Secure the Bag for distributed payouts - #183
Conversation
|
This CHIP is now a Draft. Please leave your reviews here, and feel free to discuss it in the #chips channel of our Discord. |
|
A CHIP discussion has been scheduled for Wednesday, December 10th at 10am EST. A Zoom link will be shared in Discord prior to the event. |
|
The recording of our discussion of this CHIP is now available on Youtube. |
|
I have added a bunch of new information to this CHIP. Most of what I added was to answer questions from the Zoom call. The CHIP still needs an implementation, which could also change some of the other context. |
|
The reason the optimal fanout for secure the bag deviates from e is that there's a constant added to the cost of each node. The interesting single parameter is the ratio of that cost to the cost of each individual reveal of children in the fanout. The closed form optimal fanout is r/W(r/e) where W is the lambert W function. You can reasonably approximate this by it being e at 0, 8 at 8, and 38 at 100, and drawing two line segments between those three values. This 'optimal' value is for the cost of the first thing to be unrolled. When optimizing for overall cost the higher the fanout the better and the extra costs incurred are roughly r/(f-1) |
|
This CHIP is now |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 10 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Want reviews to match your repository better? Bugbot Learning can learn team-specific rules from PR activity. A team admin can enable Learning in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 57f1383. Configure here.
|
|
||
| Else, if the highest available `i` number is `i3`, then | ||
| * Iterate through each coin in `i3` and bundle them to the `i2` coins, using `<coin number> mod <i2>` | ||
| * Iterate through each coin in `i2` and bundle them to the `i1` coins, using `<coin number> mod <i1>`. |
There was a problem hiding this comment.
Three-level tree fanout broken
High Severity
The i3 bundling step assigns leaf intermediates with mod i2, which creates only i2 middle coins instead of roughly i1 * i2. That conflicts with the i1/i2/i3 formulas, so each middle spend can create about i1 times more children than intended. Near the upper d range this can reach thousands of outputs per spend, far above the stated max of 300, and may make those coins impractical to spend.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 57f1383. Configure here.
| * This number needs to be calculated separately from the highest `i` number because of possible rounding errors | ||
|
|
||
| ### Higher n Values | ||
| If `n > 100'000'000`, then at least one additional intermediate level will be needed. Follow the same algorithm using these additional levels to create a secured bag with a greater number of addresses. This algorithm scales logarithmically, it should rarely be necessary to use more than three intermediate levels. However, doing so is permitted in this CHIP. |
There was a problem hiding this comment.
Extra-level threshold uses wrong bound
Medium Severity
Three-level recommendations stop at d <= 100,000,000 (n ≈ 1,000,000 with c = 100), but the next-level guidance only triggers when n > 100,000,000. That leaves a large destination range with no specified level structure, and the n threshold appears to reuse the d bound by mistake.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 57f1383. Configure here.
|
|
||
| #### Step 2: Bundle intermediate level(s) into the secured bag | ||
| If the highest available `i` number is `i1`, then | ||
| * Bundle each of the `i1` coins into the secured bag |
There was a problem hiding this comment.
Root bundle count contradicts examples
Medium Severity
When the highest level is i1, Step 2 says to bundle each of the i1 coins into the bag, but i1 is also a maximum and Example 1 sets i1 = 100 while only creating and bundling n = 50 coins. The spec disagrees with itself on how many coins the root spend creates.
Additional Locations (2)
Reviewed by Cursor Bugbot for commit 57f1383. Configure here.
| * Each intermediate coin spend will need to pay for less than 1% of a block's space, which should keep transaction fees reasonable, even with a busy (but not full) mempool | ||
| * If the mempool is expected to be completely full, then it's possible to set `i1` to a lower value in order to minimize the transaction fee for the intermediate spends, but this comes with tradeoffs: | ||
| * The intermediate coins will contain fewer destination coins, so there will be a smaller pool of candidates to pay the fee to spend them | ||
| * Another level of intermediate coins will be needed if `sqrt(d) > i1`, which will necessitate paying additional fees |
There was a problem hiding this comment.
Wrong rule for adding levels
Medium Severity
The guidance uses sqrt(d) > i1 to decide when another intermediate level is required, but the real limit is whether n = ceiling(d/c) fits under the root fanout. That can demand an extra level when one is enough, or skip one when c is reduced and n exceeds i1.
Reviewed by Cursor Bugbot for commit 57f1383. Configure here.
| * If `1'000'000 < d <= 100'000'000`, then using `i1`, `i2`, and `i3` is recommended | ||
| * `i1` is recommended to be `ceiling(d/1'000'000)` | ||
| * `i2` is recommended to be `ceiling((d/i1)/10'000)` | ||
| * `i3` is recommended to be `ceiling(((d/i1)/i2)/100)` |
There was a problem hiding this comment.
Fanout formulas ignore c
Medium Severity
The i2 and i3 recommendations divide by the literal 100 instead of c, while the CHIP allows lowering c when the mempool is full. Using those formulas with c ≠ 100 yields the wrong fanout versus the actual n = ceiling(d/c) tree.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 57f1383. Configure here.
|
|
||
| The `<amount>` should be of either type `CAT2` or `revocable CAT`. Both of these standards use a 64-bit number that is maximally divisible by 1000. Thus, the smallest possible value is `1/1000 = 0.001` and the largest possible value is `(2^64-1)/1000 = 18,446,744,073,709,551.615`. The `<amount>` on each line must correspond to the same asset, though this type is not specified in the `.csv` file. | ||
|
|
||
| It is recommended to organize the lines in this file by `<amount>`, in descending order (the first line should be the largest amount). The reason for this recommendation is that it will cause the amounts to be distributed evenly when the secured bag is later created. |
There was a problem hiding this comment.
Optional sort breaks fee goal
Medium Severity
Descending amount order is only recommended, but Example 1 and the fee-motivation design treat spreading the largest payouts across intermediate coins as a guaranteed result of mod n. Without a required sort, bags of only dust payouts remain possible, which is the failure mode Key Question 2 aims to avoid.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 57f1383. Configure here.
| While any memo could contain the URL, it is recommended to use the second memo. This is because the first memo typically is used for hinting. | ||
|
|
||
| #### With the CSV File | ||
| Once the .csv file is obtained, it is possible to recreate the secured bag by following the [Securing the Bag](#securing-the-bag) section of this CHIP. To verify the accuracy of the recreated secured bag, compare its simulated coin ID with that of the on-chain equivalent. If the two values don't match, then the recreated secured bag is inaccurate. |
There was a problem hiding this comment.
CSV recreation omits parameters
High Severity
The CSV-only recreation path says a secured bag can be rebuilt from the .csv alone, but bundling depends on c, i1, i2, and i3, which are chosen inputs and may deviate from the recommendations. Without those values, wallets cannot deterministically reproduce the tree or match the on-chain coin.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 57f1383. Configure here.
| ### Securing the bag | ||
|
|
||
| #### Step 1: Bundle the destination addresses into intermediate coins | ||
| Iterate through the input file, and assign each line to a coin number, using `<line number> mod n`. (The intermediate coins won't necessarily each have an identical number of destination coins.) The resulting coins are used in the highest `i` number available (`i1`, `i2`, or `i3`). |
There was a problem hiding this comment.
Line index base unspecified
High Severity
Assignment uses <line number> mod n without defining whether the first CSV line is 0 or 1. Those choices place different destination sets into each intermediate coin, so independently compliant implementations will disagree on the tree and verification will fail.
Reviewed by Cursor Bugbot for commit 57f1383. Configure here.
|
|
||
| If the `<address>` is a clawback address (see [CHIP-44](https://github.com/Chia-Network/chips/blob/main/CHIPs/chip-0044.md) for more info), then the initiator might be able to claw back the payment after it is received. However, this depends on the timer specified in the clawback coin. Due to the potential for confusion from both the sender and the receiver, clawback payments are not recommended with this standard. | ||
|
|
||
| The `<amount>` should be of either type `CAT2` or `revocable CAT`. Both of these standards use a 64-bit number that is maximally divisible by 1000. Thus, the smallest possible value is `1/1000 = 0.001` and the largest possible value is `(2^64-1)/1000 = 18,446,744,073,709,551.615`. The `<amount>` on each line must correspond to the same asset, though this type is not specified in the `.csv` file. |
There was a problem hiding this comment.
CSV amount encoding undefined
High Severity
The CSV format is only <address>,<amount>, while amounts are described both as a raw 64-bit integer and as decimal display values such as 0.001. The spec never states which form appears in the file, so implementations can disagree by a factor of 1000 on every payout.
Reviewed by Cursor Bugbot for commit 57f1383. Configure here.
| Else, if the highest available `i` number is `i3`, then | ||
| * Iterate through each coin in `i3` and bundle them to the `i2` coins, using `<coin number> mod <i2>` | ||
| * Iterate through each coin in `i2` and bundle them to the `i1` coins, using `<coin number> mod <i1>`. | ||
| * Bundle each of the `i1` coins into the secured bag |
There was a problem hiding this comment.
Intermediate amounts never defined
High Severity
The securing steps assign destinations into coins but never require each intermediate or root coin amount to equal the sum of its children’s amounts. Without that rule, implementers can build trees that do not fund their outputs, breaking spends and the “certainty of funds” claim.
Reviewed by Cursor Bugbot for commit 57f1383. Configure here.


Note
Low Risk
Documentation-only CHIP with no consensus or runtime changes; optional ecosystem guideline with no merge risk to production code.
Overview
Adds CHIP-0053, an informational guideline for building and verifying Secure the Bag payout trees that spread large and small amounts across intermediate coins so recipients with bigger payouts are more likely to pay fees to unroll the bag.
The spec defines a CSV input (
address,amount), variablesd,i1/i2/i3,c, andn, and a modulo-based bundling algorithm (line number modn, then intermediate levels into the root). It recommends sorting payouts by descending amount, forbids duplicate addresses, and documents recreating bags from CSV/metadata (optional URL memo), plus wallet discovery via issuer APIs, bulletins (TBD), and lazy-unrolling caveats.Status is Stagnant; reference implementation and test cases are explicitly not yet in the Wallet SDK.
Reviewed by Cursor Bugbot for commit 57f1383. Bugbot is set up for automated code reviews on this repo. Configure here.