Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: storage deposit #4035

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open

Conversation

piux2
Copy link
Contributor

@piux2 piux2 commented Mar 31, 2025

Solution for : #3418

BREAKING CHANGES

New Message Flag

  • MsgCall and MsgRun now include an optional deposit field to specify how many GNOT the caller is willing to lock for storage.
  • This deposit is used to pay storage changes at the end of each message.
  • The user's balance is deducted or refunded based on actual storage changes.

AddPkg Additional Logic

  • AddPkg now includes:
    • A send flag to send tokens to the realm.
    • A deposit flag to specify how much GNOT to include for storage fees.
  • Previously, the -deposit flag was used in place of -send within AddPkg.
  • The user's balance is deducted or refunded based on actual storage changes.

Purpose

  1. Paying for Storage
    Whenever data is stored in a realm (e.g., via SetObject), an amount of GNOT is locked as a “storage deposit.”

  2. Reclaiming Storage & Deposit
    When stored data is removed, the corresponding deposit is released back to the user.

  3. Encouraging Efficient Storage
    Users must pay to keep data on-chain, incentivizing them to store only what is necessary.

High-Level Design

The system imposes a GNOT deposit for stored data. Each realm tracks its total number of bytes in use and the total tokens locked for that storage. After each message is processed, the net change in storage usage is calculated, and the appropriate amount of GNOT is locked or unlocked. By default, deposit tracking is aggregated per realm rather than per user. Any user refunds or reward mechanisms for freeing storage are determined by the realm.


Usage Tracking

  1. Realm-Level Tracking

    • Storage: The total number of bytes used in this realm.
    • Deposits: The total GNOT locked to pay for the realm’s storage.
  2. Global VM Parameters

    • Storage Price: A rate (in GNOT per byte) defining how much GNOT must be locked per byte stored (e.g., “1 GNOT/KB” or another fraction).
    • Default Deposit: A fallback amount if the user does not specify a custom deposit.
  3. Message-Level Fields

    • Optional Deposit Field (in MsgCall/MsgRun/AddPkg): The user can specify how many GNOT to deposit for potential storage usage.
    • Settlement at Message End: When a message finishes, the net storage change is computed, and tokens are transferred accordingly (locked or refunded).

Deposit & Retrieval Flow

  1. Make a MsgCall / MsgRun / AddPkg Call

    • The user (or contract) sets how many GNOT to allow for extra storage usage.
  2. During Execution

    • The system tracks changes in storage size (growth or shrinkage).
    • If storage grows: GNOT from the deposit buffer is locked into the realm.
    • If storage shrinks: GNOT is unlocked from the realm and refunded to the user.
  3. Anyone Can Free Storage

    • Because deposits are tracked per realm (not per user), anyone can remove data.
    • This can create a “cleanup reward” scenario where the person who deletes data gains the released deposit.
    • It also gives the realm developer flexibility to design and manage user storage.

Tools

  • gnokey query vm/qstorage -data <realm_path>: Shows a realm’s locked tokens and current storage usage.
  • Storage Directive in Tests: A Storage: directive can be added to file tests to display storage usage across realms.

Future Improvements

  • Large Default Deposit: Currently, the default deposit is set to a high number, so below that threshold, users do not need to explicitly specify their own deposit. This is used as a workaround for genesis transactions.
  • High Storage Price: A rate of 1 GNOT per KB can significantly increase the cost of deploying a package.
  • Better User Information: Tools and feedback should be improved to show users how much deposit is being locked or released.
  • The realm subsidizes the storage deposit.
  • More examples to manage the storage deposit and refund.

@github-actions github-actions bot added 🧾 package/realm Tag used for new Realms or Packages. 📦 🤖 gnovm Issues or PRs gnovm related 📦 🌐 tendermint v2 Issues or PRs tm2 related 📦 ⛰️ gno.land Issues or PRs gno.land package related labels Mar 31, 2025
@piux2 piux2 changed the title storage deposit feat: storage deposit Mar 31, 2025
@Gno2D2
Copy link
Collaborator

Gno2D2 commented Mar 31, 2025

🛠 PR Checks Summary

All Automated Checks passed. ✅

Manual Checks (for Reviewers):
  • IGNORE the bot requirements for this PR (force green CI check)
Read More

🤖 This bot helps streamline PR reviews by verifying automated checks and providing guidance for contributors and reviewers.

✅ Automated Checks (for Contributors):

🟢 Maintainers must be able to edit this pull request (more info)

☑️ Contributor Actions:
  1. Fix any issues flagged by automated checks.
  2. Follow the Contributor Checklist to ensure your PR is ready for review.
    • Add new tests, or document why they are unnecessary.
    • Provide clear examples/screenshots, if necessary.
    • Update documentation, if required.
    • Ensure no breaking changes, or include BREAKING CHANGE notes.
    • Link related issues/PRs, where applicable.
☑️ Reviewer Actions:
  1. Complete manual checks for the PR, including the guidelines and additional checks if applicable.
📚 Resources:
Debug
Automated Checks
Maintainers must be able to edit this pull request (more info)

If

🟢 Condition met
└── 🟢 And
    ├── 🟢 The base branch matches this pattern: ^master$
    └── 🟢 The pull request was created from a fork (head branch repo: piux2/gno)

Then

🟢 Requirement satisfied
└── 🟢 Maintainer can modify this pull request

Manual Checks
**IGNORE** the bot requirements for this PR (force green CI check)

