CKBFS Protocol: A standard for Witnesses based content storage system

Short Abstract

CKBFS is a protocol designed to describe a witnesses based file storage system. With CKBFS, one can:

  • Store files on Nervos CKB Network
  • Publish large files that may exceed block size limitation(~500kb), across multiple blocks, while still keep the index and retrieve action simple and straight

It contains:

Backgrounds

Storing files on the blockchain can become troublesome and hard to manage. Solutions like Ordinals and DOBs on RGB++ use similar methods to inscribe images, text, and other content on BTC witnesses. While it works and becomes basement the current DOB ecosystem, it have some identical drawbacks:

  • No oracle guarantees
  • High costs
  • Even if you develop a native dapp on CKB, you still need to build various infrastructures across two chains

These led to the idea of designing this protocol.

Leveraging CKB’s powerful type system and programming model, this protocol was designed to be simple, flexible, usable, and reproducible.

Core Concepts

Core concepts of CKBFS is:

  • One single cell(called CKBFS cell) to index one file, even multi-part file that splited across blocks.
  • Permanent storage. Deletion is forbidden through contract, which means the file metadata will never lost. Which also means you will need to lock some CKB capacity FOREVER in order to create it.
  • Simple cell structure, encoded with molecule.
  • Built-in checksum provided, both on-chain and off-chain side.

The Protocol - or, the standard

Data Structure

CKBFS Cell

CKBFS Cell is a cell that stores:

A CKBFS cell in should looks like following:

Data:
  content_type: Bytes # String Bytes
  filename: Bytes # String Bytes
  index: Uint32Opt # referenced witnesses index.
  checksum: Uint32 # Adler32 checksum
  backlinks: Vec<BackLink>

Type:
  hash_type: "data1"
  code_hash: CKBFS_TYPE_DATA_HASH
  args: <TypeID, 32 bytes>,[<hasher_code_hash>, optional]
Lock:
  <user_defined>

The following rules should be met in a CKBFS cell:

  • Rule 1: data structure of a CKBFS cell is molecule encoded. See Molecule definitions below.
  • Rule 2: checksum MUST match with specified witnesses. Default checksum algorithm will be Alder32 if not specify hasher_code_hash in Type script args.
  • Rule 3: if backlinks(see definition below) of a CKBFS cell is not empty, it means file was stored across different transactions.
  • Rule 4: if hasher_code_hash is specified, then it will use hasher binary from CellDeps that matches code_hash, with same input parameter.
  • Rule 5: Once created, a CKBFS cell can only be updated/transfered, which means it can not be destroyed.

BackLink

BackLink stands for the prefix part of a living CKBFS cell. The strategy of CKBFS is similar to a linked list:

[backlink]←[backlink]←[…]←[CKBFS cell]

BackLink:
  tx_hash: Bytes,
  index: Uint32,
  checksum: Uint32,

Witnesses

File contents are stored in witnesses.

Witnesses:
  <"CKBFS"><0x00><CONTENT_BYTES>

The following rules should be met for witnesses used in CKBFS:

  • Rule 6: The first 5 bytes must be UTF8 coded string bytes of CKBFS, which should be: 0x434b424653
  • Rule 7: The 6th byte of witnesses MUST be the version of CKBFS protocol, which should be: 0x00.
  • Rule 8: File contents bytes are stored from 7th byte. Checksum hasher should also take bytes from [7…].

Operations

This section describes operations and restrictions in CKBFS implementation

Publish

Publish operation creates one or more new CKBFS cell.

// Publish
Witnesses:
  <...>
  <0x434b424653, 0x0, CKBFS_CONTENT_BYTES>
  <...>
Inputs:
  <...>
Outputs:
  <vec> CKBFS_CELL
    Data:
      content-type: string
      filename: string
      index: uint32
      checksum: uint32
      backlinks: empty_vec
    Type:
      code_hash: ckbfs type script
      args: 32 bytes type_id, (...)
  <...>

