zoukankan      html  css  js  c++  java
  • 以太坊DApp开发指南

    https://www.jianshu.com/p/72c4a0443d4e

    DApp架构设计

     
    DApp架构.png

    如上图,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/

     
    Footmark.jpg
     
    Footmark_detail.jpg

    编译的过程和结果都非常直观,更方便的一点是可以帮助开发者及时发现问题

    合约部署

    合约的部署需要借助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,"stateMutability":"nonpayable","type":"constructor"}];
    let binaryData = '0x6060604052341561000f57600080fd5b61082f8061001e6000396000f300606060405260043610610062576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063762db2b4146100675780639d4ff8ad14610120578063b2c21c9114610191578063bcea56e014610221575b600080fd5b341561007257600080fd5b61009e600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506102da565b6040518083815260200180602001828103825283818151815260200191508051906020019080838360005b838110156100e45780820151818401526020810190506100c9565b50505050905090810190601f1680156101115780820380516001836020036101000a031916815260200191505b50935050505060405180910390f35b341561012b57600080fd5b61017b600480803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091905050610486565b6040518082815260200191505060405180910390f35b341561019c57600080fd5b61020b600480803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610499565b6040518082815260200191505060405180910390f35b341561022c57600080fd5b610258600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190505061058a565b6040518083815260200180602001828103825283818151815260200191508051906020019080838360005b8381101561029e578082015181840152602081019050610283565b50505050905090810190601f1680156102cb5780820380516001836020036101000a031916815260200191505b50935050505060405180910390f35b60006102e4610736565b6000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600001546000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600101808054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156104765780601f1061044b57610100808354040283529160200191610476565b820191906000526020600020905b81548152906001019060200180831161045957829003601f168201915b5050505050905091509150915091565b60006104928233610499565b9050919050565b60006104a361074a565b839050600081511180156104b8575060648151105b15156104c357600080fd5b4291506040805190810160405280838152602001858152506000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008201518160000155602082015181600101908051906020019061057c92919061075e565b509050508191505092915050565b6000610594610736565b6000808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600001546000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600101808054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156107265780601f106106fb57610100808354040283529160200191610726565b820191906000526020600020905b81548152906001019060200180831161070957829003601f168201915b5050505050905091509150915091565b602060405190810160405280600081525090565b602060405190810160405280600081525090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061079f57805160ff19168380011785556107cd565b828001600101855582156107cd579182015b828111156107cc5782518255916020019190600101906107b1565b5b5090506107da91906107de565b5090565b61080091905b808211156107fc5760008160009055506001016107e4565b5090565b905600a165627a7a72305820578c1cdd7be62a4f2138d05a15b241857bc9f554fe0f176a194620b04cd8344e0029';
    var creationContract = web3.eth.contract(abi);
    var creation = creationContract.new(
       {
         from: web3.eth.accounts[0], 
         data: binaryData, 
         gas: '4700000'
       }, function (e, contract){
        console.log(e, contract);
        if (typeof contract.address !== 'undefined') {
             console.log('Contract mined! address: ' + contract.address + ' transactionHash: ' + contract.transactionHash);
        }
     });
    

    这里的abi和binaryData来自上一步的编译结果
    这里有以太坊测试网络Ropsten的部署结果:

    0xc59565465f95Cd80E7317dd5a03929E6900090Ff

    DApp开发

    完成合约的编译和部署之后就可以进行接下来的DApp的开发了。
    用之前提到过的MetaMask插件可以实现Chrome浏览器的轻量级钱包功能。MetaMask会在DApp运行环境中注入Web3框架,如果对MetaMask有强依赖的化我们只需要判断web3对象是否存在即可。

    if (typeof web3 === "undefined") {
        alert('未检测到Web3环境,请使用集成以太坊钱包的浏览器查看');
        return;
    }
    

    环境检测成功后就可以准备合约调用相关的依赖了,编译合约的时候生成的ABI描述文件可以直接构造为Javascript对象

    let footmarkABI = [
        {
            "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"
        },
        {
            "anonymous": false,
            "inputs": [
                {
                    "indexed": true,
                    "name": "from",
                    "type": "address"
                },
                {
                    "indexed": true,
                    "name": "to",
                    "type": "address"
                },
                {
                    "indexed": false,
                    "name": "text",
                    "type": "string"
                }
            ],
            "name": "LeaveMessage",
            "type": "event"
        },
        {
            "inputs": [],
            "payable": false,
            "stateMutability": "nonpayable",
            "type": "constructor"
        },
        {
            "constant": true,
            "inputs": [
                {
                    "name": "from",
                    "type": "address"
                }
            ],
            "name": "lookupFrom",
            "outputs": [
                {
                    "name": "time",
                    "type": "uint256"
                },
                {
                    "name": "text",
                    "type": "string"
                }
            ],
            "payable": false,
            "stateMutability": "view",
            "type": "function"
        },
        {
            "constant": true,
            "inputs": [
                {
                    "name": "to",
                    "type": "address"
                }
            ],
            "name": "lookupTo",
            "outputs": [
                {
                    "name": "time",
                    "type": "uint256"
                },
                {
                    "name": "text",
                    "type": "string"
                }
            ],
            "payable": false,
            "stateMutability": "view",
            "type": "function"
        }
    ];
    export default footmarkABI;
    

    ABI是对合约接口的描述,调用线上合约的时候需要用到,当然生成的合约地址也是必要数据

    data: function() {
        return {
            account: '',
            messageTo: '',
            messageContent: '',
            lookupMessageTo: '',
            lookupMessageFrom: '',
            contractInstance: null,
            contractAddress: '0xc59565465f95Cd80E7317dd5a03929E6900090Ff'
        };
    }
    
    // 实例化合约
    if(!this.contractInstance) {
        var contract = web3.eth.contract(footmarkABI);
        this.contractInstance = contract.at(this.contractAddress);
    }
    

    到这里合约就得到了一个实例化的合约对象,可以调用合约的方法,完成相应的功能。

    获取钱包当前账户

    getAccount() {
        web3.eth.getAccounts((error, accounts) => {
            if (!error) {
                this.account = accounts[0];
            }
        });
    }
    

    给其他人留言

    leaveMessageTo() {
        this.contractInstance.leaveMessage(this.messageContent, this.messageTo, { from: self.account }, (error, result) => {
            if (!error) {
                var event = this.contractInstance.LeaveMessage((eerror, eresult) => {
                    event.stopWatching();
                    alert("留言成功!");
                });
            } else {
                alert("留言失败!");
            }
        });
        alert("交易签发,静等回复。。。");
    }
    

    获取其他人给自己的留言

    getMessageFrom() {
        this.contractInstance.lookupFrom(this.lookupMessageFrom, { from: self.account }, (error, result) => {
            let time = result[0].toNumber() * 1000;
            let msg = result[1];
            let date = new Date(time);
            if (time > 0) {
                alert(date.toLocaleDateString() + ' ' + date.toLocaleTimeString() + '
    ' + result[1]);
            } else {
                alert("未查询到留言!");
            }
        });
    }
    

    获取自己给其他人的留言

    getMessageTo() {
        this.contractInstance.lookupTo(this.lookupMessageTo, { from: self.account }, (error, result) => {
            let time = result[0].toNumber() * 1000;
            let msg = result[1];
            let date = new Date(time);
            if (time > 0) {
                alert(date.toLocaleDateString() + ' ' + date.toLocaleTimeString() + '
    ' + result[1]);
            } else {
                alert("未查询到留言!");
            }
        });
    }
    

    功能完成DApp就可以发布了,当然这只是一个功能非常简单的DApp,这里只是把DApp的开发流程描述了一下,隐藏了一些比较繁琐的细节,比如交易签名(钱包帮我们完成,其实我们也可以自己来做,后续有时间再把这块内容详细介绍一下)、交易状态监听等等。

    Done

    这里有DApp的合约信息(Ropsten网络):0xc59565465f95Cd80E7317dd5a03929E6900090Ff

    和DApp的发布版本:
    http://dawntech.top/contract



    作者:Kirn
    链接:https://www.jianshu.com/p/72c4a0443d4e
    来源:简书
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
  • 相关阅读:
    Git使用教程
    H5项目常见问题汇总及解决方案
    globalAlpha 示例
    canvas 绘制三次贝塞尔曲线
    canvas 绘制二次贝塞尔曲线
    html5 图片360旋转
    HTML5 arc的例子
    HTML5 绘制阴影
    HTML5 canvas绘制文本
    html5 lineTo的使用例子
  • 原文地址:https://www.cnblogs.com/dhcn/p/14537282.html
Copyright © 2011-2022 走看看