區塊鏈的基本理論與實踐

一、前言

1.1 敘敘舊

對於區塊鏈的學習,自己是從大四做畢設的時候開始。看到網上的文章,文章質量層次不齊,要麼只是講述理論,要麼就是貼幾行代碼,對於初學者很不友好。由於一些原因,最近再次接觸到了區塊鏈,加上有重新開始寫博客的打算,就將它作爲第一篇文章發佈吧。

1.2 文章摘要

這篇文章主要圍繞基於以太坊的區塊鏈開發,主要分爲兩個模塊:區塊鏈的理論介紹和相關的學習資料推薦;前端通過JavaScript語言,利用瀏覽器插件MetaMask作爲錢包管理工具,使用官方提供的web3.js庫實現和以太坊進行交互。

二、區塊鏈基本理論


提到區塊鏈,對於非區塊鏈開發者,想到的大概是上面這些詞語。關於上面的詞語在網上隨處可以找到解釋,但所有的解釋卻似乎千篇一律,下面我將以我的理解通俗的簡單的解釋一下這幾個詞語。

2.1 比特幣

我認爲“中本聰之於比特幣,似張小龍之於微信”,比特幣是中本聰發行的一種電子貨幣,而微信是張小龍打造的一款產品。比特幣底層採用的技術是區塊鏈,微信底層採用的則是傳統的移動端開發。比特幣只是一款“產品”,但是很多人在評價區塊鏈技術是否有用的時候卻根據比特幣的前景來判斷,我認爲這樣有些不妥。不過這樣的想法卻是可以理解的,畢竟比特幣是區塊鏈的第一款“產品”。

關於上面說的電子貨幣,可以理解爲我們銀行卡里的人民幣的數量。隨着國家不斷的發行貨幣,理論上所有銀行卡的總額會增加,因此錢也變得不那麼值錢。但是世界上所有的比特幣總量卻不會增加,始終保持在2100萬個,給人的感覺就是一種限量版的“物品”,所以大家爭相搶購,導致比特幣的價格一漲再漲(下圖是比特幣的價格波動情況,更多信息可以點擊這裏)。我們在使用銀行卡進行轉賬時,一邊數字減少,另一邊數字增加;通過比特幣轉賬時也同理。

2.2 去中心化

在現階段,通過銀行卡向另一方轉賬,我們銀行卡上的數字減少,對方數字增加,這些信息都保存在銀行的服務器中。如果銀行卡的數據被修改或者丟失,就會出現很嚴重的問題(雖然現在從來沒有出現過,不過出現了也不會媒體曝光,_)。當然銀行肯定會多地備份這些數據,並不會那麼輕易的丟失或者被修改。

在區塊鏈技術中,爲了實現去中心化,則在每個用戶那裏都存儲一份數據,這樣數據就不會被權威機構掌握,此外如果想要篡改數據,必須同時在一半以上的用戶那裏修改數據,也就是我們說的51%攻擊。如果用戶足夠的多,理論上將是不可能實現的。這裏說的不可篡改是指不能惡意的去修改,比如一個人的姓名一旦寫入區塊鏈中就永久記錄在區塊鏈上,你如果正常的修改姓名,則是可以的。

2.3 以太坊

以太坊是1994年出生的一個俄羅斯小夥創立的,他的全名是Vitalik Buterin,因此也被人稱爲V神,由於創立了以太坊,目前身價已經百億(以前是經商使人致富,未來大概是技術使人致富)。下面放一張V神的圖片

關於以太坊的詳細介紹,最好的方法當然是看以太坊白皮書,中文版請點擊這裏,英文版請點擊這裏。不過如果沒有相關的知識儲備,直接看白皮書可能覺得會有點突兀。通俗的來講就是:以太坊通過以太坊虛擬機把機器虛擬化,這樣某種程度來講,每臺機器都一樣。通過RPC(遠程過程調用,Remote Procedure Call)技術實現每個節點的連接和通信。我們在基於以太坊進行開發的時候不必在意底層的實現,只需要知道如何使用以及它的基本原理即可。就像我們在使用TCP/IP協議的時候,我們不用在意每一層協議的具體實現,只需要知道如何使用以及它的基本原理(當然你想要自己實現一個除外)。