Publish operation must satisfy following rule:

  • Rule 9: in a publish operation, checksum MUST be equal with hash(Witnesses[index]).

Append

Append operation updates exist live CKBFS cell, validates the latest checksum.

// Append
Witnesses:
  <...>
  <CKBFS_CONTENT_BYTES>
  <...>
Inputs:
  <...>
  CKBFS_CELL
    Data:
      content-type: string
      filename: string
      index: uint32
      checksum: uint32
      backlinks: empty_vec
    Type:
      code_hash: ckbfs type script
      args: 32 bytes type_id, (...)
  <...>
Outputs:
  <...>
  CKBFS_CELL:
    Data:
      content-type: string
      filename: string
      index: uint32
      checksum: uint32 # updated checksum
      backlinks: vec<BackLink>
    Type:
      code_hash: ckbfs type script
      args: 32 bytes type_id, (...)
  • Rule 10: backlinks field of a CKBFS cell can only be appended. Once allocated, all records in the vector CAN NOT be modified.
  • Rule 11: new checksum of updated CKBFS cell should be equal to: hasher.recover_from(old_checksum).update(new_content_bytes)
  • Rule 12: content-type, filename, and Type args of a CKBFS cell CAN NOT be updated in ANY condition
  • Rule 13: in an append operation, Output CKBFS Cell’s index can not be null

Transfer

Transfer operation transfers ownership of a CKBFS cell, and ensure it did not lost tracking of backlinks.

// Transfer
Witnesses:
  <...>
Inputs:
  <...>
  CKBFS_CELL
    Data:
      content-type: string
      filename: string
      index: uint32
      checksum: uint32
      backlinks: empty_vec
    Type:
      code_hash: ckbfs type script
      args: 32 bytes type_id, (...)
    Lock:
      <USER_DEFINED>
  <...>
Outputs:
  <...>
  CKBFS_CELL:
    Data:
      content-type: string
      filename: string
      index: null
      checksum: uint32 # updated checksum
      backlinks: vec<BackLink>
    Type:
      code_hash: ckbfs type script
      args: 32 bytes type_id, (...)
    Lock:
      <USER_DEFINED>
  • Rule 14: in a transfer operation, Output CKBFS Cell’s index MUST be null
  • Rule 15: if Input CKBFS Cell’s backlinks is empty, then output’s backlink should be append following Rule 10. Otherwise, the backlinks should not be updated
  • Rule 16: in a transfer operation, checksum CAN NOT be updated

Other Notes

Molecule Definitions:

Here’s molecule definitions of CKBFS data structures

vector Bytes <byte>;
option BytesOpt (Bytes);
option Uint32Opt (Uint32);

table BackLink {
  tx_hash: Bytes,
  index: Uint32,
  checksum: Uint32,
}

vector BackLinks <BackLink>;

table CKBFSData {
  index: Uint32Opt,
  checksum: Uint32,
  content_type: Bytes,
  filename: Bytes,
  backlinks: BackLinks,
}

Checksum Validator Procedure:

Bellow is pseudocodes shows how one can validates the checksum:

function validate_checksum(witness, expected_checksum, backlinks);
var
  hasher: thasher;
  computed_checksum: uint32;
  content_bytes: bytes;
  last_backlink: backlink;
begin
  // If backlinks is not empty, recover hasher state from the last backlink's checksum
  if length(backlinks) > 0 then
  begin
    last_backlink := backlinks[length(backlinks) - 1];
    hasher.recover(last_backlink.checksum);
  end;

  // Extract the content bytes from the witness starting from the 7th byte
  content_bytes := copy(witness, 7, length(witness) - 6);
  
  // Update the hasher with the content bytes
  hasher.update(content_bytes);

  // Finalize and compute the checksum
  computed_checksum := hasher.finalize;
  
  // Compare the computed checksum with the expected checksum
  if computed_checksum = expected_checksum then
    validate_checksum := true
  else
    validate_checksum := false;
end;

Advanced Usage - Branch Forking File Appendix

Assuming that we have created a CKBFS Cell:

CKBFS_CELL:
  Data:
    content-type: string
    filename: string
    index: 0x0
    checksum: 0xFE02EA11
    backlinks: [BACKLINK_1, BACKLINK_2, ...]
  Type:
    code_hash: CKBFS_CODE_HASH
    args: TYPE_ID_A
  Lock:
    <USER_DEFINED>

It is able to creating a forking of this CKBFS by a special publish, similar to append but put the referenced CKBFS Cell in CellDeps:

CellDeps:
  <...>
  CKBFS_CELL:
	  Data:
	    content-type: string
	    filename: string
	    index: 0x0
	    checksum: 0xFE02EA11
	    backlinks: [BACKLINK_1, BACKLINK_2, ...]
	  Type:
	    code_hash: CKBFS_CODE_HASH
	    args: TYPE_ID_A
	  Lock:
	    <USER_DEFINED>
	<...>
Witnesses:
  <...>
  <CKBFS_CONTENT_BYTES>
  <...>
Inputs:
  <...>
Outputs:
  <...>
  CKBFS_CELL
    Data:
      content-type: string
      filename: string
      index: uint32
      checksum: UPDATED_CHECKSUM
      backlinks: [BACKLINK_1, BACKLINK_2, ...]
    Type:
      code_hash: ckbfs type script
      args: TYPE_ID_B
  <...>

And we are able to create a variant versions from a same reference data, allowing us to achieve something like git branching, header-shared data, etc.

12 Likes

Here are example transactions that use deployed testnet contract. The final whole content is:

HELLO CKBFS
HELLO CKB
By Code Monad, 2024
3 Likes

Hey @codemonad, congratulations on your hard work, finally a design for an IPFS on Nervos L1!! :partying_face:

I was wondering, could you detail a bit more which data is immutable and which data is mutable in this design?

3 Likes

While it’s all described in “Rules”, i can give you a simple summary about the mutability if you don’t understand.

The structure
Here’s the data structure of a CKBFS Cell:

Data:
  content_type: Bytes # String Bytes
  filename: Bytes # String Bytes
  index: Uint32Opt # referenced witnesses index.
  checksum: Uint32 # Adler32 checksum
  backlinks: Vec<BackLink>

and BackLink’s Structure:

BackLink:
  tx_hash: Bytes,
  index: Uint32,
  checksum: Uint32,

What is immutable

  • CBFS’s content_type
  • CKBFS’s filename
  • all allocated Backlinks. Which means all fields in Backlink can not be modified if it was already appended to the CKBFS Cell’s data.

What is mutable

  • CKBFS’s index. this should be referenced to the witness index everytime you append(write) a new content part to the file
  • CKBFS’s checksum this should be updated everytime the content body changes.
  • CKBFS’s backlinks can be appended(push new items), but can not delete and modify previous items.
2 Likes

Sorry, still not seeing the full picture :thinking:

I’ll propose a simple ideas (that you likely already considered) and you tell me why you choose the proposed one. @xxuejie did the same with the iCKB design, so it’s a fair game.

Diffs

Reading once again your proposal, I noticed that the the underlying reason why you choose the linked list (instead of other more commonly used structures in FS like trees of inodes) is because you can recover the previous checksum and use it to checksum the full file. This is smart because:

Now we have a different issue, diffs.

Let’s assume we have a JSON split across 100 txs and I need to update the one in the first tx, then in your design I have to re-deploy all 100 txs, in correct? Could you detail more the Branch Forking File section?

Maybe it would be easier to support only immutable data, but having a CKBFS that fully support diffs would prime it for broader adoption.

Relaxing the assumption on the checksum of the full file and so the necessity of using a linked list, we could switch back to the usual trees of inodes seen in filesystem and Merkle trees.

Sure, we may lose the ability to reference a file with a checksum (as the same file may have many different checksum, depending on the underlying structure), but is this really relevant?

We can just anchor all references to the root inode OutPoint.

