1.创建项目
我们使用的是Truffle框架
Truffle是一套针对基于以太坊的Solidity语言开发框架,本身基于javascript,Truffle为以太坊提供了开发环境,测试框架和资产管道(pipeline)。
安装truffle
npm install -g truffle@3.4.11
创建并初始化项目
mkdir eth-weibo
git clone http://github.com/truffle-box/bare-box
2.合约
2.1 WeiboAccount合约
WeiboAccount合约存储发送的微博内容,每一个微博账户对应一个WeiboAccount合约,WeiboAccount合约的所有者是合约的创建者,所以你的微博只能由自己管理,没有任何一个机构可以删除你自己的合约
WeiboAccount包含以下几个功能:
- 发送微博
- 查找微博
- 打赏
具体实现如下:
pragma solidity ^0.4.10;
/**
微博账户
*/
contract WeiboAccount{
//data structure of a single Weibo
struct Weibo{
uint timestamp;
string weiboString;
}
//这个微博账户的所有微博,微博ID映射微博内容
mapping(uint => Weibo) _weibos;
//账户发的微博数量
uint _numberOfWeibos;
//微博账户的所有者
address _adminAddress;
// 权限控制,被这个修饰符修饰的方法,表示该方法只能被微博所有者操作
modifier onlyAdmin{
require(msg.sender==_adminAddress);
_;
}
//微博合约的构造方法
function WeiboAccount(){
_numberOfWeibos=0;
_adminAddress=msg.sender;
}
//发新微博
function weibo(string weiboString) onlyAdmin{
require(bytes(weiboString).length<=160);
_weibos[_numberOfWeibos].timestamp=now;
_weibos[_numberOfWeibos].weiboString=weiboString;
_numberOfWeibos++;
}
//根据ID查找微博
function getWeibo(unit weiboId) constant returns (string weiboString,uint timestamp){
weiboString=_weibos[weiboId].weiboString;
timestamp=_weibo[weiboId].timestamp;
}
//返回最新的一条微博
function getLatestWeibo() constant returns (string weiboString,uint timestamp,unit numberOfWeibos){
weiboString=_weibos[_numberOfWeibos-1].weiboString;
timestamp=_weibos[_numberOfWeibos-1].timestamp;
numberOfWeibos=_numberOfWeibos;
}
//返回微博所有者
function getOwnerAddress() constant returns(address adminAddress){
return _numberOfWeibos;
}
//取回打赏
function adminRetrieveDonations(address receiver) onlyAdmin{
assert(receiver.send(this.balance));
}
//摧毁合约
function adminDeleteAccount() onlyAdmin{
selfdestruct(_adminAddress);
}
//记录每条打赏记录
event LogDonate(address indexed from,uint _amount);
//接受别人的打赏
function() payable{
LogDonate(msg.sender,msg.value);
}
//返回微博账户总数
function getNumberOfWeibos() constant returns(uint numberOfWeibos){
return _numberOfWeibos;
}
}
2.2 WeiboRegistry合约
WeiboRegistry合约提供展示微博账号的平台,在weiboRegistry维护这账户昵称,账户ID到weiboAccount合约之间的映射关系,主要有以下几个功能:
- 注册微博
- 返回已注册账户数量
- 查找微博账户
- 打赏
具体实现如下:
pragma solidity ^0.4.10;
/**
微博管理平台
*/
contract WeiboRegister{
//根据用户昵称,ID,地址查找微博账户
mapping (address=>string) _addressToAccountName;
mapping(uint => address) _accountIdToAccountAddress;
mapping(string=>address) _accountNameToAddress;
//平台上的注册数量
uint _numberOfAccounts;
//微博平台管理员
address _registryAdmin;
//权限控制,被这个修饰符修饰的方法,表示该方法只能被微博平台管理员操作
modifier onlyRegistryAdmin{
require(msg.sender==_registryAdmin);
_;
}
//微博平台构造函数
function WeiboRegister(){
_registryAdmin=msg.sender;
_numberOfAccounts=0;
}
//在平台上注册微博:用户名,微博账户
function register(string name,address accountAddress){
//账户之前没有注册过
require(_accountNameToAddress[name]==address(0));
//昵称之前未注册过
require(bytes(_addressToAccountName[accountAddress]).length==0);
//昵称不能超过64字符
require(bytes(name).length<64);
_addressToAccountName[accountAddress]=name;
_accountNameToAddress[name]=accountAddress;
_accountIdToAccountAddress[_numberOfAccounts]=accountAddress;
_numberOfAccounts++;
}
//返回已注册账户数量
function getNumberOfAccounts() constant returns (uint numberOfAccounts){
numberOfAccounts=_numberOfAccounts;
}
//返回昵称对应的微博账户地址
function getAddressOfName(string name) constant returns(address addr){
addr=_accountNameToAddress[name];
}
//返回与微博账户对应的昵称
function getNameOfAddress(address addr) constant returns(string name){
name=_addressToAccountName[addr];
}
//根据Id返回账户
function getAddressOfId(uint id) constant returns(address addr){
addr=_accountIdToAccountAddress[id];
}
//取回打赏
function adminRetrieveDonations() onlyRegistryAdmin{
assert(_registryAdmin.send(this.balance));
}
//摧毁合约
function adminDeleteRegistry() onlyRegistryAdmin{
selfdestruct(_registryAdmin);
}
//记录每条打赏记录
event LogDonate(address indexed from,uint256 _amount);
//接受别人的打赏
function() payable{
LogDonate(msg.sender,msg.value);
}
}
3.部署合约
在truffle中只需要部署WeiboRegister合约就可以了,WeiAccount合约可以通过前端页面部署。在部署WeiboRegistry合约之前需要编写迁移脚本,具体如下:
文件 2_weiboregistry_migration.js:
var WeiboRegistry=artifacts.require("WeiboRegister");
module.exports=function(deployer){
deployer.deploy(WeiboRegistry);
}
迁移脚本完成后,就可以执行迁移命令了
这里是将智能合约部署到Testrpc环境中。
安装Testrpc环境
npm install -g ethereumjs-testrpc
启动testrpc
testrpc
执行迁移命令:
truffle migrate
以下说明成功:
Compiling ./contracts/WeiboRegistry.sol… Writing artifacts to
./build/contractsUsing network ‘development’.
Running migration: 2_weiboregistry_migration.js Deploying
WeiboRegister… …
0x0642a947b1d4baaa834f88961a4097537a8e1ce0a9c046e5fc77dd9d19b93bef
WeiboRegister: 0xab3fed12e76473334a801cc5252a48d7437247e2 Saving
successful migration to network… …
0x441972c747810af18818442514afc9a4634dfd21c07ce18df71738a2c6e8df55
Saving artifacts…
4. 前端应用
为了能够在前端JavaScript代码中使用Truffle提供的合约抽象,需要truffle-default-builder,truffle-default-builder可以帮助我们把合约抽象整合到JavaScript脚本中。运行以下命令安装truffle-default-builder。
cd eth-weibo
npm init -y
npm install --save truffle-default-builder@2.0.0
安装完成后,需要在truffle.js中配置一下,文件truffle-config.js:
var DefaultBuilder=require("truffle-default-builder");
module.export={
build:new DefaultBuilder({
"index.html":"index.html",
"app.js":[
"javascripts/app.js"
],
"app.css":[
"stylesheets/app.css"
],
}),
networks:{
development:{
host:"localhost",
port:8545,
network_id:"*" //match any networkid
}
}
};
配置完成后执行build命令,在build文件下会生成app.js
truffle build
如果出现以下错误:
Error: Couldn't find file at .//app/javascripts/app.js.
可以根据下面的callback查找node_modules/truffle-default-builder/index.js文件
我将index.js中的280修改如下后即可:
var display_path = expected_path.replace(this.working_directory, ".");
如果出现以下错误:
Couldn't find file at ./app/stylesheets/app.css.
手动创建文件即可
build/app.js提供了以下几个全局变量,在app.js文件中任意位子使用这几个全局变量
- web3对象
- WeiboAccount和WeiboRegistry合约抽象
truffle-default-builder可以让开发者专注于前端业务逻辑,不需要担心ES6,ES7规范与浏览器规范之间的区别,它会自动作转换。
所有前端脚本位于app/javascript/app.js文件中,前端页面逻辑主要功能如下: - 生成微博账号
- 在微博中注册昵称
- 发送微博
- 展示微博内容
- 显示打赏金额
具体实现如下,app.js
//部署好weiboRistry
5.运行DAPP
这里使用的是testrpc,启动testrpc。启动后可以看到一些地址和私钥
打开谷歌浏览器(需要先安装metamask插件)
打开metamask插件,然后将网络切换到localhost:8545
将testrpc中提供的私钥选择一个导入到metamask账户中