精华 通过truffle部署以太坊智能合约
发布于 2 年前 作者 路人-戊 17753 次浏览 来自 分享

此文写作时使用的truffle版本为2.1,由于truffle版本已经更新到3.x,所以参照此文操作可能会有些差异。后面会出3.x版本的教程,敬请期待


直接在geth的控制台通过solc进行编译部署的示例已经很多了,比如这篇博客,此处不再赘述。
本文主要演示怎样通过truffle部署以太坊智能合约。truffle是一个以太坊智能合约开发框架,它会帮你做很多琐碎的事情,安装使用都很简单

1、安装(安装之前你需要先安装nodejs)

npm install -g truffle

2、新建一个项目

mkdir test
cd test
truffle init  #初始化

初如化后test目录下会创建如下的目录和文件

app  build  contracts  migrations  test  truffle.js

3、编写合约

contracts 就是合约存储的目录,默认情况下已经帮你创建好了几个文件, 可以不用管

ConvertLib.sol   MetaCoin.sol  Migrations.sol

在contracts目录下面新建一个合约 Test.sol,内容如下

pragma solidity ^0.4.2;
contract Test { 
	function multiply(uint a) returns(uint d) { 
		return a * 7; 
	}
}

合约内容很简单,就是输入一个整数,返回 它乘以7的结果

4、修改配置

在truffle.js里面添加rpc配置信息,我这里是搭的私有链,添加后配置信息如下(更多配置请参照官方文档)

module.exports = {
  build: {
    "index.html": "index.html",
    "app.js": [
      "javascripts/app.js"
    ],
    "app.css": [
      "stylesheets/app.css"
    ],
    "images/": "images/"
  },
  rpc: {
    host: "localhost",
    port: 8545
  },
  networks: {
	"staging": {
	  network_id: 999, // custom private network
	  host:"localhost",
	  port: 8545
	},
  }
};

这里只需要发布 Test这一个合约, 修改migrations/1_initial_migration.js 如下, 并删除 migrations/2_initial_migration.js

module.exports = function(deployer) {
  deployer.deploy(Test);
};

5、启动geth

打开一个新的终端,打开geth

./geth --identity "newEth" --rpc --rpccorsdomain "*" --datadir "./cdata" --port 30303 --rpcapi "db,eth,net,web3" --networkid 999 console

注意这里的 networkid 和上面的truffle.js 里面的配置要一样,端口号默认是 8545,所以可以不用指定

6、创建账户,并解锁

如果已经有账户了,可以不用再创建,但是要保证账户里面资产不为0 ,否则发布的时候会报错。可以通过挖矿的方式让账户获得奖励。创建账户和挖矿的过程 不再赘述,可以 戳这里 解锁账户,很重要,不然后部署合约的时候会报错

personal.unlockAccount('0xa04ab66a12291b53496a60df57aae8eebb6d8398', "123456", 10000)

如上,第二个参数是创建账户时设置的密码,第三个参数是解锁时间,因为是开发环境上自己搭的私有链,可以把时间设的长一点

7、部署合约

在编写合约的那个终端下执行 truffle compile 编译,然后再执行 truffle migrate, 如果没有报错会显示如下

Running migration: 1_initial_migration.js
  Deploying Test...

如上,表示正在发布。geth的控制台也会有一条提示信息,如下

> I1202 20:09:46.354075 internal/ethapi/api.go:1045] Tx(0x218db3d2c2665ed3a264139316b9744223df30cac4498539f1be3ac3e6e01317) created: 0xbf6080eaae18a6eb4d9d3b9ef08a8bdf02e3caa8

这个时候合约正在部署,geth 控制台执行 如下

> txpool.status
{
  pending: 1,
  queued: 0
}

可以看到,有一个交易在等待确认,需要继续挖矿,把交易打包到下一个区块

miner.start()

好了,耐心等待下一个区块被挖出来吧! 成功后部署合约的终端会显示如下

Running migration: 1_initial_migration.js
  Deploying Test...

  Test: 0xbf6080eaae18a6eb4d9d3b9ef08a8bdf02e3caa8
Saving successful migration to network...

Test 后面那串就是合约被存储的地址

8、调用合约