Pretty sure there are better and more formalized designs based on an immutable FS and Merkle trees, this is just to give an idea.

So, why did the presented design uses a Linked List instead of an inode Tree?

Homogeneity

The proposed design seems homogeneous, maybe too homogeneous.

Data:
  content_type: Bytes # String Bytes
  filename: Bytes # String Bytes
  index: Uint32Opt # referenced witnesses index.
  checksum: Uint32 # Adler32 checksum
  backlinks: Vec<BackLink>

Why does every element has to contain content_type and filename?

Every element in the list except the root does not seem to need this particular information.

Why putting the full backlinks: Vec<BackLink>?

Strictly speaking, only the last one is used in the checksum verification, the rest is only used by the off-chain system to get all the relevant information faster. (An inode design on the other side would make full use of this info tho)

Generally, why putting all this information in cell data?

Once we put the checksum as data and the cheksum is checked, the safety of information in witness is aussured, why not moving all this data to the witness?

Why not employing at least two types of cells?

One type could store the files metadata and link all the data, the other one would be used to store the actual data.

Why every lock needs to be <USER_DEFINED>?

Every element in the list (except possibly the root) does not seem to need this particular information as the inner cells are immutable, correct?

Why not directly indicating an always-failure lock?

For now back to working on iCKB, I’ll be waiting for your replies. In the meantime, I wish you a nice day :hugs:

Love & Peace, Phroi

1 Like

given the diversity of regulation in the world, we should assume that some nodes will prune some data that is committed to the chain.

Is there anywhere consideration for this should be made in CKBFS?

3 Likes

I think you may misunderstood something about this design.
Back to the core concepts, there’re a few points i do want to mention about:

  1. only use one single cell to index one file:
    CKB has a block size limitation, so the point we upload our file into multiple part is not just we want multipart, it is because we do not want to limit to just ~500Kb.

  2. simple cell structure:
    Just like how i designed the Spore protocol, introducing extra complexity will always be treat with ultra high caution.
    The goal of CKBFS is NOT a reimplementation of somehow existed filesystems, and is NOT a port of unix-style fs structure. It is for file STORAGE, just as this thread title says. And do what feats the CKB model itself.

Simplicity is important to me, and also when distributing generic protocols or proposals. Take in one thing is simple, but we need to consider whether if it is necessary, does it provide anything unique and important.

also in the design, i have written something like:<vec>CKBFS Cell, and it does not mean we need splitted cells in order to publish one file. it just means you can publish multiple CKBFS Cell at once, with no issue, and they will not influence each other.

And here i will add responses to your questions, maybe not order by order of your original post, but i hope it can give you and other readers a more clear image of this design.

So, why did the presented design uses a Linked List instead of an inode Tree?

The first important thing is to keep the complexity in control.
As i mentioned previously, this is not a port of unix-style filesystem, but i do want to point out that the layout, or the design philosophy is similar(tho not same) to what you would usually see on a realworld’s inode. Their core abstract are similar.

Why every lock needs to be <USER_DEFINED>?

As you can check in many CKB’s official RFC, and also the Spore Protocol’s RFC, we can see it anywhere. <USER_DEFINED> means we do not care what lock it uses. it can be a simple secp lock, or a omnilock, or anyonecanpay, or a custom implemented lock script. thats is why we have two scripts. by composing different lock scripts with type scripts, we can implement many different features and reusing generic parts we want.

For example, i can put an “anyone can pay” lock to this ckbfs cell, and it just simply becomes a “co-creation” work, just like in the realworld you can share a notebook to others to continue your story on it. remember this is just a example i provided for you to understand why we can, and should allow users to use different locks. putting “always fail” on it will means: this fill can not be modified once published, which may also be a way to use it if you want a certain feature like this.

Why does every element has to contain content_type and filename?

Just ensure we did not lost how the design is, one cell is just for one file. we are not composing multiple cells. content_type and filename are hints for “users” that may referencing and get the file. for example dapps, indexers.

Why putting the full backlinks: Vec<BackLink>?

