学习目标
- 了解智能合约
- 简单环境搭建
- 能够利用
solidity
编写Hello World
合约 - 合约部署
- 和合约互动
智能合约是什么
在区块链上运行的程序,通常称为智能合约(Smart Contract)
。所以通常会把写区块链程序
改称写智能合约
。虽然比特币(Bitcoin)上也能写智能合约,但是比特币所支持的语法仅与交易有关,能做的事情比较有限。因此目前提到写智能合约,通常指的是支持执行图灵完备程序的以太坊(Ethereum)区块链 。
智能合约的应用场景
目前最常见的智能合约是各种加密货币合约,开发者可以很容易地透过部署一个智能合约,来提供运行于以太坊上的新加密代币。如果这份智能合约相容于 ERC20 标准 1,开发者不需要重新开发从挖矿到交易的整个代币生态系,你的新加密代币就可以直接使用支持支持以太坊的电子钱包来收送,大大降低了建立新加密代币的门槛。
智能合约也可以用来运作各种公开公正的自动服务机构(DAO,权力下放自治组织)。透过分散在全球各节点上运作的智能合约,所有运作与决策都是公开透明的,降低了交易的不确定性(不确定性)。
如何编写智能合约?
Ethereum
上的智能合约需要使用solidity
语言来编写。官方宣传上说solidity
是一种类似的JavaScript
的语言,而且围绕着JavaScript
的各种开发工具链都是使用属于使用 Javascript 生态系的 NPM 来提供的。
将智能合约部署到区块链的流程
写好solidity
代码(.sol)后,需要先将程序代码编译(编译)成 EVM(Ethereum Virtual Machine)能读懂的二进制度Contract ByteCode
,才能部署到 Ethereum 的区块链上执行。部署到区块链上的合约会有一个和钱包地址(地址)一样格式的合约地址(Contract Address)。
部署后智能合约可自动执行。后续呼叫智能合约的时候,使用者可以使用部署合约的钱包地址(所有者帐户),或依据编写的智能合约条件,让其他钱包地址也能呼叫这个智能合约。 呼叫智能合约
,其实就是向这个合约地址发起交易,只是交易的不只是代币,而可以是智能合约提供的呼叫方法。
开发所需工具
- 以太坊内存块链测试环境
ganache-cli
- 智能合约开发框架
truffle
- 编辑器
Atom
搭配solidity
插件
开发工具安装
首先开发机上必须装好 Node.js,再使用以下命令安装ganache-cli
和truffle
:
$ npm install -g ganache-cli truffle
注意:如果安装过程中遇到问题,可以升级下node的版本试试。
Atom
下载安装,安装solidity
相关插件
启动 ganache-cli
安装好后随时可以使用ganache-cli
命令来启动以太坊测试环境。
wangsanjundeMacBook-Pro:~ wangsanjun$ ganache-cli
Ganache CLI v6.0.3 (ganache-core: 2.0.2)
Available Accounts
==================
(0) 0xde12463cc19e098ba773adfd236fedb0b21faeb6
(1) 0xdfd59924386b19ad1cfea2088c3279363d2fc936
(2) 0xa19c5064e75fee1dad8bdeb9f4e7a373ce3fa1f7
(3) 0x53d5e0b93659bd7f7f7263365c031e318b2261c0
(4) 0x06b6da8c19b3a0f770f57fe5f6ff469d95ebe098
(5) 0x78121378f88cc8295255f65822e0da64829d1464
(6) 0xf2e3eb6669a941d7ad0984d0fa1e8b88ea6eab3d
(7) 0x39400e42b45691b8ad06507e80f644976d512db7
(8) 0x52c644f97806cf3371150561dd1a44e768378d11
(9) 0xf30705a7669c67d96f80288018e7993918ca62d6
Private Keys
==================
(0) 71a96e34a8bd71f2380bc0b46f62109f0c460afc469ab02c7a89374dba107223
(1) 8ec9ab82818b47eea746d0be5a75c159a21107422ee2624a6b8427bdf0e837d0
(2) c50a555a76aa502805da94b6facac4b3784965f10e57218f23704f4d8526a7ed
(3) 81088a0a92e8898c455b607080b0b82e78033b5728494df98ed2a06d1e7ab323
(4) 21b347c449c0e7571fd6f82ef6feea881a56e9d6c172c0134316036fabe6ccdc
(5) 0498c47475049e0c8799593b063ff2e0ee5f07e9fb6d4db05a9d57adc4b9bc23
(6) 4528f26f7fdc64c54d12c32dad9c1836155ef8fadce316bc351ffd47fbcdb6fa
(7) 371eb0e4f309117d53d10c007550acb19c1a387f9e034a52f3e741b8d955fe3c
(8) 4d65400d1f12bf2bb26189e8beadec98bb34e85e34db33ee969449e4993c2b2c
(9) 160a3031008a3583f9cacfe3aebf34a5bc0cbc4c0556cda97baa32c90ea92859
HD Wallet
==================
Mnemonic: creek found enroll warm flame door used essay indoor cry carpet journey
Base HD Path: m/44'/60'/0'/0/{account_index}
Listening on localhost:8545
可以看到ganache-cli
启动后自动建立了10
个帐号(Accounts),与每个帐号对应的私钥(Private Key)。每个帐号中都有100
个测试用的以太币(Ether)。要注意ganache-cli
仅运行在內存中,因此每次重开时都会回到全新的状态。
建立项目
开启另一个终端窗口,输入以下命令以建立项目:
wangsanjundeMacBook-Pro:Desktop wangsanjun$ mkdir SmartContractDemo
wangsanjundeMacBook-Pro:Desktop wangsanjun$ cd SmartContractDemo/
wangsanjundeMacBook-Pro:SmartContractDemo wangsanjun$ ls
wangsanjundeMacBook-Pro:SmartContractDemo wangsanjun$ mkdir HelloWorld
wangsanjundeMacBook-Pro:SmartContractDemo wangsanjun$ cd HelloWorld/
wangsanjundeMacBook-Pro:HelloWorld wangsanjun$ ls
wangsanjundeMacBook-Pro:HelloWorld wangsanjun$ truffle init
Downloading...
Unpacking...
Setting up...
Unbox successful. Sweet!
Commands:
Compile: truffle compile
Migrate: truffle migrate
Test contracts: truffle test
目录结构:
-
/contracts:
存放智能合约原始代码的地方,可以看到里面已经有一个sol
文件,我们开发的HelloWorld.sol
文件就存放在这个文件夹。wangsanjundeMacBook-Pro:HelloWorld wangsanjun$ cd contracts/ wangsanjundeMacBook-Pro:contracts wangsanjun$ ls Migrations.sol
-
/migrations:
这是Truffle
用来部署智能合约的功能,待会儿我们会新建一个类似1_initial_migration.js
的文件来部署HelloWorld.sol
。wangsanjundeMacBook-Pro:HelloWorld wangsanjun$ cd migrations/ wangsanjundeMacBook-Pro:migrations wangsanjun$ ls 1_initial_migration.js
/test:
测试智能合约的代码放在这里,支持js
与sol
测试。truffle.js/truffle-config.js:
Truffle
的配置文件,需要配置要连接的以太坊网络
新建 HelloWorld 合约
在contracts
文件夹下新建HelloWorld.sol
文件
HelloWorld.sol
文件內容如下:
pragma solidity ^0.4.17;
contract HelloWorld {
function sayHello() returns (string) {
return "HelloWorld";
}
}
讲解
pragma solidity ^0.4.17;
第一行指的是目前使用的solidity
版本,不同版本的solidity
可能会编译出不同的bytecode
。^
代表兼容solidity``0.4.17 ~ 0.4.9
的版本。
contract HelloWorld {
...
}
contract
关键字类似于其他语言中较常见的class
。因为solidity
是专为智能合约(Contact)设计的语言,声明contract
后即内置了开发智能合约所需的功能。也可以把这句理解为class HelloWorld extends Contract
。
function sayHello() returns (string) {
return "HelloWorld";
}
函数的结构与其他程序类似,但如果有传入的参数或回传值,需要指定参数或回传值的类型(type)。
编译
现在执行truffle compile
命令,我们可以将HelloWorld.sol
原始码编译成Ethereum bytecode
。
wangsanjundeMacBook-Pro:HelloWorld wangsanjun$ truffle compile
Compiling ./contracts/HelloWorld.sol...
Compiling ./contracts/Migrations.sol...
Compilation warnings encountered:
/Users/wangsanjun/Desktop/SmartContractDemo/HelloWorld/contracts/HelloWorld.sol:3:3: Warning: No visibility specified. Defaulting to "public".
function sayHello() returns (string) {
^
Spanning multiple lines.
,/Users/wangsanjun/Desktop/SmartContractDemo/HelloWorld/contracts/HelloWorld.sol:3:3: Warning: Function state mutability can be restricted to pure
function sayHello() returns (string) {
^
Spanning multiple lines.
Writing artifacts to ./build/contracts
wangsanjundeMacBook-Pro:HelloWorld wangsanjun$ cd build
wangsanjundeMacBook-Pro:build wangsanjun$ ls
contracts
wangsanjundeMacBook-Pro:build wangsanjun$ cd contracts/
wangsanjundeMacBook-Pro:contracts wangsanjun$ ls
HelloWorld.json Migrations.json
Warning暂时忽略,编译成功后,会在HelloWorld
文件夹下面的build/contracts
文件夹下面看见HelloWorld.json
文件。
部署
truffle
框架中提供了方便部署合约的脚本。新建migrations/2_deploy_contracts.js
文件(脚本使用Javascript
编写),将内容修改如下:
var HelloWorld = artifacts.require("HelloWorld");
module.exports = function(deployer) {
deployer.deploy(HelloWorld);
};
使用artifacts.require
语句来取得准备部署的合约。使用deployer.deploy
语句将合约部署到区块链上。这边HelloWorld
是contract
的名称而不是文件名。因此可以用此语法读入任一.sol
文件中的任一合约。
现在执行truffle migrate
命令:
wangsanjundeMacBook-Pro:HelloWorld wangsanjun$ truffle migrate
Error: No network specified. Cannot determine current network.
at Object.detect (/Users/wangsanjun/.nvm/versions/node/v7.10.1/lib/node_modules/truffle/build/webpack:/~/truffle-core/lib/environment.js:31:1)
at /Users/wangsanjun/.nvm/versions/node/v7.10.1/lib/node_modules/truffle/build/webpack:/~/truffle-core/lib/commands/migrate.js:91:1
at finished (/Users/wangsanjun/.nvm/versions/node/v7.10.1/lib/node_modules/truffle/build/webpack:/~/truffle-workflow-compile/index.js:53:1)
at /Users/wangsanjun/.nvm/versions/node/v7.10.1/lib/node_modules/truffle/build/webpack:/~/truffle-compile/index.js:303:1
at /Users/wangsanjun/.nvm/versions/node/v7.10.1/lib/node_modules/truffle/build/webpack:/~/truffle-compile/profiler.js:157:1
at /Users/wangsanjun/.nvm/versions/node/v7.10.1/lib/node_modules/truffle/build/webpack:/~/truffle-compile/~/async/dist/async.js:3874:1
at /Users/wangsanjun/.nvm/versions/node/v7.10.1/lib/node_modules/truffle/build/webpack:/~/truffle-compile/~/async/dist/async.js:473:1
at replenish (/Users/wangsanjun/.nvm/versions/node/v7.10.1/lib/node_modules/truffle/build/webpack:/~/truffle-compile/~/async/dist/async.js:993:1)
at iterateeCallback (/Users/wangsanjun/.nvm/versions/node/v7.10.1/lib/node_modules/truffle/build/webpack:/~/truffle-compile/~/async/dist/async.js:983:1)
at /Users/wangsanjun/.nvm/versions/node/v7.10.1/lib/node_modules/truffle/build/webpack:/~/truffle-compile/~/async/dist/async.js:958:1
at /Users/wangsanjun/.nvm/versions/node/v7.10.1/lib/node_modules/truffle/build/webpack:/~/truffle-compile/~/async/dist/async.js:3871:1
at /Users/wangsanjun/.nvm/versions/node/v7.10.1/lib/node_modules/truffle/build/webpack:/~/truffle-compile/profiler.js:153:1
at /Users/wangsanjun/.nvm/versions/node/v7.10.1/lib/node_modules/truffle/build/webpack:/~/truffle-compile/~/async/dist/async.js:1126:1
at /Users/wangsanjun/.nvm/versions/node/v7.10.1/lib/node_modules/truffle/build/webpack:/~/truffle-compile/~/async/dist/async.js:473:1
at iteratorCallback (/Users/wangsanjun/.nvm/versions/node/v7.10.1/lib/node_modules/truffle/build/webpack:/~/truffle-compile/~/async/dist/async.js:1050:1)
at /Users/wangsanjun/.nvm/versions/node/v7.10.1/lib/node_modules/truffle/build/webpack:/~/truffle-compile/~/async/dist/async.js:958:1
at /Users/wangsanjun/.nvm/versions/node/v7.10.1/lib/node_modules/truffle/build/webpack:/~/truffle-compile/~/async/dist/async.js:1123:1
at /Users/wangsanjun/.nvm/versions/node/v7.10.1/lib/node_modules/truffle/build/webpack:/~/truffle-compile/profiler.js:132:1
at FSReqWrap.oncomplete (fs.js:114:15)
报错了,Error: No network specified. Cannot determine current network.
哦,原来找不到我们的私有链网络。也是啊,怎么着才能连接到我们本地的ganache-cli
测试环境呢。原来需要配置truffle.js
文件
module.exports = {
networks: {
development: {
host: "127.0.0.1",
port: 8545,
network_id: "*" // Match any network id
}
}
};
重新执行truffle migrate
命令:
wangsanjundeMacBook-Pro:HelloWorld wangsanjun$ truffle migrate
Using network 'development'.
Running migration: 1_initial_migration.js
Deploying Migrations...
... 0xb821e6898642eac825b02a571f8ce4a3e0d0443e49274bbc4c70971a5ddea5c4
Migrations: 0xb414c2a65d595dcae17c5c0c51076b95c8babd73
Saving successful migration to network...
... 0xd67eafbab4a191dbd18d70b7065f4857374d4770cdd4c44e6fb4f0a13dc27ce3
Saving artifacts...
Running migration: 2_deploy_contracts.js
Deploying HelloWorld...
... 0x5f278a700ea1d9f5f9565841a621d9f4878184c43deb6819f7217435528c7bde
HelloWorld: 0x046692c66a173925b95e378349433222e7a139bb
Saving successful migration to network...
... 0x02f4e867c23a4e21a850935f4642d5a2c5c2b195ce35ac0ba2e0cbda8c0f9a86
Saving artifacts...
如此一来合约已经部署到ganache-cli
中。切换到ganache-cli
窗口,可以看到ganache-cli
有反应了。
与合约互动
truffle
提供命令行工具,执行truffle console
命令后,可用Javascript
来和刚刚部署的合约互动。
truffle(development)> let contract;
undefined
truffle(development)> HelloWorld.deployed().then(instance => contract = instance)
TruffleContract {
constructor:
{ [Function: TruffleContract]
_static_methods:
{ setProvider: [Function: setProvider],
new: [Function: new],
at: [Function: at],
deployed: [Function: deployed],
defaults: [Function: defaults],
hasNetwork: [Function: hasNetwork],
isDeployed: [Function: isDeployed],
detectNetwork: [Function: detectNetwork],
setNetwork: [Function: setNetwork],
resetAddress: [Function: resetAddress],
link: [Function: link],
clone: [Function: clone],
addProp: [Function: addProp],
toJSON: [Function: toJSON] },
_properties:
{ contract_name: [Object],
contractName: [Object],
abi: [Object],
network: [Function: network],
networks: [Function: networks],
address: [Object],
transactionHash: [Object],
links: [Function: links],
events: [Function: events],
binary: [Function: binary],
deployedBinary: [Function: deployedBinary],
unlinked_binary: [Object],
bytecode: [Object],
deployedBytecode: [Object],
sourceMap: [Object],
deployedSourceMap: [Object],
source: [Object],
sourcePath: [Object],
legacyAST: [Object],
ast: [Object],
compiler: [Object],
schema_version: [Function: schema_version],
schemaVersion: [Function: schemaVersion],
updated_at: [Function: updated_at],
updatedAt: [Function: updatedAt] },
_property_values: {},
_json:
{ contractName: 'HelloWorld',
abi: [Object],
bytecode: '0x6060604052341561000f57600080fd5b6101578061001e6000396000f300606060405260043610610041576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063ef5fb05b14610046575b600080fd5b341561005157600080fd5b6100596100d4565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561009957808201518184015260208101905061007e565b50505050905090810190601f1680156100c65780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6100dc610117565b6040805190810160405280600a81526020017f48656c6c6f576f726c6400000000000000000000000000000000000000000000815250905090565b6020604051908101604052806000815250905600a165627a7a72305820b527bfcbe8419a648eb4e7568b2e6c491f30059cc3f7bed7a7695d10400d57150029',
deployedBytecode: '0x606060405260043610610041576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063ef5fb05b14610046575b600080fd5b341561005157600080fd5b6100596100d4565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561009957808201518184015260208101905061007e565b50505050905090810190601f1680156100c65780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6100dc610117565b6040805190810160405280600a81526020017f48656c6c6f576f726c6400000000000000000000000000000000000000000000815250905090565b6020604051908101604052806000815250905600a165627a7a72305820b527bfcbe8419a648eb4e7568b2e6c491f30059cc3f7bed7a7695d10400d57150029',
sourceMap: '25:93:0:-;;;;;;;;;;;;;;;;;',
deployedSourceMap: '25:93:0:-;;;;;;;;;;;;;;;;;;;;;;;;49:67;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;23:1:-1;8:100;33:3;30:1;27:2;8:100;;;99:1;94:3;90;84:5;80:1;75:3;71;64:6;52:2;49:1;45:3;40:15;;8:100;;;12:14;3:109;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;49:67:0;78:6;;:::i;:::-;92:19;;;;;;;;;;;;;;;;;;;;49:67;:::o;25:93::-;;;;;;;;;;;;;;;:::o',
source: 'pragma solidity ^0.4.17;\ncontract HelloWorld {\n function sayHello() returns (string) {\n return "HelloWorld";\n }\n}\n',
sourcePath: '/Users/wangsanjun/Desktop/SmartContractDemo/HelloWorld/contracts/HelloWorld.sol',
ast: [Object],
legacyAST: [Object],
compiler: [Object],
networks: [Object],
schemaVersion: '2.0.0',
updatedAt: '2018-03-09T00:54:30.721Z' },
setProvider: [Function: bound setProvider],
new: [Function: bound new],
at: [Function: bound at],
deployed: [Function: bound deployed],
defaults: [Function: bound defaults],
hasNetwork: [Function: bound hasNetwork],
isDeployed: [Function: bound isDeployed],
detectNetwork: [Function: bound detectNetwork],
setNetwork: [Function: bound setNetwork],
resetAddress: [Function: bound resetAddress],
link: [Function: bound link],
clone: [Function: bound clone],
addProp: [Function: bound addProp],
toJSON: [Function: bound toJSON],
web3:
Web3 {
_requestManager: [Object],
currentProvider: [Object],
eth: [Object],
db: [Object],
shh: [Object],
net: [Object],
personal: [Object],
bzz: [Object],
settings: [Object],
version: [Object],
providers: [Object],
_extend: [Object] },
class_defaults:
{ from: '0x4068364a85fa3bac7e83c45581397fe9c5111b39',
gas: 6721975,
gasPrice: 100000000000 },
currentProvider:
HttpProvider {
host: 'http://127.0.0.1:8545',
timeout: 0,
user: undefined,
password: undefined,
headers: undefined,
send: [Function],
sendAsync: [Function],
_alreadyWrapped: true },
network_id: '1520386125068' },
abi:
[ { constant: false,
inputs: [],
name: 'sayHello',
outputs: [Object],
payable: false,
stateMutability: 'nonpayable',
type: 'function' } ],
contract:
Contract {
_eth:
Eth {
_requestManager: [Object],
getBalance: [Object],
getStorageAt: [Object],
getCode: [Object],
getBlock: [Object],
getUncle: [Object],
getCompilers: [Object],
getBlockTransactionCount: [Object],
getBlockUncleCount: [Object],
getTransaction: [Object],
getTransactionFromBlock: [Object],
getTransactionReceipt: [Object],
getTransactionCount: [Object],
call: [Object],
estimateGas: [Object],
sendRawTransaction: [Object],
signTransaction: [Object],
sendTransaction: [Object],
sign: [Object],
compile: [Object],
submitWork: [Object],
getWork: [Object],
coinbase: [Getter],
getCoinbase: [Object],
mining: [Getter],
getMining: [Object],
hashrate: [Getter],
getHashrate: [Object],
syncing: [Getter],
getSyncing: [Object],
gasPrice: [Getter],
getGasPrice: [Object],
accounts: [Getter],
getAccounts: [Object],
blockNumber: [Getter],
getBlockNumber: [Object],
protocolVersion: [Getter],
getProtocolVersion: [Object],
iban: [Object],
sendIBANTransaction: [Function: bound transfer] },
transactionHash: null,
address: '0x046692c66a173925b95e378349433222e7a139bb',
abi: [ [Object] ],
sayHello:
{ [Function: bound ]
request: [Function: bound ],
call: [Function: bound ],
sendTransaction: [Function: bound ],
estimateGas: [Function: bound ],
getData: [Function: bound ],
'': [Circular] },
allEvents: [Function: bound ] },
sayHello:
{ [Function]
call: [Function],
sendTransaction: [Function],
request: [Function: bound ],
estimateGas: [Function] },
sendTransaction: [Function],
send: [Function],
allEvents: [Function: bound ],
address: '0x046692c66a173925b95e378349433222e7a139bb',
transactionHash: null }
truffle(development)> contract.sayHello.call()
'Hello World'
讲解
HelloWorld.deployed().then(instance => contract = instance)
truffle console
中预载了truffle-contract
函数库,以方便操作部署到区块链上的合约。
这边使用HelloWorld.deployed().then
语句来取得HelloWorld
合约的Instance
(实例),并存到contract
变量中,以方便后续的调用。
上面用的是Javascript ES6+
的语法,这句也可以写成:
HelloWorld.deployed().then(instance => {
contract = instance
});
还可以用 ES5 的写法:
HelloWorld.deployed().then(function(instance) {
hello = instance;
});
truffle(development)> contract.sayHello.call()
'Hello World'
如此一来,我们已写好并部署完成了第一个智能合约,也验证了合约确实可以运作。
加入新方法
我们在HelloWorld.sol
中再加入一个echo
方法,echo
方法接受输入一个参数,并回传传送的参数。
function echo(string name) constant returns (string) {
return name;
}
新的echo
方法中传入了一个name
参数。我们也为echo
方法加入一个constant
声明,表示调用这个方法并不会改变区块链的状态。如此一来,透过truffle-contract
来调用此方法时,会自动选用call
来呼叫,也不需要额外提供 gas。
由于更新了合约内容,我们需要先重新新编译一次,将编译结果部署到ganache-cli
上,再透过truffle console
执行看看结果。
wangsanjundeMacBook-Pro:HelloWorld wangsanjun$ truffle compile
Compiling ./contracts/HelloWorld.sol...
Compilation warnings encountered:
/Users/wangsanjun/Desktop/SmartContractDemo/HelloWorld/contracts/HelloWorld.sol:3:3: Warning: No visibility specified. Defaulting to "public".
function sayHello() returns (string) {
^
Spanning multiple lines.
,/Users/wangsanjun/Desktop/SmartContractDemo/HelloWorld/contracts/HelloWorld.sol:7:3: Warning: No visibility specified. Defaulting to "public".
function echo(string name) constant returns (string) {
^
Spanning multiple lines.
,/Users/wangsanjun/Desktop/SmartContractDemo/HelloWorld/contracts/HelloWorld.sol:3:3: Warning: Function state mutability can be restricted to pure
function sayHello() returns (string) {
^
Spanning multiple lines.
,/Users/wangsanjun/Desktop/SmartContractDemo/HelloWorld/contracts/HelloWorld.sol:7:3: Warning: Function state mutability can be restricted to pure
function echo(string name) constant returns (string) {
^
Spanning multiple lines.
Writing artifacts to ./build/contracts
wangsanjundeMacBook-Pro:HelloWorld wangsanjun$ truffle migrate --reset
Using network 'development'.
Running migration: 1_initial_migration.js
Replacing Migrations...
... 0x9f104e49cbee7b4d058109400529078162b180f402e23b9ff237157a1599a019
Migrations: 0x95fa54d8abaf328c0d0c2c6713c369feb0eda7fe
Saving successful migration to network...
... 0x069eb545ba8689dfd9ea76d4160b47c37f7c6bb7a8bd91f4f7d7ee3d45e2c85c
Saving artifacts...
Running migration: 2_deploy_contracts.js
Replacing HelloWorld...
... 0x8a2d20fb9009bd631e602ede2605bf1b7e750ec81569bdd202e2379e276f05ad
HelloWorld: 0xb49e482f7b9201d5305e2d6a1bf1bad3d4c05540
Saving successful migration to network...
... 0x70a4b9f5234b2bfc67f70ccd1722b1256a9b1587079dd7aca54c957f40c00443
Saving artifacts...
wangsanjundeMacBook-Pro:HelloWorld wangsanjun$ truffle console
truffle(development)> let contract;
undefined
truffle(development)> HelloWorld.deployed().then(intance => contract = instance)
ReferenceError: instance is not defined
at HelloWorld.deployed.then.intance (evalmachine.<anonymous>:1:50)
at process._tickDomainCallback (internal/process/next_tick.js:135:7)
truffle(development)> HelloWorld.deployed().then(instance => contract = instance)
TruffleContract {
constructor:
{ [Function: TruffleContract]
_static_methods:
{ setProvider: [Function: setProvider],
new: [Function: new],
at: [Function: at],
deployed: [Function: deployed],
defaults: [Function: defaults],
hasNetwork: [Function: hasNetwork],
isDeployed: [Function: isDeployed],
detectNetwork: [Function: detectNetwork],
setNetwork: [Function: setNetwork],
resetAddress: [Function: resetAddress],
link: [Function: link],
clone: [Function: clone],
addProp: [Function: addProp],
toJSON: [Function: toJSON] },
_properties:
{ contract_name: [Object],
contractName: [Object],
abi: [Object],
network: [Function: network],
networks: [Function: networks],
address: [Object],
transactionHash: [Object],
links: [Function: links],
events: [Function: events],
binary: [Function: binary],
deployedBinary: [Function: deployedBinary],
unlinked_binary: [Object],
bytecode: [Object],
deployedBytecode: [Object],
sourceMap: [Object],
deployedSourceMap: [Object],
source: [Object],
sourcePath: [Object],
legacyAST: [Object],
ast: [Object],
compiler: [Object],
schema_version: [Function: schema_version],
schemaVersion: [Function: schemaVersion],
updated_at: [Function: updated_at],
updatedAt: [Function: updatedAt] },
_property_values: {},
_json:
{ contractName: 'HelloWorld',
abi: [Object],
bytecode: '0x6060604052341561000f57600080fd5b6102488061001e6000396000f30060606040526004361061004c576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063ef5fb05b14610051578063f15da729146100df575b600080fd5b341561005c57600080fd5b6100646101b5565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100a4578082015181840152602081019050610089565b50505050905090810190601f1680156100d15780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34156100ea57600080fd5b61013a600480803590602001908201803590602001908080601f016020809104026020016040519081016040528093929190818152602001838380828437820191505050505050919050506101f8565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561017a57808201518184015260208101905061015f565b50505050905090810190601f1680156101a75780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6101bd610208565b6040805190810160405280600a81526020017f48656c6c6f576f726c6400000000000000000000000000000000000000000000815250905090565b610200610208565b819050919050565b6020604051908101604052806000815250905600a165627a7a7230582009219be2f4196357c15d51af4de237288899cefcc0f0d8f9b1e81f2537de00e20029',
deployedBytecode: '0x60606040526004361061004c576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063ef5fb05b14610051578063f15da729146100df575b600080fd5b341561005c57600080fd5b6100646101b5565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100a4578082015181840152602081019050610089565b50505050905090810190601f1680156100d15780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34156100ea57600080fd5b61013a600480803590602001908201803590602001908080601f016020809104026020016040519081016040528093929190818152602001838380828437820191505050505050919050506101f8565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561017a57808201518184015260208101905061015f565b50505050905090810190601f1680156101a75780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6101bd610208565b6040805190810160405280600a81526020017f48656c6c6f576f726c6400000000000000000000000000000000000000000000815250905090565b610200610208565b819050919050565b6020604051908101604052806000815250905600a165627a7a7230582009219be2f4196357c15d51af4de237288899cefcc0f0d8f9b1e81f2537de00e20029',
sourceMap: '25:172:0:-;;;;;;;;;;;;;;;;;',
deployedSourceMap: '25:172:0:-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;49:67;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;23:1:-1;8:100;33:3;30:1;27:2;8:100;;;99:1;94:3;90;84:5;80:1;75:3;71;64:6;52:2;49:1;45:3;40:15;;8:100;;;12:14;3:109;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;120:75:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;23:1:-1;8:100;33:3;30:1;27:2;8:100;;;99:1;94:3;90;84:5;80:1;75:3;71;64:6;52:2;49:1;45:3;40:15;;8:100;;;12:14;3:109;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;49:67:0;78:6;;:::i;:::-;92:19;;;;;;;;;;;;;;;;;;;;49:67;:::o;120:75::-;165:6;;:::i;:::-;186:4;179:11;;120:75;;;:::o;25:172::-;;;;;;;;;;;;;;;:::o',
source: 'pragma solidity ^0.4.17;\ncontract HelloWorld {\n function sayHello() returns (string) {\n return "HelloWorld";\n }\n\n function echo(string name) constant returns (string) {\n return name;\n }\n}\n',
sourcePath: '/Users/wangsanjun/Desktop/SmartContractDemo/HelloWorld/contracts/HelloWorld.sol',
ast: [Object],
legacyAST: [Object],
compiler: [Object],
networks: [Object],
schemaVersion: '2.0.0',
updatedAt: '2018-03-09T01:43:29.205Z' },
setProvider: [Function: bound setProvider],
new: [Function: bound new],
at: [Function: bound at],
deployed: [Function: bound deployed],
defaults: [Function: bound defaults],
hasNetwork: [Function: bound hasNetwork],
isDeployed: [Function: bound isDeployed],
detectNetwork: [Function: bound detectNetwork],
setNetwork: [Function: bound setNetwork],
resetAddress: [Function: bound resetAddress],
link: [Function: bound link],
clone: [Function: bound clone],
addProp: [Function: bound addProp],
toJSON: [Function: bound toJSON],
web3:
Web3 {
_requestManager: [Object],
currentProvider: [Object],
eth: [Object],
db: [Object],
shh: [Object],
net: [Object],
personal: [Object],
bzz: [Object],
settings: [Object],
version: [Object],
providers: [Object],
_extend: [Object] },
class_defaults:
{ from: '0x4068364a85fa3bac7e83c45581397fe9c5111b39',
gas: 6721975,
gasPrice: 100000000000 },
currentProvider:
HttpProvider {
host: 'http://127.0.0.1:8545',
timeout: 0,
user: undefined,
password: undefined,
headers: undefined,
send: [Function],
sendAsync: [Function],
_alreadyWrapped: true },
network_id: '1520386125068' },
abi:
[ { constant: false,
inputs: [],
name: 'sayHello',
outputs: [Object],
payable: false,
stateMutability: 'nonpayable',
type: 'function' },
{ constant: true,
inputs: [Object],
name: 'echo',
outputs: [Object],
payable: false,
stateMutability: 'view',
type: 'function' } ],
contract:
Contract {
_eth:
Eth {
_requestManager: [Object],
getBalance: [Object],
getStorageAt: [Object],
getCode: [Object],
getBlock: [Object],
getUncle: [Object],
getCompilers: [Object],
getBlockTransactionCount: [Object],
getBlockUncleCount: [Object],
getTransaction: [Object],
getTransactionFromBlock: [Object],
getTransactionReceipt: [Object],
getTransactionCount: [Object],
call: [Object],
estimateGas: [Object],
sendRawTransaction: [Object],
signTransaction: [Object],
sendTransaction: [Object],
sign: [Object],
compile: [Object],
submitWork: [Object],
getWork: [Object],
coinbase: [Getter],
getCoinbase: [Object],
mining: [Getter],
getMining: [Object],
hashrate: [Getter],
getHashrate: [Object],
syncing: [Getter],
getSyncing: [Object],
gasPrice: [Getter],
getGasPrice: [Object],
accounts: [Getter],
getAccounts: [Object],
blockNumber: [Getter],
getBlockNumber: [Object],
protocolVersion: [Getter],
getProtocolVersion: [Object],
iban: [Object],
sendIBANTransaction: [Function: bound transfer] },
transactionHash: null,
address: '0xb49e482f7b9201d5305e2d6a1bf1bad3d4c05540',
abi: [ [Object], [Object] ],
sayHello:
{ [Function: bound ]
request: [Function: bound ],
call: [Function: bound ],
sendTransaction: [Function: bound ],
estimateGas: [Function: bound ],
getData: [Function: bound ],
'': [Circular] },
echo:
{ [Function: bound ]
request: [Function: bound ],
call: [Function: bound ],
sendTransaction: [Function: bound ],
estimateGas: [Function: bound ],
getData: [Function: bound ],
string: [Circular] },
allEvents: [Function: bound ] },
sayHello:
{ [Function]
call: [Function],
sendTransaction: [Function],
request: [Function: bound ],
estimateGas: [Function] },
echo:
{ [Function]
call: [Function],
sendTransaction: [Function],
request: [Function: bound ],
estimateGas: [Function] },
sendTransaction: [Function],
send: [Function],
allEvents: [Function: bound ],
address: '0xb49e482f7b9201d5305e2d6a1bf1bad3d4c05540',
transactionHash: null }
truffle(development)> contract.echo.call("HelloWorld")
'HelloWorld'
truffle(development)> contract.echo("HelloWorld")
'HelloWorld'
echo
方法确实将我们输入的内容回传了。同时因为声明了 constant,我们不需要直接调用call()
方法,truffle
会自动选用call
来呼叫。
另一点需要注意的,是这次如果还是用truffle migrate
命令,我们会得到如下信息:
$ truffle migrate
Using network 'development'.
Network up to date.
Truffle
会告诉你现在网络上的合约都已是最新的,但事实上刚刚程序中新增的方法并没有更新到内存块链上。要更新内存块链上已部署的程序,需要改写migrations
中的脚本,但现在还不到介绍migration
的时候。还好我们开发用的内存块链是怎么修改都没关系的ganache-cli
,可以使用truffle migrate --reset
命令直接重新在ganache-cli
上部署一次。
总结
这篇文章非常简单,通过这篇文章,你将掌握如何配置开发环境、如何创建新项目、如何编译、如何部署合约以及了解整个智能合约开发的流程。