solidity学习

2019-01-16  本文已影响10人  arthur25

资料整理于以太坊Dapp开发教程案例

本文以参考教程中《游戏:僵尸军团》的所有实例代码作为笔记记录

关于随机数安全性的讨论

contract ZombieFactory {

    uint dnaDigits = 16;
    uint dnaModulus = 10 ** dnaDigits;

    struct Zombie {
        string name;
        uint dna;
    }
    
    // 定义 public 数组,
    Zombie[] public zombies;

    // 如果说 contract 相当于 class,那么这里的 function 相当于 class 的方法。
    function _createZombie(string _name, uint _dna) private {
        zombies.push(Zombie(_name, _dna));
    }

}

变量

    在 Solidity 中,变量是被永久地保存在合约中的。也就是说它们被写入以太币区块链中,想象成写入一个数据库。变量有两种类型 private 和 public 的类型。变量默认是 private 类型。private 指的是其它的合约不可以从这个变量中读取数据。public 指的是其它的合约可以从这个变量中读取数据(但不能写入数据)。

函数

    Solidity 不仅变量有 private 和 public 类型之分,函数也有 private 和 public 类型之分。Solidity 定义的函数的类型默认为 public。 这就意味着任何一方 (或其它合约) 都可以调用你合约里的函数。
    显然,不是什么时候都需要这样,而且这样的合约易于受到攻击。 所以将自己的函数定义为私有是一个好的编程习惯,只有当你需要外部世界调用它时才将它设置为公共。
    记住,我们有一个规范,局部及形参我们在前面加上前缀”_”来于public或external区分。

    我们定义一个 private _createZombie 函数用于创建并保存僵尸对象。创建一个僵尸对象,就是调用事先定义好的数据结构的变量 Zombie。保存就是,把创建好的僵尸对象,push 到事先定义好的数组 zombies 中。现在调用 _createZombie 函数就可以创建僵尸对象,并将对象保存在以太坊“数据库”上了。

补充

我们有决定函数何时和被谁调用的可见性修饰符: private 意味着它只能被合约内部调用; internal 就像 private 但是也能被继承的合约调用; external 只能从合约外部调用;最后 public 可以在任何地方调用,不管是内部还是外部。

我们也有状态修饰符, 告诉我们函数如何和区块链交互: view 告诉我们运行这个函数不会更改和保存任何数据; pure 告诉我们这个函数不但不会往区块链写数据,它甚至不从区块链读取数据。这两种在被从合约外部调用的时候都不花费任何gas(但是它们在被内部其他函数调用的时候将会耗费gas)。

然后我们有了自定义的 modifiers。 对于这些修饰符我们可以自定义其对函数的约束逻辑,这些修饰符可以同时作用于一个函数定义上。

payable 修饰符

payable 方法是让 Solidity 和以太坊变得如此酷的一部分 —— 它们是一种可以接收以太的特殊函数。

先放一下。当你在调用一个普通网站服务器上的API函数的时候,你无法用你的函数传送美元——你也不能传送比特币。

但是在以太坊中, 因为钱 (以太), 数据 (事务负载), 以及合约代码本身都存在于以太坊。你可以在同时调用函数 并付钱给另外一个合约。

这就允许出现很多有趣的逻辑, 比如向一个合约要求支付一定的钱来运行一个函数
contract OnlineStore {
  function buySomething() external payable {
    // 检查以确定0.001以太发送出去来运行函数:
    require(msg.value == 0.001 ether);
    // 如果为真,一些用来向函数调用者发送数字内容的逻辑
    transferThing(msg.sender);
  }
}
在这里,msg.value 是一种可以查看向合约发送了多少以太的方法,另外 ether 是一个內建单元。

这里发生的事是,一些人会从 web3.js 调用这个函数 (从DApp的前端), 像这样 :
// 假设 `OnlineStore` 在以太坊上指向你的合约:
OnlineStore.buySomething().send(from: web3.eth.defaultAccount, value: web3.utils.toWei(0.001))
注意这个 value 字段, JavaScript 调用来指定发送多少(0.001)以太。如果把事务想象成一个信封,你发送到函数的参数就是信的内容。 添加一个 value 很像在信封里面放钱 —— 信件内容和钱同时发送给了接收者。

注意: 如果一个函数没标记为payable, 而你尝试利用上面的方法发送以太,函数将拒绝你的事务。

监听事件

cryptoZombies.events.NewZombie()
.on("data", function(event) {
  let zombie = event.returnValues;
  console.log("一个新僵尸诞生了!", zombie.zombieId, zombie.name, zombie.dna);
}).on('error', console.error);
注意这段代码将在 任何 僵尸生成的时候激发一个警告信息——而不仅仅是当前用用户的僵尸。如果我们只想对当前用户发出提醒呢?

使用 indexed

合约中:

event Transfer(address indexed _from, address indexed _to, uint256 _tokenId);
在这种情况下, 因为_from 和 _to 都是 indexed,这就意味着我们可以在前端事件监听中过滤事件
cryptoZombies.events.Transfer({ filter: { _to: userAccount } })
.on("data", function(event) {
  let data = event.returnValues;
  // 当前用户更新了一个僵尸!更新界面来显示
}).on('error', console.error);

查询过去事件

我们甚至可以用 getPastEvents 查询过去的事件,并用过滤器 fromBlock 和 toBlock 给 Solidity 一个事件日志的时间范围("block" 在这里代表以太坊区块编号):
cryptoZombies.getPastEvents("NewZombie", { fromBlock: 0, toBlock: 'latest' })
.then(function(events) {
  // events 是可以用来遍历的 `event` 对象 
  // 这段代码将返回给我们从开始以来创建的僵尸列表
});
上一篇下一篇

猜你喜欢

热点阅读