2.4 挖礦

如何沒有出現區塊鏈技術,我們聽到挖礦這個詞語,想到的場景大概是下面這樣的:在一個山洞中,一羣人拿着工具不停的挖,希望能夠挖到金礦或者鐵礦。這在區塊鏈中也類似,在去中心化中提到,區塊鏈中的數據需要保存在每一個節點中,當有一個新的數據需要記錄的時候,到底誰第一個來記錄這個數據呢?因爲記錄的數據並不是我們明文的數據,而是經過加密的,在第一個記錄以後,其他人直接複製第一個就可以了(例如老師佈置一道題目,第一個同學做好以後,其他人抄一下就好了,到底誰來做呢?)於是區塊鏈中制定了一個規則,第一個記錄的人給予一定的獎勵,因此大家爲了獲得這個獎勵都不停的去爭取這個記賬權,這就是挖礦。爭取記賬權就是挖礦。

舉例來說,如果需要把password:123456這條數據放在區塊鏈中,通過把需要放的數據與機器產生的一個隨機數做哈希運算,如果得到的哈希值的前面五位是零,那麼就由你來記賬,也就是挖到礦了。當然也可以設置前面十位是零,這樣難度就會更高。隨機數是你電腦隨機產生的,所以挖礦就有點像買彩票,理論上你買的越多,中的可能性越大。所以不斷的挖,挖到的可能性也越大。

現在大量的人都在挖比特幣,隨着比特幣的數量越來越少,挖礦模式也隨着變化。最開始一個人買一些機器,就放在家裏,獨自的去挖礦,挖到了就賺了,沒挖到就損失電費以及機器的損耗費用。由於最初的挖礦難度比較簡單,所以大家都願意冒這個風險,但是隨着挖礦難度的增加,現在大家把所有的機器都放在一起,如果挖到礦了,那麼就根據你所提供的算力佔所有機器總算力的百分比來獲取收益。

2.5 智能合約

把Smart Contract翻譯爲智能合約,我覺得不太好。因爲很多人第一次聽到智能合約這個詞語的時候第一反應是這個合約是智能的嗎?但事實是,智能合約的智能和人工智能的智能沒有任何關係。關於智能合約,我聽到最好的解釋是李笑來的一個解釋:所謂的智能合約,就是一段可以執行但是不可以篡改的程序代碼。智能合約就是普通的代碼而已,只是因爲部署在區塊鏈中,去中心化的特點導致我們無法修改這段代碼。

2.6 加密算法和P2P

關於加密算法在區塊鏈中主要採用非對稱加密和HASH-256,也沒有什麼可以說的,而P2P則是我們很熟悉的技術了。

2.7、小結

區塊鏈技術是由很多基礎的技術結合而成的一種新技術,造出了很多新的詞語,加上比特幣的火爆,因此區塊鏈技術也出現在了技術人的視野中。對於區塊鏈技術,我認爲其核心是解決信用問題。我們在淘寶購買商品的時候,我們擔心付錢了賣家不發貨,賣家擔心發貨了我們不給錢,所以出現了支付寶,先把錢給支付寶,等買家收到貨以後再給賣家。我們信任支付寶,是因爲它足夠的大,馬雲也足夠的有錢,我們覺得它不會拿着我們的錢跑路。但是如果支付寶很小,並且不出名呢?這時候我們恐怕就會有很多擔憂了。另外支付寶也只能夠在金錢的支付方面解決信用問題。如果這時候我們採用區塊鏈技術,則一切都變得簡單,通過一段無法篡改的代碼,一旦滿足條件,就執行,沒有誰能夠阻擋。

