區塊鏈-NFT 的實現原理
作者:林冠宏 / 指尖下的幽靈。轉載者,請: 務必標明出處。
GitHub : //github.com/af913337456/
出版的書籍:
NFT (Non-Fungible Token),這2年又火了起來,早在18年已經火過一波。
本文只從寫代碼實現NFT的技術方案
層面去介紹 NFT,不從其金融意義、案例,等層面去談,因為這類內容可以隨便在瀏覽器搜索到,而我接下來要談的內容,淺層搜索下,數量不多。
第一步: 製作id
製作id,這是把物質製作成 NFT 的第一步。物質有哪些?一段文字、一張圖片,一件衣服等,誇張的說,現實世界的物質,無論是虛擬的(遊戲裝備)或實質物質,都可以被通證化。
如何操作?
通過第三方技術手段
獲取物質的唯一標誌性中間產物。
因此製作 NFT 第一步,廣義於下面等式:
- id = F(I)
- I = 輸入的物質
- F = 處理函數,代表一種方法
- id 唯一標誌性的中間產物
最簡單的例子就是哈希函數,不考慮哈希碰撞,它就可以根據不同的內容輸出不同的哈希值。思維在這裡不要局限於哈希函數。
獲取圖片的唯一id
這裡用圖片代表一系列的文件類數據。
- 我們可以將圖片轉換成 []byte 位元組數組,然後計算其哈希值。這種操作雖然比較簡單,但是圖片別人卻不能訪問,看不到;
- 如果我們想向外部任何人提供圖片的讀權限,在計算完 id 後,有兩種做法:
- 上傳圖片到文件服務器,任何人可以通過 url 鏈接訪問。這裡的服務器是中心化的;
- 增加區塊鏈屬性。上傳文件到 IPFS (ipfs是什麼,自行搜索),如此一來,文件別人能訪問,同時還具備了區塊鏈的去中心化等屬性。其中 IPFS 會在上傳完文件後,會使用它的算法,幫你計算好哈希值返回,可以直接用它的作為id。
獲取衣服的唯一id
這裡用衣服來代表一系列的實際物質。如果獲取它們的唯一id呢?做法可以放飛思維去思考,比如可以:
- 衣服的出廠信息、掃描內容、照片,等系列關於它的信息,數據化,然後用這些數據製作成文件,最後參考圖片的做法。
第二步:通證化
第一步中獲取了物質的id,現在要把它們通證化。切記一點:目前公認的 NFT 都是基於區塊鏈公鏈的,那麼以後是不是會一直這樣呢?不一定,說不準出來了新的共識。
基於不同公鏈的流程
通證化的流程如下:
- 選擇一條區塊鏈公鏈。這裡的選擇會決定後面
智能合約
等系統組件的技術棧,這一點很核心; - 在所選的公鏈上開發智能合約;
- 所開發的智能合約需要遵循一些基礎約定,比如至少能保證物質的id能達到驗證去重,什麼意思呢?意思是,如果 A 在今天上傳了 id=1 到鏈上,明天 B 也上傳同個 id=1 到鏈上,合約要能告訴 B,你不能上傳了,id 已經存在;
- 部署智能合約到鏈上,此時它變成 DApp;
- 通過發交易的方式,調用該智能合約的方法,將id等相關數據存儲到鏈上。
NFT 的智能合約
NFT 智能合約可以基於不同的公鏈開發,它不局限於任何一條公鏈。不同公鏈的智能合約方案實現也不同,下面以 以太坊 公鏈舉例說明。
在以太坊上面,開發 NFT 智能合約,已經有很多標準,比如 ERC-721 \1155 \998,各有各的特點,但它們的特點是在基礎屬性上拓展而來的。(各標準文檔: //eips.ethereum.org/EIPS/eip-721)
如果選擇 ERC-721 標準開發 NFT 智能合約,在元數據存儲部分,就有 tokenUrl 這項,它相當於物質的唯一id,像下面的樣子, _tokenURIs
存儲的就是通證當前計數id與其對應的 tokenUrl,這裡的tokenUrl 是字符串格式,一般是文件url,存儲在 IPFS 或其他服務上面的文件的鏈接,但不局限於鏈接,也可以是其它的內容。
// 偽代碼
contract MyERC721 is IERC721Metadata, ... {
...
mapping(uint256 => address) private _tokenOwner;
mapping(uint256 => string) private _tokenURIs;
uint256 public tokenCounter; // 計數,當前總的 NFT 的數量,累增
constructor () public ERC721 ("name", "symbol"){
tokenCounter = 0;
}
// 外部調用方,調用這個函數,傳參數:tokenURI 即物質的id,tokenURI 唯一
function createNFT(string memory tokenURI) public returns (uint256) {
uint256 tokenId = tokenCounter;
_mint(msg.sender, tokenId); // 將交易發送者和當前的 tokenId 綁定
_setTokenURI(tokenId, tokenURI); // tokenId 映射到 tokenUrl
tokenCounter = tokenCounter + 1; // 累加
return tokenId;
}
// _exists 函數判斷 tokenId 是否存在,_tokenOwner[tokenId]
// 根據 id 讀取對應的 url
function tokenURI(uint256 tokenId) external view returns (string memory) {
require(_exists(tokenId));
return _tokenURIs[tokenId];
}
// 根據 tokenId 和 url 建立 map 數據關係
function _setTokenURI(uint256 tokenId, string memory uri) internal {
require(_exists(tokenId)); // _exists
_tokenURIs[tokenId] = uri;
}
... // 省略系列接口,包含讀接口
}
上面的 tokenUrl 是標準要求的存儲數據項。整個合約具備下面約束功能:
- NFT 持有者,即 msg.sender(owner) 和 tokenId 一對多關係,代表一個人可以擁有多個 NFT;
- tokenId 和 tokenUrl 一對一關係,代表每份數據一個鏈上的唯一id,同時 tokenUrl 沒要求是唯一,但在調用方,一般會把 tokenUrl 設置唯一,即使不唯一也沒關係,衝突的時候,tokenId 越小的,其當初被設置的時間就越早;
- NFT 持有者在將數據寫入鏈上後,能夠獲取 NFT 的鏈上唯一 id,後續可以根據 id 進行系列的讀寫操作。
一般來說,我們常規的 NFT 有一個和數據建立關係的項就足夠了,但並不局限於此,合約在實現了標準要求的接口後,完全可以自己添加自定義數據項及其讀寫函數。
第三步:展示與修改
展示 NFT 內容
所謂展示,就是對 NFT 的數據進行讀取再展示。一般的流程如下:
- 根據當初設置 NFT 數據到鏈上時獲得的 id 去智能合約讀取信息;
- 將獲得的信息通過某介質應用還原出原始的 NFT 數據。
比如將圖片 NFT 展示出。(藉助上面的 721 合約標準和 IPFS 結合為例)
- 假設調用合約存儲數據時候得到的 tokenId 是 3,那麼使用這個 tokenId 去調用合約的讀數據方法;
- 執行完 1 步驟,可以得到 tokenUrl,即文件存儲在 IPFS 中所得到的鏈接;
- 直接將 tokenUrl 鏈接在瀏覽器打開,看到圖片。
修改 NFT 內容
修改是一項 NFT 智能合約的拓展功能,可有可無,具體是怎樣的方式,完全看需求的實現。比如:
- 允許重置 tokenId 所對應的內容;
- 在 NFT 原數據中增加其他字段內容,再允許修改這些字段;
- 轉讓 NFT,可以把某 tokenId 對應的 NFT 信息轉讓給其他 owner,達到轉讓目的;
出售 NFT
、拍賣 NFT
等操作….
所有權共識
目前 NFT,非同質化通證。本質是想藉助區塊鏈的屬性來標示一種資產的所有權證明。
比如曾拍賣出6000多萬美金的數字作品(圖片)《Everydays: The First 5000 Days》,中標者能獲得原圖 和 該圖的 NFT。這兩樣東西,一樣是實質的作品,一樣是它的所有權者的證明。
我們假設下,如果持有某作品的人,是一位匿名者A,過了多年後,該作品本身不小心被盜並被找回。那麼如何證明 A 是真正的擁有者,此時 A 只需要展示他對該作品的 NFT 擁有權,就可以證明。
那麼 NFT 是不是類似於我們現實中的證書?不全是,分兩點:
- NFT 和證書都能證明某資產的所有權;
- 對比的存儲介質 與 永恆時效:
- 證書可能要找個保險柜保養放着,但它終究佔據一方土地,僅受一方土地容納的保險柜保證安全,在時過境遷的影響下,持續性存儲下去的時間會較短;
- NFT 存儲在區塊鏈上,受整個互聯網的鏈節點所保護。它能夠存活到整個鏈網絡垮掉那天,對於節點數量眾多的公鏈來說,這個概率幾乎等同於互聯網終結那天。
第三方平台
現在已經有很多的第三方的 NFT 製作與發佈平台。比如 opensea、rarible 等,這些平台自己實現了 NFT 的智能合約 和 NFT 展示應用(介質應用—網站),方便大眾 0 代碼基礎體會 NFT。但也有一些門檻,需要具備錢包和發交易的油費。