If

🟢 Condition met
└── 🟢 On every pull request

Can be checked by

  • Any user with comment edit permission

@Kouteki Kouteki moved this from Triage to In Review in 🧙‍♂️gno.land core team Mar 31, 2025
@Kouteki
Copy link
Contributor

Kouteki commented Mar 31, 2025

Closes #3418

@Kouteki Kouteki requested a review from jaekwon March 31, 2025 15:05
@zivkovicmilos zivkovicmilos added this to the 🚀 Mainnet beta launch milestone Apr 1, 2025
@piux2 piux2 requested review from moul and zivkovicmilos April 2, 2025 04:42
@@ -24,7 +24,7 @@ func main() {

// Realm:
// switchrealm["gno.land/r/test"]
// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:4]=
// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:4](2)=
Copy link
Contributor

Choose a reason for hiding this comment

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

i like this syntax.

@@ -0,0 +1,80 @@
# test the storage rent
Copy link
Contributor

@jaekwon jaekwon Apr 11, 2025

Choose a reason for hiding this comment

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

It isn't rent. It's ownership. (but we already use "owner" for objects so need a different term).
Rent is when you pay an upkeep for something.
GNOT deposits are forever.

just "deposit".

@@ -328,6 +330,7 @@ func (vm *VMKeeper) AddPackage(ctx sdk.Context, msg MsgAddPackage) (err error) {
creator := msg.Creator
pkgPath := msg.Package.Path
memPkg := msg.Package
send := msg.Send
Copy link
Contributor

Choose a reason for hiding this comment

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

note to self: is send amount segregated from storage deposits?
(the realm should not be able to spend its storage deposits)

m2.RunMemPackage(memPkg, true)

// use the parameters before executing the message, as they may change during execution.
// The message should not fail due to parameter changes in the same transaction.
Copy link
Contributor

Choose a reason for hiding this comment

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

wild edge case!

// QueryStorage returns storage and deposit for a realm.
func (vm *VMKeeper) QueryStorage(ctx sdk.Context, pkgPath string) (string, error) {
store := vm.newGnoTransactionStore(ctx) // throwaway (never committed)
// Ensure pkgPath is realm.
Copy link
Contributor

Choose a reason for hiding this comment

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

why not allow the query for packages? could be good to know, even if it is not mutable.

// An error is returned if there's insufficient deposit, a transfer error occurs,
// or an invariant violation happens during deposit or storage adjustments.

func (vm *VMKeeper) processDeposit(ctx sdk.Context, caller crypto.Address, deposit std.Coins, gnostore gno.Store, params Params) error {
Copy link
Contributor

Choose a reason for hiding this comment

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

process storage deposit

} else {
// release storage used and return deposit
released := -diff
if rlm.Storage < uint64(released) {
Copy link
Contributor

@jaekwon jaekwon Apr 11, 2025

Choose a reason for hiding this comment

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

note to self: why is this check needed?

rlmPath, rlm.Deposit, ugnot.Denom, depositUnlocked, ugnot.Denom))
}

err := vm.refundStorageDeposit(ctx, caller, rlm, depositUnlocked, released)
Copy link
Contributor

Choose a reason for hiding this comment

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

the refund should happen aggregated once, not per realm.

@@ -87,6 +87,24 @@ func Quotient8(a, b int8) (int8, int8, bool) {
return c, a % b, status
}

// AddUint8 performs + operation on two uint8 operands, returning a result and status.
func AddUint8(a, b uint8) (uint8, bool) {
Copy link
Contributor

Choose a reason for hiding this comment

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

this is cool, is it used?

// Unstable.
// This function is used to clear the object cache every transaction.
// It also sets a new allocator.
func (ds *defaultStore) ClearObjectCache() {
ds.alloc.Reset()
ds.cacheObjects = make(map[ObjectID]Object) // new cache.
ds.opslog = nil // new ops log.
ds.objectSizeCache = make(map[ObjectID]int64)
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's put this in ObjectInfo as unexposed lastObjectSize so as to avoid a map lookup.

@@ -86,6 +87,7 @@ func (msg MsgAddPackage) GetReceived() std.Coins {
type MsgCall struct {
Caller crypto.Address `json:"caller" yaml:"caller"`
Send std.Coins `json:"send" yaml:"send"`
Deposit std.Coins `json:"deposit" yaml:"deposit"`
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggest "MaxDeposit" so it's clear it's not just sending coins, it's doing something and you get something back.
Also json omitempty so that users don't have to worry about it usually.

Copy link
Contributor

@jaekwon jaekwon left a comment

Choose a reason for hiding this comment

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

Overall direction looks great, just needs some changes. Please call me after changes.

sysNamesPkgDefault = "gno.land/r/sys/names"
chainDomainDefault = "gno.land"
defaultDepositDefault = "600000000ugnot"
storagePriceDefault = "1000ugnot" // cost per byte (1 gnot per KB)
Copy link
Contributor

Choose a reason for hiding this comment

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

100, 1B GNOT == 10TB.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
📦 🌐 tendermint v2 Issues or PRs tm2 related 📦 ⛰️ gno.land Issues or PRs gno.land package related 📦 🤖 gnovm Issues or PRs gnovm related 🧾 package/realm Tag used for new Realms or Packages.
Projects
Status: In Progress
Status: In Review
Development

Successfully merging this pull request may close these issues.

5 participants