Skip to content

Commit ebf34f3

Browse files
authored
Merge pull request ipfs#1362 from ipfs/immutability/update-cids
add: section on updating cids
2 parents 70a1af8 + 6de8fc3 commit ebf34f3

File tree

1 file changed

+46
-6
lines changed

1 file changed

+46
-6
lines changed

docs/concepts/immutability.md

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,15 @@ related:
1212

1313
# Immutability
1414

15+
## Overview
16+
1517
An immutable object is an object whose state cannot be altered or modified once created. Once a file is added to the IPFS network, the content of that file cannot be changed without altering the [content identifier (CID)](../concepts/content-addressing.md) of the file. This feature is excellent for storing data that does not need to change. However, when it comes to content that needs to be altered or updated, immutability becomes a problem. This page discusses how to keep a mutable _state_ built from immutable building blocks.
1618

1719
A CID is an _absolute_ pointer to content. No matter when we request a CID, the CID value will always be the same. This is part of the content's architecture and cannot be changed. To manage _immutable_ files in a _mutable_ system, we need to add another layer that sits on top of CIDs.
1820

1921
As a basic example, let's have two blocks of content with the strings `hello` and `world` hashed into two leaf nodes with the CIDs `A` and `B`. If we concatenate these two nodes, then we are given CID `C`. On top of this root CID, we assign a pointer `Pointer`.
2022

21-
```
23+
```shell
2224
+-----------+
2325
| Pointer |
2426
+-----------+
@@ -35,7 +37,7 @@ As a basic example, let's have two blocks of content with the strings `hello` an
3537

3638
If we change the content of `B` to `IPFS!`, all the upstream paths will change as well. In this simple example, the only upstream path is `C`. If we request content from the pointer we get back new content since the pointer is now _pointing_ at a completely different node. Node `B` is not being edited, updated, or otherwise changed. Instead, we are creating a new DAG where the pointer points to CID `E` that joins node `A` and a new node, node `D`.
3739

38-
```
40+
```shell
3941
+-----------+
4042
| Pointer | --------------+
4143
+-----------+ |
@@ -52,7 +54,7 @@ If we change the content of `B` to `IPFS!`, all the upstream paths will change a
5254

5355
Again, node `B` does not change. It will always refer to the same content, `world`. Node `A` also appears in the new DAG. This does not necessarily mean we copied the memory/buffer that contained the `hello` string into our new message; that would imply the location-addressed paradigm that focuses on the _where_ and not the _what_. In a content-addressed system, any time someone writes the string `hello` it will always have CID `A`, regardless of whether we copied the string from a previous location or we wrote it from scratch.
5456

55-
## Website explanation
57+
### Website explanation
5658

5759
Here we have a website that displays two headers called `header_1` and `header_2`. The content of the headers is supplied from the variables `string_1` and `string_2`.
5860

@@ -73,15 +75,15 @@ The CID of this website is `QmWLdyFMUugMtKZs1xeJCSUKerWd9M627gxjAtp6TLrAgP`. Use
7375

7476
Having a user visit the site using the CID is cumbersome since the CID will change every time a variable is updated. So instead, we can use a _pointer_ that maintains the CID of the page with the latest update. This way, users can go to `example.com`, and always be directed to the latest content. This pointer is _mutable_; it can be updated to reflect the changes downstream.
7577

76-
```
78+
```shell
7779
+--------+ +---------+ +----------+
7880
| User | ---> | Pointer | ---> | QmWLd... |
7981
+--------+ +---------+ +----------+
8082
```
8183

8284
In the website example, when we change a variable, the CID of the webpage is different. The pointer must be updated to redirect users to the latest webpage. What's important is that the _old_ CID still exists. Nothing is overwritten. The original CID `QmWLdyFMUugMtKZs1xeJCSUKerWd9M627gxjAtp6TLrAgP` will always refer to a webpage with the headers `hello` and `world`. What we're doing is constructing a new [DAG](../concepts/merkle-dag.md).
8385

84-
```
86+
```shell
8587
+--------+ +---------+ +----------+
8688
| User | ---> | Pointer | | QmWLd... |
8789
+--------+ +---------+ +----------+
@@ -93,8 +95,46 @@ In the website example, when we change a variable, the CID of the webpage is dif
9395

9496
This process is essentially what the [InterPlantery Naming Service (IPNS)](../concepts/ipns.md) does! CIDs can be difficult to deal with and hard to remember, so IPNS saves users from the cumbersome task of dealing with CIDs directly. More importantly, CIDs change with the content because they are the content. Whereas the inbound reference of URLs/pointers stay the same, and the outbound referral changes:
9597

96-
```
98+
```shell
9799
+--------+ +----------------+ +-------------------------------------------------------------+
98100
| User | ---> | docs.ipfs.tech | ---> | bafybeigsddxhokzs3swgx6mss5i3gm6jqzv5b45e2xybqg7dr3jmsykrku |
99101
+--------+ +----------------+ +-------------------------------------------------------------+
100102
```
103+
104+
## Updating CIDs
105+
106+
### Using IPNS to create pointers to CIDs
107+
108+
To avoid the pitfalls of immutability and ensure that CIDs accurately reflect the current state of content, developers can use IPNS to create pointers to CIDs that can be updated. Instead of updating the CID itself, developers can update the IPNS address, which points to the current CID. This allows users to reference a stable, fixed IPNS address while still being able to access the latest version of the content. Learn more about IPNS [here](ipns.md).
109+
110+
### Using smart contracts to manage CIDs
111+
112+
Another approach is to use smart contract logic to manage the CIDs in an application. For example, if your application receives a CID from a smart contract, you can use smart contract functions to update the CID given to users. This allows you to change the CID without breaking the integrity of the content.
113+
114+
First, you will need a smart contract that manages the CID for the content you want to update. This contract should have a function that constructs the `tokenURI` for the content, using the `baseURI` stored in the contract's storage. For example:
115+
116+
```py
117+
def tokenURI(this, tokenID: uint256) -> string:
118+
return this.baseURI + str(tokenID) + ".json"
119+
```
120+
121+
This function returns a `tokenURI`. It will look something along the lines of `ipfs://Qmfoo/1234.json`, where _1234_ is the `tokenID` for the content.
122+
123+
Next, you will need to add a setter function to the contract that allows the `baseURI` to be updated. This function should only be accessible to the contract owner to ensure that only authorized users can update the CID. For example:
124+
125+
```py
126+
def setBaseURI(this, newBaseURI: string):
127+
require(msg.sender == this.owner)
128+
this.baseURI = newBaseURI
129+
```
130+
131+
To use this function, you can call it from your contract's code or interact with it using a tool like Remix.
132+
For example, to update the `baseURI` to `ipfs://Qmbar/`, you could use the following code:
133+
134+
```shell
135+
contract.setBaseURI("ipfs://Qmbar/")
136+
```
137+
138+
This will update the `baseURI` in the contract's storage, and the `tokenURI` function will now return `ipfs://Qmbar/1234.json` instead of `ipfs://Qmfoo/1234.json`.
139+
140+
This is one way you can use smart contract logic to manage CIDs, without changing the CID itself. This allows you to ensure that the CID accurately reflects the current state of the content and enables users to access the latest version of the content using a stable, fixed `tokenURI`.

0 commit comments

Comments
 (0)