美文网首页
如何编写智能合约(Smart Contract)- 从零构建和部

如何编写智能合约(Smart Contract)- 从零构建和部

作者: Venture_Mark | 来源:发表于2018-10-05 10:05 被阅读0次

    转载于:黎跃春老师

    学习内容

    1. 了解区块链智能合约
    2. 学会搭建智能合约开发环境
    3. 学会如何编译智能合约
    4. 学会如何将智能合约部署到区块链
    5. 学会如何通过WebApp和智能合约尽心互动
    6. 掌握DApp(去中心化App)的整个开发部署流程
    7. 掌握去中心化在实战产品中应用的重大意义

    项目效果图

    去中心化投票App

    编辑器选择

    理论上讲任何编辑器都可以编写Solidity合约代码,比如:WebStorm,VSCode,Sublime,等等。我选择的是Atom,没有任何理由,因为Atom轻量并且界面漂亮。

    • 移步https://atom.io/地址,下载安装Atom。

    • autocomplete-solidity代码自动补齐

    autocomplete-solidity
    • linter-soliumlinter-solidity代码错误检查

    • language-ethereum支持Solidity代码高亮以及Solidity代码片段

    language-ethereum

    安装所需工具

    首先开发机上必须装好Node.js,再使用以下命令安装所需的工具:

    $ npm install -g ethereumjs-testrpc truffle
    
    
    liyuechun:~ yuechunli$ npm install -g ethereumjs-testrpc truffle
    /usr/local/bin/testrpc -> /usr/local/lib/node_modules/ethereumjs-testrpc/build/cli.node.js
    /usr/local/bin/truffle -> /usr/local/lib/node_modules/truffle/build/cli.bundled.js
    + truffle@3.4.9
    + ethereumjs-testrpc@4.1.3
    added 1 package and updated 7 packages in 76.132s
    liyuechun:~ yuechunli$
    
    

    创建项目

    /Users/liyuechun/Desktop/1012/Voting
    liyuechun:Voting yuechunli$ ls
    liyuechun:Voting yuechunli$ pwd
    /Users/liyuechun/Desktop/1012/Voting
    liyuechun:Voting yuechunli$ truffle unbox react-box
    Downloading...
    Unpacking...
    Setting up...
    Unbox successful. Sweet!
    
    Commands:
    
      Compile:              truffle compile
      Migrate:              truffle migrate
      Test contracts:       truffle test
      Test dapp:            npm test
      Run dev server:       npm run start
      Build for production: npm run build
    liyuechun:Voting yuechunli$
    
    

    项目结构

    truffle unbox react-box
    • contracts:编写智能合约的文件夹,所有的智能合约文件都放置在这里
    • migrations:部署合约配置的文件夹
    • src:基于React的Web端源码
    • test:智能合约测试用例文件夹

    编写投票Dapp智能合约

    contracts文件夹下创建Voting.sol文件,将下面的代码拷贝到文件中。

    pragma solidity ^0.4.4;
    
    contract Voting {
    
      // liyuechun -> 10
      // xietingfeng -> 5
      // liudehua -> 20
      mapping (bytes32 => uint8) public votesReceived;
    
      // 存储候选人名字的数组
      bytes32[] public candidateList;
    
      // 构造函数 初始化候选人名单
      function Voting(bytes32[] candidateNames) {
    
        candidateList = candidateNames;
      }
    
      // 查询某个候选人的总票数
      function totalVotesFor(bytes32 candidate)  constant returns (uint8) {
        require(validCandidate(candidate) == true);
        // 或者
        // assert(validCandidate(candidate) == true);
        return votesReceived[candidate];
      }
    
      // 为某个候选人投票
      function voteForCandidate(bytes32 candidate) {
        assert(validCandidate(candidate) == true);
        votesReceived[candidate] += 1;
      }
    
      // 检索投票的姓名是不是候选人的名字
      function validCandidate(bytes32 candidate) constant returns (bool) {
        for(uint i = 0; i < candidateList.length; i++) {
          if (candidateList[i] == candidate) {
            return true;
          }
        }
        return false;
      }
    }
    
    

    通过remix + metamask部署合约到Kovan Test Net

    • 在Google浏览器里面安装MetaMask插件
    image image
    • 确保MetaMask账号处于等于状态,并且有一定的以太币支付给矿工。
    • 确保EnvironmentInjected Web3,如果切换不过来,关掉浏览器重新启动
    • create函数中输入一个数组,数组里面的内容为候选人名单
    • 点击create按钮,会弹出MetaMask界面让你确认,确认提交,过一会儿,合约就部署成功
    • 可以测试给某个候选人投票,查询某个候选人的票数

    拷贝合约地址

    image
    0xd3f33a2e553b363b432d7f81f721a2a6202ecc67
    
    

    编译合约

    liyuechun:Voting yuechunli$ truffle compile
    Compiling ./contracts/Migrations.sol...
    Compiling ./contracts/SimpleStorage.sol...
    Compiling ./contracts/Voting.sol...
    Writing artifacts to ./build/contracts
    
    liyuechun:Voting yuechunli$
    
    

    编译完合约以后在build/contracts文件夹下面会有一个Voting.jsonabi文件。

    查看Voting.json文件内容

    {
      "contract_name": "Voting",
      "abi": [
        {
          "constant": true,
          "inputs": [
            {
              "name": "candidate",
              "type": "bytes32"
            }
          ],
          "name": "totalVotesFor",
          "outputs": [
            {
              "name": "",
              "type": "uint8"
            }
          ],
          "payable": false,
          "type": "function"
        },
        {
          "constant": true,
          "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": "",
              "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"
        },
        {
          "inputs": [
            {
              "name": "candidateNames",
              "type": "bytes32[]"
            }
          ],
          "payable": false,
          "type": "constructor"
        }
      ],
      "unlinked_binary": "0x6060604052341561000f57600080fd5b6040516103113803806103118339810160405280805190910190505b600181805161003e929160200190610046565b505b506100b5565b828054828255906000526020600020908101928215610083579160200282015b828111156100835782518255602090920191600190910190610066565b5b50610090929150610094565b5090565b6100b291905b80821115610090576000815560010161009a565b5090565b90565b61024d806100c46000396000f300606060405263ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416632f265cf78114610069578063392e6678146100955780637021939f146100bf578063b13c744b146100eb578063cc9ab26714610113575b600080fd5b341561007457600080fd5b61007f60043561012b565b60405160ff909116815260200160405180910390f35b34156100a057600080fd5b6100ab60043561015d565b604051901515815260200160405180910390f35b34156100ca57600080fd5b61007f6004356101af565b60405160ff909116815260200160405180910390f35b34156100f657600080fd5b6101016004356101c4565b60405190815260200160405180910390f35b341561011e57600080fd5b6101296004356101e7565b005b60006101368261015d565b151560011461014457600080fd5b5060008181526020819052604090205460ff165b919050565b6000805b6001548110156101a457600180548491908390811061017c57fe5b906000526020600020900160005b5054141561019b57600191506101a9565b5b600101610161565b600091505b50919050565b60006020819052908152604090205460ff1681565b60018054829081106101d257fe5b906000526020600020900160005b5054905081565b6101f08161015d565b15156001146101fb57fe5b6000818152602081905260409020805460ff8082166001011660ff199091161790555b505600a165627a7a723058206783a7ff47eae16f18011a9db2a3cc983350b779bf3181b7623c18d4bce363180029",
      "networks": {},
      "schema_version": "0.0.5",
      "updated_at": 1507806214330
    }
    
    

    这个文件是编译后的abi文件,待会儿需要将这个文件的json导入到App.json中。

    查看src/utils/getWeb3.js文件内容

    import Web3 from 'web3'
    
    let getWeb3 = new Promise(function(resolve, reject) {
      // Wait for loading completion to avoid race conditions with web3 injection timing.
      window.addEventListener('load', function() {
        var results
        var web3 = window.web3
    
        // Checking if Web3 has been injected by the browser (Mist/MetaMask)
        if (typeof web3 !== 'undefined') {
          // Use Mist/MetaMask's provider.
          web3 = new Web3(web3.currentProvider)
    
          results = {
            web3: web3
          }
    
          console.log('Injected web3 detected.');
    
          resolve(results)
        } else {
          // Fallback to localhost if no web3 injection.
          var provider = new Web3.providers.HttpProvider('http://localhost:8545')
    
          web3 = new Web3(provider)
    
          results = {
            web3: web3
          }
    
          console.log('No web3 instance injected, using Local web3.');
    
          resolve(results)
        }
      })
    })
    
    export default getWeb3
    
    

    这个文件主要是封装了一个getWeb3promiss供我们直接使用,可以从getWeb3直接获取到web3对象供App.js文件中使用。

    修改app.js前端代码和合约进行互动

    import React, { Component } from 'react'
    import VotingContract from '../build/contracts/Voting.json'
    import getWeb3 from './utils/getWeb3'
    
    import './css/oswald.css'
    import './css/open-sans.css'
    import './css/pure-min.css'
    import './App.css'
    
    const contractAddress = "0xd3f33a2e553b363b432d7f81f721a2a6202ecc67";
    var votingContractInstance;
    var account;
    
    var _modifyVotingCount = (candidates,i,votingCount) => {
    
        console.log("---------");
        console.log(candidates);
        console.log(i);
        console.log(votingCount);
    
        let obj = candidates[i];
        obj.votingCount = votingCount;
        return candidates;
    }
    
    class App extends Component {
      constructor(props) {
        super(props)
    
        this.state = {
          candidates: [
                        {
                          "name": "Rama",
                          "id": 100,
                          "votingCount": 0
                        },
                        {
                          "name": "Nick",
                          "id": 101,
                          "votingCount": 0
                        },
                        {
                          "name": "Jose",
                          "id": 102,
                          "votingCount": 0
                        },
                        {
                          "name": "liyuechun",
                          "id": 103,
                          "votingCount": 0
                        }
                      ],
          candidatesVoteCount: ["0","0","0","0"],
          web3: null
        }
      }
    
      componentWillMount() {
        // Get network provider and web3 instance.
        // See utils/getWeb3 for more info.
    
        getWeb3
        .then(results => {
          this.setState({
            web3: results.web3
          })
    
          // Instantiate contract once web3 provided.
          this.instantiateContract()
        })
        .catch(() => {
          console.log('Error finding web3.')
        })
      }
    
      instantiateContract() {
        /*
         * SMART CONTRACT EXAMPLE
         *
         * Normally these functions would be called in the context of a
         * state management library, but for convenience I've placed them here.
         */
    
        const contract = require('truffle-contract')
        const votingContract = contract(VotingContract)
        votingContract.setProvider(this.state.web3.currentProvider)
    
        // Declaring this for later so we can chain functions on SimpleStorage.
    
        // Get accounts.
        this.state.web3.eth.getAccounts((error, accounts) => {
          votingContract.at(contractAddress).then((instance) => {
            account = accounts[0];
            votingContractInstance = instance;
            for (let i = 0; i < this.state.candidates.length; i++) {
                let object = this.state.candidates[i];
                console.log(accounts[0]);
                console.log(votingContractInstance);
                console.log(votingContractInstance.totalVotesFor(object.name));
                votingContractInstance.totalVotesFor(object.name).then(result => {
                  console.log(i);
                  console.log(result.c[0]);
                  this.setState({
                    candidates: _modifyVotingCount(this.state.candidates,i,result.c[0])
                  });
                });
            }
          })
        })
      }
    
      render() {
        return (
          <div className="App">
          <ul>
            {
             this.state.candidates.map((object) => {
               console.log(object);
               return (
    
                    <li key={object.id}>候选人:{object.name}          支持票数:{object.votingCount}</li>
                )
             })
            }
          </ul>
    
          <input
                style=
                placeholder="请输入候选人姓名..."
                ref="candidateInput"
          />
    
          <button style= onClick={() => {
            console.log(this.refs.candidateInput);
            console.log(this.refs.candidateInput.value);
            let candidateName = this.refs.candidateInput.value;
            console.log(this.state.web3.eth.accounts[0]);
            votingContractInstance.voteForCandidate(candidateName,{from:account}).then((result => {
              console.log(result);
              console.log(candidateName);
              let number = 0;
              for(let i = 0; i < this.state.candidates.length; i++) {
                let object = this.state.candidates[i];
                if (object.name === candidateName) {
                  number = i;
                  break;
                }
              }
              votingContractInstance.totalVotesFor(candidateName).then(result => {
    
                this.setState({
                  candidates: _modifyVotingCount(this.state.candidates,number,result.c[0])
                });
              });
    
            }));
          }}>Voting</button>
    
          </div>
        );
      }
    }
    
    export default App
    
    
    去中心化投票App

    打赏地址

    相关文章

      网友评论

          本文标题:如何编写智能合约(Smart Contract)- 从零构建和部

          本文链接:https://www.haomeiwen.com/subject/osnxaftx.html