Scry.info社区开发者技术分享——智能合约


智能合约(smart contract)这个术语至少可以追溯到1995年,是由多产的跨领域法律学者尼克·萨博(Nick Szabo)提出来的。他在发表在自己的网站的几篇文章中提到了智能合约的理念。


“一个智能合约是一套以数字形式定义的承诺(promises),包括合约参与方可以在上面执行这些承诺的协议。”


基于区块链的智能合约构建及执行由多方用户共同参与制订一份智能合约,合约通过P2P网络扩散到每个节点并存入内存中,等待共识时间到了,验证节点会把所有合约打包成一个合约集合(set),并算出这个合约集合的hash值,组装成一个区块机构扩散到全网,全网各节点通过多轮的对比验证,最终达成共识合约成立。当触发智能合约条件后,进入新一轮的验证,验证达成共识,区块链构建的智能合约自动执行。


以下代码演示了一个简单的智能合约:

pragma solidity ^0.4.4;

// This is just a simple example of a coin-like contract.

// It is not standards compatible and cannot be expected to talk to other

// coin/token contracts. If you want to create a standards-compliant

// token, see: https://github.com/ConsenSys/Tokens. Cheers!

 

contract MetaCoin {

    mapping (address => uint) balances;

    event Transfer(address indexed _from, address indexed _to, uint256 _value);

 

    function MetaCoin() {

  // The initial number of creators is 10000

        balances[msg.sender] = 10000;

    }

 

 /** Send token to the specified address */

    function sendCoin(address receiver, uint amount) returns(bool sufficient) {

        if(balances[msg.sender] < amount) return false; // The sender's balance is insufficient to return false

        balances[msg.sender] -= amount; // Deduct the number of tokens from the sender

        balances[receiver] += amount;    // Increase the number of recipient tokens 

        Transfer(msg.sender, receiver, amount); // Throw event

        return true;

    }

 

/** Gets the balance of the specified address */

    function getBalance(address addr) returns(uint) {

        return balances[addr];

    }

}


通过solcsolcjs编译该合约,得到相应的abibin

geth控制台输入(或将此内容存为meta.js文件,然后使用loadScript(meta.js)来加载):

personal.unlockAccount(eth.accounts[0],"yan",99); // 这里应该替换为自己的账号密码.

var abi = '[{"constant":true,"inputs":[],"name":"init2","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"getInit2","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"getInit1","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"receiver","type":"address"},{"name":"amount","type":"uint256"}],"name":"sendCoin","outputs":[{"name":"sufficient","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"init1","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"addr","type":"address"}],"name":"getBalance","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"inputs":[],"payable":false,"type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_from","type":"address"},{"indexed":true,"name":"_to","type":"address"},{"indexed":false,"name":"_value","type":"uint256"}],"name":"Transfer","type":"event"}]';

var bytecode = '0x6060604052341561000f57600080fd5b5b6127106000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555032600260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505b5b6104e6806100e86000396000f30060606040523615610076576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063069489a21461007b5780637b1b0bb0146100d05780638a3959361461012557806390b98a111461017a578063f2eb3e34146101d4578063f8b2cb4f14610229575b600080fd5b341561008657600080fd5b61008e610276565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156100db57600080fd5b6100e361029c565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b341561013057600080fd5b6101386102c7565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b341561018557600080fd5b6101ba600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919080359060200190919050506102f2565b604051808215151515815260200191505060405180910390f35b34156101df57600080fd5b6101e761044b565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b341561023457600080fd5b610260600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610471565b6040518082815260200191505060405180910390f35b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690505b90565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690505b90565b6000816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410156103435760009050610445565b816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540392505081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3600190505b92915050565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490505b9190505600a165627a7a72305820da8820ab8e6ee92312c9f4dae7f46b7aa4b70df7df8cfab644ba330a2c25d5540029';

 var abiArray = JSON.parse(abi)

 var metaCoin = eth.contract(abiArray);//(abi);

 var txDeploy = {from:eth.accounts[0], data: bytecode, gas: 4700000};

 var myContractPartialInstance = metaCoin.new(txDeploy, function(err, ctract){

  console.log(">>>>>>deploy end:>> ",err,ctract.address);

  if (!err && ctract.address){

   console.log("deploy success:",ctract.address);

} else {

   console.log("deploy failed");

  }

 });


再启动miner.start()既可看到部署结果,下次使用时通过

var _contract = eth.contract(abiArray);

var _contractInst = _contract.at(ctractAddress); 

得到合约实例.此时就可以通过contractInst来调用其方法.但要注意的是:

contractInst .getBalance.call(addr) // 没有交易的行为使用call

contractInst.sendCoin.sendTransaction(addr, 100, {from:eth.accounts[0]}) // 有交易的行为使用sendTransaction

因为sendCoin函数内在修改数据存储,是一种交易行为.

具体参见Solidity官方文档:

http://solidity.readthedocs.io/en/develop/


以上合约不是标准的ERC20代币合约,ERC20代币合约包含几个方法和属性:

contract  ERC20 {

string public constant name = "Token Name";

string public constant symbol = "SYM";

uint8 public constant decimals = 1;

 

     function totalSupply() constant returns (uint totalSupply);

     function balanceOf(address _owner) constant returns (uint balance);

     function transfer(address _to, uint _value) returns (bool success);

     function transferFrom(address _from, address _to, uint _value) returns (bool success);

     function approve(address _spender, uint _value) returns (bool success);

     function allowance(address _owner, address _spender) constant returns (uint remaining);

     event Transfer(address indexed _from, address indexed _to, uint _value);

     event Approval(address indexed _owner, address indexed _spender, uint _value);

 }

ERC20标准实现的合约能被钱包客户端识别,并能通过界面进行方面操作.

具体参考:

https://github.com/ethereum/eips/issues/20

https://theethereum.wiki/w/index.php/ERC20_Token_Standard


这里的decimals表示将代币总数细化到小数点后多少位(但不是真正的小数). 比如总量初始化1000, decimals1, 则实际是100个代币(100 * 10**1 = = 1000), 此时如果你想向别人发送1个代币,在钱包里不能写1, 而是要写10, 因为写1表示发送0.1,通过交易可以查看到实际发送的是0.1:


所以如果你要发行1000个代币,那么在智能合约中的初始设置时应该是total = 1000 * 10**decimals. (要乘上10decimals次方)


智能合约虽然提供较为完备的可编程能力,但其中的坑还是很多的(比如存储,变量类型转换等),不同的写法可能导致隐患甚至错误(Solidity文档中也有较为详细的介绍),所以在编写智能合约时要非常小心.

由于篇幅有限,无法深入介绍,具体请参考以上官方文档.








阅读更多

更多精彩内容