Skip to content

SWIP 049 - Fixed set of stamps for stamp sampling phase#98

Open
lat-murmeldjur wants to merge 1 commit into
ethersphere:masterfrom
lat-murmeldjur:swip_049_stamp_set
Open

SWIP 049 - Fixed set of stamps for stamp sampling phase#98
lat-murmeldjur wants to merge 1 commit into
ethersphere:masterfrom
lat-murmeldjur:swip_049_stamp_set

Conversation

@lat-murmeldjur

Copy link
Copy Markdown

Bee and the Redistribution contract must use the same set of batches and indexes. This SWIP fixes that set when sampling begins and keeps it fixed through the last claim block. A batch may be used only if it already existed, had at least 456 blocks of balance at the price in force when sampling began, and already contained the proved index at that time. New batches and newly created indexes remain usable for uploads immediately, but they cannot be used by an already open redistribution round.

Furthermore, the suggestion is for the PostageStamp contract to keep one previous price and two previous batch depths. The Redistribution contract can use that history to reconstruct the price and index range that applied when sampling began at claim. A top-up is allowed only while the batch still has at least six rounds of balance, and a dilution must leave at least six rounds of balance. The claim verifies that the batch is still present and live, verifies all postage proofs, including that the index existed at the start of sampling, and only then allows the price to change.

@lat-murmeldjur lat-murmeldjur changed the title Fixed set of stamps for stamp sampling phase SWIP 049 - Fixed set of stamps for stamp sampling phase Jun 24, 2026

@zelig zelig left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UPDATE:
After talking to the author I am conviced that this is close to the right solution. The SWIP is difficult though.
So i think it would help if the text was iterated on:

  • add proper context, motivation
  • make it clear that this is about the storage incentives phase 5 problem
  • makt it clear which aspect of the phase 5 this solves and which it leaves open
  • the calculations for the validity periods should be either before the 'spec' or referenced.
  • the calculations should be done formally using generics first such as 'maximum per-round-price-increase' and concrete numbers only after, emphasize that skipped rounds is important here NOT because it is the highest increase but because it causes the cumulation of price increase without being applied due to lack of transactions.
  • I recommend better naming (of functions and variables)
  • align transformed slot reference with phase 4 ones (anchor nonce as a prefix)

I have read through it and commented thoroughly, but
there is a major flaw here https://github.com/ethersphere/SWIPs/pull/98/changes#r3485677904
and I am not sure of the motivation for increased complexity.

What we do need is a delay in dilution and (maybe require a long effective ttl after)

This is a short and Sweet SWIP of its own, not worth mixing in othr stuff
https://github.com/ethersphere/SWIPs/pull/98/changes#r3484124428

Comment thread SWIPs/swip-49.md

## Abstract

Phase 5 adds a postage-stamp sample beside the existing chunk-data sample. For each distinct stamp identity, Bee calculates `hash(batchId, fullStampIndex, roundAnchor)` and keeps the required lowest values. One purchased index can therefore contribute only one transformed value in a round, so overreporting storage depth requires a correspondingly large number of real stamp indexes. The separate chunk sample remains necessary to show that participants also store the same chunk data.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the "transformed hash" is using the roundAnchor as a prefix, so it is hash( roundAnchor, batchId, index) to be the same as the phase 4 transformed hash.

Comment thread SWIPs/swip-49.md
- 38 reveal blocks; and
- 76 claim blocks.

For target round `r`, sampling begins at the first reveal block of round `r - 1`. This is the first scheduled block in which a successful reveal can produce the anchor used by target round `r`. If the preceding reveal phase has no successful reveal, the existing skipped-round seed derivation is used and the target round remains playable. The sampling boundary does not move.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For target round r, sampling begins at the first reveal block of round r - 1.

this is not true, but as you say:

This is the first scheduled block in which a successful reveal can produce the anchor used by target round r.

I suggest a dofferent wording:

The anchor target round r is set by the first successful reveal transaction of the previous round. Since the earliest block this can happen is at the first reveal block of the previous round, we can set that as the "starting block" for sampling.

Comment thread SWIPs/swip-49.md

The state used by the round is the state after the preceding block has been processed. A batch creation or dilution included before `samplingStartBlock` may therefore affect the target round. An operation included in `samplingStartBlock` or later is too late for that round.

Counting the sampling-start block and the final target-round claim block, the round's sampling-to-claim window spans 266 blocks. During that window, later postage operations must not change:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

266 = 2 * 152 - 38

Comment thread SWIPs/swip-49.md
4. The batch is still present and live when the target claim verifies the stamp.
5. The existing signature, bucket-alignment, inclusion-proof, and other postage checks pass.

The target claim can be submitted as late as the end of the 266-block window. Requiring 456 blocks of balance at sampling start leaves 190 starting-price blocks of headroom over that claim window. This headroom is used to tolerate the price increase that the preceding round's claim may apply while target-round sampling is already in progress.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how do you know that this length tolerates the potential price increase of the previous round's claim. This must be calculated.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes so this is the actual aim. The claim will only check whether the balance was at least 456 blocks remaining by the price in force at the time of the starting block for sampling, which is an exact balance requirement that does not shift after sampling start block. However, the batch has to remain in the registry and not expire despite the price change that happens after the starting block of sampling. So this 456 blocks is chosen so that it tolerates an 80% price increase and makes sure that a batch that had only 456 blocks of validity left will still not expire and be deleted from the registry after 266 blocks (which is the number of blocks between the last possible claim and the sampling start block). Will add this explanation to the SWIP too

Comment thread SWIPs/swip-49.md

A deployment may require a larger minimum, but it must not permit less than 912 blocks.

These rules remove the need for per-batch balance history. A top-up cannot rescue a nearly expired batch into an already open round, because the batch must already have six rounds of balance before it can be topped up. A dilution cannot remove a batch from an already open round, because the diluted batch must still have six rounds of balance afterward.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why 6? how do you make sure that the degree of the change of price has these properties?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

6 rounds was chosen arbitrarily. It could be 4, or even close to 3, but not less, so that it's not possible to top up a batch that has less remaining balance than the 3 rounds worth of balance required by the sampling, or that it's not possible to dilute a batch to less balance than what is required by the sampling.

What needs to be considered here is potential price decrease caused by a claim. It should not be possible to decrease a batch balance by dilution to become less than the 456 block worth of remaining balance by the previous price even after a price decrease. So if the maximum price decrease is 1%, the minimum balance that can be created by batch operations needs to be around 1% more than 456 blocks. Also going to add this to the SWIP description

Comment thread SWIPs/swip-49.md

Bee may reconstruct this state locally from events. The PostageStamp read functions defined by this SWIP are the canonical contract reference. Later creation, top-up, dilution, expiry, or price updates do not change an already constructed target-round sample.

### Batch ID reuse

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a totally unrelated issue that is kind of hidden here. Consider reifing it as a separate SWIP

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Batch ID reuse is really not necessary to be part of this SWIP. If a batch is recreated, it will have new creation blocks, and will not be usable for sampling in the same round anyway

Comment thread SWIPs/swip-49.md

After 756 skipped rounds, the remaining balance represents approximately 227.871 blocks at the increased price, so survival through the complete conservative window is no longer guaranteed. Integer division in the oracle can only reduce the realised increase, so 755 skipped rounds is a conservative guaranteed tolerance under the current constants.

This number is a safety analysis, not an on-chain limit. If a larger catch-up causes a selected batch to expire before proof verification, the target claim rejects that batch normally. No expired-batch data is retained for redistribution.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not understand these two sentences

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there are for example 800 skipped rounds, the price increase catch-up could be more than 80%, and it could mean that a batch that was sampled with near minimal validity could become expired by the last claim block. However this tolerance is already huge and only influences rare edge cases, a batch that has a bit more than the minimum balance would already survive more than 80% price increase too.