由於知識和文筆有限,很多地方闡述不到位,如果想對區塊鏈的有更多的瞭解,下面幾篇文章值得一讀:
區塊鏈入門教程 比特幣入門教程 巴比特 以太坊

三、基於MetaMask和web3.js的Web端開發


如果你在網上找區塊鏈實戰開發教程,上面的詞語是你會經常看見的。對於一個沒有多少知識積累的人,看到這麼多陌生的詞語一定會覺得頭都大了,但是當你深入瞭解以後,就會覺得其實也不是想的那麼難,下面會一步一步的解釋圖片中的詞語。備註:在對下面的內容進行實踐的時候,請保證你已經解決了科學上網問題。

3.1 開發流程介紹

我們在開發傳統網站的時候大概有下面幾個步驟。1、安裝開發環境;2、編寫建立數據庫的語句;3、編寫存儲過程(有些開發方式可能會省略);4、連接數據庫;5、使用數據庫中的select,update,delete,insert對數據庫進行操作或調用存儲過程;6、前端獲取到數據並渲染到頁面上。

下面是開發區塊鏈的幾個步驟,我覺得是非常相似的;1、安裝開發環境;2、編寫智能合約;3、在智能合約中定義方法(就像在數據庫中定義存儲過程);4、連接智能合約;5、操作智能合約;6、前端獲取到數據並渲染到頁面。是不是非常相似?看完下面的內容,你應該會有更深的體會。

3.2 Dapp、MetaMask和Gas的介紹

Dapp是Decentralized Application的縮寫,有人叫它分佈式應用,有人叫它去中心化應用。在網上找教程的時候,隨處都可以看到Dapp開發教程,在理論部分提到了區塊鏈技術是去中心化的,所以其實通俗來說:使用了區塊鏈技術的應用都是Dapp。

我們知道區塊鏈中的數據會在每一個節點都存放,當我們在存放的時候肯定會消耗cpu的計算資源以及硬盤的存儲資源。讓這麼多節點幫你存放東西,總得給一點好處給別人吧。既然需要給錢給別人,那麼肯定需要錢包,MetaMask就是以太坊中的一個錢包。我們通過這個錢包可以完成轉賬,支付以及收款等操作。MetaMask並不是一個軟件,而是一個瀏覽器插件,官方聲明只在Chrome和Firefox、Brave瀏覽器中提供了該插件。

Gas翻譯爲中文是燃料,汽油的意思。如果我們讓別人把一件物品從北京開車送到杭州,我們通常是給錢給對方,但是在區塊鏈中則不是,別人幫你存放了東西,你是給Gas給它,並不是直接給以太幣。

3.3、Brave與MetaMask的安裝、以太幣的獲取

經過筆者的測試,Brave瀏覽器和Metamask的兼容性最好,所以我們採用這個來開發。Brave瀏覽器是JavaScript語言創立者編寫的,對用戶隱私以及廣告攔截也都做得很好。Brave下載地址請點擊這裏,安裝比較簡單就不多贅述。

3.3.1 安裝MetaMask


右上角出現MetaMask的圖標就表示已經安裝成功

3.3.2 註冊賬號


後面會有一些列的NEXT和CONFIRM按鈕,一路點擊就好。出現下圖的時候,點擊圖中鎖,會出現12個英語單詞,一定一定要牢記,裏面有再多的錢也找不回來。

根據前面的12個單詞依次選擇

3.3.3 選擇測試網絡

我們如果採用以太坊的主網絡,那麼在測試過程中也需要支付費用,所以開發中都是採用測試網絡,而Rinkeby Test Network是以太坊官方提供的,相對更加穩定,建議採用這個,後面獲取以太幣也是發送到這個測試網絡的。

3.3.4 獲取以太幣

