【Solidity学练系列3---高级Solidity理论】

备注

1. 智能协议的永固性

到现在为止,我们讲的Solidity和其他语言没有质的区别,它长得很像JavaScript;

但是,在有几点以太坊上的DApp跟普通的应用程序有着天壤之别。

第一个例子,在你把智能协议传上以太坊之后,它就变得不可更改,这种永固性意味着你的代码永远不能被调整或更新。

你编译的程序会一直,永久的、不可更改的,存在以太坊上。这就是Solidity代码的安全性如此重要的一个原因。如果你的智能协议有任何漏洞,即使你发现了也无法补救,你只能让你的用户们放弃这个智能协议,然后转移到一个新的修复后的合约上。

但这恰好也是智能合约的一大优势。代码说明一切。如果你去读智能合约的代码,并验证它,你会发现,一旦函数被定义下来,每一次的运行,程序都会严格遵守函数中原有的代码逻辑一丝不苟地执行,完全不用担心函数被人篡改而得到意外的结果。

外部依赖关系

在第2课中,我们将加密小猫(CryptoKitties)合约的地址硬编码到DApp中去了。有没有想过,如果加密小猫除了点问题,比方说,集体消失了会怎样?虽然这种事情几乎不可能发生,但是,如果小猫没了,我们的DApp也会随之失效—因为我们在DApp的代码中庸“硬编码”的方式指定了加密小猫的地址,如果这个根据地址找不到小猫,我们的僵尸也就吃不到小猫了,而按照前面的叙述,我们却没法修改合约去应对这个变化!

因此,我们不能硬编码,而要采用“函数”,以便于DApp的关键部分可以以参数形式修改;

比方说,我们不再一开始就把猎物抵制给写入代码,而是写个函数setKittyContractAddress,运行时在设定猎物的地址,这样我们就可以随时去锁定新的猎物,也不用担心加密小猫集体消失了;

实战演习

请修改第2课的代码使得可以通过程序更改CryptoKitty合约地址。

  1. 删除采用硬编码 方式的 ckAddress 代码行。
  2. 之前创建 kittyContract 变量的那行代码,修改为对 kittyContract 变量的声明 – 暂时不给它指定具体的实例。
  3. 创建名为 setKittyContractAddress 的函数, 它带一个参数 _address(address类型), 可见性设为external。
  4. 在函数内部,添加一行代码,将 kittyContract 变量设置为返回值:KittyInterface(_address)。
  // 1. 移除这一行:
  //address ckAddress = 0x06012c8cf97BEaD5deAe237070F9587f8E7A266d;
  // 2. 只声明变量:
  //KittyInterface kittyContract = KittyInterface(ckAddress);
	KittyInterface kittyContract;

  // 3. 增加 setKittyContractAddress 方法
  	function setKittyContractAddress(address _address) external {
		kittyContract = KittyInterface(_address);
	}

2. Ownable Contracts

上一章中,您有没有发现任何安全漏洞呢?

呀!setKittyContractAddress可见性居然申明为“外部的” external,岂不是任何人都可以调用它! 也就是说,人和调用该函数的人都可以更改CryptoKitties合约的地址,使得其他人都没法在运行我们的程序了;

我们确实是希望这个地址能够在合约中修改,但我可没说让每个人去改它啊。

要对付这样的情况,通常的做法只指定合约的“所有权”。就是说,给它制定一个主人,只有主人对它享有特权。

Openzepplin库Ownalbe合约

下面是一个Ownable合约的例子:来自**_OpenZeppelin_Solidity*库的Ownable合约。OpenZeppelin是主打安保和社区审查的智能合约库,你可以在自己的DApp中引用。

//Ownable合约有一个owner地址,并提供了基本的授权控制功能,这简化了“用户权限”的实现。
contract Ownable {
	address public owner;
	event OwnershpTransferred(address indexed previousOwner, address indexed newOwner);

	//Ownable构造函数将合同的原始“所有者”设置为发送方帐户
	function Ownable() public {
		owner = msg.sender;
	}

	//如果不是owner地址则抛出异常
	modifier onlyOwner() {
		require(msg.sender == owner);
		_;
	}

	//允许当前的所有者将合同的控制权转移给新的所有者
	function transferOwnership(address newOwner) public onlyOwner {
		require(newOwner != address(0));
		OwnershipTransferred(owner,newOwner);
		owner = newOwner;
	}
}
  • 构造函数: function Ownable() 是一个 constructor (构造函数), 构造函数不是必须的,它与合约同名,构造函数一生中惟一的一次执行,就是在合约最初被创建的时候;
  • 函数修饰符:modifier onlyOwner() 修饰符跟函数很类似,不过是用来修饰其他已有函数用的, 在其他语句执行前,为它检查下先验条件。在这个例子中,我们就可恶意下个修饰符onlyOwner检查下调用者,确保只有合约的主人才能运行本函数。

所以Owner合约基本都会这么干:

  1. 合约创建,构造函数线性,将其owner设置为msg.sender(其部署者);
  2. 为它加上一个修饰符onlyOwner,它会限制陌生人的访问,将访问某些函数的权限锁定在owner上;
  3. 允许合约所有权转让给他人;

onluOwner简直人见人爱,大多数人开发自己的Solidity DApps,都是从复制/粘贴Ownable开始的,从它再继承出子类,并在之上进行功能开发;

实战演习

首先,将 Ownable 合约的代码复制一份到新文件 ownable.sol 中。 接下来,创建一个 ZombieFactory,继承 Ownable。

  1. 在程序中导入 ownable.sol 的内容。 如果您不记得怎么做了,参考下 zombiefeeding.sol。
  2. 修改 ZombieFactory 合约, 让它继承自 Ownable。 如果您不记得怎么做了,看看 zombiefeeding.sol。

                
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值