If we do not want to lost tracking for the full contents, i think this is necessary. checksum is just a verification – you sure will want this if you are retrieving files from remote.

Generally, why putting all this information in cell data?

The core design may follow this: the actual data goes to witnesses, metadata goes to cell data. since we want the cell to be the key we reference it. a living cell means a existence of a file. and also prevents people falling into unexpected data correction hell. And also, this will ensure that people can not do scamming stuffs, since type script can help us verify it everytime we operates on chain.


Last part might be a bit longer and verbose, so i put a split line before this.

Could you detail more the Branch Forking File section?

For example, i have a text file:

HELLO CKBFS
HELLO CKB

which the adler-32 checksum of this would be: 1014105452
And i published it as a CKBFS Cell#1:

Data:
  content_type: "plain/text"
  filename: "hello_ckbfs.txt"
  index: 0 # just assume we put it at Witnesses[0] while publishing
  checksum: 1014105452
  backlinks: []

Now i want to publish another file, that put content in tail of CKBFS Cell#1, so that the content will be:

HELLO CKBFS
HELLO CKB
HELLO BTC

and i will get a different CKBFS Cell#2, the publish operation tx will looks like this:

CellDeps:
  <...>
  CKBFS_CELL #1:
	  Data:
	    content-type: "plain/text"
	    filename: "hello_ckbfs.txt"
	    index: 0x0
	    checksum: 1014105452
	    backlinks: []
	  Type:
	    code_hash: CKBFS_CODE_HASH
	    args: TYPE_ID_A
	  Lock:
	    <USER_DEFINED>
	<...>
Witnesses:
  <CKBFS, 0x00, "HELLO BTC" >
  <...>
Inputs:
  <...>
Outputs:
  <...>
  CKBFS_CELL
    Data:
      content-type: "plain/text"
      filename: "hello_ckbfs_btc.txt"
      index: 0x0
      checksum: 2136344547
      backlinks: 
        BackLink: 
            tx_hash:  TX_HASH_1 #`CKBFS#1`'s Outpoint.tx_hash
            index: 0x0 # keep it as same as #1
    Type:
      code_hash: CKBFS_CODE_HASH
      args: TYPE_ID_B
  <...>

Notice that after this, we will get TWO different files(CKBFS Cell), one is CKBFS Cell#1, and CKBFS Cell#2, they be belongs to different locks(different user), and they can do updates(appends) to their cells without any confliction.
Like Alice is gonna update #1 into:

HELLO CKBFS
HELLO CKB
HELLO RGB++

And Bob can still update #2 into:

HELLO CKBFS
HELLO CKB
HELLO BTC
HELLO DOB

Here we got a “branching forking” feature, just like how we use git. Remember we are using Outpoint.tx.hash as the actual value, if it does not meet, will cause a failure. just use load_input_out_point(index, Source::CellDep).tx.hash() as you can see in the implementation

1 Like

since we can not control how node runners treat with historical datas, especially only through contract scripts, i think it is hard to say “what we can do with this protocol”

just like how we encourage miners, we can do something like how PT(Private Torrent Trackers) works

Usually BT protocols will lost their ability to download/seeking files if no one is seeding(uploading) anymore. PT have many interesting rules to keep torrents alive, for example they will give rewards to people who provides upload workloads, and downloads will cause people’s share ratio downgrades so that their download speed will be limited, etc.

Just like when we launched CKB Node Probe, we want more people start running full nodes, to make the network stronger. it do needs to consider how people would like to store historical committed datas.

5 Likes

I don’t think cells can’t not be destroyed is a good idea. this will let ckb unavailable in a long future.

it looks like a small amount of CKB though, the data is needed to make sense of the data in witness from what i gather

4 Likes

I do think the immortal option should still be available, because for those who intend to use this option for their particular array of requirements on their particular projects.

2 Likes

why use CKBFS instead of Spore in this case?

1 Like

I thought the same, and had conversation on imagiNation discord. For my limited scope of Proof-of-Ownership, I’ll use Spore.