在測試網絡中,我們可以通過Twitter,Facebook,Google向以太坊官網獲取,具體獲取方法可以點擊這裏。但是在主網絡就只能自己拿錢去購買了,目前一個以太幣大約200美元,也就是1200人民幣,不過放心,我們交易一般都是0.000001個以太幣。下圖是獲取不同數量需要等待的時間,如果是3個需要8小時,但事實上是幾分鐘就可以到,主要和獲取的人數有關,獲取的人越多,就越久。

MetaMask中還可以創建賬戶,可以轉賬,收錢,這些操作就暫時不演示。根據提供的可視化選項試試就知道了。到這裏我們的開發環境就搭好了,下面我們正式開發。

3.4 Solidity介紹與智能合約編寫

Solidity是一種編寫只能合約的語言,就像編寫數據庫,我們採用SQL;編寫前端頁面,採用HTML;編寫頁面事件監聽採用JavaScript。Solidity是採用面向對象的思想,但是語法卻和JavaScript類似,所以你如果學過面向對象和JavaScript,那麼你會很快上手的。Solidty的英文網站點擊這裏,中文網站點擊這裏,如果你想邊玩遊戲邊學習怎麼寫智能合約,可以點擊這裏,如果遊戲進度太慢,可以看熱心博主的系列這裏。看了上面的資料,普通的智能合約可以隨便寫啦。

3.5 Remix介紹與智能合約編寫

我們可以在記事本里面寫Java代碼,也可以在MyEclipse中編寫Java代碼,這裏的Remix就和MyEclipse一樣,在編寫智能合約的可以爲我們進行語法檢查,代碼提示等等。我們在安裝MyEclipse總是需要花費很多時間,想着要是可以不用安裝就可以使用該多好。碰巧Remix就是這樣一個在線編輯器,可以爲我們節約安裝時間。查看Remix在線編輯器,請點擊這裏

// 聲明用什麼版本的編譯器來編譯
pragma solidity >=0.4.22 <0.6.0;

// contract是合約聲明關鍵字,和Class是類的聲明關鍵字一樣
contract Order {
    
    // 訂單結構體(有沒有覺得和C語言的結構體很像)
    struct Item {
        uint128 itemId; // 訂單ID
        address payable user; // 用戶錢包地址
        address payable supplier; // 供應商錢包地址
        uint amount; // 總金額
        uint8 status; // 訂單狀態,0表示未完成,1表示已完成(添加這個字段,是問了防止重複結束訂單)
    }

    Item[] public items; // 訂單數組

    // 監聽支付事件(通過事件監聽,可以知道支付的交易情況)
    event EtherChange(
        address user, // 調用地址
        uint8 types, // 0 => 支付  1 => 獲取
        uint amount // 總金額
    );

    // 訂單ID和訂單的映射
    mapping(uint128 => Item) itemIdToItem; 


    // 增加訂單
    function addItem(uint128 itemId, address payable supplier, uint amount) public payable {
        // 判斷用戶支付的錢是是否等於總價格(msg.value是用戶調用智能合約支付的錢,這時候把電子貨幣存在區塊鏈中)
        require(msg.value == amount, "The Ether is not collect");
        // 創建訂單
        Item memory item = Item(itemId, msg.sender, supplier, amount, 0);
        // 進行itemId和訂單條目的映射
        itemIdToItem[itemId] = item;
        // 觸發定義的事件
        emit EtherChange(msg.sender, 0, amount);
        // 把創建的訂單添加到訂單數組中
        items.push(item);
    }


    // 完成訂單
    function finishItem(uint128 itemId) public {
        // 通過映射獲取訂單
        Item memory item = itemIdToItem[itemId];
        
        // 判斷調用該合約的是否是創建該訂單的用戶,如果不是,則提示越權訪問(require和if else類似,如果滿足條件就向下繼續執行,否則提示"Permission denied")
        require(msg.sender == item.user, "Permission denied");
        
        // 判斷該訂單是否已經完成,如果已經完成,則提示已經完成
        require(item.status == 0, "Order has finished");
        
        // 轉賬給供應商(將之前保存在區塊鏈中的電子貨幣轉賬到供應商賬戶)
        item.supplier.transfer(item.amount);
        // 觸發轉賬事件
        emit EtherChange(msg.sender, 1, item.amount);
        // 修改訂單狀態
        for(uint128 i = 0; i < items.length; i++) {
            if(items[i].itemId == itemId) {
                items[i].status = 1;
                itemIdToItem[itemId].status = 1;
                break;
            }
        }
    }

    // 獲取訂單數據(如果想要獲取所有訂單數據,可以直接返回items數組,但是這在java的web3j庫並不支持,所以目前是還沒有一種方法可以一次性返回所以訂單數據,只能在調用的時候寫個循環^_^)
    function getItemData(uint128 itemId) public view returns(uint128, address, address, uint, uint8) {
        Item memory res = itemIdToItem[itemId];
        return (res.itemId, res.user, res.supplier, res.amount, res.status);
    }
}

