Github: https://github.com/Alva112358/BlockChain
项目的选题为去中心化的手机交易平台,其实就是一个去中心化的网店。选择这个项目其实还是有考虑的,毕竟区块链的特点是去中心化的分布式系统,而且具有很好的数据隐秘性,因此利用区块链的这一特点,能够在网络交易平台上实现一些很好的应用。
当下像淘宝、京东等线上交易平台都有一个特点,就是他们都是中心化的交易系统,在客户和店家之间进行交易时,需要有一个可信任的第三方用户进行中介,以确保交易的可靠性。虽然当前第三方交易平台的发展前景非常好,客户的满意度也非常高,但是毕竟还是存在隐秘的风险,而且更多的小平台存在的风险也越大,因此发展一个去中心化的交易平台,就能够增加交易的可靠性,而且也是中小企业网络交易平台发展的方向。
本项目的实际环境是一个手机交易平台,利用智能合约进行编程。合约由店主发起,当用户挑选到自己喜欢的手机的时候,就能够提交订单,此时合约内部就能够收到客户订单中的手机数目和手机类型,从而将客户中扣除相应的以太币到合约内部,而不是直接交易给店家。店家只有将货给到物流,物流公司确认买家发货,并且买家最终确定收货以后,钱才能够从合约中扣除交给店家。整个过程中没有第三方中介的参与,实现去中心化。
项目的测试的操作系统为mac OS,使用truffle框架进行实现,使用Ganache在私有链上进行测试。truffle是目前流行的以太坊开发框架,支持智能合约的编译、部署和测试,本项目属于一个开发学习项目,因此在私有链上进行测试。相应配件的版本如下所示:
truffle -v5.0.0
node -v11.4.0
npm -v6.4.1
由于Github文件大小的限制,在上传的时候将node_modules文件夹删除,因此在运行项目前先在当前目录下执行下列语句:
npm install
如果依然缺某些modules,则需要自行npm进行安装即可。
项目文件夹为DApp,运行的方法为在DApp目录下开启两个终端,首先在第一个终端上输入如下命令,在Ganache上创建一个私有链:
ganache-cli -e 3001 -l 99999999999999 -g 20000
需要注意的是,如果未安装Ganache,需要在终端下先运行:
sudo npm install -g ganache-cli
本次实验没有用Ganache-cli的图形化界面,只是在终端上运行,启动后,显示的界面如下图所示:
)
为了方便测试,设置初始以太币为3000,同时由于在测试过程中发现的一些gas的限制问题,因此将gas的限制设置为一个很大的数。私钥可以用于在metamask等插件中进行账户的部署,本实验没有用到。当然在使用的过程中,可能会由于系统缺乏某些nodejs的安装包而无法启动,需要缺乏哪些就用npm进行安装即可。
首先是truffle的安装,truffle的安装只需要在终端中输入下列指令即可:
sudo npm install -g truffle
需要注意的是,truffle的安装过程在不同的系统中可能会造成失败,这只能在网上找到相应的错误,因为错误的分析实在过于繁杂。
由于已经是完整的项目,因此truffle工程的创建过程在此处省略,只显示使用的方法。首先需要对智能合约进行编译,输入如下命令:
truffle compile --all
然后就需要将合约部署到私有链上,需要在终端输入如下命令:
truffle migrate --reset
同时在运行Ganache-cli的终端上,应该会输出如下信息:
最后就是运行智能合约,输入如下指令:
npm run dev
需要注意,在上图中中项目的运行端口为http://localhost:8083/,具体的端口号需要根据运行时输出的端口号,在谷歌浏览器上访问相应的端口,即有可能为8080,8081,8082等,需要特别注意。
这时候,打开谷歌浏览器,输入localhost:8083就可以访问到项目的Web页面。注意需要先将谷歌浏览器的Metamask插件关闭,否则会有不知名错误。最后得到的Web页面如下所示:
智能合约的初步测试可以在Remix上对智能合约的函数进行测试,Remix上拥有初始化的账户,可以对某些函数进行简单的测试,这样可以避免智能合约部署到以太坊时,某些函数出现问题而造成以太币的损失。智能合约在Remix上的测试在合约部署阶段已经完成,因此在此处不再累述。合约的Solidity代码如下所示:
pragma solidity ^0.5.0;
contract computerShop {
// Use to finish the transaction.
enum States { NOTHING, ORDERING, SENDING }
// The shop's keeper.
address public owner;
// The computers.
mapping(bytes32 => uint) public computers;
// State
States purchase_state;
// Constructor, open a new computer shop.
constructor() public {
// Initialize the computers.
computers["iPhone"] = 500;
computers["Sumsung"] = 350;
computers["HUAWEI"] = 390;
purchase_state = States.NOTHING;
}
// Get specific computer's price.
function get_price_by_name(bytes32 _name, uint number) public view returns (uint) {
return computers[_name] * number;
}
// Buyers order a computer.
function ordering(uint money) public payable {
require (purchase_state == States.NOTHING, "NOT NOTHING");
require (msg.sender != owner, "NOT BUYER");
require (msg.sender.balance >= money, "NOT ENOUGH MONEY");
purchase_state = States.ORDERING;
}
// Owner send the computer.
function sending() public {
require (purchase_state == States.ORDERING, "NOT ORDERING");
require (owner == msg.sender, "NOT SELLER");
purchase_state = States.SENDING;
}
// Buyers comfirm.
function comfirm() public {
require (purchase_state == States.SENDING, "NOT SENDING");
require (msg.sender != owner, "NOT BUYER");
purchase_state = States.NOTHING;
}
function withdraw() public payable {
require (msg.sender == owner, "NOT SELLER");
require (purchase_state == States.NOTHING, "NOT NOTHING");
msg.sender.transfer(address(this).balance);
}
function check_balances() public view returns (uint) {
require (owner == msg.sender, "NOT OWNER");
return address(this).balance;
}
function get_status() public view returns (uint) {
if (purchase_state == States.NOTHING) return 1;
if (purchase_state == States.ORDERING) return 2;
if (purchase_state == States.SENDING) return 3;
}
function getBalance() public view returns(uint) {
return msg.sender.balance;
}
function sellerInit(address addr) public {
owner = addr;
}
}
在Web端的测试,首先是用户购买其中自己喜欢的商品的流程(本项目指定第0个账户为合约的发起者):
同时店主的余额此时并没有立刻增加,而是先转账给合约进行冻结,等待客户确认,手机店的购买信号由NOTHING转换为ORDERING,此时店主可以确认购买信息,如下图所示:
此时只有店主可以确认发货,等待客户确认收货,但是店主不能够确认收货,如果此时店主点击确认收货,就会报错,如下图所示:
当正确点击确认发货后,手机店销售状态由ORDERING转为SENDING,如下图所示:
此时客户就能够确认收货了,当客户确认收货后,钱就能够从合约中调出,转账给店主,店的状态由SENDING转为NOTHING,此时客户可以再次购买商品,确认收货,确认后显示的信息如下所示:
此时切换到店主账户,可以发现店主余额增加了1000,说明交易成功,如下图所示:
如果想购买的手机的总价格超过了自己所拥有的钱,那么就会报错,如下图所示:
由于这次作业时间比较紧凑,所以实现的功能非常简陋,也只是简单实现了界面,结合上次的智能合约的编程作业,而且因为学习过程占用了大量时间,因此也删去了部分功能,后续可以对这个项目作出的改进有如下几点:
客户在同一家店可能会购买多次物品,而区块链本身具有回溯和信息隐秘性的功能,因此可以实现一个客户查询购买信息的功能,特定的客户只能查询特定的资料。
由于时间比较紧凑,因此没有实现这个功能,其实这个功能还是比较简单的,只需要简单修改智能合约的内容即可。
本项目的登录界面简单至极,因此方便测试,其实可以用Vue等框架写一个简单的交互界面,可能会更加友善,加一个服务器和数据库,就能成为比较完善的项目了。
这个项目的功能比较简单,只能够实现单用户交易,其他用户如果想要购买手机的话就需要排队,因此在后续的补充中可以加入并发操作。
本次实验是区块链智能合约编程的一次实战,由于以前的课程和课程内容上都很少接触智能合约的编程,因此在实战的过程中遇到了非常非常非常多的问题。以前都只是听说过区块链这个名词,没有听说过Solidity,以太坊这些概念,而且网上的资料也是非常少,基本上都是入门教程,所以就更加困难了。
首先最困难的莫过于搭建环境了,第一次搭建环境是用的Windows操作系统,在truffle的安装和初始化过程中就遇到了许多问题,后来就放弃了用Windows。然后就是用Linux虚拟机进行实验,但是同样在truffle安装过程中不停报错,而且错误也很难找到,曾经想过不用框架,但是想到为这个框架学了那么多东西,还是决定换mac OS进行实验,幸运的是,在mac OS上整个安装过程非常顺利,运行过程也很顺利。
然后就是Web前端和web3js的一些编程,智能合约在上次作业中已经大概写好了,因此只是对智能合约进行简单的修改,主要是web3js调用智能合约函数的过程比较费劲,也学了很多东西,搞着搞着还是搞出来了。但是这个项目的功能非常简陋,只有简单的下单、确认和收货功能,但是蕴含在其中的努力还是很多的,花了整整4天来做这个项目。
项目源代码:Github