a)、查看abi (abi, application binary interface) vim build/contracts/Test.sol.js 找到下面这段

  Contract.all_networks = {
  "default": {
    "abi": [
      {
        "constant": false,
        "inputs": [
          {
            "name": "a",
            "type": "uint256"
          }
        ],
        "name": "multiply",
        "outputs": [
          {
            "name": "d",
            "type": "uint256"
          }
        ],
        "payable": false,
        "type": "function"
      }
    ],
    "unlinked_binary": "0x606060405260388060106000396000f3606060405260e060020a6000350463c6888fa18114601c575b6002565b3460025760076004350260408051918252519081900360200190f3",
    "events": {},
    "updated_at": 1480738754835,
    "address": "0xbf6080eaae18a6eb4d9d3b9ef08a8bdf02e3caa8",
    "links": {}
  }
};

b)、把abi取出来在线压缩, 并在geth终端 赋值给abi变量

> abi = [{"constant":false,"inputs":[{"name":"a","type":"uint256"}],"name":"multiply","outputs":[{"name":"d","type":"uint256"}],"payable":false,"type":"function"}]
[{
    constant: false,
    inputs: [{
        name: "a",
        type: "uint256"
    }],
    name: "multiply",
    outputs: [{
        name: "d",
        type: "uint256"
    }],
    payable: false,
    type: "function"
}]

c)、创建合约实例

> test = eth.contract(abi).at('0xbf6080eaae18a6eb4d9d3b9ef08a8bdf02e3caa8')

上面的地址就是合约存储的地址 d)、调用

> test.multiply.call(3)
21

总结

  • 上面只是一个简单的demo,并没有实际用途!还有更多的精彩需要探索
  • 通过truffle来开发、部署智能合约确实要简单很多,推荐使用

如果觉得本文对您有用,请随意打赏。您的支持将鼓励我继续创作!

zhifu.png

36 回复

@路人-戊 **Test: 0xbf6080eaae18a6eb4d9d3b9ef08a8bdf02e3caa8 ** 这个返回的不是交易的地址,那这个地址有什么作用呢。交易一般不是用txid来唯一标识吗

@15622149705 这个是合约账户的地址,不是交易的地址,换句话说就是你合约代码存储的地址。以太坊中有两种账户,外部账户合约账户外部账户的地址是由公钥算出来的,而合约账户的地址是由合约创建者的地址和该地址发出过的交易数量计算得到,地址发出过的交易数量也被称作"nonce"

也就是这个就是合约账户~理解了

test.multiply.call(3) 这个call()是系统自带的函数对吧?

pragma solidity ^0.4.6;
 contract SimpleStorage{
	uint storedData;
	function set(uint x){
	   storedData=x;
        }
 function get() constant returns(uint retVal){
 	return storedData;
    }
}  

调用的时候报错

test.set.call(3)
TypeError: Cannot access member 'call' of undefined
    at <anonymous>:1:1

@15622149705 官方文档有介绍,不过我记不太清了,不般不推荐使用call ,call应该是属于api提供的方法,不是合约代码本身的

我们可以使用3种方法来调用自己定义的multiply函数:sendTransaction(3, {from: address}),call(3)。当然,我们也可以让系统自动决定使用什么方法调用函数。下面的代码跟这里有点不对应(自己解决) 原来我以为这种只有返回值的函数调用是不写入区块链的(因为用call调用他就是不会返回交易的哈细值,而是返回函数调用的结果)

>myMultiply7.multiply.sendTransaction(3, {from: '0x414d9703aa1c26115dc2cdabe225db3bedc7a91f'})
"0xb178df05af53ceb9d45fa9e9945e5d5a415c70ae99fd278a66e10f72299df120"
> myMultiply7.multiply.call(3)
21
//myMultiply7.multiply(3, {from: address})自己的账户地址
> myMultiply7.multiply(3, {from: '0x414d9703aa1c26115dc2cdabe225db3bedc7a91f'})
"0xbd4cedda4138bd0c9e0f147b62750ba845e08c3ee9a73a184093a3b24c00023d"

@路人-戊 myMultiply7.multiply.sendTransaction(3, {from: ‘0x414d9703aa1c26115dc2cdabe225db3bedc7a91f’}) 查询他返回的哈希值,并不能得到合同的执行结果21,那21有没有写入区块链呢?或者除了是他除了call返回执行结果这个语句要怎么得到返回结果呢

@15622149705 看这里http://blog.csdn.net/u013137970/article/details/53018423 这个博客的最后调用部分可以解答你的问题