2 Likes

Although this reply comes after some time, I feel it’s important to revisit and elaborate on this topic. I’ve noticed that many perceive this aspect as merely a design preference. However, it fundamentally concerns the network’s health, future development, and the philosophy of responsibly utilizing decentralized resources.

This design choice in CKBFS’s immortal can also be find in Spore protocol, where a cluster cell is also not able to destroy. But they have different meanings.​

This isn’t merely a design preference but a fundamental limitation rooted in how CKBFS manages the balance between data and costs. Specifically, CKBFS stores data within transaction witnesses, meaning the cell’s data serves primarily as a reference to the actual content.​

If we permitted users to destroy the actual cell, the witnesses would continue to grow indefinitely. This scenario would allow users to store data across distributed full nodes while incurring minimal gas costs, and only need to pay ONCE, leading to potential abuse and unsustainable network growth. Such an outcome contradicts the principles of fair resource usage and network sustainability.​

Let’s say, to “pin” a file on the network effectively, users must lock their capacities. Given that the file data becomes a permanent part of the network, it’s equitable that users commit their capacities.

3 Likes

I’ve thought on ideas for CKBFS and the biggest thing that I don’t think scales well is the backlinks, as I believe Phroi was pointing out early on. CKBFS can be used for all sorts of things: gallery, blog, IPFS-style storage, messaging, history, forum storage, etc.

Even if Spore can do something similar, the distinction is that Spores represent real-world assets, while CKBFS is purely informational and always permanent.

The cost grows exponentially, and as CKB appreciates, it only gets worse. Take the 100-transaction example: someone uploads a 50 MB file, which I believe is reasonable for many users varying on the data they are dealing with, but with appends averaging 60 CKB each (I believe) and an initial cell of around 200 CKB, you’re looking at ~6 200 CKB.

Whether it’s enforced in the contract or handled by a dApp, charging 10 CKB per append could make things feasible and can be lowered later for scaling if it ever got there. There could be a first-upload discount, so instead of 1 000 CKB for 100 appends, maybe only 500 CKB. Thinking later, lets say at “2 cents” per CKB, that’s a massive difference.

Maybe the cell data only holds the latest backlink, and a checksum of (current witnessData + previous data & witnessData), letting any dApp validate the entire history from end to start.

Also on the topic of Witness Data, if it can be pruned, maybe there’s a way to sync with miners or align incentives so CKBFS data stays intact? Especially with the permanent-state rent being applied. The long term incentive is actually on the miner since they get secondary distribution from the network based on more CKB locked, correct?

This is where “archive nodes” might also come in. This could even expand into “pinning,” and services where holding witness data is important, so users pay extra to guarantee long-term data availability.

Anyway just wanted to throw my 2 cents into it because I think CKBFS has huge potential for a permanent storage solution if scaling is better considered from all ends.

1 Like

Exactly, that’s the elephant in the room:

We all agree that once the witness economic model is fixed, protocols like CKBFS could make sense. Before that, we should not rely on witness for data availability.

Another angle from your suggestion came from a friend: a distributed search of witness data by hash and we could use micro-payments to incentivize nodes. Fiber would be a perfect candidate, similar to what Lightning folks did some time ago with video streaming. Node operators would be pretty happy too.

Open Questions:

  • What is a proven working model for this kind of incentives?
  • Payment at Transaction mining? (Already in place, they don’t go to nodes but miners)
  • Payment at RPC Request? (Great for micropayment, not great at defining witness lifetimes)
  • Micro-payment subscription for keeping the data? (Like your pinning idea, interesting)
  • How can we tell if the data we receive is actually the right data? (Not tampered)

Love & Peace, Phroi

2 Likes

I think if using CKB as a source of storing data, it’s for a long term mindset, looking at projects like Filecoin. Or maybe for the sake of interoperability their is hybrid methods, where CKBFS is used, but stores a reference to a 3rd party “decentralized” solution. Arweave, Storj, IPFS, etc.

What is a proven working model for this kind of incentives?

