因为一个并不周知的 issue,geth 客户端将不再提供 solc 编译相关功能。我们必须借助外部编译器,比如 solc/remix。

所谓 Contract,只是 Martin fowler 的书里面经常提到的一个富血的类型罢了。

注意,要用高版本的 npm,来安装 solc:

1
npm install -g solc

智能合约代码:

1
2
3
4
5
6
7
8
pragma solidity ^0.4.0;
contract TestContract
{
function multiply(uint a, uint b) returns (uint)
{
return a * b;
}
}

用 solcjs 来编译代码:

1
2
solcjs --bin  testContract.sol
solcjs --abi testContract.sol

它会产生 testContract_sol_TestContract.bin 和 testContract_sol_TestContract.abi。结尾应该是 Contract 的类型。

使用以下命令先解锁账户:

1
2
3
4
5
6
7
geth attach http://localhost:8545

#在 geth 实例运行期间永久解锁某个账户
personal.unlockAccount(eth.accounts[0], "123456", 0)

# 或者考虑:
geth --unlock bbb --password 123456

在控制台里使用以下命令生成新的合约:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 注意这个 0x 要手动加上去
var code = '0x6060604052341561000f57600080fd5b336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555061038a8061005e6000396000f30060606040526004361061006d576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306661abd14610072578063771602f71461009b5780638da5cb5b146100db578063a87d942c14610130578063e752be4e14610159575b600080fd5b341561007d57600080fd5b610085610199565b6040518082815260200191505060405180910390f35b34156100a657600080fd5b6100c5600480803590602001909190803590602001909190505061019f565b6040518082815260200191505060405180910390f35b34156100e657600080fd5b6100ee61027d565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b341561013b57600080fd5b6101436102a2565b6040518082815260200191505060405180910390f35b341561016457600080fd5b6101836004808035906020019091908035906020019091905050610351565b6040518082815260200191505060405180910390f35b60015481565b6000806001600081548092919060010191905055506101be8484610351565b90506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167fbcc141744793f7e93083ceb1e4a61664d4a6428dadf6db4d2e8b1a162d0582b233868685604051808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200184815260200183815260200182815260200194505050505060405180910390a28091505092915050565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167fd54b813e36aaac7612686fa27ef81917785024960e408194ccaeb6b17926cecc33600154604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018281526020019250505060405180910390a2600154905090565b60008183019050929150505600a165627a7a72305820d297e7e10c1b3d3f05cad089467287d5315e1957fe323040bcdea71f4eed65ad0029';
var abi = [{"constant":true,"inputs":[],"name":"count","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"a","type":"uint256"},{"name":"b","type":"uint256"}],"name":"add","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"getCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"a","type":"uint256"},{"name":"b","type":"uint256"}],"name":"realAdd","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"pure","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":false,"name":"transactionSender","type":"address"},{"indexed":false,"name":"a","type":"uint256"},{"indexed":false,"name":"b","type":"uint256"},{"indexed":false,"name":"result","type":"uint256"}],"name":"AddEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":false,"name":"transactionSender","type":"address"},{"indexed":false,"name":"result","type":"uint256"}],"name":"CountEvent","type":"event"}];
myContract = eth.contract(abi)
contract = myContract.new({from:eth.accounts[0],data:code,gas:1000000})#部署

INFO [09-12|08:05:19] Submitted contract creation fullhash=0x0a7dfa9cac7ef836a72ed1d5bbfa65c0220347cde4efb067a0b03b15fb70bce1 contract=0x7cbe4019e993f9922b8233502d94890099ee59e6
{
abi: [{
constant: false,
inputs: [{...}, {...}],
name: "multiply",
outputs: [{...}],
payable: false,
stateMutability: "nonpayable",
type: "function"
}],
address: undefined,
transactionHash: "0x0a7dfa9cac7ef836a72ed1d5bbfa65c0220347cde4efb067a0b03b15fb70bce1"
}

注意看,address 还没有被 transaction 写入确认。如果挖矿了,最终它会被部署上去。

用两种方式来调用只能合约:

1
2
3
4
contract.multiply.sendTransaction(2, 4, {from:eth.accounts[0]})
或者
contract.multiply.call(2,4)
# 注意,有参数的函数必须通过 call 来调用,无参数的函数应该可以直接通过 multiply()调用

sendTransaction 的文档在这里。

查看当前合约变量的地址:

1
2
contract.address
"0x46a0efeda982381e8a6d75b9b3d25cabcd6b16e9"

通过地址反推代码:

1
2
eth.getCode(contract.address)
"0x606060405260043610603f576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063165c4a16146044575b600080fd5b3415604e57600080fd5b606b60048080359060200190919080359060200190919050506081565b6040518082815260200191505060405180910390f35b60008183029050929150505600a165627a7a72305820870c2d8ed1031cc2e944cdf40f350758d70db55177e72ef7e9718ea2af0273540029"

用一个典型的方式来在其他节点定位到这个合约

1
2
var contract = eth.contract(ABI).at(Address);
var contract = eth.contract([{"constant":false,"inputs":[{"name":"a","type":"uint256"},{"name":"b","type":"uint256"}],"name":"multiply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"}]).at("0x519ba7bfb5576c14b8dd3b7f267f4a56fb366bb6")