一、前言

看了一些区块链的教程,论文,在网上刚刚找到了一个项目实战,CryptoZombies。


二、 永久存储变量(Storage)和 临时存储变量(Memory)

1、讲解

我们在代码中会经常需要存储变量,有些变量是我们永久存储的,有些则是临时存储的,在solidity中,也是如此。solidity有两种存储变量:

1.storage 变量是永久存储在区块链中的变量

2.memory 变量是临时存储在区块链中的变量,当外部函数对某合约调用完成时,内存型变量即被移除。

当然,大多数情况下,我们很少会用到,solidity会自动处理,状态变量(在函数外声明的变量)默认为storage类型的变量,即永久存储,状态变量会永久写入区块链。在函数内部声明的变量,是memory类型的变量,函数调用结束就会消失。

除了系统默认之外,我们也可以自己手动声明,一般常用于我们处理函数内部的结构体structs和数组arrays

例如下面的示例。

contract SandwichFactory {
struct Sandwich {
string name;
string status;
}

Sandwich[] sandwiches;

function eatSandwich(uint _index) public {
// Sandwich mySandwich = sandwiches[_index];
}
}

上面添加注释的这句话,看上去很直接,不过 Solidity 会给出警告 ,应该明确在这里定义 `storage` 或者 `memory`。 正确写法如下:

contract SandwichFactory {
struct Sandwich {
string name;
string status;
}

Sandwich[] sandwiches;

function eatSandwich(uint _index) public {
// Sandwich mySandwich = sandwiches[_index];

Sandwich storage mySandwich = sandwiches[_index];
mySandwich.status = "Eaten!";

}
}

其中 `mySandwich` 是指向 `sandwiches[_index]`的指针 ,这将永久把 `sandwiches[_index]` 变为区块链上的存储。

如果你只想要一个副本,可以这样写:

contract SandwichFactory {
struct Sandwich {
string name;
string status;
}

Sandwich[] sandwiches;

function eatSandwich(uint _index) public {
Sandwich memory anotherSandwich = sandwiches[_index + 1];

anotherSandwich.status = "Eaten!";

}
}

这样 `anotherSandwich` 就仅仅是一个内存里的副本了 ,后续操作也仅仅修改临时变量,对 `sandwiches[_index + 1]` 没有任何影响。

如果想把副本的改动保存回区块链存储,可以这样做:

contract SandwichFactory {
struct Sandwich {
string name;
string status;
}

Sandwich[] sandwiches;

function eatSandwich(uint _index) public {

Sandwich memory anotherSandwich = sandwiches[_index + 1];

anotherSandwich.status = "Eaten!";

sandwiches[_index + 1] = anotherSandwich;

}
}

所以我们在具体使用过程中,也需要考虑使用storge还是memory。

2、实战1

1.要求

当一个僵尸猎食其他生物体时,它自身的DNA将与猎物生物的DNA结合在一起,形成一个新的僵尸DNA。

1.创建一个名为 ​​feedAndMultiply​​​ 的函数。 使用两个参数:​​_zombieId​​​( ​​uint​​​类型 )和​​_targetDna​​​ (也是 ​​uint​​​ 类型)。设置属性为 ​​public​​ 的。

2.我们不希望别人用我们的僵尸去捕猎。 首先,我们确保对自己僵尸的所有权。 通过添加一个​​require​​​ 语句来确保 ​​msg.sender​​​ 只能是这个僵尸的主人(类似于我们在 ​​createRandomZombie​​ 函数中做过的那样)。

3.为了获取这个僵尸的DNA,我们的函数需要声明一个名为 ​​myZombie​​​ 数据类型为​​Zombie​​​的本地变量(这是一个 ​​storage​​​ 型的指针)。 将其值设定为在 ​​zombies​​​ 数组中索引为​​_zombieId​​所指向的值。

到目前为止,包括函数结束符 ​​}​​ 的那一行, 总共4行代码。

2.代码

pragma solidity >=0.5.0 <0.6.0;

import "./zombiefactory.sol";

contract ZombieFeeding is ZombieFactory {

// Start here
function feedAndMultiply(uint _zombieId, uint _targetDna) public {
require(msg.sender == zombieToOwner[_zombieId]);
Zombie storage myZombie = zombies[_zombieId];
}

}

 

3、实战2

1.要求

1.首先确保 ​​_targetDna​​​ 不长于16位。要做到这一点,设置 ​​_targetDna​​​ 为 ​​_targetDna % dnaModulus​​ ,并且只取其最后16位数字。

2.接下来为函数声明一个名叫 ​​newDna​​​ 的 ​​uint​​​类型的变量,并将其值设置为 ​​myZombie​​​的 DNA 和 ​​_targetDna​​ 的平均值(如上例所示)。

注意:可以用 myZombie.name 或 ​myZombie.dna​ 访问 ​myZombie​ 的属性

3.一旦计算出新的DNA,再调用 ​​_createZombie​​​ 就可以生成新的僵尸了。如果忘记调用这个函数所需要的参数,可以查看 ​​zombiefactory.sol​​​ 选项卡。请注意,需要先给它命名,把新的僵尸的名字设为​​NoName​​ - 也可以编写一个函数来更改僵尸的名字。

2.代码

pragma solidity >=0.5.0 <0.6.0;

import "./zombiefactory.sol";

contract ZombieFeeding is ZombieFactory {

function feedAndMultiply(uint _zombieId, uint _targetDna) public {
require(msg.sender == zombieToOwner[_zombieId]);
Zombie storage myZombie = zombies[_zombieId];
// start here
_targetDna = _targetDna%dnaModulus;
uint newDna = (myZombie.dna + _targetDna) / 2;
_createZombie("NoName", newDna);
}

}