@路人-戊 ok~ 如果只想得知运算的结果,那么只需要使用方法call。而如果想要改变合约的状态,那么就使用sendtransaction 。但是呢我用sendtransaction是改变了结果,结果不会存储在这个合约的状态信息中吗

@15622149705 认真看我给你的那个博客啊,骚年!原文里写的很清楚使用这个方法调用合约将会使调用的结果成为全局共识的一部分,你可以Ctrl + F看下那段,至于原博主写的这个是否正确我没验证过,你可以验证一下…

@路人-戊 我看到了这句话,但是共识只是说大家共同验证,跟他是否存在交易提供的状态信息有关系吗? 博主的那些我都验证过,之前看过几乎一样的帖子。

@路人-戊 Ctrl + F看下那段?这是什么鬼

@15622149705 不知道怎么聊下去了…… 1、你打开那个链接 2、ctrl+f 搜索我给你的那段,然后仔细看看那段!

@路人-戊 666 说好的鼓励新人呢

另外百度Ctrl+F瞬间得到了安慰

竟然有90%的人不会用Ctrl+F的小技巧! 胖子老湿 发表于  2011-08-24 18:15 大约一年前,谷歌搜索专家丹·罗素(Dan Russell)得知一个朋友在检索自己需要的字段时,花了整整5分钟才找出他们所需要的部分。于是,他就萌发了一个想法,他想了解一下究竟有多少人跟他的朋友一样,不会使用Ctrl+F组合键的查找功能。最近,《大西洋月刊》的调查统计显示,竟然有大约90%的人不知道电脑上的组合键Ctrl+F具有“查找”的功能。

非常详细的讲解, 能够使像我这样的新手很快了解如何使用。

@这里只需要发布 Test这一个合约, 修改migrations/1_initial_migration.js 如下, 并删除 migrations/2_initial_migration.js 这个migration是做什么用的,如果需要发布多个合同怎么做呢

@15622149705 因为这里只需要部署一个合约,所以migrations/2_initial_migration.js 没什么用; 你重新初始化一个项目truffle init 再看看migrations下面的文件内容就知道怎么部署多个合约了

建议你看看 Truffle官方文档,学下它的框架。里面都有解释

@路人-戊 当然是他了。。

@PS363707170 你要@ 她才能看到的

@路人-戊 @PS363707170 谢谢两位大神

如今的版本truffle init 已经不出这几个文件夹了,app build contracts migrations test truffle.js,没有前端了,如果用truffle init webpack又会加一个webpack模块管理器,又增加了理解难度 Webpack 中文指南http://webpackdoc.com/

看了一下上面的文章,纯开发的话,可以用EthereumJs TestRPC啊,因为是在内存中,效果有点像sodity-brower那个浏览器编译器。debug或开发时都可以很快啊,不用等挖矿。

我照着您的方法做,在truffle migrate时出现了,发布错误1.png Using network ‘development’.

Running migration: 1_initial_migration.js

/home/zxk/test/migrations/1_initial_migration.js:4 deployer.deploy(Test); ^ ReferenceError: Test is not defined at module.exports (/home/zxk/test/migrations/1_initial_migration.js:4:19) at /usr/lib/node_modules/truffle/node_modules/truffle-migrate/index.js:85:7 at /usr/lib/node_modules/truffle/node_modules/truffle-migrate/node_modules/truffle-require/index.js:90:7 at tryToString (fs.js:414:3) at FSReqWrap.readFileAfterClose [as oncomplete] (fs.js:401:12)

但是test我已经创建在了contract文件下,不知到还会有什么原因导致这个问题?@路人-戊

@叫我真真 你是不是没有执行truffle compile

@路人-戊 错误截屏.png

额,我是复制的,为什么会说test definde!这是我可能错哪了?tset截屏.png

@叫我真真 我演示的时候truffle版本是2.1,你用的可能是3.x的,变化比较大。后面有空了我再出一个3.x版本的教程

  • @叫我真真 你的1_initial_migration写错了,不是改deployer.deploy() 而是改

var Migrations = artifacts.require("./Test.sol");

module.exports = function(deployer) { deployer.deploy(Migrations); };

改动的内容嗯在第一行

我看您调用合约的时候,abi还是从truffle那边拿过来的,合约不是已经部署到以太坊私有链上了吗?那么我们从geth客户端拿不到合约的abi和合约地址吗?

@idiot abi是编译的产物,geth里面是没有的,合约账户地址,部署成功后即返回,这个在geth里面也是没有存储的

回到顶部