此文档为个人整理学习使用,部分内容来源于网络,也期待能给你带来益处。
智能合约是存储在区块链上的一段代码,它们可以被区块链上的交易所触发,触发后,这段代码可以从区块链上读取数据或者向区块链上写入数据。
部署到区块链后,按合约地址codehash存储在区块链上。
Solidity中的智能合约和传统面向对象语言中的类很相像,因此有构造函数,有继承,有变量,有function,也有抽象类等等传统概念。
由Solidity所写的智能合约,经过编译后就会由EVM来部署执行。
Solidity语言是一种类JS的语言,因此很多编码规范和JS很相似。
Solidity编码风格
https://solidity.readthedocs.io/en/develop/style-guide.html
Browser-solidity
https://ethereum.github.io/browser-solidity
Browser-solidity Github
https://github.com/ethereum/browser-solidity
web3.js.API
https://github.com/ethereum/wiki/wiki/JavaScript-API
注意合约部署的时候,以太坊的私有链必须处在挖矿进行的状态,否则合约部署将不会生效
先解锁帐号
> personal.unlockAccount(acc0)
Unlock account 0x54b865714068f5f03574ace39a1f3279c4e83e2c
Passphrase:
true
复制合约内容到geth命令行
var a_demotypesContract = web3.eth.contract([{"constant":false,"inputs":[{"name":"a","type":"uint256"}],"name":"f","outputs":[{"name":"b","type":"uint256"}],"payable":false,"type":"function"}]);
var a_demotypes = a_demotypesContract.new(
{
from: web3.eth.accounts[0],
data: '0x6060604052341561000c57fe5b5b60ab8061001b6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063b3de648b14603a575bfe5b3415604157fe5b60556004808035906020019091905050606b565b6040518082815260200191505060405180910390f35b600060006008830290508091505b509190505600a165627a7a7230582010decdc0b0a43b565814fe904eae2544665457d6353c7d906fc2c43c81c867e40029',
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 a_demotypesContract = web3.eth.contract([{"constant":false,"inputs":[{"name":"a","type":"uint256"}],"name":"f","outputs":[{"name":"b","type":"uint256"}],"payable":false,"type":"function"}]);
undefined
> var a_demotypes = a_demotypesContract.new(
... {
...... from: web3.eth.accounts[0],
...... data: '0x6060604052341561000c57fe5b5b60ab8061001b6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063b3de648b14603a575bfe5b3415604157fe5b60556004808035906020019091905050606b565b6040518082815260200191505060405180910390f35b600060006008830290508091505b509190505600a165627a7a7230582010decdc0b0a43b565814fe904eae2544665457d6353c7d906fc2c43c81c867e40029',
...... gas: '4700000'
...... }, function (e, contract){
...... console.log(e, contract);
...... if (typeof contract.address !== 'undefined') {
......... console.log('Contract mined! address: ' + contract.address + ' transactionHash: ' + contract.transactionHash);
......... }
...... })
null [object Object]
undefined
> null [object Object]
Contract mined! address: 0xf56d3164a66552e6208e972bc2f76bfcf0133cdf transactionHash: 0x026cf57f0841861f075aa6475a70a78787872948864b11319b873367fd8c4688
输入合约部署的实例a_demotypes,可以看到详情。
> a_demotypes
{
abi: [{
constant: false,
inputs: [{...}],
name: "f",
outputs: [{...}],
payable: false,
type: "function"
}],
address: "0xf56d3164a66552e6208e972bc2f76bfcf0133cdf",
transactionHash: "0x026cf57f0841861f075aa6475a70a78787872948864b11319b873367fd8c4688",
allEvents: function(),
f: function()
}
调用合约方法:
> a_demotypes.f.call(100)
800
> a_demotypes.f.call(8.8)
64
如果挖矿没有开始,布署合约时会提示undefined
在网上找到一个最简单的众筹合约,试试:
pragma solidity 0.4.9;
contract NBICO {
address public organizer;
event FundTransfer(address backer, uint amount, bool isContribution);
function NBICO() {
organizer = msg.sender;
}
function destory() {
if(msg.sender == organizer) {
suicide(organizer);
}
}
}
在Browser-solidity中得到布署代码,粘贴到命令行:
var nbicoContract = web3.eth.contract([{"constant":true,"inputs":[],"name":"organizer","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function","stateMutability":"view"},{"constant":false,"inputs":[],"name":"destory","outputs":[],"payable":false,"type":"function","stateMutability":"nonpayable"},{"inputs":[],"payable":false,"type":"constructor","stateMutability":"nonpayable"},{"anonymous":false,"inputs":[{"indexed":false,"name":"backer","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"isContribution","type":"bool"}],"name":"FundTransfer","type":"event"}]);
var nbico = nbicoContract.new(
{
from: web3.eth.accounts[0],
data: '0x6060604052341561000c57fe5b5b33600060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505b5b6101908061005f6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806361203265146100465780636bdebcc914610098575bfe5b341561004e57fe5b6100566100aa565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156100a057fe5b6100a86100d0565b005b600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561016157600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16ff5b5b5600a165627a7a72305820b3bdd88f2f26c2b101929d343f50df91008cf31b742107b471701767ec53b0630029',
gas: '4700000'
}, function (e, contract){
console.log(e, contract);
if (typeof contract.address !== 'undefined') {
console.log('Contract mined! address: ' + contract.address + ' transactionHash: ' + contract.transactionHash);
}
})
看起来已经布署成功了,如下:
> miner.start()
null
> null [object Object]
Contract mined! address: 0x9bf83a6139b77ff94464907c7923d9565f72d5d3 transactionHash: 0x1a7c9feff38e6ddc34cc9dacdb5c89d1386e153398ce256f6550ad53d6d95dbf
> nbico
{
abi: [{
constant: true,
inputs: [],
name: "organizer",
outputs: [{...}],
payable: false,
stateMutability: "view",
type: "function"
}, {
constant: false,
inputs: [],
name: "destory",
outputs: [],
payable: false,
stateMutability: "nonpayable",
type: "function"
}, {
inputs: [],
payable: false,
stateMutability: "nonpayable",
type: "constructor"
}, {
anonymous: false,
inputs: [{...}, {...}, {...}],
name: "FundTransfer",
type: "event"
}],
address: "0x9bf83a6139b77ff94464907c7923d9565f72d5d3",
transactionHash: "0x1a7c9feff38e6ddc34cc9dacdb5c89d1386e153398ce256f6550ad53d6d95dbf",
FundTransfer: function(),
allEvents: function(),
destory: function(),
organizer: function()
}
也可以给这个合约地址转帐,执行如下:
> nbico = "0x9bf83a6139b77ff94464907c7923d9565f72d5d3"
"0x9bf83a6139b77ff94464907c7923d9565f72d5d3"
> personal.unlockAccount(acc0)
Unlock account 0x54b865714068f5f03574ace39a1f3279c4e83e2c
Passphrase:
true
> eth.sendTransaction({from:acc0, to:nbico, value:100})
"0x8d880ae0e5a38d4dc4340f6d610ac071b4cb081a1d3f4ea2584a80c92f284348"
pragma solidity 0.4.9;
/*数组类型Demo*/
contract DemoTypes303 {
/*String数组例子*/
string[] strArr;
function add(string str){
strArr.push(str);
}
function getStrAt(uint n) constant returns (string s){
string tmp = strArr[n];
return tmp;
}
function updateStrAt(uint n, string str) {
strArr[n] = str;
}
function deleteStrAt(uint index) {
uint len = strArr.length;
if (index >= len) return;
for (uint i = index; i<len-1; i++) {
strArr[i] = strArr[i+1];
}
delete strArr[len-1];
strArr.length--;
}
}
pragma solidity 0.4.9;
contract Token4 {
uint[] public balancesOf;
function Token() {
balancesOf.push(100);
balancesOf.push(200);
}
function transfer(uint _from, uint _to, uint _amount) {
balancesOf[_from] -= _amount;
balancesOf[_to] += _amount;
}
function mint(uint value) {
balancesOf[0] += value;
}
}
pragma solidity 0.4.9;
contract Token {
mapping (address => uint) public balancesOf;
address public owner;
function Token() {
owner = msg.sender;
balancesOf[msg.sender] = 10000;
}
function transfer(address _to, uint _value) {
if (balancesOf[msg.sender] < _value) throw; //避免转移出去的代币超过当前的存货
if (balancesOf[_to] + _value < balancesOf[_to]) throw; //避免自己调用自己,或者递归调用
balancesOf[msg.sender] -= _value;
balancesOf[_to] += _value;
}
function mint(uint _amount) {
balancesOf[owner] += _amount;
}
}
--
contract Conference {
address public organizer;
mapping (address => uint) public registrantsPaid;
uint public numRegistrants;
uint public quota;
event Deposit(address _from, uint _amount); // so you can log these events
event Refund(address _to, uint _amount);
function Conference() { // Constructor
organizer = msg.sender;
quota = 500;
numRegistrants = 0;
}
function buyTicket() public returns (bool success) {
if (numRegistrants >= quota) { return false; }
registrantsPaid[msg.sender] = msg.value;
numRegistrants++;
Deposit(msg.sender, msg.value);
return true;
}
function changeQuota(uint newquota) public {
if (msg.sender != organizer) { return; }
quota = newquota;
}
function refundTicket(address recipient, uint amount) public {
if (msg.sender != organizer) { return; }
if (registrantsPaid[recipient] == amount) {
address myAddress = this;
if (myAddress.balance >= amount) {
recipient.send(amount);
registrantsPaid[recipient] = 0;
numRegistrants--;
Refund(recipient, amount);
}
}
}
function destroy() { // so funds not locked in contract forever
if (msg.sender == organizer) {
suicide(organizer); // send funds to organizer
}
}
}
代码解释:
1、address public organizer;
存放组织者的钱包地址,在构造函数中被以下语句
organizer = msg.sender;
赋值,也叫做“owner”。
2、对一些操作可以判断是否组织者,如下:
if (msg.sender != organizer)
3、转给合约的资金会保存在合约地址中,这些资金通过destroy函数被释放给了构造函数中的组织者地址。如下:
suicide(organizer);
合约布署成功后,合约的构造函数也会被调用,可以取到合约组织者地址了,如下:
conference.organizer()
"0x54b865714068f5f03574ace39a1f3279c4e83e2c"
acc0
"0x54b865714068f5f03574ace39a1f3279c4e83e2c"
public变量并不是直接使用,在abi中是function()类型,使用时也得带上括号,如下:
conference.quota()
500
conference.organizer()
"0x54b865714068f5f03574ace39a1f3279c4e83e2c"
只从区块链取数据,而不修改数据,在函数后加上.call()的方式
conference.quota.call()
888
函数要修改合约内部的数据,必须向区块链发送交易,在函数后面加上.sendTransaction()来指明这是一笔交易,同时要指定{from:}参数,也就是需要矿工把改变存储到链上去,如下:
personal.unlockAccount(acc0);
Unlock account 0x54b865714068f5f03574ace39a1f3279c4e83e2c
Passphrase:
true
conference.changeQuota.sendTransaction(888, {from:acc0});
"0xf45851fc09fdac6562100b1dc9ea1a96759af5510f8be33b23335f42ac7eb323"
conference.buyTicket.sendTransaction({from:acc1, value:18})
"0x1e69ae29695f2fa9345f87a09fb71c7738cefbfbd3697dd36fc3dd3bff75358e"
conference.registrantsPaid.call(acc1)
18
conference.registrantsPaid.call(acc0)
0
conference.refundTicket.sendTransaction(acc1, 18, {from:acc0})
"0xb98eff4290bd515875412df169a7e33325c7739148350e3a07ad03b899de0f46"
conference.numRegistrants()
0
conference.destroy.sendTransaction({from:acc0})
"0x02fce40c5c7d498514bccca3e5e2b403147602be87e324d021c3c6a79dc1ee97"
合约上的以太币数量也可以通过eth.getBalance()获得,如下:
eth.getBalance(conference.address)
18
合约布署后会创建一个合约帐户,合约帐户里保存着智能合约的可执行字节码,并且有存储空间,合约能对合约帐户的空间写入信息。而abi是一个接口结构,利用abiDefinition可以创建调用该合约的结构。abi应该由合约所有方自己保存和提供。
黄皮书中费用存储256bit的字需要20kgas,1KB是640kgas。如果1Gas=50Gwei,则1KB存储花费0.032ETH,1GB存储需要32000ETH。
也可以用类似的合约进行测试:
contract test {
bytes data;
function addData(bytes _data) public {
data = _data;
}
}
有人测试数据
所有的复杂类型如数组(arrays)和数据结构(struct)有一个额外属性:数据的存储位置,可为memory和storage。
根据上下文不同,大多数时候数据位置有默认值,也可指定关键字memory和storage修改它。
函数参数(包含返回的参数)默认是memory。局部复杂类型变量(local variables)和状态变量(state variables)默认是storage。
合约destroy()了,还能继续调用?
答:是可以继续调用的,这个destroy()其实只是一个函数,把函数名改为nothing(),就容易理解了。
geth中可以直接模拟假的地址,实际网络呢?
答:这个私链、测试、公网应该是一样的,毕竟变量地址是可以随便设定的,但是只能读取数据,因为一旦交易就需要密码或者私钥了。
一种方法是提供Event,在外部接收事件并日志;
另外可以设置pulic测试变量,函数执行结果后修改,直接用Mist查看。
这个比大小的智能合约是比较完整的了
https://etheroll.com
http://www.8btc.com/browser-solidity-token
http://ethfans.org/posts/101-noob-intro
http://blog.csdn.net/misshqzzz/article/details/77834856
https://hackernoon.com/ether-purchase-power-df40a38c5a2f
https://hackernoon.com/costs-of-a-real-world-ethereum-contract-2033511b3214
https://github.com/ethereum/wiki/wiki/Standardized_Contract_APIs
http://solidity.readthedocs.io/en/develop/introduction-to-smart-contracts.html
拍卖的例子
http://blog.csdn.net/wo541075754/article/details/53256504
***************************************
老鱼,从事互联网技术十余年,知名上市公司CTO;炒股十余年,从亏损几十万到略有盈利;顺应创业大潮多次创业,在传统行业和手游行业有所获;研习佛学易理,性格淡泊清净!
欢迎有兴趣的朋友加入微信群和电报群,一起学习探讨,一起实现咱们的小目标!