Truffle 4.0、Geth 1.7.2、TestRPC在私有链上搭建智能合约

Truffle 4.0、Geth 1.7.2、TestRPC在私有链上搭建智能合约

目录

[toc]

1、什么是 Truffle?

  Truffle 是最流行的开发框架,能够在本地编译、部署智能合约,使命是让开发更容易。

  Truffle 需要以太坊客户端支持,需要支持标准的JSON RPC API。

2、适合 Truffle 开发的客户端

  有许多的以太坊客户端可以选择。我们推荐在开发和部署时使用不同客户端。

  适用开发的客户端

  适用正式发布的客户端

  当开发基于 Truffle 的应用时,推荐使用EthereumJS TestRPC。它是一个完整的在内存中的区块链仅仅存在于你开发的设备上。相对于 GethTestRPC 它在执行交易时是实时返回,而不等待默认的出块时间,这样你可以快速验证你新写的代码,当出现错误时,也能即时反馈给你。它同时还是一个支持自动化测试的功能强大的客户端。Truffle 充分利用它的特性,能将测试运行时间提速近90%。

  

3、Truffle的源代码地址

  https://github.com/trufflesuite/truffle

  

4、如何安装?

  接下来的例子,我们会使用 Truffle 分别连接 GethTestRPC 测试智能合约的部署,首先我们先分别安装TruffleGethTestRPC

  

4.1、安装 Go-Ethereum 1.7.2

  Go-Ethereum 的安装参考这篇文章:使用 Go-Ethereum 1.7.2搭建以太坊私有链

  

4.2、安装 Truffle 4.0

  依赖环境:
* NodeJS 5.0+
* Windows,Linux,或Mac OS X

  安装很简单:

npm install -g truffle@4.0.0

  查看安装的版本:

➜ /Users/lion >truffle version
Truffle v4.0.0 (core: 4.0.0)
Solidity v0.4.18 (solc-js)

  

4.3、安装 TestRPC

➜ /Users/lion >npm install -g ethereumjs-testrpc
/usr/local/bin/testrpc -> /usr/local/lib/node_modules/ethereumjs-testrpc/build/cli.node.js
+ ethereumjs-testrpc@6.0.1
added 1 package and updated 2 packages in 10.648s

  

5、使用 Truffle 进行智能合约的开发

5.1、初始化一个 Truffle 项目

  通过truffle init命令,可以初始化一个默认的以太坊代币合约项目,后面我们可以通过这个项目来快速学习:

➜ /Users/lion/my_project/_eth >mkdir test_truffle
➜ /Users/lion/my_project/_eth >cd test_truffle
➜ /Users/lion/my_project/_eth/test_truffle >truffle init

Downloading...
Unpacking...
Setting up...
Unbox successful. Sweet!

Commands:

  Compile:        truffle compile
  Migrate:        truffle migrate
  Test contracts: truffle test

  完成后,你将拥有如下目录:
* contracts 智能合约目录
* migrations 发布脚本目录
* test 存放测试文件
* truffle.js Truffle的配置文件

  

5.2、编译合约

  要编译合约,使用truffle compile命令,可以将原始代码编译成以太坊认可的字节码:

➜ /Users/lion/my_project/_eth/test_truffle >truffle compile
Compiling ./contracts/Migrations.sol...
Writing artifacts to ./build/contracts

  Truffle仅默认编译自上次编译后被修改过的文件,来减少不必要的编译。如果你想编译全部文件,可以使用--compile-all选项

truffle compile --compile-all

  Truffle需要定义的合约名称和文件名准确匹配,这种匹配是区分大小写的,也就是说大小写也要一致。推荐大写每一个开头字母。

  文件之间的相互依赖,可以使用import进行合约间的引用,Truffle将会按正确顺序依次编译合约,并在需要的时候自动关联库。例如:

import "./AnotherContract.sol";

  

5.3、创建一个 Hello mshk.top 的合约并编译

  在contracts目录中新建一个Hello_mshk_top.sol文件,代码如下:

pragma solidity ^0.4.17;

contract Hello_mshk_top {

  //say hello mshk.top
  function say() public pure returns (string) {
    return "Hello mshk.top";
  }

  //print name
  function print(string name) public pure returns (string) {
    return name;
  }
}

  代码中有两个方法:say()方法是输出一段文字Hello mshk.topprint(string name)方法是输出传入的内容。

  编辑migrations/1_initial_migration.js部署脚本,将我们刚才创建的Hello_mshk_top.sol文件设置到发布配置文件中,内容如下:

