区块链
去中心化App
- 去中心化 github下载(Mist)
- Chrome插件 MetaMask
- 以太坊的四种钱包:Ethereum Wallet、METAMASK、 Parity Ethereum、MyEtherWallet
- 编辑器:Atom、Webstorm、Sublime、Vscode
在区块链上运行的程序,通常称为智能合约(Smart Contract)。所以通常会把写区块链程序改称写智能合约。
简单点来讲,合约就是运行在区块链上的一段程序。
Solidity合约结构(状态变量、局部变量、构造函数、析构函数、生命周期)
Demo 一个完整的合约
pragma solidity ^0.4.4;
contract Counter {
uint count = 0;
address owner;
function Counter() {
owner = msg.sender;
}
function increment() public {
uint step = 10;
if (owner == msg.sender) {
count = count + step;
}
}
function getCount() constant returns (uint) {
return count;
}
function kill() {
if (owner == msg.sender) {
selfdestruct(owner);
}
}
}
合约部署
要想发布我们的合约到区块链,打开Ethereum Wallet然后点击Contracts。
部署合约时,因为要往区块链写入数据,需要矿工进行验证,所以需要花费一些gas奖励给矿工,还有当我们每次调用increment方法时,也属于写入数据,同样需要花费gas,但是调用getCount方法时只是从区块链读取数据,无需验证,读取数据无须花费gas。
Solidity值类型与引用类型
值类型(Value Type)
值类型包含:
- 布尔(Booleans)
- 整型(Integer)
- 地址(Address)
- 定长字节数组(fixed byte arrays)
- 有理数和整型(Rational and Integer Literals,String literals)
- 枚举类型(Enums)
- 函数(Function Types)
引用类型(Reference Types)
引用类型包含:
- 不定长字节数组(bytes)
- 字符串(string)
- 数组(Array)
- 结构体(Struts)
当函数参数为memory类型时,相当于值传递,而storage类型的函数参数将是指针传递。
案例:集资(CrowdFunding)智能合约(Smart Contract)
结构体和字典综合案例
下面的案例是一个集资合约的案例,里面有两个角色,一个是投资人Funder,也就是出资者。另一个角色是运动员Campaign,被赞助者。一个Funder可以给多个Campaign赞助,一个Campaign也可以被多个Funder赞助。
完整合约:
pragma solidity ^0.4.4;
contract CrowdFunding {
// 定义一个`Funder`结构体类型,用于表示出资人,其中有出资人的钱包地址和他一共出资的总额度。
struct Funder {
address addr; // 出资人地址
uint amount; // 出资总额
}
// 定义一个表示存储运动员相关信息的结构体
struct Campaign {
address beneficiary; // 受益人钱包地址
uint fundingGoal; // 需要赞助的总额度
uint numFunders; // 有多少人赞助
uint amount; // 已赞助的总金额
mapping (uint => Funder) funders; // 按照索引存储出资人信息
}
uint numCampaigns; // 统计运动员(被赞助人)数量
mapping (uint => Campaign) campaigns; // 以键值对的形式存储被赞助人的信息
// 新增一个`Campaign`对象,需要传入受益人的地址和需要筹资的总额
function newCampaign(address beneficiary, uint goal) public returns (uint campaignID) {
campaignID = numCampaigns++; // 计数+1
// 创建一个`Campaign`对象,并存储到`campaigns`里面
campaigns[campaignID] = Campaign(beneficiary, goal, 0, 0);
}
// 通过campaignID给某个Campaign对象赞助
function contribute(uint campaignID) public payable {
Campaign storage c = campaigns[campaignID];// 通过campaignID获取campaignID对应的Campaign对象
c.funders[c.numFunders++] = Funder({addr: msg.sender, amount: msg.value}); // 存储投资者信息
c.amount += msg.value; // 计算收到的总款
c.beneficiary.transfer(msg.value);
}
// 检查某个campaignID编号的受益人集资是否达标,不达标返回false,否则返回true
function checkGoalReached(uint campaignID) public returns (bool reached) {
Campaign storage c = campaigns[campaignID];
if (c.amount < c.fundingGoal)
return false;
return true;
}
}
总而言之,智能合约实现上要达到的目标是:完备的业务功能、精悍的代码逻辑、良好的模块抽象、清晰的合约结构、合理的安全检查、完备的升级方案。
从业务视角来看,智能合约只需要做两件事,其一是如何定义数据的结构和读写方式,其二是如何处理数据并对外提供服务接口。
IPFS + 区块链
- IPFS简介
IPFS(InterPlanetary File System)是一个点对点的分布式超媒体分发协议,它整合了过去几年最好的分布式系统思路,为所有人提供全球统一的可寻址空间,包括Git、自证明文件系统SFS、BitTorrent和DHT,同时也被认为是最有可能取代HTTP的新一代互联网协议。
IPFS用基于内容的寻址替代传统的基于域名的寻址,用户不需要关心服务器的位置,不用考虑文件存储的名字和路径。我们将一个文件放到IPFS节点中,将会得到基于其内容计算出的唯一加密哈希值。哈希值直接反映文件的内容,哪怕只修改1比特,哈希值也会完全不同。当IPFS被请求一个文件哈希时,它会使用一个分布式哈希表找到文件所在的节点,取回文件并验证文件数据。
IPFS是通用目的的基础架构,基本没有存储上的限制。大文件会被切分成小的分块,下载的时候可以从多个服务器同时获取。IPFS的网络是不固定的、细粒度的、分布式的网络,可以很好的适应内容分发网络的要求。这样的设计可以很好的共享各类数据,包括图像、视频流、分布式数据库、整个操作系统、模块链、8英寸软盘的备份,还有静态网站。
IPFS提供了一个友好的WEB访问接口,用户可通过http://ipfs.io/hash 获取IPFS网络中的内容,也许在不久的将来,IPFS协议将会彻底替代传统的HTTP协议。
基于以太坊Ethereum & IPFS的去中心化Ebay区块链项目
-
完整源码
import React, {Component} from ‘react’;
import ‘./App.css’;const ipfsAPI = require(‘ipfs-api’);
const ipfs = ipfsAPI({host: ‘localhost’, port: ‘5001’, protocol: ‘http’});function Utf8ArrayToStr(array) {
var out,
i,
len,
c;
var char2,
char3;out = ""; len = array.length; i = 0; while (i < len) { c = array[i++]; switch (c >> 4) { case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: // 0xxxxxxx out += String.fromCharCode(c); break; case 12: case 13: // 110x xxxx 10xx xxxx char2 = array[i++]; out += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F)); break; case 14: // 1110 xxxx 10xx xxxx 10xx xxxx char2 = array[i++]; char3 = array[i++]; out += String.fromCharCode(((c & 0x0F) << 12) | ((char2 & 0x3F) << 6) | ((char3 & 0x3F) << 0)); break; default: break; } } return out;
}
class App extends Component {
constructor(props) { super(props); this.state = { strHash: null, strContent: null } } saveTextBlobOnIpfs = (blob) => { return new Promise(function(resolve, reject) { const descBuffer = Buffer.from(blob, 'utf-8'); ipfs.add(descBuffer).then((response) => { console.log(response) resolve(response[0].hash); }).catch((err) => { console.error(err) reject(err); }) }) } render() { return (<div className="App"> <input ref="ipfsContent" style=/> <button onClick={() => { let ipfsContent = this.refs.ipfsContent.value; console.log(ipfsContent); this.saveTextBlobOnIpfs(ipfsContent).then((hash) => { console.log(hash); this.setState({strHash: hash}); }); }}>提交到IPFS</button> <p>{this.state.strHash}</p> <button onClick={() => { console.log('从ipfs读取数据。') ipfs.cat(this.state.strHash).then((stream) => { console.log(stream); let strContent = Utf8ArrayToStr(stream); console.log(strContent); this.setState({strContent: strContent}); }); }}>读取数据</button> <h1>{this.state.strContent}</h1> </div>); }
}
export default App;
- 介绍
1.eBay简介
eBay,(EBAY,中文电子湾、亿贝、易贝)是一个管理可让全球民众上网买卖物品的线上拍卖及购物网站。ebay于1995年9月4日由Pierre Omidyar以Auctionweb的名称创立于加利福尼亚州圣荷西。人们可以在ebay上通过网络出售商品。
因为我们会将所有的业务逻辑和数据存储在以太坊区块链上,所以这将是一个完全去中心化的应用程序。与此同时,如果我们将所有的图片和大量文本都存储在以太坊区块链上,这将非常昂贵,甚至由于以太坊EVM的一些限制,我们根本不可能将大量的图片和文本存储到区块链。为了解决这个问题,我们将在IPFS(Inter Planetary File System)上存储大量的文本和图像。
- 为什么要去中心化
在我们开始构建应用程序之前,花一分钟的时间来了解在像Ethereum这样的去中心化平台上构建产品的动机。
eBay取得了巨大的成功,因为它使得买卖非常有效率。在互联网成为主流之前,人们只能在当地社区购买和出售商品,当然也可以在一定的地理范围内购物。随着越来越多的人上网,像eBay这样的公司完全可以体验整个线下购物拍卖的场景,任何人都可以在互联网上从世界任何地方买卖任何东西。 eBay对于消费者和商人都具有划时代的意义。
尽管这对大家都有好处,并且总体上改善了贸易和经济,但是它有一些缺点。
- 参与的商人都在受到公司的相关限制:公司可以随时决定是否阻止商家自行处理交易,这对商人来说可能是一个巨大的打击。
- 商家支付费用列出他们的产品,并支付销售佣金。付费本身并不是那么糟糕,因为eBay提供服务。然而,上市费有时太高,商家要么保证金很高,要么把这笔费用交给消费者。
- 商家/消费者不拥有他们的任何数据。评论、购买历史等都是这些公司所有。例如,如果商家想将自己的操作转移到另一个提供商,那么导出她的评论或其他数据是几乎不可能的。
在以太坊这样的平台上构建产品解决了这些问题。商户的账户不能被封锁,数据是公开的,所以它可以很容易导出,交易费用比中心化的公司少很多很多。
- 项目详情
现在,您已经了解了构建此应用程序的原因以及为什么要构建这个应用程序,接下来我们来看一个高级别的,我们将在此项目中实现的所有功能。
- 项目展示:一个网站应该允许商家列出他们的项目。我们将为任何人建立免费列出他们的项目的功能。我们会将这些项目都存储在区块链和非区块链的数据库中,以方便查询。
- 将文件添加到IPFS:我们将添加将产品图像和产品描述(大文本)上传到IPFS的功能。
- 浏览产品:我们将添加根据类别、拍卖时间等过滤和浏览产品的功能。
- 拍卖:就像eBay,我们将实现维克里拍卖(Vickrey auction),即次价密封投标拍卖(Second-price sealed-bid auction)对物品进行投标。因为以太坊上的一切都是公开的,不像中心化应用程序,所以我们的实现将有所不同。我们的实现将非常类似于ENS的招标流程。
- 托管合约:一旦投标结束,产品有赢家,我们将在买方,卖方和第三方仲裁人之间创建一个托管合同。
- 2-of-3 数字签名:我们将通过实施2-of-3 数字签名解决方案来增加欺诈保护,其中3名参与者中的2名必须投票将资金释放给卖方或将金额退还给买方。
- 技术需求
要成功完成本课程,您应该对以下语言/技术有基本的了解:
- Solidity面向对象编程:在讲解这个项目之前,我们会先给大家讲解Solidity面向对象编程基础,如何编写简单合约,部署合约,合约简单互动。
- HTML/CSS/React:您应该对构建前端的HTML / CSS有基本的了解。
- Javascript:我们在这个过程中广泛使用JavaScript。它在服务器端用于将数据保存到数据库并查询数据库并将结果返回到前端。前端使用Web3.js与区块链进行交互。我们尽力保持javascript代码尽可能简单,以迎合不同背景的学生。
- Database:我们将在本课程中使用MongoDB来存储产品信息。没有必要特别了解MongoDB的知识,但是对这个过程需要对数据库有基本的了解。
- 项目架构
在我们开始执行代码之前,让我们来看看我们将在本课程中构建的Dapp的体系结构。
- Web前端:Web前端是HTML,CSS和Javascript的组合(大量使用web3js)。用户将通过这个前端应用程序与区块链,IPFS和nodeJS服务器交互。
- 区块链:这是所有代码和交易所在的应用程序的心脏。商店中的所有产品、用户出价和托管都写在区块链上。
- MongoDB:尽管产品存储在区块链中,但是查询区块链展示产品和应用各种过滤器(仅显示特定类别的产品,显示即将过期的产品等)效率并不高。我们将使用MongoDB数据库来存储产品信息并查询它以展示产品。
- NodeJS服务器:这是前端通过其与数据库进行通信的后端服务器。我们将公开一些简单的API来为前端查询和从数据库中检索产品。
- IPFS:当用户在商店中列出商品时,前端会将产品文件和描述上传到IPFS,并将上传文件的散列HASH存储到区块链中。
- 实现步骤
- 先通过truffle framework和Solidity实现合约代码,并将其部署到truffle develop自带的测试网络中,并且在truffle console中可以自由交互。
- 然后我们将学习IPFS,通过命令行安装并与之交互。
- 在后端实现完成后,我们将构建Web前端以与合约和IPFS进行交互。我们也会实现招标,揭示前端的拍卖功能。
- 我们将安装MongoDB并设计数据结构来存储产品。
- 数据库启动并运行后,我们将实现监听合约事件的NodeJS服务器端代码,并将请求记录到控制台。然后我们将执行代码将产品插入数据库。
- 我们将更新我们的前端,从数据库而不是区块链中查找产品。
- 我们将实现托管合同和相应的前端,参与者可以向买方/卖方发放或退款。