精华内容
下载资源
问答
  • 以太坊DAPP开发实例: 全栈投票系统

    千次阅读 2018-04-11 11:17:47
    原文地址: Full Stack Hello World Voting Ethereum Dapp Tutorial — Part 1[教程最近更新于2018年1月]在我以前的文章中,我解释了以太坊平台和web 应用的差别。作为一个开发者,学习任何新技术的最好方法是潜入...

    原文地址: Full Stack Hello World Voting Ethereum Dapp Tutorial — Part 1

    [教程最近更新于2018年1月]


    在我以前的文章中,我解释了以太坊平台和web 应用的差别。作为一个开发者,学习任何新技术的最好方法是潜入和尝试构建应用。在这篇文章中,让我们构建一个简单的"Hello World!" 应用程序, 这是一个投票应用程序。

    该应用程序非常简单,它所做的只是初始化一组候选人,让任何人投票给候选人,并显示每个候选人收到的总票数。我们的目标不是简单地编写应用程序,而是学习编译、部署和交互的过程。

    我有意避免使用任何DAPP框架构建这个应用程序,因为框架抽象掉很多细节,你不了解系统的内部。此外,当您使用框架时,您将对框架为您所做的所有繁重工作有更多的感激!

    在许多方面,这篇文章是上一篇文章的续篇。如果你是刚接触以太坊,那我推荐你先阅读以下我的上一篇文章。

    这个练习的目的是:
            建立开发环境。
            学习编写智能合约、编译和将其部署到开发环境中的过程。
            与通过Nodejs控制台与blockchain上的智能合约交互

            通过一个简单的网页与智能合约交互,通过页面进行投票,显示候选人的票数

    整个应用程序运行在Ubuntu 16.04 xenial。macOS上我也测试过了。

    下面的视图将是我们要构建的应用


    1. 设置开发环境
         我们使用一个模拟的内存区块链(ganache)代替真实的区块链在进行开发。在本教程的2章,我们将与真实的区块链交互。下面是安装ganache、web3js的步骤,然后在linux上启动一个测试链。在macOS上安装过程也是一样的。对于Windows,你可以参考

    注意:本教程目前与web3js 0.20.2版本。web3js 1.0稳定版本如果发布,我会更新一下教程.


    你可以看到ganache-cli自动创建了10个测试账号,每个账号预分配了100(虚构的)ethers

    2.简单的投票合约
     我们将使用solidity编程语言来编写我们的合约。如果您熟悉面向对象编程,学习编写solidity合约应该是轻而易举的事。我们将编写一个合约对象,含有一个构造函数初始化候选人数组。合约对象有2个方法:

    1.返回候选人获得的总票数

    2.增加候选人的投票数。

    注意:构造函数只被调用一次,当您部署合约到区块链。不像在网络世界里的每一个部署你的代码覆盖旧的代码,部署后的代码在区块链上是不变的。例如,如果你更新你的合约并且再次部署,旧合约仍然会在区块链上, 它所存储的数据不受影响,新的部署将创建一个新实例的合约。

    下面是投票合约的代码:

    pragma solidity ^0.4.18;
    // We have to specify what version of compiler this code will compile with
    
    contract Voting {
      /* mapping field below is equivalent to an associative array or hash.
      The key of the mapping is candidate name stored as type bytes32 and value is
      an unsigned integer to store the vote count
      */
      
      mapping (bytes32 => uint8) public votesReceived;
      
      /* Solidity doesn't let you pass in an array of strings in the constructor (yet).
      We will use an array of bytes32 instead to store the list of candidates
      */
      
      bytes32[] public candidateList;
    
      /* This is the constructor which will be called once when you
      deploy the contract to the blockchain. When we deploy the contract,
      we will pass an array of candidates who will be contesting in the election
      */
      function Voting(bytes32[] candidateNames) public {
        candidateList = candidateNames;
      }
    
      // This function returns the total votes a candidate has received so far
      function totalVotesFor(bytes32 candidate) view public returns (uint8) {
        require(validCandidate(candidate));
        return votesReceived[candidate];
      }
    
      // This function increments the vote count for the specified candidate. This
      // is equivalent to casting a vote
      function voteForCandidate(bytes32 candidate) public {
        require(validCandidate(candidate));
        votesReceived[candidate] += 1;
      }
    
      function validCandidate(bytes32 candidate) view public returns (bool) {
        for(uint i = 0; i < candidateList.length; i++) {
          if (candidateList[i] == candidate) {
            return true;
          }
        }
        return false;
      }
    }

    Voting.sol hosted with ❤ by GitHub

    复制上面的代码,在hello_world_voting目录下创建一个Voting.sol文件。现在让我们来编译代码并将其部署到ganache的区块链上.

    为了编译solidity代码,我们需要安装名字为solc的npm模块

    mahesh@projectblockchain:~/hello_world_voting$ npm install solc

    我们将在node控制台中使用这个库来编译我们的合约。在上一篇文章中我们提到,web3js是一个让我们可以通过rpc访问区块链的库。我们将使用该库来部署我们的应用程序并与之交互。

    首先,在命令行中断运行`node`命令进入node控制台,初始化solc和文本对象。下面的所有代码片段都需要在mode控制台中键入

    mahesh@projectblockchain:~/hello_world_voting$ node
    > Web3 = require('web3')
    > web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));

    为了确保web3对象已经初始化、区块链能够访问,让我们试一下查询区块链上的所有账户。您应该看到如下的结果:

    > web3.eth.accounts
    ['0x9c02f5c68e02390a3ab81f63341edc1ba5dbb39e',
    '0x7d920be073e92a590dc47e4ccea2f28db3f218cc',
    '0xf8a9c7c65c4d1c0c21b06c06ee5da80bd8f074a9',
    '0x9d8ee8c3d4f8b1e08803da274bdaff80c2204fc6',
    '0x26bb5d139aa7bdb1380af0e1e8f98147ef4c406a',
    '0x622e557aad13c36459fac83240f25ae91882127c',
    '0xbf8b1630d5640e272f33653e83092ce33d302fd2',
    '0xe37a3157cb3081ea7a96ba9f9e942c72cf7ad87b',
    '0x175dae81345f36775db285d368f0b1d49f61b2f8',
    '0xc26bda5f3370bdd46e7c84bdb909aead4d8f35f3']
    
    

    从voting.sol加载代码,保存在一个字符串变量中,然后开始编译

    > code = fs.readFileSync('Voting.sol').toString()
    > solc = require('solc')
    > compiledCode = solc.compile(code)
    
    
     当你的代码编译成功并打印了合约对象的内容(在node控制台中输出的内容),有2个字段很重要,需要理解它们:
    compiledCode.contracts[‘:Voting’].bytecode: Voting.sol源代码编译后得到的字节码。这是将被部署到blockchain的代码。
    compiledCode.contracts[‘:Voting’].interface: 合约接口或模板(称为ABI)告诉用户合约含有哪些方法。您需要这些ABI的定义,因为将来你总是需要与合约交互的。更多ABI信息请参考: 这里

    现在我们来部署合约。你先创建一个合约对象(下面代码中的 VotingContract),用于在区块链上部署和初始化智能合约。

    > abiDefinition = JSON.parse(compiledCode.contracts[':Voting'].interface)
    > VotingContract = web3.eth.contract(abiDefinition)
    > byteCode = compiledCode.contracts[':Voting'].bytecode
    > deployedContract = VotingContract.new(['Rama','Nick','Jose'],{data: byteCode, from: web3.eth.accounts[0], gas: 4700000})
    > deployedContract.address
    > contractInstance = VotingContract.at(deployedContract.address)
    VotingContract.new将智能合约部署到区块链。第一个参数是一个候选人数组。让我们看看第二个参数中的是什么:

    data:这是我们部署到链上的字节码。

    from:blockchain要追踪谁部署了合约。目前,我们只是挑选web3.eth.accounts中的第一个帐户作为合约的拥有者。记住,在我们启动测试链时web3.eth.accounts有10个ganache创建的账号。在真是的链上,你不能使用任何账号,你必须先获取并解锁账号。创建账号时会让你设置密码,用来保证你的这个帐户的所有权。Ganache为了方便默认10个账户都解锁了。

    gas:与区块链交互的手续费。这些钱是给那些把你的合约写入区块链等所有动作的矿工们的。通过‘gas’字段,你设置想要付多少钱给矿工。你的账户中的ether(ETH)资产将用于购买gas。gas的价格是由网络设定的。

    我们现在已经部署了合约并拥有了合约实例(上面的变量:contractinstance),可以用它来跟合约交互。在区块链上的智能合约数量成千上万。那么,怎么去标识合约呢?答:deployedContract.address。当你想要跟合约交互时,你需要合约的部署地址和前面提到的 ABI定义。

    3. 在nodejs控制台中与智能合约交互

    > contractInstance.totalVotesFor.call('Rama')
    { [String: '0'] s: 1, e: 0, c: [ 0 ] }
    > contractInstance.voteForCandidate('Rama', {from: web3.eth.accounts[0]})
    '0xdedc7ae544c3dde74ab5a0b07422c5a51b5240603d31074f5b75c0ebc786bf53'
    > contractInstance.voteForCandidate('Rama', {from: web3.eth.accounts[0]})
    '0x02c054d238038d68b65d55770fabfca592a5cf6590229ab91bbe7cd72da46de9'
    > contractInstance.voteForCandidate('Rama', {from: web3.eth.accounts[0]})
    '0x3da069a09577514f2baaa11bc3015a16edf26aad28dffbcd126bde2e71f2b76f'
    > contractInstance.totalVotesFor.call('Rama').toLocaleString()
    '3'
    
    

    在nodejs控制台中尝试以上命令你将看到选票计数在递增。每次你投票给一个候选人,你得到一个交易ID:例如:“0xdedc7ae544c3dde74ab5a0b07422c5a51b5240603d31074f5b75c0ebc786bf53。这个交易ID是发生交易的证明,在任何时间你都可以通过交易ID溯源。交易是不可篡改的,这也是想以太坊这样的区块链的一大优势。在后续教程我们将利用这一特性来构建应用.

    4. 连接区块链并且可以投票的网页

    现在大部分的工作已经完成,我们现在要做的就是用HTML和js实现一个简单页面来展示候选人和调用投票命令。下面你可以找到HTML代码和js文件。把它们都丢在hello_world_voting目录并在浏览器中打开index.html。

    index.html hosted with ❤ by GitHub

    <!DOCTYPE html>
    <html>
    <head>
      <title>Hello World DApp</title>
      <link href='https://fonts.googleapis.com/css?family=Open+Sans:400,700' rel='stylesheet' type='text/css'>
      <link href='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css' rel='stylesheet' type='text/css'>
    </head>
    <body class="container">
      <h1>A Simple Hello World Voting Application</h1>
      <div class="table-responsive">
        <table class="table table-bordered">
          <thead>
            <tr>
              <th>Candidate</th>
              <th>Votes</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td>Rama</td>
              <td id="candidate-1"></td>
            </tr>
            <tr>
              <td>Nick</td>
              <td id="candidate-2"></td>
            </tr>
            <tr>
              <td>Jose</td>
              <td id="candidate-3"></td>
            </tr>
          </tbody>
        </table>
      </div>
      <input type="text" id="candidate" />
      <a href="#" οnclick="voteForCandidate()" class="btn btn-primary">Vote</a>
    </body>
    <script src="https://cdn.rawgit.com/ethereum/web3.js/develop/dist/web3.js"></script>
    <script src="https://code.jquery.com/jquery-3.1.1.slim.min.js"></script>
    <script src="./index.js"></script>
    </html>

    index.js

    web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
    abi = JSON.parse('[{"constant":false,"inputs":[{"name":"candidate","type":"bytes32"}],"name":"totalVotesFor","outputs":[{"name":"","type":"uint8"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"candidate","type":"bytes32"}],"name":"validCandidate","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"votesReceived","outputs":[{"name":"","type":"uint8"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"x","type":"bytes32"}],"name":"bytes32ToString","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"candidateList","outputs":[{"name":"","type":"bytes32"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"candidate","type":"bytes32"}],"name":"voteForCandidate","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"contractOwner","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"inputs":[{"name":"candidateNames","type":"bytes32[]"}],"payable":false,"type":"constructor"}]')
    VotingContract = web3.eth.contract(abi);
    // In your nodejs console, execute contractInstance.address to get the address at which the contract is deployed and change the line below to use your deployed address
    contractInstance = VotingContract.at('0x2a9c1d265d06d47e8f7b00ffa987c9185aecf672');
    candidates = {"Rama": "candidate-1", "Nick": "candidate-2", "Jose": "candidate-3"}
    
    function voteForCandidate() {
      candidateName = $("#candidate").val();
      contractInstance.voteForCandidate(candidateName, {from: web3.eth.accounts[0]}, function() {
        let div_id = candidates[candidateName];
        $("#" + div_id).html(contractInstance.totalVotesFor.call(candidateName).toString());
      });
    }
    
    $(document).ready(function() {
      candidateNames = Object.keys(candidates);
      for (var i = 0; i < candidateNames.length; i++) {
        let name = candidateNames[i];
        let val = contractInstance.totalVotesFor.call(name).toString()
        $("#" + candidates[name]).html(val);
      }
    });
    如果您还记得,我们前面说过,我们需要ABI和合约地址来与智能合约进行交互。你可以看到在index.js文件中是如何使用的。

    这是你在浏览器打开index.html将看到的画面


    如果您能够在文本框中输入候选名称并进行投票并看到选票计数在增加,那么你的第一个应用成功了!祝贺你!
    总结: 设置你的开发环境,编写了一个简单的合约,编译并部署合约到blockchain,通过nodejs控制台与合约交互, 通过网页与合约交互。如果你还没有开始的话,现在是你佩服自己的好时机:)

    第2章中,我们将把这个合约部署到公共测试网络,以便全世界都能看到它并为候选人投票。我们也会变得越来越复杂和使用truffle开发框架(而不使用Nodejs控制台管理全过程)。希望这个教程帮助你得到一个实际的想法如何开始在以太坊平台开发DAPP。

    如果你想要一个更具挑战性的项目,我创建了一个教程: 基于以太坊和IPFS建立一个去中心化的的eBay
    展开全文
  • ubuntu16.04,64位 还需要安装以太坊相关的环境: *nodejs *truffle *solidity*testrpc 另外,本篇还会用到webpack,安装教程网上也有很多。这部分如果不熟悉的话请自行查阅学习下。需
  • 以太坊有几个公共测试网络和一个主网。1. 测试网络:如ropsten blockchains,Rinkeby,Kovan。它们仅用于测试目的。在这些网络上使用的以太币都是假的。2. 主网(Homestead):这是整个世界真实交易的链。你在这个网络...

    在第1章(全栈投票系统),我们用ganache在我们的开发环境建立了一个简单的投票程序。现在,让我们把这个应用在真正的blockchain。以太坊有几个公共测试网络和一个主网。

    1. 测试网络:如ropsten blockchains,Rinkeby,Kovan。它们仅用于测试目的。在这些网络上使用的以太币都是假的。

    2. 主网(Homestead):这是整个世界真实交易的链。你在这个网络上使用的以太币有真正的价值。

    在本教程中,我们将完成以下内容:

    1. 安装geth-用于下载以太坊区块链和在本地运行以太坊节点的客户端。
    2. 安装Truffle 用于编译和部署智能合约的以太坊DAPP开发框架
    3. 我们要在上一章的基础上做一些小改动, 以便之前的投票系统能够运行在truffle框架上
    4. 编译并部署合约到Rinkeby测试网络

    5. 通过truffle控制台与合约交互, 然后通过网页与合约交互。


    1. 安装geth并同步区块数据

    我已经在MacOS和Ubuntu安装和测试。安装非常简单:

    Mac:

    mahesh@projectblockchain:~$ brew tap ethereum/ethereum
    mahesh@projectblockchain:~$ brew install ethereum

    Ubuntu:

    mahesh@projectblockchain:~$ sudo apt-get install software-properties-common
    mahesh@projectblockchain:~$ sudo add-apt-repository -y ppa:ethereum/ethereum
    mahesh@projectblockchain:~$ sudo apt-get update
    mahesh@projectblockchain:~$ sudo apt-get install ethereum

    在这里你可以找到各种平台的安装说明:https://github.com/ethereum/go-ethereum/wiki/building-ethereum

    一旦你已经安装了geth,在命令行控制台运行下面的命令:

    mahesh@projectblockchain:~$ geth --rinkeby --syncmode "fast" --rpc --rpcapi db,eth,net,web3,personal --cache=1024  --rpcport 8545 --rpcaddr 127.0.0.1 --rpccorsdomain "*"

    这将启动以太坊节点,连接到其他节点开始下载区块。下载时间取决于多种因素,如您的网速,计算机的内存,硬盘等,cache根据你的机器内存调整, 我的机器是8GB内存50Mbps带宽,下载区块花了我30–45分钟。

    geth运行的时候,你会看到类似下面的输出。你可以在输出中查看区块号。如果你的区块完全同步了,你会看到区块号与这个页面上的接近:https://rinkeby.etherscan.io/

    I0130 22:18:15.116332 core/blockchain.go:1064] imported   32 blocks,    49 txs (  6.256 Mg) in 185.716ms (33.688 Mg/s). #445097 [e1199364… / bce20913…]
    I0130 22:18:20.267142 core/blockchain.go:1064] imported    1 blocks,     1 txs (  0.239 Mg) in  11.379ms (20.963 Mg/s). #445097 [b4d77c46…]
    I0130 22:18:21.059414 core/blockchain.go:1064] imported    1 blocks,     0 txs (  0.000 Mg) in   7.807ms ( 0.000 Mg/s). #445098 [f990e694…]
    I0130 22:18:34.367485 core/blockchain.go:1064] imported    1 blocks,     0 txs (  0.000 Mg) in   4.599ms ( 0.000 Mg/s). #445099 [86b4f29a…]
    I0130 22:18:42.953523 core/blockchain.go:1064] imported    1 blocks,     2 txs (  0.294 Mg) in   9.149ms (32.136 Mg/s). #445100 [3572f223…]

    2. 安装Truffle框架

    使用npm安装truffle,使用的truffle版本是 3.1.1.

    npm install -g truffle
    3. 启动投票合约

    首先,启动truffle工程:

    mahesh@projectblockchain:~$ mkdir voting
    mahesh@projectblockchain:~$ cd voting
    mahesh@projectblockchain:~/voting$ npm install -g webpack
    mahesh@projectblockchain:~/voting$ truffle unbox webpack
    mahesh@projectblockchain:~/voting$ ls
    README.md               contracts               node_modules            test                    webpack.config.js       truffle.js
    app                     migrations              package.json            
    mahesh@projectblockchain:~/voting$ ls app/
    index.html  javascripts  stylesheets
    mahesh@projectblockchain:~/voting$ ls contracts/
    ConvertLib.sol  MetaCoin.sol  Migrations.sol
    mahesh@projectblockchain:~/voting$ ls migrations/
    1_initial_migration.js  2_deploy_contracts.js

    正如您在上面看到的,truffle创建了运行完整dapp所需的必要文件和目录。truffle还创建了一个示例应用程序来帮助您开始使用(本教程中我们不会使用它)。您可以安全地删除该项目的contracts目录中的ConvertLib.sol和MetaCoin.sol文件。


    理解migrations目录的内容很重要。这些migrations文件用于将合约部署到区块链。 (如果您还记得,在上一篇文章中,我们使用了VotingContract.new将合约部署到区块链,现在我们不需要这样做)。第一个文件:1_initial_migration.js将名为Migrations的合同部署到区块链中,并用于存储您已部署的最新合约。每次运行 migration时,truffle都会查询区块链以获取已部署的最后一个合约,然后部署尚未部署的任何合约。然后更新Migrations合约中的last_completed_migration字段,以指示部署的最新合约。您可以简单地将其视为名为 migration的数据库表,其中名为last_completed_migration的列始终保持最新状态。你可以在 truffle文件页面找到更多细节。

    现在让我们用我们在前一个教程中编写的所有代码更新该项目,并在下面进行一些更改。

    首先,将上一教程中的Voting.sol复制到contracts目录(这个文件没有变化)。

    pragma solidity ^0.4.18;
    // We have to specify what version of compiler this code will compile with
    
    contract Voting {
      /* mapping field below is equivalent to an associative array or hash.
      The key of the mapping is candidate name stored as type bytes32 and value is
      an unsigned integer to store the vote count
      */
      
      mapping (bytes32 => uint8) public votesReceived;
      
      /* Solidity doesn't let you pass in an array of strings in the constructor (yet).
      We will use an array of bytes32 instead to store the list of candidates
      */
      
      bytes32[] public candidateList;
    
      /* This is the constructor which will be called once when you
      deploy the contract to the blockchain. When we deploy the contract,
      we will pass an array of candidates who will be contesting in the election
      */
      function Voting(bytes32[] candidateNames) public {
        candidateList = candidateNames;
      }
    
      // This function returns the total votes a candidate has received so far
      function totalVotesFor(bytes32 candidate) view public returns (uint8) {
        require(validCandidate(candidate));
        return votesReceived[candidate];
      }
    
      // This function increments the vote count for the specified candidate. This
      // is equivalent to casting a vote
      function voteForCandidate(bytes32 candidate) public {
        require(validCandidate(candidate));
        votesReceived[candidate] += 1;
      }
    
      function validCandidate(bytes32 candidate) view public returns (bool) {
        for(uint i = 0; i < candidateList.length; i++) {
          if (candidateList[i] == candidate) {
            return true;
          }
        }
        return false;
      }
    }

    Voting.sol hosted with ❤ by GitHub

    mahesh@projectblockchain:~/voting$ ls contracts/
    Migrations.sol  Voting.sol

    接下来,用以下内容替换migrations目录中的2_deploy_contracts.js的内容:

    var Voting = artifacts.require("./Voting.sol");
    module.exports = function(deployer) {
      deployer.deploy(Voting, ['Rama', 'Nick', 'Jose'], {gas: 6700000});
    };
    /* As you can see above, the deployer expects the first argument to   be the name of the contract followed by constructor arguments. In our case, there is only one argument which is an array of
    candidates. The third argument is a hash where we specify the gas required to deploy our code. The gas amount varies depending on the size of your contract.
    */


    您还可以将gas值设置为truffle.js中的全局设置。像下面那样添加gas选项,以便将来如果您在特定migration文件忘记设置gas,它将默认使用全局值。

    require('babel-register')
    module.exports = {
      networks: {
        development: {
          host: 'localhost',
          port: 8545,
          network_id: '*',
          gas: 470000
        }
      }
    }

    用下面的内容替换app/javascripts/app.js的内容。

    // Import the page's CSS. Webpack will know what to do with it.
    import "../stylesheets/app.css";
    
    // Import libraries we need.
    import { default as Web3} from 'web3';
    import { default as contract } from 'truffle-contract'
    
    /*
     * When you compile and deploy your Voting contract,
     * truffle stores the abi and deployed address in a json
     * file in the build directory. We will use this information
     * to setup a Voting abstraction. We will use this abstraction
     * later to create an instance of the Voting contract.
     * Compare this against the index.js from our previous tutorial to see the difference
     * https://gist.github.com/maheshmurthy/f6e96d6b3fff4cd4fa7f892de8a1a1b4#file-index-js
     */
    
    import voting_artifacts from '../../build/contracts/Voting.json'
    
    var Voting = contract(voting_artifacts);
    
    let candidates = {"Rama": "candidate-1", "Nick": "candidate-2", "Jose": "candidate-3"}
    
    window.voteForCandidate = function(candidate) {
      let candidateName = $("#candidate").val();
      try {
        $("#msg").html("Vote has been submitted. The vote count will increment as soon as the vote is recorded on the blockchain. Please wait.")
        $("#candidate").val("");
    
        /* Voting.deployed() returns an instance of the contract. Every call
         * in Truffle returns a promise which is why we have used then()
         * everywhere we have a transaction call
         */
        Voting.deployed().then(function(contractInstance) {
          contractInstance.voteForCandidate(candidateName, {gas: 140000, from: web3.eth.accounts[0]}).then(function() {
            let div_id = candidates[candidateName];
            return contractInstance.totalVotesFor.call(candidateName).then(function(v) {
              $("#" + div_id).html(v.toString());
              $("#msg").html("");
            });
          });
        });
      } catch (err) {
        console.log(err);
      }
    }
    
    $( document ).ready(function() {
      if (typeof web3 !== 'undefined') {
        console.warn("Using web3 detected from external source like Metamask")
        // Use Mist/MetaMask's provider
        window.web3 = new Web3(web3.currentProvider);
      } else {
        console.warn("No web3 detected. Falling back to http://localhost:8545. You should remove this fallback when you deploy live, as it's inherently insecure. Consider switching to Metamask for development. More info here: http://truffleframework.com/tutorials/truffle-and-metamask");
        // fallback - use your fallback strategy (local node / hosted node + in-dapp id mgmt / fail)
        window.web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
      }
    
      Voting.setProvider(web3.currentProvider);
      let candidateNames = Object.keys(candidates);
      for (var i = 0; i < candidateNames.length; i++) {
        let name = candidateNames[i];
        Voting.deployed().then(function(contractInstance) {
          contractInstance.totalVotesFor.call(name).then(function(v) {
            $("#" + candidates[name]).html(v.toString());
          });
        })
      }
    });

    app.js hosted with ❤ by GitHub

    将app/index.html的内容替换为以下内容。 即使这个文件与上一章几乎相同,除了包含的js文件是第41行的app.js。

    <!DOCTYPE html>
    <html>
    <head>
      <title>Hello World DApp</title>
      <link href='https://fonts.googleapis.com/css?family=Open+Sans:400,700' rel='stylesheet' type='text/css'>
      <link href='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css' rel='stylesheet' type='text/css'>
    </head>
    <body class="container">
      <h1>A Simple Hello World Voting Application</h1>
      <div id="address"></div>
      <div class="table-responsive">
        <table class="table table-bordered">
          <thead>
            <tr>
              <th>Candidate</th>
              <th>Votes</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td>Rama</td>
              <td id="candidate-1"></td>
            </tr>
            <tr>
              <td>Nick</td>
              <td id="candidate-2"></td>
            </tr>
            <tr>
              <td>Jose</td>
              <td id="candidate-3"></td>
            </tr>
          </tbody>
        </table>
        <div id="msg"></div>
      </div>
      <input type="text" id="candidate" />
      <a href="#" οnclick="voteForCandidate()" class="btn btn-primary">Vote</a>
    </body>
    <script src="https://cdn.rawgit.com/ethereum/web3.js/develop/dist/web3.js"></script>
    <script src="https://code.jquery.com/jquery-3.1.1.slim.min.js"></script>
    <script src="app.js"></script>
    </html>

    index.html hosted with ❤ by GitHub

    4.将合约部署到Rinkeby测试网络

    在我们可以部署合约之前,我们需要一个帐户和一些以太币。当我们使用ganache时,它创建了10个测试账户并预分配100个虚假以太币。 但是对于测试网络和主网,我们必须创建帐户并自己添加一些ETH。

    在您的命令行终端中,执行以下操作:

    mahesh@projectblockchain:~/voting$ truffle console
    truffle(default)> web3.personal.newAccount('verystrongpassword')
    '0x95a94979d86d9c32d1d2ab5ace2dcc8d1b446fa1'
    truffle(default)> web3.eth.getBalance('0x95a94979d86d9c32d1d2ab5ace2dcc8d1b446fa1')
    { [String: '0'] s: 1, e: 0, c: [ 0 ] }
    truffle(default)> web3.personal.unlockAccount('0x95a94979d86d9c32d1d2ab5ace2dcc8d1b446fa1', 'verystrongpassword', 15000)
    // Replace 'verystrongpassword' with a good strong password.
    // The account is locked by default, make sure to unlock it before using the account for deploying and interacting with the blockchain.

    在之前的文章中,我们启动了一个node控制台并初始化了web3对象。当我们执行truffle控制台时,所有这些都是自动完成的,并且我们得到一个可以使用的web3对象。我们现在有一个地址为0x95a94979d86d9c32d1d2ab5ace2dcc8d1b446fa1的账户(您的账户中会是不同的地址),余额为0。

    您可以通过这个网站为Rinkeby网络分配一些测试以太币:https://faucet.rinkeby.io/。再次尝试web3.eth.getBalance以确保您拥有以太币。您也可以在https://rinkeby.etherscan.io/上输入您的地址以查看您的帐户余额。如果您可以在https://rinkeby.etherscan.io/上看到非零余额,但是如果web3.eth.getBalance仍显示0,则表示您的同步尚未完成。您只需等待本地区块链同步并追上即可。


    现在您已经有了一些以太币,请继续编译并将合约部署到区块链。如果一切顺利,下面是运行的命令和输出。

    注意: 在部署合同之前,不要忘记解锁账户(默认使用 web3.eth.accounts[0])。

    mahesh@projectblockchain:~/voting$ truffle migrate
    Compiling Migrations.sol...
    Compiling Voting.sol...
    Writing artifacts to ./build/contracts
    Running migration: 1_initial_migration.js
    Deploying Migrations...
    Migrations: 0x3cee101c94f8a06d549334372181bc5a7b3a8bee
    Saving successful migration to network...
    Saving artifacts...
    Running migration: 2_deploy_contracts.js
    Deploying Voting...
    Voting: 0xd24a32f0ee12f5e9d233a2ebab5a53d4d4986203
    Saving successful migration to network...
    Saving artifacts...
    mahesh@projectblockchain:~/voting$

    在我的机器上,部署合约大概花了70-80秒。

    5.与投票合约交互

    如果您能够成功部署合约,您现在应该能够获取投票计数并通过truffle控制台进行投票。

    mahesh@projectblockchain:~/voting$ truffle console
    truffle(default)> Voting.deployed().then(function(contractInstance) {contractInstance.voteForCandidate('Rama').then(function(v) {console.log(v)})})
    // After a few seconds, you should see a transaction receipt like this:
    receipt:
    { blockHash: '0x7229f668db0ac335cdd0c4c86e0394a35dd471a1095b8fafb52ebd7671433156',
    blockNumber: 469628,
    contractAddress: null,
    ....
    ....
    truffle(default)> Voting.deployed().then(function(contractInstance) {contractInstance.totalVotesFor.call('Rama').then(function(v) {console.log(v)})})
    { [String: '1'] s: 1, e: 0, c: [ 1] }

    如果你能够做到这一点,那表示成功了,你的已经生效了,现在继续,启动服务器

    mahesh@projectblockchain:~/voting$ npm run dev


    通过浏览器,您应该可以在localhost:8080上看到投票页面,并能够投票并看到所有候选人的投票计数。 由于我们正在处理一个真正的区块链,因此每次写入区块链(voteForCandidate)将需要几秒钟(矿工必须将您的交易包括在区块中并将区块包含在区块链中)。


    如果你看到这个页面并且能够投票,你可以在公共测试网络上建立一个完整的以太坊应用程序,恭喜!

    由于您所有的交易都是公开的,您可以在这里查看它们:http://rinkeby.etherscan.io/。 只需输入您的帐户地址,它会显示您所有的交易与时间戳。

    希望你能够跟得上我并让程序工作。 你可以在这里找到github仓库中的所有文件。 如果遇到让应用程序工作的问题,请随时通过twitter @zastrinlab来戳我。

    [本教程的第3部分现已发布!]

    如果你想要一个更具挑战性的项目,我创建了一个在Ethereum和IPFS上构建去中心化eBay的课程

    一如既往地感谢Raine Rupert Revere的所有反馈,并感谢Craig Skipsey找到所有的错误!

    展开全文
  • 以太坊DApp 教程,可以高效的学习如何开发一个DApp,很适合入门。 2. 工作流程 a. 环境搭建 建议使用Mac OS环境,不然可能会出现各种坑。 安装 NodeJS ,安装 Python 。 安装 testrpc ...

    一、区块链

    1. 分布式去中心化

    比特币设计的初衷就是要避免依赖中心化的机构,没有发行机构,也不可能操纵发行数量。既然没有中心化的信用机构,在电子货币运行的过程中,也势必需要一种机制来认可运行在区块链上的行为(包括比特币的运营,亦或是运行在区块链上的其他业务),这种机制就是共识机制。在完全去中心化的区块链上运行的比特币,采用的是PoW(Proof of Work,工作量证明),该机制完美的解决了拜占庭将军问题(存在异常的情况下仍能达成一致)。因为基础网络架构为分布式,对单独一个节点是无法控制或破坏整个网络,掌握网内51%的运算能力(非节点数)才有可能操作交易,而这个代价大概要超过270亿美元。

    2. 无须信任

    整个区块链网络中的数据是公开透明的,每个节点(参与者)都可自由加入该网络中,下载到所有的数据。任意两个节点间的数据交换无需互相信任,完全依靠区块链中的交易历史和数据的可追溯,以及共识机制来保证数据交换的正确且不可逆的执行。

    3. 不可篡改和加密安全性

    跟当前银行网银系统(特别是公司网银系统)的加密机制类似,区块链的数据结构和交易流程中大量的使用了公私钥来加解密,保证数据的安全性。基于该技术基础,甚至可以应用群组签名来保证共有数据的安全性。任何事物既然有优点,也同时会存在不足之处。根源于分布式网络架构和共识机制,在区块链上运行的交易确认时间会比较长(比特币的确认时间大概是15分钟),交易并发数受限(比特币的每秒交易数为7笔,而淘宝的每秒并发数能达到10万左右),区块的容量限制(当前为1M,区块链的扩容一直在讨论中),监管难以介入,基于工作量证明的共识机制存在浪费系统资源和带宽的问题。

    4. 区块链技术

    a. 区块

    区块是一个包含在区块链(公开账簿)里的聚合了交易信息的容器。它由一个包含元数据的区块头和紧跟其后的构成区块主体的一长串交易组成。区块头是80字节,而平均每个交易至少是250字节,而且平均每个区块至少包含超过500个交易。
    区块结构如下图

    交易(Tx)详情中的结构如下图

    b. 区块链

    当一个节点从网络接受到传入的区块时,它会验证这些区块,然后链接到现有的区块链上,链接的形态如下图:

    由于每个区块包含前一个区块的HASH值,这就使得从创世块到当前块形成了一条块链,每个区块必定按时间顺序跟随在前一个区块之后,因为如果不知道前一块区块的HASH值就没法生成当前区块。要改变一个已经在块链中存在一段时间的区块,从计算上来说是不可行的,因为如果它被改变,它之后的每个区块必须随之改变。这些特性使得双花比特币非常困难,区块链是比特币的最大创新。

    5. 比特币钱包

    a. 比特币钱包的生成

    1. 首先使用随机数发生器生成一个 私钥 。一般来说这是一个256bits的数,拥有了这串数字就可以对相应 钱包地址 中的比特币进行操作,所以必须被安全地保存起来。
    2. 私钥经过SECP256K1算法处理生成了公钥。SECP256K1是一种椭圆曲线算法,通过一个已知私钥时可以算得公钥,而公钥已知时却无法反向计算出私钥。这是保障比特币安全的算法基础。
    3. 同SHA256一样,RIPEMD160也是一种Hash算法,由公钥可以计算得到公钥哈希,而反过来是行不通的。
    4. 将一个字节的地址版本号连接到公钥哈希头部(对于比特币网络的pubkey地址,这一字节为“0”),然后对其进行两次SHA256运算,将结果的前4字节作为公钥哈希的校验值,连接在其尾部。
    5. 将上一步结果使用BASE58进行编码(比特币定制版本),就得到了钱包地址。

    流程图如下

    b .转账

    比特币钱包间的转账是通过交易(Transaction)实现的。交易数据是由转出钱包私钥的所有者生成,也就是说有了私钥就可以花费该钱包的比特币余额。生成交易的过程如下:

    1. 交易的原始数据包括“转账数额”和“转入钱包地址”,但是仅有这些是不够的,因为无法证明交易的生成者对“转出钱包地址”余额有动用的权利。所以需要用私钥对原始数据进行签名。
    2. 生成“转出钱包公钥”,这一过程与生成钱包地址的第2步是一样的。
    3. 将“转出签名”和“转出公钥”添加到原始交易数据中,生成了正式的交易数据,这样它就可以被广播到比特币网络进行转账了。

    二、以太坊Ethereum

    1. 概念

    a. 什么是以太坊

    简单来说,以太坊是一种新的法律形式。现行法律的本质是一种合约。它是由(生活于某一社群的)人和他们的领导者之间所缔结的,一种关于彼此该如何行动的共识。个体之间也存在着一些合约,这些合约可以理解为一种私法,相应的,这种私法仅对合约的参与者生效。

    例如,你和一个人订立合约,借给他一笔钱,但他最后毁约了,不打算还这笔钱。此时你多半会将对方告上法庭。在现实生活中,打官司这种事情常常混乱不堪并且充满了不确定性。将对方告上法庭,也通常意味着你需要支付高昂的费用聘请律师,来帮你在法庭上针对法律条文展开辩论,而且这一过程一般都旷日持久。而且,即使你最终赢了官司,你依然可能会遇到问题(比如,对方拒不执行法庭判决)。
    令人欣慰的是,当初你和借款人把条款写了下来,订立了合约。但法律的制定者和合约的起草者们都必须面对一个不容忽视的挑战:那就是,理想情况下,法律或者合约的内容应该是明确而没有歧义的,但现行的法律和合约都是由语句构成的,而语句,则是出了名的充满歧义。
    因此,一直以来,现行的法律体系都存在着两个巨大的问题:首先,合约或法律是由充满歧义的语句定义的,第二,强制执行合约或法律的代价非常大。
    而以太坊,通过数字货币和编程语言的结合,解决了现行法律体系的这两大问题。
    以太坊系统自身带有一种叫做以太币(Ether)的数字货币。以太币和著名的数字货币比特币(Bitcoin)有着非常多的相似之处。两者均为数字储值货币,且无法伪造,都以去中心化的方式运行来保证货币供应不被某一方所控制。两者都可以像电子邮件一样,作为货币自由地在全世界流通。而且,由于它们可以做到传统货币做不到的事情,因此用户对它们未来的价值充满期待 。

    另外
    1.详情请阅读以太坊白皮书 (中文英文)。
    2.以太坊教程

    b. 基本知识

    • 公钥加密系统。 Alice有一把公钥和一把私钥。她可以用她的私钥创建数字签名,而Bob可以用她的公钥来验证这个签名确实是用Alice的私钥创建的,也就是说,确实是Alice的签名。当你创建一个以太坊或者比特币钱包的时候,那长长的0xdf…5f地址实质上是个公钥,对应的私钥保存某处。类似于Coinbase的在线钱包可以帮你保管私钥,你也可以自己保管。如果你弄丢了存有资金的钱包的私钥,你就等于永远失去了那笔资金,因此你最好对私钥做好备份。
    • 点对点网络。 就像BitTorrent, 以太坊分布式网络中的所有节点都地位平等,没有中心服务器。
    • 区块链。 区块链就像是一个全球唯一的帐簿,或者说是数据库,记录了网络中所有交易历史。
    • 以太坊虚拟机(EVM)。 它让你能在以太坊上写出更强大的程序(比特币上也可以写脚本程序)。它有时也用来指以太坊区块链,负责执行智能合约以及一切。
    • 节点。 你可以运行节点,通过它读写以太坊区块链,也即使用以太坊虚拟机。完全节点需要下载整个区块链。轻节点仍在开发中。
    • 矿工。 挖矿,也就是处理区块链上的区块的节点。这个网页可以看到当前活跃的一部分以太坊矿工:stats.ethdev.com。
    • 工作量证明。 矿工们总是在竞争解决一些数学问题。第一个解出答案的(算出下一个区块)将获得以太币作为奖励。然后所有节点都更新自己的区块链。所有想要算出下一个区块的矿工都有与其他节点保持同步,并且维护同一个区块链的动力,因此整个网络总是能达成共识。(注意:以太坊正计划转向没有矿工的权益证明系统(POS),不过那不在本文讨论范围之内。)
    • 以太币。 缩写ETH。一种你可以购买和使用的真正的数字货币。这里是可以交易以太币的其中一家交易所的走势图。在写这篇文章的时候,1个以太币价值65美分。
    • Gas. 在以太坊上执行程序以及保存数据都要消耗一定量的以太币,Gas是以太币转换而成。这个机制用来保证效率。
    • DApp. 以太坊社区把基于智能合约的应用称为去中心化的应用程序(Decentralized App)。DApp的目标是(或者应该是)让你的智能合约有一个友好的界面,外加一些额外的东西,例如IPFS(可以存储和读取数据的去中心化网络,不是出自以太坊团队但有类似的精神)。DApp可以跑在一台能与以太坊节点交互的中心化服务器上,也可以跑在任意一个以太坊平等节点上。这里分享一个以太坊DApp教程,可以高效的学习如何开发一个DApp,很适合入门。

    2. 工作流程

    a. 环境搭建

    建议使用Mac OS环境,不然可能会出现各种坑。

    1. 安装NodeJS,安装Python
    2. 安装testrpc(测试环境中使用),安装go-ethereum(真实环境中使用)。
    3. 安装solc
    4. 安装truffle

    如果是windows的话建议用工具ethbox可以一键安装以太坊开发环境的工具: ethbox

    b. Solidity语言简介

    下面是官网上面的一段关于智能投票合约的示例代码

      contract Ballot {
       //一个选民的构造体
        struct Voter {
            uint weight; // 权重(即他可以投几票)
            bool voted;  //是否已经投过票
            address delegate; // 代表地址(他可以代表某个人进行投票)
            uint vote;   // index of the voted proposal
        }
    
        // 投票的提案的构造体
        struct Proposal
        {
            bytes32 name;   // 提案名称
            uint voteCount; //获得的票数
        }
    
        address public chairperson;//会议主席
    
        //地址 -选民 的map
        mapping(address => Voter) public voters;
    
        // 投票种类的动态数组
        Proposal[] public proposals;
    
        ///构造函数
        function Ballot(bytes32[] proposalNames) {
            chairperson = msg.sender;//初始化会议主席
            voters[chairperson].weight = 1;
    
           //初始化所有的提案
            for (uint i = 0; i < proposalNames.length; i++) {
    
                proposals.push(Proposal({
                    name: proposalNames[i],
                    voteCount: 0
                }));
            }
        }
    
        // 给予投票权
        function giveRightToVote(address voter) returns (bool b) {
            if (msg.sender != chairperson || voters[voter].voted) {
                //对于会议主席和已经投过票的选民这里不处理
                return false;;
            }
            voters[voter].weight = 1;
            return true;
        }
    
        /// 投票权转移函数
        function delegate(address to) {
            // 投票权转移的发起人
            Voter sender = voters[msg.sender];
            if (sender.voted)
                throw;
    
          //递归找到没有转移投票权的  选民
            while (
                voters[to].delegate != address(0) &&
                voters[to].delegate != msg.sender
            ) {
                to = voters[to].delegate;
            }
    
            if (to == msg.sender) {
                throw;
            }
    
           //将发起人设置为已经投过票的状态
            sender.voted = true;
            //将代表设置为刚才递归获取的选民
            sender.delegate = to;
            Voter delegate = voters[to];
            if (delegate.voted) {
                //如果代表已经投过票就在他投票的提案的票数增加
                proposals[delegate.vote].voteCount += sender.weight;
            }
            else {
               //将代表的的票数增加
                delegate.weight += sender.weight;
            }
        }
    
        /// 投票函数
        function vote(uint proposal) {
            Voter sender = voters[msg.sender];
            if (sender.voted)
                throw;
            sender.voted = true;
            sender.vote = proposal;
    
            //将投的提案票数增加
            proposals[proposal].voteCount += sender.weight;
        }
    
        ///获得票数最多的提案
        function winningProposal() constant
                returns (uint winningProposal)
        {
            uint winningVoteCount = 0;
            for (uint p = 0; p < proposals.length; p++) {
                if (proposals[p].voteCount > winningVoteCount) {
                    winningVoteCount = proposals[p].voteCount;
                    winningProposal = p;
                }
            }
        }
    }
    

    解读

    • address. 地址类型。chairperson是会议主席的钱包地址。这个地址会在合约的构造函数function Ballot()中被赋值。很多时候也称呼这种地址为’owner’(所有人)。
    • public. 这个关键字表明变量可以被合约之外的对象使用。private修饰符则表示变量只能被本合约(或者衍生合约)内的对象使用。如果你想要在测试中通过web3.js使用合约中的某个变量,记得把它声明为public。
    • Mapping或数组。mapping(address => Voter)为选民钱包地址和选民构造体的键值对。Proposal[] public proposals是一个提案构造体的数组。
    • 有特殊的变量和函数总是在全局命名空间存在,主要用于提供有关blockchain信息,例如msg,block,tx,其中msg.sender为发起人的地址。

    solidity语言更深入的理解可以阅读官方文档

    c. 使用geth部署合约

    1. 启动一个测试节点

    geth –testnet –fast –cache=512 –genesis CustomGenesis.json console

    这里的CustomGenesis.json是为了给测试的账户分配以太币

    {
        "coinbase": "0x0000000000000000000000000000000000000000",
        "difficulty": "0x20000",
        "extraData": "",
        "gasLimit": "0x2fefd8",
        "nonce": "0x0000000000000042",
        "mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000",
        "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
        "timestamp": "0x00",
        "alloc": {
            "0xe49c283bc6bf92c5833cc981b97679238dd3b5da": {
                "balance": "111111111000000000000000000000000000"
            },
            "0xd8927c296b3ebe454a6409770a0c323ec4ed23ba": {
                "balance": "222222222000000000000000000000000000"
            }
        }
    }
    

    solc下的内容要替换成你的测试账户地址。具体geth的用法请查看官方文档源码介绍

    1. 使用solc编译智能合约,获得二进制代码
      例如以下代码
    contract test { 
          function multiply(uint a) returns(uint d) { 
                 return a * 7; 
          }
    }
    

    在geth中输入

    source = “contract test { function multiply(uint a) returns(uint d) { return a * 7; } }”

    clientContract = eth.compile.solidity(source).test

    编译返回的结果的JSON格式如下

    其中,

    • code:编译后的EVM字节码
    • info:编译器返回的metadata
    • abiDefination:Application Binary Interface定义。具体接口规则参见这里
    • compilerVersion:编译此代码的solidity编译器版本
    • developerDoc:针对开发者的Natural Specification Format,类似于Doxygen
    • language:合约语言
    • languageVersion:合约语言版本
    • source:源代码
    • userDoc:针对用户的Ethereum的Natural Specification Format。

    编译器返回的JSON结构反映了合约部署的两种不同的路径。info信息真实的存在于区中心化的云中,作为metadata信息来公开验证Blockchain中合约代码的实现。而code信息通过创建交易的方式部署到区块链中。

    1. 使用solc编译智能合约,获得二进制代码
      部署合约前,确保你有一个解锁的账户并且账户中有余额,因为部署合约得过程中会消耗以太币。输入web3.fromWei(eth.getBalance(eth.accounts[0]),”ether”)可以查看账户余额。
      解锁一个账户

    personal.unlockAccount(eth.accounts[0])

    获得账户

    primaryAddress = eth.accounts[0]

    定义一个abi (abi是个js的数组,否则不成功)

    abi = [{ constant: false, inputs: [{ name: ‘a’, type: ‘uint256’ } ]}]

    创建智能合约

    MyContract = eth.contract(abi)

    发送交易部署合约

    contract = MyContract.new({from: primaryAddress, data:”0x6060604052602a8060106000396000f3606060405260e060020a6000350463c6888fa18114601a575b005b6007600435026060908152602090f3”})

    如果交易被pending,如图说明你的miner没有在挖矿

    启动一个矿工
    miner.setEtherbase(eth.primaryAddress) //设定开采账户
    miner.start(8)

    eth.getBlock(“pending”, true).transactions
    这时候发现交易已经在区块中

    不过会发现,交易还是pending,这是因为该交易区块没有人协助进行运算验证,这时候只需要再启动一个矿工就行了
    miner.start(8)

    1. 与合约进行交互

    Multiply7 = eth.contract(clientContract.info.abiDefinition);
    var myMultiply7 = Multiply7.at(contract.address);
    myMultiply7.multiply.call(3)

    myMultiply7.multiply.sendTransaction(3, {from: contract.address})

    d. 使用truffle框架

    使用truffle部署智能合约的步骤:

    1. truffle init (在新目录中) => 创建truffle项目目录结构,
    2. 编写合约代码,保存到contracts/YourContractName.sol文件。
      例如Ballot .sol,此时要找到migrations文件夹,在deploy_contracts.js文件中添加deployer.deploy(Ballot);
    3. truffile compile 编译合约代码。
    4. 启动以太坊节点(例如在另一个终端里面运行testrpc)。
    5. truffle migrate(在truffle项目目录中)。

    1. 编写单元测试

    test文件夹中新建ballot.js文件

    contract('Ballot'function(accounts)){
        //accounts是所以账户得数值
        it("获取投票权"function(){
            var meta = Ballot.deployed();
            return meta.giveRightToVote(accounts[1]).then(function(b){
               assert.equal(Boolean(b),true,"获取投票权失败");
            });
        });
    
    }
    

    在项目根目录下运行truffle test,你应该看到测试通过,如果使用自己构造的ballot对象,可以这样写:

    contract('Ballot'function(accounts)){
        //accounts是所以账户得数值
        it("获取投票权"function(){
           var proposals = [];
           proposals.push("proposal0");
           Ballot.new(proposals).then(function(meta){
               return meta.giveRightToVote(accounts[1]).then(function(b){
                   assert.equal(Boolean(b),true,"获取投票权失败");
                });
           });
    
        });
    
    }
    
    • 合约中发送以太币。 this是合约实例的地址,以变接下来检查这个地址的余额(或者直接使用this.balance)
    • 当你通过web3.js调用交易函数时(使用web3.eth.sendTransaction),交易并不会立即执行。事实上交易会被提交到矿工网络中,交易代码直到其中一位矿工产生一个新区块把交易记录进区块链之后才执行。因此你必须等交易进入区块链并且同步回本地节点之后才能验证交易执行的结果。用testrpc的时候可能看上去是实时的,因为测试环境很快,但是正式网络会比较慢。
    • Gas. (译注:以太坊上的燃料,因为代码的执行必须消耗Gas。直译为汽油比较突兀,故保留原文做专有名词。)直到现在我们都没有涉及Gas的概念,因为在使用testrpc时通常不需要显式的设置。当你转向geth和正式网络时会需要。在交易函数调用中可以在{ from: , value: , gas: _ } 对象内设置Gas参数。Web3.js提供了web3.eth.gasPrice调用来获取当前Gas的价格,Solidity编译器也提供了一个参数让你可以从命令行获取合约的Gas开销概要:solc –gas YouContract.sol.

    2. 为合约创建一个界面

    app目录中,可以编写自己的html和js文件,js与智能合约的交互与单元测试基本一致,例如一个界面上有一个输入框和一个按钮,获得选民的投票权。

    <!DOCTYPE html>
    <html>
    <head>
      <title>Ballot App</title>
      <link href='https://fonts.googleapis.com/css?family=Open+Sans:400,700' rel='stylesheet' type='text/css'>
      <link href="./app.css" rel='stylesheet' type='text/css'>
      <script src="./app.js"></script>
    </head>
    <body>
      <h1>Ballot</h1>
      <h2>Example Truffle Dapp</h2>
    
      <br>
      <h1>Send</h1>
      <br><label for="amount">Account:</label><input type="text" id="account" placeholder="e.g., 0x453468394hdfg84858345348"></input>
    
      <br><br><button id="getRightVote" onclick="getRight()">Get Right Vote</button>
      <br><br>
      <span id="status"></span>
    </body>
    </html>
    

    app.js中的代码为

    function getRight() {
     var account = document.getElementById("account").value;
      var meta = Ballot.deployed();
    
       meta.giveRightToVote(account).then(function(b){
            if(Boolean(b)){
              setStatus("Get Right Vote Success");
            }else{
              setStatus("Get Right Vote Error");
            }
      }).catch(function(e){
        setStatus("Get Right Vote Error");
        console.log(e);
      });
    
    };
    

    另外分享几个教程给大家:

    • python以太坊,主要是针对python围绕web3.py进行区块链以太坊应用开发的讲解。
    • web3j,主要是针对java和android程序员围绕web3j库进行区块链以太坊开发的讲解。
    • php以太坊,主要是介绍使用php进行智能合约开发交互,进行账号创建、交易、转账、代币开发以及过滤器和事件等内容。
    • 以太坊开发,主要是介绍使用node.js、mongodb、区块链、ipfs实现去中心化电商DApp实战,适合进阶。
    • 以太坊教程,主要介绍智能合约与dapp应用开发,适合入门。
    • C#以太坊,主要讲解如何使用C#开发基于.Net的以太坊应用,包括账户管理、状态与交易、智能合约开发与交互、过滤器和事件等。
    展开全文
  • 第一节 课程概述本课程面向初学者,内容涵盖以太坊开发相关的基本概念,并将手把手地教大家如何构建一个 基于以太坊的完整去中心化应用 —— 区块链投票系统。通过本课程的学习,你将掌握:以太坊区块链的基本知识...

    第一节 课程概述

    本课程面向初学者,内容涵盖以太坊开发相关的基本概念,并将手把手地教大家如何构建一个 基于以太坊的完整去中心化应用 —— 区块链投票系统。

    ethereum

    通过本课程的学习,你将掌握:

    • 以太坊区块链的基本知识

    • 开发和部署以太坊合约所需的软件环境

    • 使用高级语言(solidity)编写以太坊合约

    • 使用NodeJS编译、部署合约并与之交互

    • 使用Truffle框架开发分布式应用

    • 使用控制台或网页与合约进行交互

    前序知识要求

    为了顺利完成本课程,最好对以下技术已经有一些基本了解:

    • 一种面向对象的开发语言,例如:Python,Ruby,Java...

    • 前端开发语言:HTML/CSS/JavaScript

    • Linxu命令行的使用

    • 数据库的基本概念

    课程的所有代码均已在Ubuntu(Trusty、Xenial)和 macOS 上测试过。

    课程地址:http://xc.hubwiz.com/course/5a952991adb3847553d205d1?affid=csdn20180207

    第二节 课程简介

    在本课程中,我们将会构建一个去中心化的(Decentralized)投票应用。利用这个投票应用, 用户可以在不可信(trustless)的分布环境中对特定候选人投票,每次投票都会被记录在区块 链上:

    所谓去中心化应用(DApp:Dcentralized Application),就是一个不存在中心服务器 的应用。在网络中成百上千的电脑上,都可以运行该应用的副本,这使得它几乎不可能 出现宕机的情况。

    基于区块链的投票是完全去中心化的,因此无须任何中心化机构的存在。

    第三节 开发迭代

    本课程将涵盖应用开发的整个过程,我们将通过三次迭代来渐进地引入区块链应用 开发所涉及的相关概念、语言和工具:

    • Vanilla:在第一个迭代周期,我们不借助任何开发框架,而仅仅使用NodeJS来进行应用开发, 这有助于我们更好地理解区块链应用的核心理念。

    • Truffle:在第二个迭代周期,我们将使用最流行的去中心化应用开发框架Truffle进行开发。 使用开发框架有助于我们提高开发效率。

    • Token:在第三个迭代周期,我们将为投票应用引入代币(Token) —— 现在大家都改口 称之为通证了 —— 都是ICO惹的祸。代币是公链上不可或缺的激励机制,也是区块链 应用区别于传统的中心化应用的另一个显著特征。

    为什么选择投票应用作为课程项目?

    之所以选择投票作为我们的第一个区块链应用,是因为集体决策 —— 尤其是投票机制 —— 是以太坊的 一个核心的价值主张。

    另一个原因在于,投票是很多复杂的去中心化应用的基础构件,所以我们选择了投票应用作为学习区块链 应用开发的第一个项目。

    课程地址:http://xc.hubwiz.com/course/5a952991adb3847553d205d1?affid=csdn20180207

    第四节 初识区块链

    如果你熟悉关系型数据库,就应该知道一张数据表里可以包含很多行数据记录。例如,下面的数据表中 包含了6条交易记录:

    本质上,区块链首先就是一个分布式(Distributed)数据库,这个数据库维护了一个不断增长的记录列表。 现在,让我们对数据进行批量(batch)存储,比如每批 100 行,并将各存储批次连接起来,是不是就像一条链?

    在区块链里,多个数据记录组成的批次就被称为块(block),块里的每一行数据记录就被称为交易(transaction):

    最开始的那个块,通常被称为创世块(genesis block),它不指向任何其他块。

    不可篡改性

    区块链的一个显著特点是,数据一旦写入链中,就不可篡改重写。

    在传统的关系型数据库中,你可以很容易地更新一条数据记录。但是,在区块链中,一旦数据写入就无法 再更新了 —— 因此,区块链是一直增长的。

    那么,区块链是如何实现数据的不可篡改特性?

    这首先得益于哈希(Hash)函数 —— 如果你还没接触过哈希函数,不妨将它视为一个数字指纹的计算函数: 输入任意长度的内容,输出定长的码流(指纹)。哈希函数的一个重要特性就是,输入的任何一点微小变化,都会 导致输出的改变。因此可以将哈希值作为内容的指纹来使用。 你可以点击这里进一步了解哈希函数。

    由于区块链里的每个块都存储有前一个块内容的哈希值,因此如果有任何块的内容被篡改,被篡改的块之后 所有块的哈希值也会随之改变,这样我们就很容易检测出区块链的各块是否被篡改了。

    去中心化的挑战

    一旦完全去中心化,在网络上就会存在大量的区块链副本(即:全节点),很多事情都会变得比之前中心化 应用环境复杂的多,例如:

    • 如何保证所有副本都已同步到最新状态?

    • 如何保证所有交易都被广播到所有运行和维护区块链副本的节点计算机上?

    • 如何防止恶意参与者篡改区块链

    • ......

    在接下来的课程中,通过与经典的C/S架构的对比,我们将逐步理解去中心化应用的核心思路, 并掌握如何构建以太坊上的去中心化应用。

    第五节 C/S架构以服务器为中心

    理解去中心化应用架构的最好方法,就是将它与熟悉的Client/Server架构进行对比。如果你是一个web开发者, 应该对下图很了解,这是一个典型的Client/Server架构:

    一个典型web应用的服务端通常由 Java,Ruby,Python 等等语言实现。前端代码由 HTML/CSS/JavaScript 实现。 然后将整个应用托管在云端,比如 AWS、Google Cloud Platform、Heroku....,或者放在你租用的一个VPS 主机上。

    用户通过客户端(Client)与 web 应用(Server)进行交互。典型的客户端包括浏览器、命令行工具(curlwget等)、 或者是API访问代码。注意在这种架构中,总是存在一个(或一组)中心化的 web 服务器,所有的客户端都需要 与这一(组)服务器进行交互。当一个客户端向服务器发出请求时,服务器处理该请求,与数据库/缓存进行交互, 读/写/更新数据库,然后向客户端返回响应。

    这是我们熟悉的中心化架构。在下一节,我们将会看到基于区块链的去中心化架构的一些显著区别。

    第六节 去中心化架构——彼此平等的节点

    下图给出了基于以太坊的去中心化应用架构:

    你应该已经注意到,每个客户端(浏览器)都是与各自的节点应用实例进行交互,而不是向 一个中心化的服务器请求服务。

    在一个理想的去中心化环境中,每个想要跟DApp交互的人,都需要在他们的计算机或手机上面运行 一个的完整区块链节点 —— 简言之,每个人都运行一个全节点。这意味着,在能够真正使用一个 去中心化应用之前,用户不得不下载整个区块链。

    不过我们并非生活在一个乌托邦里,期待每个用户都先运行一个全节点,然后再使用你的应用是不现实的。 但是去中心化背后的核心思想,就是不依赖于中心化的服务器。所以,区块链社区已经出现了 一些解决方案,例如提供公共区块链节点的Infura, 以及浏览器插件Metamask等。通过这些方案, 你就不需要花费大量的硬盘、内存和时间去下载并运行完整的区块链节点,同时也可以利用去中心化 的优点。我们将会以后的课程中对这些解决方案分别进行评测。

    第七节 以太坊——世界计算机

    以太坊是一种区块链的实现。在以太坊网络中,众多的节点彼此连接,构成了以太坊网络:

    以太坊节点软件提供两个核心功能:数据存储、合约代码执行。

    在每个以太坊全节点中,都保存有完整的区块链数据。以太坊不仅将交易数据保存在链上,编译后 的合约代码同样也保存在链上。

    以太坊全节点中,同时还提供了一个虚拟机来执行合约代码。

    交易数据

    以太坊中每笔交易都存储在区块链上。当你部署合约时,一次部署就是一笔交易。当你为候选者投票时,一次投票 又是另一笔交易。所有的这些交易都是公开的,每个人都可以看到并进行验证。这个数据永远也无法篡改。

    为了确保网络中的所有节点都有着同一份数据拷贝,并且没有向数据库中写入任何无效数据,以太坊 目前使用工作量证明 (POW:Proof Of Work)算法来保证网络安全,即通过矿工挖矿(Mining)来达成共识(Consensus)—— 将数据同步到所有节点。

    工作量证明不是达成共识的唯一算法,挖矿也不是区块链的唯一选择。现在,我们只需要了解,共识是指各节点 的数据实现了一致,POW只是众多用于建立共识的算法中的一种,这种算法需要通过矿工的挖矿来实现非可信环境下的 可信交易。共识是目的,POW是手段。

    合约代码

    以太坊不仅仅在链上存储交易数据,它还可以在链上存储合约代码。

    在数据库层面,区块链的作用就是存储交易数据。那么给候选者投票、或者检索投票结果的逻辑放在哪儿呢? 在以太坊的世界里,你可以使用Solidity语言来编写业务逻辑/应用代码(也就是合约:Contract), 然后将合约代码编译为以太坊字节码,并将字节码部署到区块链上:

    编写合约代码也可以使用其他的语言,不过 Solidity是到目前为止最流行的选择。

    以太坊虚拟机

    以太坊区块链不仅存储数据和代码,每个节点中还包含一个虚拟机(EVM:Ethereum Virtual Machine)来执行 合约代码 —— 听起来就像计算机操作系统。

    事实上,这一点是以太坊区别于比特币(Bitcoin)的最核心的一点:虚拟机的存在使区块链迈入了2.0 时代,也让区块链第一次成为应用开发者友好的平台。

    JS开发库

    为了便于构建基于web的DApp,以太坊还提供了一个非常方便的JavaScript库web3.js,它封装了以太坊节点的API 协议,从而让开发者可以轻松地连接到区块链节点而不必编写繁琐的RPC协议包。所以,我们可以在常用的JS框架 (比如 reactjs、angularjs 等)中直接引入该库来构建去中心化应用:

    PC端课程地址:

    http://xc.hubwiz.com/course/5a952991adb3847553d205d1?affid=csdn20180207

    如果想加入 以太坊技术开发群可以加 微信:cuixuebin2 ,拉你入群。
    展开全文
  • 本视频课程面向初学者,介绍什么是区块链,什么是智能合约,内容涵盖以太坊开发相关的基本概念,Gregory(Dapp University 创始人,专注于讲解在以太坊区块链上构建DApp应用程序教程。Gregory大神的以太坊课程在...
  • 本课程面向初学者,内容涵盖以太坊开发相关的基本概念,并将手把手地教大家如何构建一个 基于以太坊的完整去中心化应用 —— 区块链投票系统。 通过本课程的学习,你将掌握: 以太坊区块链的基本知识 ...
  • 学习以太坊Dapp开发

    2018-06-12 13:17:55
    以太坊DApp 教程,可以高效的学习如何开发一个DApp,很适合入门。 2. 工作流程 a. 环境搭建 建议使用Mac OS环境,不然可能会出现各种坑。 安装 NodeJS ,安装 Python 。 安装 testrpc (测试环境中使用)...
  • 以太坊DApp 教程,可以高效的学习如何开发一个DApp,很适合入门。 2. 工作流程 a. 环境搭建 建议使用Mac OS环境,不然可能会出现各种坑。 安装 NodeJS ,安装 Python 。 安装 testrpc (测试环境...
  • 如何使用 Vue.js 开发以太坊 DApp

    千次阅读 2019-12-21 09:24:31
    如何使用 Vue.js 开发以太坊 DApp Vue 是一套在前端开发中广泛采用的用于构建用户界面的渐进式 JavaScript 框架。Vue 通过响应的数据绑定和组合的视图组件让界面开发变得非常的简单。这边文章来看看如何使用 Vue ...
  • 今天,我将向你展示如何在以太坊区块链上构建你的第一个去中心化应用程序或dApp。我将告诉你如何编写你的第一个以太坊智能合约,我们将在两个候选人之间举行选举。我们将针对智能合约编写测试,将其部署到以太坊...
  • 还需要安装以太坊相关的环境: nodejs truffle solidity testrpc 可以参考我之前的一篇文章: http://blog.csdn.net/pony_maggie/article/details/79531534 另外,本篇还会用到webpack,安装教程网上也有很多。这部分...
  • 以太坊DAPP小项目实践

    千次阅读 2018-10-05 19:42:14
    安装DAPP环境 安装node.js 下载地址(64位):... 查看安装成功。 ...ganache-cli 是以太坊节点仿真器软件ganache的命令行版本,可以方便开发者快速进行DAPP开发与测试。 安装命令: npm inst...
  • 对于初学者,我们从以太坊开发的相关基本概念开始,将手把手地教大家如何构建一个基于以太坊的完整去中心化应用 —— 区块链投票系统。 通过学习,你将掌握: 以太坊区块链的基本知识 开发和部署以太坊合约所需的...
  • 这段时间也在网上看了很多关于开发的文章,首先不得不吐槽一下,什么半个小时玩转DApp?两个小时开发DApp? 等等、等等 欢迎使用Markdown编辑器 你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想...
  • 编者按:本文系 登链科技 CTO...Jeth 围绕以太坊技术开发主题的系列线下活动。每期 Jeth 会邀请以太坊开发领域的优秀技术团队和工程师在线下分享技术干货。旨在为开发者提供线下技术交流互动机会,帮助开发者成长。...
  • 基于ganache测试节点的DApp开发
  • 原文地址:Create your first Ethereum dAPP with Web3 and Vue.JS (Part 1) 原文作者:Alt Street 译文出自:掘金翻译计划 本文永久链接:github.com/xitu/gold-m… 译者:foxxnuaa 校对者:yankwan,FateZeros ...
  • dapp目录下创建一个工程sixtest [root@ dapp]# express -e sixtest 我们在remix当中部署好的智能合约 pragma solidity ^0.4.22; contract counter{ uint256 counter; constructor() public{ counter = 1; }...
  • 以太坊硬币夹 介绍 以太坊投币硬币是一个去中心化的应用程序,使2位参与者能够执行赢家通吃的所有伪随机投币游戏。 它是这样工作的: 创建了新的硬币翻转合约实例 参与者(玩家1)向合约中存入一定数量的以太坊 ...
  • 现在我们来建立一个实战以太坊dapp案例:彩票。 我们彩票案例的目的是多个玩家能够通过发送钱参与彩票。玩家发送的钱越多,他赢得所有资金的机会就越大。当彩票的运营发行者决定关闭彩票后,就会选择一个优胜者,并...
  • 2019独角兽企业重金招聘...一个适合区块链新手的以太坊DApp开发教程: http://xc.hubwiz.com/course/5a952991adb3847553d205d1 一个用区块链、星际文件系统(IPFS)、Node.js和MongoDB来构建电商平台: ...
  • 以太坊智能合约DAPP区块链开发

    千次阅读 2018-03-13 16:05:15
    MetaMask 是一款插件形式的以太坊轻客户端,开发过程中使用MetaMask和我们的dapp进行交互是个很好的选择,通过此链接安装,安装完成后,浏览器工具条会显示一个小狐狸图标。 配置钱包 在接受隐私说明后,会出现页面...
  • 以太坊DApp开发入门1:开发环境搭建

    万次阅读 2018-01-12 11:27:45
    最近开始学习以太坊DApp开发,这里记录一下我的学习过程。 开发之前要先搞清楚各种概念: 以太坊:www.ethereum.org ,是一个区块链系统,相对于比特币网络系统的纯货币功能,以太坊区块链增加了智能合约,大大...
  • 作为一名.NET开发人员,我主要使用的是一些提供中心数据源的架构,因此区块链的去中心化概念对我来说是全新的。我很快就看到它如何提供更高的可靠性,完整性和透明化。我开始寻找可以让我开始使用熟悉的操作系统和...
  • 基于以太坊DAPP实现

    千次阅读 2018-12-03 14:10:12
    基于以太坊DAPP实现 系统采用vm下ubuntu16.04 一、 以太坊介绍 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;以太坊是一个开源的有智能合约功能的公共区块链平台。通过其专用加密货币...
  • 是的,Meteor是一个完整的堆栈框架,它的主要改进是实时Web应用程序,但Meteor也是第一个框架(我知道),它完全支持了单页app(SPA)开发并提供了所有必要的工具。 Meteor非常适合的5个理由: ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,303
精华内容 521
关键字:

以太坊dapp开发实例