var Migrations = artifacts.require("./Migrations.sol");
var Hello_mshk_top = artifacts.require("./Hello_mshk_top.sol");
module.exports = function(deployer) {
  deployer.deploy(Migrations);
  deployer.deploy(Hello_mshk_top);
};

  将项目使用truffle compile命令进行编译,编译后的文件都放在了./build/contracts目录下:

➜ /Users/lion/my_project/_eth/test_truffle >truffle compile
Compiling ./contracts/Hello_mshk_top.sol...
Compiling ./contracts/Migrations.sol...
Writing artifacts to ./build/contracts

  Hello_mshk_top.sol编译后的文件是./build/contracts/Hello_mshk_top.json中,后面在部署到geth中,我们会用到。

  

6、部署智能合约

  编辑truffle.js配置文件,设置我们稍后要部署智能合约的位置,内容如下:

module.exports = {
    networks: {
        development: {
          host: "localhost",
          port: 8545,
          network_id: "*"
        }
    }
};

  接下来,我们会使用上面的智能合约,分别在testRPCgeth中进行部署测试。

  truffle的智能合约项目部署,使用下面的命令:

truffle migrate

  这个命令会执行所有migrations目录下的js文件。如果之前执行过truffle migrate命令,再次执行,只会部署新的js文件,如果没有新的js文件,不会起任何作用。如果使用--reset参数,则会重新的执行所有脚本的部署。

  如果要部署到指定的网络,可以使用--network参数,例如:

truffle migrate --network live

  多个网络的配置格式如下:

networks: {
  development: {
    host: "localhost",
    port: 8545,
    network_id: "*" // match any network
  },
  live: {
    host: "178.25.19.88", // Random IP for example purposes (do not use)
    port: 80,
    network_id: 1,        // Ethereum public network
    // optional config values:
    // gas  Gas limit used for deploys. Default is 4712388
    // gasPrice Gas price used for deploys. Default is 100000000000 (100 Shannon).
    // from - default address to use for any transaction Truffle makes during migrations
    // provider - web3 provider instance Truffle should use to talk to the Ethereum network.
    //          - if specified, host and port are ignored.
  }
}

  

6.1、将智能合约部署到 TestRPC 中测试

6.1.2、启动 TestRPC

  直接输入testrpc命令,就可以调用起 TestRPC 客户端,启动 testrpc 经后,会默认创建10个帐号,Available Accounts是帐号列表,Private Keys是相对应的帐号密钥:

➜ /Users/lion >testrpc --gasLimit 0x800000000
EthereumJS TestRPC v6.0.1 (ganache-core: 2.0.0)

Available Accounts
==================
(0) 0x74650142c29e358b8f94a8c5d43345649009a4cd
......

Private Keys
==================
(0) f11fa29910cd639aeb6de6126f2f16e091c1da51956fefccafca0afc476fbc41
......
HD Wallet
==================
Mnemonic:      inquiry sense exit ice craft evoke april gym settle social cat uniform
Base HD Path:  m/44'/60'/0'/0'/{account_index}

Gas Limit
==================
34359738368

Listening on localhost:8545

  

6.1.3、通过 truffle migrate 命令,对合约进行部署

  使用truffle migrate命令,发布项目:

➜ /Users/lion/my_project/_eth/test_truffle >truffle migrate
Using network 'development'.

Running migration: 1_initial_migration.js
  Deploying Migrations...
  ... 0xd9e5fa242d29362e57e3da7b0bf6f71b72767972fc15240ed3a02d341e814a44
  Migrations: 0x4bedd1bb517ff9a54f6f3df8eba821ff16a4109b
  Deploying Hello_mshk_top...
  ... 0xc22ff050e189c2561d40250077ee4bf628f957899dfa8d0b5fa50e3ab7a896b0
  Hello_mshk_top: 0xa7ef5037ff81d7932e01b68a503ca4587f00b35c
Saving successful migration to network...
  ... 0x49505a54042f5f74146fbfafef63dd408cb0a7a0c66214ebbaa3217e443d792a
Saving artifacts...

  

6.1.4、测试部署成功的智能合约

  输入以下命令打开truffle控制台,测试刚才我们部署的Hello_mshk_top合约:

truffle(development)> var contract;
undefined
truffle(development)> Hello_mshk_top.deployed().then(function(instance){contract= instance;});
undefined
truffle(development)> contract.say()
'Hello mshk.top'
truffle(development)> contract.print("https://mshk.top")
'https://mshk.top'

  var contractjavascript语法一样,表示声明一个contract变量。Hello_mshk_top.deployed().then(function(instance){contract= instance;})表示,将Hello_mshk_top合约主体,传递给contract变量。后面我们就可以直接使用变量contract分别调用say()方法和print(''),得到我们想要的结果。

  

6.2、将智能合约部署到 Geth 1.7.2 私有链

6.2.1、新建 Geth 的创世区块文件,并初始化

  新建一个test_truffle_geth目录,同时新建我们的创世区块文件genesis.json

➜ /Users/lion/my_project/_eth >mkdir test_truffle_geth
➜ /Users/lion/my_project/_eth >cd test_truffle_geth
➜ /Users/lion/my_project/_eth/test_truffle_geth >vi genesis.json

  genesis.json:

{
  "config": {
        "chainId": 10,
        "homesteadBlock": 0,
        "eip155Block": 0,
        "eip158Block": 0
    },
  "coinbase"   : "0x0000000000000000000000000000000000000000",
  "difficulty" : "0x20000",
  "extraData"  : "",
  "gasLimit"   : "0x8000000",
  "nonce"      : "0x0000000000000042",
  "mixhash"    : "0x0000000000000000000000000000000000000000000000000000000000000000",
  "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
  "timestamp"  : "0x00",
  "alloc": {}
}

  初始化创世区块

➜ /Users/lion/my_project/_eth/test_truffle_geth >geth init ./genesis.json --datadir "./chain"
WARN [11-07|10:17:11] No etherbase set and no accounts found as default
INFO [11-07|10:17:11] Allocated cache and file handles         database=/Users/lion/my_project/_eth/test_truffle_geth/chain/geth/chaindata cache=16 handles=16
INFO [11-07|10:17:11] Writing custom genesis block
INFO [11-07|10:17:11] Successfully wrote genesis state         database=chaindata                                                          hash=ecf271…5269d6
INFO [11-07|10:17:11] Allocated cache and file handles         database=/Users/lion/my_project/_eth/test_truffle_geth/chain/geth/lightchaindata cache=16 handles=16
INFO [11-07|10:17:11] Writing custom genesis block
INFO [11-07|10:17:11] Successfully wrote genesis state         database=lightchaindata                                                          hash=ecf271…5269d6

  

6.2.2、使用 RPC 方式运行 Geth

  使用rpc方式运行geth

➜ /Users/lion/my_project/_eth/test_truffle_geth >geth \
  --identity "mshk.top etherum" \
  --rpcaddr 0.0.0.0 \
  --rpc \
  --rpcport 8545 \
  --maxpeers 2 \
  --rpcapi "db,eth,net,web3,debug" \
  --networkid 100 \
  --datadir "./chain" \
  --nodiscover
WARN [11-07|10:21:56] No etherbase set and no accounts found as default
INFO [11-07|10:21:56] Starting peer-to-peer node               instance="Geth/mshk.top etherum/v1.7.2-stable-1db4ecdc/darwin-amd64/go1.9.1"
INFO [11-07|10:21:56] Allocated cache and file handles         database=/Users/lion/my_project/_eth/test_truffle_geth/chain/geth/chaindata cache=128 handles=1024
WARN [11-07|10:21:56] Upgrading database to use lookup entries
INFO [11-07|10:21:56] Initialised chain configuration          config="{ChainID: 10 Homestead: 0 DAO: <nil> DAOSupport: false EIP150: <nil> EIP155: 0 EIP158: 0 Byzantium: <nil> Engine: unknown}"
INFO [11-07|10:21:56] Disk storage enabled for ethash caches   dir=/Users/lion/my_project/_eth/test_truffle_geth/chain/geth/ethash count=3
INFO [11-07|10:21:56] Disk storage enabled for ethash DAGs     dir=/Users/lion/.ethash                                             count=2
INFO [11-07|10:21:56] Initialising Ethereum protocol           versions="[63 62]" network=100
INFO [11-07|10:21:56] Database deduplication successful        deduped=0
INFO [11-07|10:21:56] Loaded most recent local header          number=0 hash=ecf271…5269d6 td=131072
INFO [11-07|10:21:56] Loaded most recent local full block      number=0 hash=ecf271…5269d6 td=131072
INFO [11-07|10:21:56] Loaded most recent local fast block      number=0 hash=ecf271…5269d6 td=131072
INFO [11-07|10:21:56] Regenerated local transaction journal    transactions=0 accounts=0
INFO [11-07|10:21:56] Starting P2P networking
INFO [11-07|10:21:56] RLPx listener up                         self="enode://eee1025474554baf3d42dc72fb6f13df8246b5ed879ca6a7764d3147c422ca2d10eba1dc6a9e609d3535794668d1e064548550683a8c34cfefbcf879b9cbaf2b@[::]:30303?discport=0"
INFO [11-07|10:21:56] IPC endpoint opened: /Users/lion/my_project/_eth/test_truffle_geth/chain/geth.ipc
INFO [11-07|10:21:56] HTTP endpoint opened: http://0.0.0.0:8545
INFO [11-07|10:21:56] Mapped network port                      proto=tcp extport=30303 intport=30303 interface=NAT-PMP(10.0.0.1)

  
  启动私有节点需要的参数:

