https://www.jianshu.com/p/72c4a0443d4e
DApp架构设计
如上图,DApp的架构我们可以简单分为以上三种类型:轻钱包模式、重钱包模式和兼容模式。
轻钱包模式
轻钱包模式下我们需要有一个开放Http RPC协议的节点与钱包通信,这个节点可以是任意链上的节点。轻钱包通常会作为一个浏览器插件存在,插件在运行时会自动注入Web3框架,DApp可以通过Web3与区块链节点通信。当DApp只是单纯的获取数据时是不需要钱包介入的,但是当DApp需要发送交易到链上时需要通过钱包完成对交易签名的过程。
优点:不需要用户同步区块链节点就可以使用
缺点:需要一个公开的节点提供服务,可能会存在安全性问题
重钱包模式
重钱包会自己同步并持有一个区块链节点,提供一个浏览器环境,其他与钱包相似。
优点:自己持有并同步节点,安全性高
缺点:需要持有一个全量的区块链节点
兼容模式
兼容模式可以在轻钱包和重钱包下同时使用,与钱包通信的节点可以选择在钱包外本地持有,也可以自己搭建服务持有并公布节点。
DApp开发
理解了DApp的架构设计就可以开始一步步的搭建我们的DApp了,这里我们不选择用各种成熟的框架。从最基础的开始,会更容易理解核心的思想。选择一个轻量级的钱包插件MetaMask,安装并创建自己的账号。
MetaMask默认会提供以下节点可以使用:
- Main Ethereum Network
- Ropsten Test Network
- Kovan Test Network
- Rinkeby Test Network
- Localhost 8545
当然你也可以手动添加自己的节点
编写并编译智能合约
以太坊提供一个图灵完备的开发环境,理论上可以构建任意复杂的智能合约,但是也要考虑到越复杂的逻辑越容易出错,并且会消耗更多的Gas,因此在设计上需要谨慎考虑。关于智能合约的编写这里不再赘述。这里有一个简单的合约:
pragma solidity ^0.4.21;
/**
Footmark
*/
contract Footmark {
struct Log {
uint time;
string text;
}
mapping (address=>mapping(address=>Log)) private logs;
function Footmark() public {
}
// Leave a message to somebody
function leaveMessage(string text,address to) public returns(uint time) {
bytes memory textBytes = bytes(text);
require(textBytes.length > 0 && textBytes.length < 100);
time = now;
logs[msg.sender][to] = Log(time, text);
return time;
}
// Leave a message to myself
function enter(string text) public returns(uint time) {
return leaveMessage(text, msg.sender);
}
// Lookup message from somebody
function lookupFrom(address from) public view returns(uint time, string text) {
return (logs[from][msg.sender].time,logs[from][msg.sender].text);
}
// Lookup message from myself to somebody
function lookupTo(address to) public view returns(uint time, string text) {
return (logs[msg.sender][to].time,logs[msg.sender][to].text);
}
}
逻辑非常简单,任何人都可以在该合约中给其他人留言,所有人都可以查看留给自己的信息或者自己留给其他人的信息。
接下来我们编译一下我们刚刚写的智能合约。各种框架都有提供合约编译的功能,比如Truffle。为了方便了解合约的编译过程,我们使用比较基础的Solidity的编译器solc来完成。
如果通过
npm install -g solc
方式安装,会另外得到一个命令行工具solcjs,当然直接引用solc模块是可以用js脚本完成编译的
var solc = require('solc')
var input = 'pragma solidity ^0.4.21;contract Footmark {struct Log {uint time;string text;}mapping (address=>mapping(address=>Log)) private logs;function Footmark() public {}function leaveMessage(string text,address to) public returns(uint time) {bytes memory textBytes = bytes(text);require(textBytes.length > 0 && textBytes.length < 100);time = now;logs[msg.sender][to] = Log(time, text);return time;}function enter(string text) public returns(uint time) {return leaveMessage(text, msg.sender);}function lookupFrom(address from) public view returns(uint time, string text) {return (logs[from][msg.sender].time,logs[from][msg.sender].text);}function lookupTo(address to) public view returns(uint time, string text) {return (logs[msg.sender][to].time,logs[msg.sender][to].text);}}'
// Setting 1 as second paramateractivates the optimiser
var output = solc.compile(input, 1)
for (var contractName in output.contracts) {
// code and ABI that are needed by web3
console.log(contractName + ': ' + output.contracts[contractName].bytecode)
console.log(contractName + '; ' + JSON.parse(output.contracts[contractName].interface))
}
方便期间我们使用命令行编译
solcjs Footmark.sol --abi --bin
会得到两个文件,后续我们会用到这两个文件的内容
- Footmark_sol_Creation.bin :编译后的binary code
- Footmark_sol_Creation.abi :编译后的abi
还有一种更方便和直观的合约编译方式http://remix.ethereum.org/
编译的过程和结果都非常直观,更方便的一点是可以帮助开发者及时发现问题
合约部署
合约的部署需要借助Web3框架来完成,对于以太坊节点来说合约的部署会被视作一次交易,合约的内容会被存储在链上,因此部署过程需要借助钱包来完成交易签名,部署代码如下:
let abi = [{"constant":true,"inputs":[{"name":"to","type":"address"}],"name":"lookupTo","outputs":[{"name":"time","type":"uint256"},{"name":"text","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"text","type":"string"}],"name":"enter","outputs":[{"name":"time","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"text","type":"string"},{"name":"to","type":"address"}],"name":"leaveMessage","outputs":[{"name":"time","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"from","type":"address"}],"name":"lookupFrom","outputs":[{"name":"time","type":"uint256"},{"name":"text","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[],"payable":false,