Comment thread SWIPs/swip-49.md
1.832897789
```

This is an increase of approximately **83.2898%**. The remaining 418 starting-price block units then represent approximately:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'represent' is a funny word to use here. I think it is better to say 'covers the storage rent for a shorter period with the higher price.

Comment thread SWIPs/swip-49.md
1049417 / 1048576
```

After 755 skipped rounds, the combined maximum price factor is approximately:

@zelig zelig Jun 27, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No no no. You seem to be compounding a multiplicative factor. But it is actually just the price itself.
No matter how many skipped rounds happened. the rate of change is capped at a maximum of the above ratio. which means that the price change is minuscule during a

UPDATE: I was wrong herein the sense that surely the multiplicative factor applies per round, In the caes of a skipped round their application cannot happen, so the cumulated change does apply at once right after the stretch of skipped rounds, in which there is no claim transactions.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it is worth elaboratign why only the change cumulated in the previous rounds matter and not the 2 price changes after.

Comment thread SWIPs/swip-49.md
Comment on lines +238 to +249

```text
418 / 1.832897789 = 228.054 blocks
```

Using the conservative 266-block window, 228 blocks remain after the earliest possible price update. The batch therefore remains live through the latest target-round claim.

After 756 skipped rounds, the remaining balance represents approximately 227.871 blocks at the increased price, so survival through the complete conservative window is no longer guaranteed. Integer division in the oracle can only reduce the realised increase, so 755 skipped rounds is a conservative guaranteed tolerance under the current constants.

This number is a safety analysis, not an on-chain limit. If a larger catch-up causes a selected batch to expire before proof verification, the target claim rejects that batch normally. No expired-batch data is retained for redistribution.

### Bee behaviour

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets not call it bee but node client changes instead. There are a lot of alternative clients

@zelig

zelig commented Jun 28, 2026

Copy link
Copy Markdown
Member

Right. I talked it through with the author and they conviced me that the proposed solution is necessary, sound and very likely not possible to simplify much.

@lat-murmeldjur

lat-murmeldjur commented Jun 28, 2026

Copy link
Copy Markdown
Author

Thank you for the review

So to state the reasoning behind the necessity of the different parts behind this SWIP:

Choosing 456 blocks of validity based on the price in force at the sampling start block.
This seems reasonable, because waiting for the claim to actually set a price would decrease the time for sampling.
So to have a definitive minimal balance that won't shift until the claim it seems reasonable to use the price in effect when the sampling can start earliest. For this to work we need some tolerance expecting a potential price increase (456 blocks of validity instead of 266), and we need to remember the price in effect at sampling start, so that the claim can verify each batch meets this exact requirement. This creates an unambigous balance requirement for both the client and the contract with the introduction of one new contract variable and a short formula function to calculate this balance requirement.

Choosing 912 block of minimum validity accepted for the result of batch operations:
Could be decreased to little more than 456 blocks. Otherwise this makes sure that batch operations can not move a batch in or out of the scope of valid batches for a round that already began sampling.

Dilution depth history.
Whenever a dilution happens, there can be two rounds ongoing, one that is close to the claim, and the other that just started sampling. For both of these rounds we need to remember the specific depth of a batch before the sampling of the round began, so that new indexes can not be created during sampling and used already in the same period claim (however they still remain immediately usable for uploads). So at any given moment, we might need to remember the depth of a batch before the latest sampling began, and before the sampling for the preceding round began 156 blocks before. This SWIP is really close to minimising the administrative cost of keeping track of this, as this proposal tracks exactly up to 2 previous depths for any batch (with their introduction blocks). It does not require extra updates, only when a dilution happens, and in claim we can deduct the range of valid indexes for a batch from this limited history for any batch. The achievement of this solution is that dilutions can still happen with any frequency, the UX remains unchanged and not constrained compared to the previous experience.

Batch ID reuse
Not necessary

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants