Skip to content

Feat/postage decoupled#300

Open
0xCardiE wants to merge 11 commits intomasterfrom
feat/postage_decoupled
Open

Feat/postage decoupled#300
0xCardiE wants to merge 11 commits intomasterfrom
feat/postage_decoupled

Conversation

@0xCardiE
Copy link
Contributor

@0xCardiE 0xCardiE commented Dec 11, 2025

PostageStamp Storage Decoupling Architecture

Overview

Separates PostageStamp into two contracts:

  • PostageStampStorage - Immutable contract holding all BZZ tokens and batch data (deployed once, never upgraded)
  • PostageStamp - Upgradeable logic contract containing business rules (versioned, can be replaced)

How It Works

Layer Component Description
Storage PostageStampStorage Permanent, holds tokens & batch data
Logic PostageStamp v1, v2, v3... Upgradeable business logic
Client Bee v1.0, v1.1, v2.0... Each version uses specific logic contract

Key Points

  • Storage uses WRITER_ROLE (OpenZeppelin AccessControl) - multisig grants this to logic contracts
  • Multiple logic contracts can have write access simultaneously (enables gradual network migration)
  • Bee nodes are hardcoded to use a specific logic contract address
  • Upgrades require only: deploy new logic → multisig grants WRITER_ROLE → release new Bee version

Benefits

  • ✅ Zero token migration on upgrades
  • ✅ Zero batch data migration on upgrades
  • ✅ Old and new Bee versions can coexist during transition
  • ✅ Rollback capability (revoke new version, keep old)
  • ✅ Simple governance (only role management needed)

Upgrade Process

  1. Deploy new PostageStamp logic (points to existing storage)
  2. Multisig calls storage.grantRole(WRITER_ROLE, newLogicAddress)
  3. Release new Bee version with new logic address
  4. (Optional) Revoke old logic after full migration

Add storage decoupling architecture to enable upgrading PostageStamp
logic without migrating funds or batch data. This introduces a
separation between immutable storage and upgradeable logic contracts.

Key components:
- PostageStampStorage: Immutable contract holding all data and BZZ tokens
- PostageStampV2: Upgradeable logic contract implementing all operations
- IPostageStampStorage: Interface defining storage operations

Benefits:
- Zero-migration upgrades (no token or data movement needed)
- Reduced risk and complexity for future upgrades
- Swarm nodes only need to update contract address
- Storage contract remains trusted and immutable

Documentation:
- SWIP proposal with full specification and rationale
- Complete implementation guide for deployment and migration
- Example deployment and migration scripts

Files added:
- src/interface/IPostageStampStorage.sol
- src/PostageStampStorage.sol
- src/PostageStampV2.sol
- deploy/PostageStampV2.deploy.ts
- scripts/migration/migrateToStorageDecoupling.ts
- SWIP-storage-decoupling.md
- docs/STORAGE_DECOUPLING_GUIDE.md
- STORAGE_DECOUPLING_README.md

This is a preparation for future upgrades and provides a one-time
migration path from the existing monolithic PostageStamp contract.
Remove version numbers (V2, V3, etc.) from contract names in favor
of git tag-based versioning. This provides cleaner contract naming
where the logic contract is always named PostageStamp, with versions
tracked via git tags.

Changes:
- Renamed PostageStampV2.sol to PostageStamp.sol
- Renamed old PostageStamp.sol to PostageStampLegacy.sol
- Renamed deploy/PostageStampV2.deploy.ts to deploy/PostageStamp.deploy.ts
- Updated all documentation to reflect git tag-based versioning
- Updated SWIP to explain versioning approach
- Updated migration script references
- Updated deployment script to reference correct contract names

Versioning strategy:
- Logic contract is always named PostageStamp
- Versions tracked via git tags (v2.0.0, v2.1.0, etc.)
- When upgrading: checkout new tag, deploy, update storage pointer
- No version numbers in contract or file names
…ture

- Update local deployment script to deploy PostageStampStorage + PostageStamp
- Use fully qualified contract name to resolve PostageStamp ambiguity
- Update mintAndApprove to approve tokens for PostageStampStorage
- Fix token balance checks to use storage contract instead of logic contract
- Add copyBatch function to PostageStamp for testing/migration with specific IDs
- Skip legacy copyBatch tests (not relevant to new architecture)

All tests passing: 145 passing, 13 pending (legacy tests)
- Add minimumValidityBlocks property to networkConfigItem interface
- Configure values for all networks based on their block times
- Fixes deployment script error in CI/CD
…cess

BREAKING CHANGE: PostageStampStorage no longer has mutable logic contract pointer

## Architecture Changes

### Before:
- Storage had mutable `logicContract` address
- Required admin to call `updateLogicContract()` to upgrade
- Created trust issues and admin dependency

### After:
- Storage uses role-based access control (WRITER_ROLE)
- Logic contracts are granted WRITER_ROLE to modify storage
- Storage is truly immutable - no mutable state
- Logic contracts point to storage (immutable reference)

## Upgrade Process (New):
1. Deploy new PostageStamp logic pointing to same storage address
2. Admin grants WRITER_ROLE to new logic contract
3. Update Bee nodes to use new logic contract address
4. (Optional) Revoke WRITER_ROLE from old logic contract

## Benefits:
- True immutability - no mutable pointers in storage
- Multiple logic versions can coexist (for gradual rollout)
- Bee node versions are hinged to specific logic contract addresses
- No need for admin intervention in contracts during upgrades
- Reduces trust requirements

## Changes:
- Remove `logicContract` state variable from PostageStampStorage
- Remove `updateLogicContract()` function
- Replace `onlyLogicContract` modifier with WRITER_ROLE checks
- Update deployment to grant WRITER_ROLE instead of setting logic address
- Storage constructor now only takes (bzzToken, admin)

All tests passing: 145 passing, 13 pending
- Rename _admin to _multisig in constructor for clarity
- Add detailed documentation on role management workflow
- Add isWriter() and isAdmin() helper functions
- Document the exact multisig calls needed for upgrades:
  - grantRole(WRITER_ROLE, newPostageStamp) to add new logic
  - revokeRole(WRITER_ROLE, oldPostageStamp) to remove old logic

The multisig is set once in constructor and can manage WRITER_ROLE forever.
- Add architecture diagram showing multiple logic versions
- Document WRITER_ROLE based access control
- Explain Bee node versioning model
- Document multisig operations for granting/revoking roles
- Update deployment and migration instructions
- Add FAQ section addressing common questions
- Remove outdated references to updateLogicContract()
@0xCardiE
Copy link
Contributor Author

ROUND_LENGTH Parameter Change Verification

Context

ROUND_LENGTH changed from hardcoded 152 (Gnosis, 5s blocks) to configurable immutable set at deployment. Base uses 380 (2s blocks).

Goal: Maintain identical wall-clock timing across networks.

  • Gnosis: 152 blocks × 5s = 760s per round
  • Base: 380 blocks × 2s = 760s per round

Checklist for Reviewers

1. Round Calculation

Files: PriceOracle.sol:197, Redistribution.sol:814

Same number of rounds per day on both networks:

  • Gnosis: 86400s ÷ 760s = ~113 rounds/day
  • Base: 86400s ÷ 760s = ~113 rounds/day ✅

2. Phase Durations

File: Redistribution.sol:821, 907, 935

Phase Blocks Gnosis Time Base Time
Commit 0 → ROUND_LENGTH/4 38 × 5s = 190s 95 × 2s = 190s ✅
Reveal ROUND_LENGTH/4 → ROUND_LENGTH/2 38 × 5s = 190s 95 × 2s = 190s ✅
Claim ROUND_LENGTH/2 → end 76 × 5s = 380s 190 × 2s = 380s ✅

All phases have identical wall-clock duration.

3. Staking Wait Period (2 Rounds)

File: Redistribution.sol:305, 844

  • Gnosis: 2 × 152 × 5s = 1520s (~25 min)
  • Base: 2 × 380 × 2s = 1520s (~25 min) ✅

Same minimum staking wait time before participating.

4. Freeze Penalties

File: Redistribution.sol:560, 570

Freeze duration in blocks converted to time:

Depth Gnosis Freeze Base Freeze
16 152 × 65536 × 5s = 576 days 380 × 65536 × 2s = 576 days ✅
17 152 × 131072 × 5s = 1152 days 380 × 131072 × 2s = 1152 days ✅

Freeze penalties result in same wall-clock duration.

5. Last Block Prevention

File: Redistribution.sol:321

Still prevents commits on last block of commit phase (works with any ROUND_LENGTH value).


Configuration Values

Network Chain ID ROUND_LENGTH minimumValidityBlocks Block Time
Gnosis 100 152 17280 (24h) 5s
Base 8453 380 43200 (24h) 2s

Verification formula:

  • ROUND_LENGTH_base = ROUND_LENGTH_gnosis × (gnosis_block_time / base_block_time)
  • 380 = 152 × (5 / 2) = 152 × 2.5

Files Modified

  • src/PriceOracle.sol - ROUND_LENGTH constant → immutable
  • src/Redistribution.sol - ROUND_LENGTH constant → immutable
  • src/PostageStamp.sol - minimumValidityBlocks now constructor param
  • helper-hardhat-config.ts - Network-specific values added

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.

1 participant