Library
在Solidity中,library是一种不同类型的合约,它没有任何存储空间,也不能拥有ether,不能被继承。 可将library视为EVM中的单例,它是一段可以从任何合约中调用的代码,无需再次部署。它的声明是这样:library C {}
library 也不能有payable function和 fallback function ,这种限制在编译时就有了。
调用库函数将使用特殊指令(DELEGATECALL),这会导致调用上下文被传递给库,就好像它是在合同中运行的代码一样。
library C {
function a() returns (address) {
return address(this);
}
}
contract A {
function a() constant returns (address) {
return C.a();
}
}
这段代码中,当方法a()被调用返回的address是A合约的,而不是library C 的。msg变量等全局变量也是一样的道理。
library如何被调用
具体的连接发生在字节码编译时,当合约 A 编译时,会给library 地址留出占位符,比如:0073__C_____________________________________630dbe671f。630dbe671f 是方法a的签名。library链接非常简单,只需将合约字节码中的所有占位符替换为区块链中已部署的库的地址即可。 一旦合约和库产生连接,它可以被部署了。
using的使用
library 可以修改与之相连的合约数据,当将storage声明作为参数传递给library调用时,所做的任何修改都将保存在合约自己的存储中。
library CounterLib {
struct Counter { uint i; }
function incremented(Counter storage self) returns (uint) {
return ++self.i;
}
}
contract CounterContract {
using CounterLib for CounterLib.Counter;
CounterLib.Counter counter;
function increment() returns (uint) {
return counter.incremented();
}
}
OpenZeppelin
OpenZeppelin是一个github上的开源库,旨在提供安全的 library 供人使用来提升自己合约的健壮性。它们统称为符合ERC20Lib 标准的库。现在对于实战,ERC20Lib是包含与管理ERC20令牌相关的所有业务逻辑的库。
- 其中的SafeMath.sol 可以保证数学运算安全性的library。什么?加减乘除也会出错?这就是上溢、下溢错误。
- 其中的TokenStorage,它包含使用token需要的所有功能,比如收发、存储。
- 这种方法的有趣之处在于ERC20Lib和SafeMathLib都只需部署一次,所有链接ERC20Lib的合约都将使用相同的安全审核代码。
代码重用
parity 钱包事件
多签名钱包的逻辑因为比较复杂,不希望每次都要重新编写, 这时要用一些底层的汇编调用。一个 function 部署时需要先 copy 到本地的栈上再执行。 但是这样copy很有风险,多签名钱包被攻击就是这样,合约是以前的合约,但是为了可以copy而不用每次部署,但是就是因为没有将原始合约的自杀函数屏蔽掉,有个闲人就把原合约干掉了,代码不复存在了,所以用户 copy 代码时得到空指针,一个 0x0 ,导致 parity 钱包的资金被锁死。
代理library
想必大家已经知道了无论是合约还是library 一旦部署就无法修改的了,但是如果我所调用得到library升级了,而想用它的最新版该怎么办呢???或是遭遇了上述悲剧怎么办呢??
当然是通过代理模式解决啦。
如图所示:前端用户接口与主合约交互,主合约如果要调用library必须通过dispatcher,而dispatcher调取合约的地址是从 dispatcher storage 这个合约中读取的,dispatcher storage相当于数据库,如果我们调一个新的library的话改变它所存的地址就可以了。
但是有些方面需要注意:
- 需要确定该库调用返回的内存大小是多少。 现在可以通过mapping将函数签名映射到它们的返回类型大小来解决。
- 鉴于delegatecall在EVM级别上的工作方式,一个合约只能和具有相同存储空间的另一合约交互。 由于library没有存储空间,必须让dispatcher也没有存储空间。 这就是为什么一个独立的dispatcherStorage来保存所有需要的数据。