参数名称 参数描述
identity 区块链的标示,随便填写,用于标示目前网络的名字
init 指定创世块文件的位置,并创建初始块
datadir 设置当前区块链网络数据存放的位置
port 网络监听端口 默认是30303
rpc 启动rpc通信,可以进行智能合约的部署和调试
maxpeers 网络节点的最大数量,默认是25
rpcapi 设置允许连接的rpc的客户端,一般为db,eth,net,web3
networkid 设置当前区块链的网络ID,用于区分不同的网络,是一个数字
console 启动命令行模式,可以在Geth中执行命令
dev 开发者模式,带调试模式的专有网络
nodiscover 私有链地址,不会被网上看到

  
  rpc方式启动geth以后,会一直停止在那里,没有办法在geth中进行任何输入。浏览一下我们刚才创建区块链数据的文件夹,会发现有一个geth.ipc文件。

➜ /Users/lion >ll /Users/lion/my_project/_eth/test_truffle_geth/chain
total 0
drwxr-xr-x  7 lion  staff  224 Nov  7 10:21 geth
srw-------  1 lion  staff    0 Nov  7 10:21 geth.ipc
drwx------  2 lion  staff   64 Nov  7 10:17 keystore

  然后用下面这个命令,进入geth控制台:

➜ /Users/lion >geth attach ipc://Users/lion/my_project/_eth/test_truffle_geth/chain/geth.ipc
Welcome to the Geth JavaScript console!

instance: Geth/mshk.top etherum/v1.7.2-stable-1db4ecdc/darwin-amd64/go1.9.1
 modules: admin:1.0 debug:1.0 eth:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0

>

  

6.2.3、在 Geth 中新建帐户,开始挖矿

  创建一个帐户,并解锁用户一段时间(单位是秒),然后启动挖矿

> web3.personal.newAccount("123456")
"0x0a67659a161410a4a1e0a5889ff05a1417915172"
> personal.unlockAccount(eth.accounts[0], "123456", 15000)
true
> miner.start(1)
null

  过一会后查看下帐户地址的余额,将帐户地址赋值给变量acc0,可以看到里面有了以太币:

> acc0 = web3.eth.accounts[0]
"0xc90747b99362c41fa89d2e7dea1b5b8d9567b741"
> web3.eth.getBalance(acc0)
415000000000000000000

  

6.2.4、在 Geth 中部署合约

  这时,我们就要用到刚刚编译后的Hello_mshk_top.json文件了,打开https://www.bejson.com网址,把abi部分取出并进行json转义。然后在geth中输入以下内容,将json转义后的内容,赋值给mshk_abi变量:

mshk_abi=JSON.parse('[{\"constant\":true,\"inputs\":[{\"name\":\"name\",\"type\":\"string\"}],\"name\":\"print\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"stateMutability\":\"pure\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"say\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"stateMutability\":\"pure\",\"type\":\"function\"}]')

  会得到下面的返回值:

[{
    constant: true,
    inputs: [{
        name: "name",
        type: "string"
    }],
    name: "print",
    outputs: [{
        name: "",
        type: "string"
    }],
    payable: false,
    stateMutability: "pure",
    type: "function"
}, {
    constant: true,
    inputs: [],
    name: "say",
    outputs: [{
        name: "",
        type: "string"
    }],
    payable: false,
    stateMutability: "pure",
    type: "function"
}]

  找到Hello_mshk_top.json文件中的bytecode部分,然后在geth中,将值赋值给mshk_bytecode变量,在geth中输入以下内容:

mshk_bytecode="0x6060604052341561000f57600080fd5b6102488061001e6000396000f30060606040526004361061004c576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806311114af114610051578063954ab4b214610127575b600080fd5b341561005c57600080fd5b6100ac600480803590602001908201803590602001908080601f016020809104026020016040519081016040528093929190818152602001838380828437820191505050505050919050506101b5565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100ec5780820151818401526020810190506100d1565b50505050905090810190601f1680156101195780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561013257600080fd5b61013a6101c5565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561017a57808201518184015260208101905061015f565b50505050905090810190601f1680156101a75780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6101bd610208565b819050919050565b6101cd610208565b6040805190810160405280600e81526020017f48656c6c6f206d73686b2e746f70000000000000000000000000000000000000815250905090565b6020604051908101604052806000815250905600a165627a7a72305820aa448d81e0f4bf12f805920c9cefd0d3cc6f053d87dd8547663721256179ebcd0029"

  评估下创建合约需要的手续费是207296gas

> web3.eth.estimateGas({data: mshk_bytecode})
207296

  部署合约,并将合约传递给mshk变量,在geth中输入以下内容:

mshk_Contract = web3.eth.contract(mshk_abi);
mshk_hello = mshk_Contract.new({from:acc0, data:mshk_bytecode, gas:300000}, function(e, contract){
    if(!e) {

      if(!contract.address) {
        console.log("Contract transaction send: TransactionHash: " + contract.transactionHash + " waiting to be mined...");

      } else {
        console.log("Contract mined! Address: " + contract.address);
        console.log(contract);
      }

    }
});

  输入成功后,会看到下面的信息,0x1d055281899007cbe6865a48d0a79239dac8e486是合约创建成功的地址:

> Contract mined! Address: 0x1d055281899007cbe6865a48d0a79239dac8e486
[object Object]

  

6.2.5、测试部署成功的智能合约

  恭喜你,如果能看到上面的信息,说明智能合约已经部署成功了。接下来,我们可以使用下面的命令在geth中调用,刚刚部署成功的合约:

> mshk_hello.say()
"Hello mshk.top"
> mshk_hello.print("Hello https://mshk.top")
"Hello https://mshk.top"

  通过这些章节的练习,你可以将之前章节中的代币合约、众筹合约进行部署。

  

7、代币合约、高级代币合约、众筹合约项目地址

  文章中使用的完整代码,存放在了github上面:https://github.com/idoall/truffle_solidity_contractsExample

  

8、扩展阅读

upgrading-from-truffle-2-to-3

Full Stack Hello World Voting Ethereum Dapp Tutorial

Truffle Documentation

Building a smart contract using the command line

truffle3.0-integrate-nodejs


博文作者:迦壹
博客地址:Truffle 4.0、Geth 1.7.2、TestRPC在私有链上搭建智能合约
转载声明:可以转载, 但必须以超链接形式标明文章原始出处和作者信息及版权声明,谢谢合作!

比特币地址:1KdgydfKMcFVpicj5w4vyn3T88dwjBst6Y
以太坊地址:0xbB0a92d634D7b9Ac69079ed0e521CC2e0a97c420


18
说点什么

avatar
9 Comment threads
9 Thread replies
0 Followers
 
Most reacted comment
Hottest comment thread
7 Comment authors
Cicileo迦壹alanzhaoxiaochong Recent comment authors
  Subscribe  
最新 最旧 得票最多
提醒
翟宇波
游客
翟宇波

这里面abi转义具体是转义成什么形式?

liusong
游客
liusong

6.2.3 miner.start(1) 不知道为什么,一直挖不倒矿

zhaoxiaochong
游客
zhaoxiaochong

您好,非常感谢您的教程,收益匪浅!
顺便问下,合约部署到公链的话,如何部署,以及如何使用?

trackback
alan
游客
alan

作者你好,首先非常感谢您的博客,对我帮助很大。
我在私有链上部署您github上的众筹合约时,报出了这样的错:
“gas required exceeds allowance or always failing transaction”
我在创世链上已经把gaslimit设置为了最大,依然没法解决,所以请问您应该怎么办。
再次感谢

leo
游客
leo

作者大大.我再部署Hello_mshk_top.json 智能合约报错:ReferenceError: ‘acc0’ is not defined .这个acc0 啥呢

trackback
Cici
游客
Cici

您好,看您的文章质量非常高,想邀请您成为我们虫洞社区的首批内容创作者,不知道是否加您一个微信细说?