美文网首页
Windows搭建私有链,创建部署Hello world智能合约

Windows搭建私有链,创建部署Hello world智能合约

作者: 卡累利阿派 | 来源:发表于2018-07-11 17:12 被阅读0次

    环境:Windows 10 64 位
    注意:本文涉及的很多网站可能都要科学上网,所以附上了百度网盘下载链接。


    Phase 1 : 私有链搭建

    1 环境搭建

    1.1 安装Node.js

    可以直接在Node.js官方下载,下载后直接安装即可。安装完毕,打开控制台窗口,可以查看是否安装成功。

    打开控制台的方法有两种:
    ①在win10环境下,直接在系统右下角的搜索栏输入cmd回车;
    ②同时按住win+R键,输入cmd回车打开。

    C:\Users\aby>node -v
    v8.11.3
    

    百度网盘
    链接:https://pan.baidu.com/s/1RcSREiv3Vguyw5CaxawtGQ
    密码:5vzm

    1.2 安装Geth

    Geth是一个以太坊客户端,可以直接在Go Ethereum官方下载,下载后直接安装即可。安装完毕,打开控制台窗口,可以查看是否安装成功。

    C:\Users\aby>geth -v
    Geth
    Version: 1.8.3-stable
    Git Commit: 329ac18ef617d0238f71637bffe78f028b0f13f7
    Architecture: amd64
    Protocol Versions: [63 62]
    Network Id: 1
    Go Version: go1.10
    Operating System: windows
    GOPATH=
    GOROOT=C:\go
    

    百度网盘
    链接:https://pan.baidu.com/s/11rmMCsNXZXiQZaOl3eIUdw
    密码:3mvd

    1.3 安装Solidity

    新手使用Remix -Solidity IDE来进行开发。
    Remix 是一个基于浏览器的Solidity,就可以不用安装Solidity,本文的Hello World教程也将基于Remix Solidity IDE来进行。
    如果想安装,可以参考Solidity安装指南,也可以查看网上的另一篇文章:搭建智能合约开发环境Remix IDE及使用

    1.4 安装Ethereum Wallet

    可以官网下载Ethereum Wallet

    百度网盘
    方法一:直接解压缩
    链接:https://pan.baidu.com/s/1sublDw6v-v8sdTsNdpsatw
    密码:a4t1
    方法二:setup安装软件
    链接:https://pan.baidu.com/s/1qg3Rl5hiT3s3VzB0_OcxJg
    密码:tx8a

    2 启动环境,搭建私有链

    2.1 配置创世区块

    搭建私有链前需要创建一个 json 配置文件,用于初始化创世区块。这里自己将创建一个名为 genesis.json的文件。
    在这里给出修正过的方法,后面也会介绍我在网上搜索各种资源时碰到的错误,比如发送交易会提示Insufficient funds for gas * price + value错误,是因为创始区块genesis.json没有配置好,chainId字段配置太小,当时学习网上的写的15,0也不可以,后来改成了1000。extraData字段同样经常报错,将它置为空就可以了。

    {
      "config": {
            "chainId": 1000,
            "homesteadBlock": 0,
            "eip155Block": 0,
            "eip158Block": 0
        },
      "coinbase"   : "0x0000000000000000000000000000000000000000",
      "difficulty" : "0x400",
      "extraData"  : "",
      "gasLimit"   : "0x2fefd8",
      "nonce"      : "0x0000000000000042",
      "mixhash"    : "0x0000000000000000000000000000000000000000000000000000000000000000",
      "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
      "timestamp"  : "0x00",
      "alloc": {}
    }
    
    • config.chainId:用来声明以太坊网络编号,选择一个大于10的数字即可(实际操作最好大于1000,不然很容易报错)。
    • config.homesteadBlock: Homestead 硬分叉区块高度,不需要关注
    • config.eip155Block: EIP 155 硬分叉高度,不需要关注
    • config.eip158Block: EIP 158 硬分叉高度,不需要关注
    • coinbase:矿工账号,第一个区块挖出后将给这个矿工账号发送奖励的以太币
    • difficulty:用来声明挖矿难度,越小的值难度越低,也就能更快速地出块。
    • extraData: 附加信息随便填
    • gasLimit: gas 的消耗总量限制,用来限制区块能包含的交易信息总和,因为我们是私有链,所以填最大
    • nonce: 一个 64 位随机数
    • mixhash: 与 nonce 配合用于挖矿,由上一个区块的一部分生成的 hash
    • parentHash: 上一个区块的 hash 值
    • alloc: 预设账号以及账号的以太币数量,私有链挖矿比较容易可以不配置

    2.2 初始化创世区块

    geth --datadir "D:\chain" init genesis.json
    

    2.2 初始化私有链节点

    直接进入D:\chain文件夹,同时按住Shift鼠标右键,点击在此处打开Power shell窗口
    利用gethinit命令初始化一个以太坊(开发者)网络节点。

    geth --datadir testNet --dev --dev.period 1 console 2>> test.log
    

    原文使用geth --datadir testNet --dev console 2>> test.log
    注意:发布合约必须要进行挖矿才行,只有发送交易才会自动挖矿。

    • --dev 启用开发者网络(模式),开发者网络会使用POA共识,默认预分配一个开发者账户并且会自动开启挖矿。这是原来的版本,由于geth版本更新之后,--dev模式下新增了一个参数项:
    --dev               Ephemeral proof-of-authority network with a pre-funded developer account, mining enabled
    --dev.period value  Block period to use in developer mode (0 = mine only if transaction pending) (default: 0)
    

    我们先看一下上面的两个参数,--dev是我们常用的参数,之前版本中我们只用使用--dev然后执行miner.start()就可以挖矿,但是在后面的版本中,当我们会发现只有发送交易了才会挖一个块。
    引起此问题的原因就是新增了--dev.period value配置项。此配置默认值为0,也就是说只有有pending中的交易才会挖矿。
    明白了这个参数的含义之后,解决问题就很简答了,之前的–dev参数依旧使用,然后再在后面添加--dev.period 1,注意,参数值为1,不是默认的0。
    再重新启动节点,然后执行挖矿,先不管返回是否是null,执行之后,无论查看日志或执行eth.blockNumber都会发现块在不停的增高。

    • --datadir后面的参数是区块数据及秘钥存放目录。
      第一次输入命令后,它会放在当前"D:\chain"目录下新建一个testNet目录来存放数据。
    • console 进入控制台。
    • 2>> test.log 表示把控制台日志输出到test.log文件,文件名字可以随意。

    为了更好的理解,建议新开一个命令行终端,实时显示日志:

    tail -f test.log
    

    2.3 准备账户

    部署智能合约需要一个外部账户,我们先来看看分配的开发者账户,在控制台使用以下(仍然在D:\chain文件夹下操作)命令查看当前区块:(如果不像上面所述的加上--dev.period 1,区块数不会变化,只有转账等交易一次,才会挖一个区块)

    > eth.blockNumber
    45
    > eth.accounts
    ["0xa9f6cba12313eb1d6c0999d28c6b47b6cdb15655"]
    

    也可以使用personal.listAccounts查看账户
    再查看当前账户里的余额,使用命令如下:

    > acc0 = eth.accounts[0]
    "0xa9f6cba12313eb1d6c0999d28c6b47b6cdb15655"
    > eth.getBalance(acc0)
    1.15792089237316195423570985008687907853269984665640564039457584007913129639927e+77
    

    也可以直接用地址:

    > eth.getBalance(eth.accounts[0])
    1.15792089237316195423570985008687907853269984665640564039457584007913129639927e+77
    

    eth.accounts[0]表示账户列表第一个账户
    回车后,可以看到大量的余额,如:1.15792089237316195423570985008687907853269… e+77

    开发者账户因余额太多,如果用这个账户来部署合约时会无法看到余额变化,为了更好的体验完整的过程,这里选择创建一个新的账户。

    创建账户

    使用如下命令:

    > personal.newAccount("aby")
    "0x8b7bd64fbbec7a76ae87bca43f34afc9d9e49ff6"
    > eth.accounts
    ["0xa9f6cba12313eb1d6c0999d28c6b47b6cdb15655", "0x8b7bd64fbbec7a76ae87bca43f34afc9d9e49ff6"]
    > acc1 = eth.accounts[1]
    "0x8b7bd64fbbec7a76ae87bca43f34afc9d9e49ff6"
    > eth.blockNumber
    199
    
    • personal.newAccount("aby")中的aby是自己设置的账户密码,需要记住,后面解锁账户需要。

    可以看到当前账户数组有两个账户,新账户在第二个(索引为1)位置。
    我们查看账户1的余额,回车后返回值为0:

    > eth.getBalance(eth.accounts[1])   // 可以写成eth.getBalance(acc1)
    0
    

    给新帐户转账

    账户必须要有ether才能进行交易和部署智能合约,因此从默认账户转10以太币给新账户,使用以下命令(请使用你自己eth.accounts对应输出的账户):

    > eth.sendTransaction({from: acc0 , to: acc1, value: web3.toWei(10,"ether")})
    "0xc291fea6261056145a416d968a32024490cd6df6520ebe202fe4737f900ad050"
    > eth.getBalance(acc1)
    10000000000000000000
    

    也可以写成

    > eth.sendTransaction({from: "0xa9f6cba12313eb1d6c0999d28c6b47b6cdb15655" , to: "0x8b7bd64fbbec7a76ae87bca43f34afc9d9e49ff6", value: web3.toWei(10,"ether")})
    "0xc291fea6261056145a416d968a32024490cd6df6520ebe202fe4737f900ad050"
    > eth.getBalance(acc1)
    10000000000000000000
    

    可以看到账户1的余额变成了10ether。

    Phase 2 : 创建部署智能合约

    3 编写智能合约

    现在我们来开始编写第一个智能合约代码,solidity代码如下:

    pragma solidity ^0.4.0;
    contract hello {
        string greeting;
        
        function hello(string _greeting) public {
            greeting = _greeting;
        }
    
        function say()constant public returns (string) {
            return greeting;
        }
    }
    

    简单解释下,我们定义了一个名为hello的合约,在合约初始化时保存了一个字符串(我们会传入hello world),每次调用say返回字符串。

    把这段代码写(拷贝)到Browser-Solidity,记住勾选Auto compile如果没有错误,点击右边的Details获取部署代码。

    在弹出的对话框中找到WEB3DEPLOY部分,点拷贝,粘贴到编辑器后。
    此处要修改两个地方:
    (1)修改初始化字符串/* var of type string here */Hello, Joyce
    (2)修改from: web3.eth.accounts[0]from: web3.eth.accounts[1] 。因为我们使用账户1来部署智能合约。

    也就是将

    var _greeting = /* var of type string here */ ;
    var helloContract = web3.eth.contract([{"constant":true,"inputs":[],"name":"say","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"_greeting","type":"string"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]);
    var hello = helloContract.new(
       _greeting,
       {
         from: web3.eth.accounts[0], 
         data: '0x608060405234801561001057600080fd5b506040516102a83803806102a8833981018060405281019080805182019291905050508060009080519060200190610049929190610050565b50506100f5565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061009157805160ff19168380011785556100bf565b828001600101855582156100bf579182015b828111156100be5782518255916020019190600101906100a3565b5b5090506100cc91906100d0565b5090565b6100f291905b808211156100ee5760008160009055506001016100d6565b5090565b90565b6101a4806101046000396000f300608060405260043610610041576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063954ab4b214610046575b600080fd5b34801561005257600080fd5b5061005b6100d6565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561009b578082015181840152602081019050610080565b50505050905090810190601f1680156100c85780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b606060008054600181600116156101000203166002900480601f01602080910402602001604051908101604052809291908181526020018280546001816001161561010002031660029004801561016e5780601f106101435761010080835404028352916020019161016e565b820191906000526020600020905b81548152906001019060200180831161015157829003601f168201915b50505050509050905600a165627a7a72305820a7ef9308b4f91e2cbcb6bc84b3196736625ae7e0b242863d77c56dd80fc1e6480029', 
         gas: '4700000'
       }, function (e, contract){
        console.log(e, contract);
        if (typeof contract.address !== 'undefined') {
             console.log('Contract mined! address: ' + contract.address + ' transactionHash: ' + contract.transactionHash);
        }
     })
    

    改成

    var _greeting = "Hello, Joyce" ;
    var helloContract = web3.eth.contract([{"constant":true,"inputs":[],"name":"say","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"_greeting","type":"string"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]);
    var hello = helloContract.new(
       _greeting,
       {
         from: web3.eth.accounts[1], 
         data: '0x608060405234801561001057600080fd5b506040516102a83803806102a8833981018060405281019080805182019291905050508060009080519060200190610049929190610050565b50506100f5565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061009157805160ff19168380011785556100bf565b828001600101855582156100bf579182015b828111156100be5782518255916020019190600101906100a3565b5b5090506100cc91906100d0565b5090565b6100f291905b808211156100ee5760008160009055506001016100d6565b5090565b90565b6101a4806101046000396000f300608060405260043610610041576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063954ab4b214610046575b600080fd5b34801561005257600080fd5b5061005b6100d6565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561009b578082015181840152602081019050610080565b50505050905090810190601f1680156100c85780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b606060008054600181600116156101000203166002900480601f01602080910402602001604051908101604052809291908181526020018280546001816001161561010002031660029004801561016e5780601f106101435761010080835404028352916020019161016e565b820191906000526020600020905b81548152906001019060200180831161015157829003601f168201915b50505050509050905600a165627a7a72305820a7ef9308b4f91e2cbcb6bc84b3196736625ae7e0b242863d77c56dd80fc1e6480029', 
         gas: '4700000'
       }, function (e, contract){
        console.log(e, contract);
        if (typeof contract.address !== 'undefined') {
             console.log('Contract mined! address: ' + contract.address + ' transactionHash: ' + contract.transactionHash);
        }
     })
    

    4 部署智能合约

    将修改过的代码拷贝回geth控制台里,回车后,看到输出:

    Error: authentication needed: password or unlock undefined
    

    这是因为我们没有解锁账户,而且以太坊客户端每隔一段时间都会锁定账户,要经常解锁。使用以下命令:

    > personal.unlockAccount(acc1,"aby")
    true
    

    重新将代码复制进去,回车,可以看到:

    null [object Object]
    undefined
    > null [object Object]
    Contract mined! address: 0xca3e2f09f4ad8e175b4ac673ac692263523458ea transactionHash: 0x873f3657548777640bcc07671f6e273cae1e3f05790965419915e7e6a6428435
    

    如果前面没有加上--dev.period 1那么这里一直不会看到Contract mined! address: 0xca3e2f09f4ad8e175b4ac673ac692263523458ea transactionHash: 0x873f3657548777640bcc07671f6e273cae1e3f05790965419915e7e6a6428435这句话。因为一个合约的部署需要经过12个区块的确认,这里不会进行挖矿,就无法部署成功。我当时查了资料,就自己发了12次转账交易,才部署成功。

    此时我们可以查看部署成功的结果,调用hello:

    > hello
    {
      abi: [{
          constant: true,
          inputs: [],
          name: "say",
          outputs: [{...}],
          payable: false,
          stateMutability: "view",
          type: "function"
      }, {
          inputs: [{...}],
          payable: false,
          stateMutability: "nonpayable",
          type: "constructor"
      }],
      address: "0xca3e2f09f4ad8e175b4ac673ac692263523458ea",
      transactionHash: "0x873f3657548777640bcc07671f6e273cae1e3f05790965419915e7e6a6428435",
      allEvents: function(),
      say: function()
    }
    

    调用hello.say()

    > hello.say()
    "Hello, Joyce"
    

    命令行中的变化,在以太坊钱包可视化界面中会随着变化。

    注意:如果没有部署成功,有的时候可以看到Contract mined! address: 0xca3e2f09f4ad8e175b4ac673ac692263523458ea transactionHash: 0x873f3657548777640bcc07671f6e273cae1e3f05790965419915e7e6a6428435,但是调用hello后会显示不同的结果如下,也无法调用hello.say()

    > hello
    {
      abi: [{
          constant: true,
          inputs: [],
          name: "say",
          outputs: [{...}],
          payable: false,
          stateMutability: "view",
          type: "function"
      }, {
          inputs: [{...}],
          payable: false,
          stateMutability: "nonpayable",
          type: "constructor"
      }],
      address: undefined,
      transactionHash: "0x873f3657548777640bcc07671f6e273cae1e3f05790965419915e7e6a6428435"
    }
    
    > hello.say()
    TypeError:''say' is not a function
            at <anonymous>:1:1
    

    最后,让我们看一下现在区块数:

    > eth.blockNumber
    9747
    

    即使不进行操作,后台也在一直挖矿。

    本文作者:Joyce
    版权声明:转载请注明出处!

    2018年7月5日


    声明:本文参考智能合约开发环境搭建及Hello World合约一文,由于各种软件版本更新的原因,实际操作过程出现很多问题,因此将注意点和解决办法做了总结。

    相关文章

      网友评论

          本文标题:Windows搭建私有链,创建部署Hello world智能合约

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