一个简单的实现对数组增删改查的智能合约

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–;
}
}
代码分析
string[] strArr; 定义了一个字符串的数组变量strArr, 且该变量没有public因此不可见
strArr.push(str); 其中的push是数组类型的两个member之一,另一个是length. 这里的push就是给该数组增加一个元素。//这里实现了对数组的新增功能
function add(string str){
strArr.push(str);
}
getStrAt(uint n) 是一个简单的读取字符串的函数,//这里实现对数组的读取功能
updateStrAt(uint n, string str) // 实现对数组的更新功能
deleteStrAt(uint index) 这个值得一说,因为solidity默认的数组类型中没有删除功能,因此此处自己写了一个删除代码,核心方法就是保证删除某项后,后面的元素依次向前,同时删除数据,同时保证数组的member length正确。
Browser solidity 部署和调用p111
将上面的代码复制黏贴到Browser-solidity上面,可以看到编译成功,并生成了可部署的web3 deploy代码。
这里我们在内存中试试看该智能合约的4个基本方法 增删改查

1. add function

p1

输入hello 01, 然后点击add按钮,我们可以看到在经过一部分gas的消耗后,该数据被执行成功!
PS:需要注意的是这里的getStrAt和add,update,delete等方法颜色不同,按上方的分类,一个是transaction,一个则是call,因此在geth上的调用方法也是不同的。
2. getStrAt function

既然已经增加了一个元素,我们就可以查看数组strArr的数值了,根据数组常识,我们只增加了一个元素,因此getStrAt[0] 应该为hello 01. 而getStrAt[1] 则应该为错误。

实际结果如下:getStrAt[0]

p2

getStrAt[1]:

p3

3. updateStrAt functoin

p4

4. deleteStrAt function

要测试这个方法,稍微麻烦点,最好是

step 1需要先add(“hello 01”), add(“hello 02”),add(“hello 03”)
step 2然后调用deleteStrAt(0);
step 3 检查是否第一项被删除,而getStrAt(0)应该等于”hello 02″, getStrAt(1)应该等于”hello 03″
然后重复step1-3,作用于deleteStrAt(1),删除中间项目,或者deleteStrAt(2)删除最后一项,还有deleteStrAt(3)/deleteStrAt(100)/deleteStrAt(-100) 等检查是否做了边界检查。
这里时间有限,就不试验了,有兴趣的同学可以自己在browser solidity上面尝试。

作为礼物,这里有另外的几个简单的智能合约,有兴趣的同学都可以多用browser-solidity来尝试

pragma solidity ^0.4.4;

contract DemoTypes {
/*uint public resultOfF;*/
event LogUint(uint value);
event LogInt (int value);

function f(uint a) returns (uint b)
{
uint result = a * 8;
/*resultOfF = result; //debug used only;*/
LogUint(result);
return result;
}

/*输入长度宽度,返回面积*/
function f2(int width, int height) returns (int square) {
if (width < 0 || height < 0) throw;
int result = width * height;
LogInt(result);
return result;
}

/*输入N,计算N的阶乘,循环实现*/
function f3(uint n) returns (uint jiecheng) {
if (n == 0) throw; uint result = 1;
for (uint i=1; i<=n; i++) {
result *= i;
}
return result;
}

/*计算从1到N的求和*/
function f4(uint n) returns (uint sum) {
if (n == 0) throw; uint result = 0;
for (uint i=0; i<=n; i++) {
result +=i;
}
return result;
}
}
下一章我们将介绍一个简化版的代币合约,敬请期待!
—————————————————————————————————————

先从最小化的Token合约开始;
pragma solidity 0.4.9;
contract Token {
function Token() {

}
}
之前有说过,solidity是类js的语言,和所有的面向对象语言一样,solidity也拥有最基本的构造函数。
上面的代码就是一个最简单化的一个Token合约,function Token() {}是该contract 的构造函数。

该合约在browser-solidity上可以执行成功,这里就不演示了。

我们给这个Token代币合约添加一个数字数组,并赋予一个初值。
pragma solidity 0.4.9;

contract Token2 {
uint[] public balancesOf;
function Token() {
balancesOf.push(100);
balancesOf.push(200);
}
}
这个合约很简单,先定义了一个public的变量balancesOf, 然后在构造阶段,就给这个变量添加两个初始值。100 & 200

