区块链的基本理论与实践

一、前言

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

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