3.6 智能合約編譯與部署

3.6.1 智能合約編譯

如下圖所示,在插件中把Deploy&Run Transaction、Gas Profile、Solidity Compiler激活。

如下圖選擇編譯的版本,點擊Compiler Order.sol即可。如果你想邊寫邊編譯,檢查語法錯誤,可以把Auto compile的勾打上。

3.6.2 智能合約部署

如下圖,選擇環境爲Injected Web3。

點擊Deploy按鈕,支付部署所消耗的Gas就可以將智能合約成功部署在以太坊中。(下圖是部署成功後的情況,需要記住這個合約的地址,我們和智能合約交互的時候需要用到它)

3.7 智能合約的調用

調用智能合約,需要使用以太坊官方提供的一個JavaScript庫函數,也就是web3.js,關於web3.js的調用文檔,中文版英文版。此外還需要編譯成功後產生的abi文件,點擊上上圖中ABI幾個字就把abi複製下來了,找一個地方粘貼即可。

3.7.1 判斷是否安裝Metamask

// 在頁面加載的時候判斷瀏覽器是否安裝MetaMask
window.addEventListener('load', function() {
	if(typeof web3 !== 'undefined') {
	    web3js = new Web3(web3.currentProvider);
	    startApp();
	} else {
		alert('請安裝MetaMask');
	}
});

3.7.2 獲取調用合約的用戶地址

var myAddress = Contract._provider.selectedAddress;

3.7.3 通過abi和合約地址獲取到合約對象

var orderContract = new web3js.eth.Contract(abi, contractAddress);

3.7.4 向區塊鏈中增加訂單函數

function addItem(itemId, supplier, amount) {
    // value: amount 就是智能合約中msg.value獲取到的值,單位爲wei,我們日常的說的一個以太幣單位是Ether,1Ether = 10的18次方
	orderContract.methods.addItem(itemId, supplier, amount).send({from: myAddress, value: amount}, function(error, result){
		if(error) {
			console.log(error);
		} else {
			console.log(result);
		}
	});
}

3.7.5 獲取訂單數據函數

// 獲取訂單數據
function getItemData(orderid) {
	orderContract.methods.getItemData(orderid).call({from: myAddress}, function(error, result) {
		if(error) {
			console.log(error);
		} else {
			console.log(result);
		}
	});
}

3.7.6 完成訂單函數

// 完成訂單
function finishItem(orderid) {
	orderContract.methods.finishItem(orderid).send({from: myAddress}, function(error, result){
		if(error) {
			console.log(error);
		} else {
			console.log(result);
		}
	});
}

3.7.7 事件監聽函數

function EtherChange() {
	orderContract.events.EtherChange({
		fromBlock: 0
	}, function(error, event) {
		if(event) {
			console.log(event);
		}
	});
}	

3.8 運行結果

3.9 源碼地址

https://github.com/deng1234/Block-chain-demo

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章