p5

用户先点击Token这个构造函数,然后再输入0和1,然后点击balancesOf 可以分别看到两个数值。

p6

给该Token合约添加一个账户转账功能 transfer()
pragma solidity 0.4.9;
contract Token3 {
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;
}
}
transfer功能的代码就是把一定量_amount 从_from转移到_to;

该功能在browser solidity调试成功,如下图所示:

p7

给Token代币合约,增加一个挖矿方法 mint()
如上面的代码所示,我们可以看到,只有在构造函数的时候balancesOf才有赋值,之后的transfer无论如何操作,总量始终不变。因此我们可以增加一个挖矿方法,增加的金额默认都归纳到第一个账号中 balancesOf[0]中。

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;
}
}
通过mint() 我们就可以随意的给balancesOf[0] 增加代币了。该代码有兴趣的同学,请在browser-solidity上实验。

注意以上代码只用于学习使用,很多场景还未考虑,例如transfer中没有考虑_amount>当前账户余额的场景。不可用于商业化应用。

本文初步介绍了Token的核心功能,包括构造,挖矿、转账等。
但是,我们知道以太坊的核心是账户体系和智能合约,真正有意义的是在各个账户之间可以流通的代币合约。有了这些代币,我们才能用于众筹、众包等各种商业应用的实现。

下篇将介绍一个包含账户的代币合约
——————————————————————————————————————————

基于账户地址的Token合约
pragma solidity 0.4.9;
contract Token {
mapping (address => uint) public balancesOf;
function Token() {
balancesOf[msg.sender] = 10000;
}
}
mapping 关键词的定义

Mapping types are declared as mapping(_KeyType => _ValueType). Here _KeyType can be almost any type except for a mapping, a dynamically sized array, a contract, an enum and a struct. _ValueType can actually be any type, including mappings.
Mappings can be seen as hashtables which are virtually initialized such that every possible key exists and is mapped to a value whose byte-representation is all zeros: a type’s default value. 引用自here

address的定义:

address: Holds a 20 byte value (size of an Ethereum address). Address types also have members and serve as base for all contracts.
members of addresses: balance and send
引用自here
这里的address简单地说就是geth中的账户的地址(公钥)
因此此处的代码mapping (address => uint) balancesOf 代表定义了一个 key为address类型, value为uint类型的hashtable,名字为 balancesOf.

所以任何在区块链私链上的账户都可以通过balancesOf[address] 来查询他们的代币数量。

msg 的定义

msg就是指谁调用该智能合约时候发来的信息,之前说过智能合约也是一个账户。因此在以太坊中调用智能合约从底层看来就是一个账户给智能合约账户发送了一个transaction,里面包含了谁发送的,发送了多少以太币,发送了多少gas
下面是msg的一些变量

msg.data (bytes): complete calldata
msg.gas (uint): remaining gas
msg.sender (address): sender of the message (current call)
msg.sig (bytes4): first four bytes of the calldata (i.e. function identifier)
msg.value (uint): number of wei sent with the message 引用自here
最主要的就是msg.sender,代表那个账户调用该智能合约。msg.value

因此balancesOf[msg.sender] = 10000; 代表给msg.sender也就是创建该合约的地址赋予一个初始值 10000;
一个带transfer & 挖矿的Token合约
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;
}
}
在上面的代码中,我们设置一个public owner, 保存下创建合约的msg.sender, 记录下创建者的地址。
在function transfer()中,我们设置了两个条件,避免转移出去的代币超过存货的情况,以及自己转移给自己的问题。
在function mint()中,我们给合约的owner增加我们定义的代币。从而让其方便的分给别人。
以上合约代码在Browser-solidity上调试通过,查看下图

p8

我们在代码中将owner = msg.sender,因此owner就是创建合约的地址。我们可以看到balancesOf[owner] 的数据为10000,和代码相同。
p9

调用方法mint(500),自动给balancesOf[owner] += 500;查看存额从10000增加到10500.
p10

调用方法 transfer(“0xccccc”,300), 可以看到owner的存额减少300,balancesOf[“0xccccc”]增加300.
p11

这个合约实现了最简化的代币功能,但在browser-solidity中我们可以看到一个很明显的问题,那就是没有和我们在第二部分中所搭建的geth中的几个以太坊账号结合起来。