Arweave with a cost on upload, might be similar and seems to be working out for them.

But not strictly blockchain related is Pinata as a service using IPFS as its storage.

Payment at Transaction mining? (Already in place, they don’t go to nodes but miners)

The payment at mining (or cell deploy) would be based on the dApp (service) since an interface is required, this could include a cost for holding something forever (base_cost * size) or have an added measure of (* length) where length is the minimum time this service will keep it available.

Payment at RPC Request? (Great for micropayment, not great at defining witness lifetimes)

I don’t think the RPC scenario changes unless there are dedicated service, in which people pay for private access instead of spinning up a local Node, but these services are tied in with the data-holding and have “pinned” data “ready to go”. Even a “Staking” mechanism could prove you’re subscribed and allows you to have an API Key or however access would look.

Micro-payment subscription for keeping the data? (Like your pinning idea, interesting)

I think the subscription model could work too, but is also service dependant. Like maybe you pay 100CKB a month to host 10,000 bytes of data. And could be used on the RPC calls too, 100 CKB = 5,000 RPC calls, etc.


Another thing could be “Long Term Recovery”. If services are holding archives of data. What if you generated data today, then in 15 years wanted to recover it, an archive node somewhere still has this data and you would be super happy to pay, if it was worth it.

One big thing would be a loss of a file (for any reason), would be services being honest or maybe a built-in smart contract that’s job is to validate this service’s data and if a user checks for their checksum and it comes back empty, a refund could happen, but this is probably a whole topic and rabbit hole as well but comes down to services, less blockchain specific, just using smart contracts to tie it together…

How can we tell if the data we receive is actually the right data? (Not tampered)

That’s where the checksums (or similar mechanism if swapping to merkle, etc) would play a big role, because if you get data a year later or 5 years later, you still want to verify what you have received 100%. So this kind of service wouldn’t work for normal cells (without danger) given there is no validation.

And then just from using this protocol, miners would be incentivized over the longer term with the growing permanent-state rent.

Another play at this would be extending CKBFS to have a mechanism that if “X” time has passed (hardcoded timestamp in cell data perhaps), a user could reclaim part of the CKB (or all that isn’t capacity for the Cell). and the time built-in could be 1, 5, 10 years, something that is longer term that is creating some incentives for the network.

All of this comes down to how much do miners/node-runners/users want a permanent storage on this Network? As I opened up with we could utilize extending to Arweave or Filecoin, etc to keep things decentralized and not bloat Nervos while not relying on what could be inconsistent data in the future. (I don’t see pruning being a real issue, but who knows what data is looking like in 10 years??)

I could go on to maybe draw in the direction of ‘iCKB’ and how providers utilize DAO Staking with all this CKB users could be throwing to store permanent data. Something like data_size * retention_blocks * base_rate and penalties could be involved if proper validation can occur, but lets leave it simple since im really going on…

And lastly… When I think of NOSTR as an example, it just reminds me that data lives locally and some data could get lost because of anything. This idea could be a NOSTR on CKB so that every other person doesn’t have to spin up a local node for their own data. Because encrypted data could also be used, the checksum in the cell data can validate it and only the owner knows how to decode it, etc. They could be used together, but I’m going to end my rambling here :smiley:

When Mimblewimble!

this does require additional mechanisms to enforce the agreement, which unfortunately expands the scope of the idea significantly

i think holding (roughly) the entire chain history is reasonable, right now it can grow at ~2.3tb/year, as solid state drives get better, even if this increases it should remain reasonable

i could see someone paying the store the entire chain on one of these services but it seems more likely they would just own the hardware themselves (because it’s a reasonable requirement)… maybe there’s sort of public goods funding that could pay for storing the chain history for x years

yea i imagine like a rolling auction, if no one delivers the data the bid is increased until ultimately a node returns it

https://x.com/roasbeef/status/911415042368614400

this idea has always been interesting to me, the user should have an idea of what data they’re looking for and can verify as they start to receive it that it’s correct