


















学习 solidity 合约多重继承时,官方文档介绍 solidity 采用 C3线性化算法 来确定多重依赖中的继承顺序。
维基百科上有很好的说明:
C3线性化
C3 linearization
下面通过实验来深入理解一下。
在 c3_notok.py 中定义如下类
class Type(type):
def __repr__(cls):
# Will make it so that repr(O) is "O" instead of "<__main__.O>",
# and the same for any subclasses of O.
return cls.__name__
class O(object, metaclass=Type): pass
class A(O): pass
class X(O, A): pass
if __name__ == '__main__':
print(X.mro())
执行会 throw error:
$ python3 c3_notok.py
TypeError: Cannot create a consistent method resolution
order (MRO) for bases O, A
根据 C3 线性化算法,计算 X 的 mro (Method Resolution Order):
L(O) := [O]
L(A) := [A] + merge(L(O), [O])
:= [A] + merge([O], [O])
:= [A, O]
L(X) := [X] + merge(L[O], L[A], [O, A])
:= [X] + merge([O], [A, O], [O, A])
可以看到在最后一步计算中, O 和 A 在 [A, O], [O, A] 两个列表中分别为 head 和 tail,算法无法继续进行下去,所以 class X(O, A) 的定义不合法。
在 c3_ok.py 中定义如下
class Type(type):
def __repr__(cls):
# Will make it so that repr(O) is "O" instead of "<__main__.O>",
# and the same for any subclasses of O.
return cls.__name__
class O(object, metaclass=Type): pass
class A(O): pass
class X(A, O): pass
if __name__ == '__main__':
print(X.mro())
执行脚本,可以看到正常的输出结果:
$ python3 c3_ok.py
[X, A, O, <class 'object'>]
根据 C3 线性化算法,计算 X 的 mro:
L(O) := [O]
L(A) := [A] + merge(L(O), [O])
:= [A] + merge([O], [O])
:= [A, O]
L(X) := [X] + merge(L[A], L[O], [A, O])
:= [X] + merge([A, O], [O], [A, O])
:= [X, A] + merge([O], [O], [O])
:= [X, A, O]
需要注意的是,在 solidity 中定义合约时,如果是多重继承,parent contracts 的顺序跟 python 中的 parent classes 的顺序是相反的,即 farthest parent contract 应该书写得离当前定义的合约最近,如下:
contract O {}
contract A is O {}
contract X is O, A {}
在 remix 中编写如下合约
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.0 <0.9.0;
import "hardhat/console.sol";
struct UintStruct {
uint value;
}
contract O {
uint v0 = 0;
}
contract A is O {
uint v1= 1;
}
contract B is O {
uint v2= 2;
}
contract C is O {
uint v3= 3;
}
contract D is O {
uint v4= 4;
}
contract X1 is O, A, B, C, D {
uint vx1 = 6;
function showStorageInX1() public view {
UintStruct storage us;
uint i;
for (; i < 6; ++i) {
assembly {
us.slot := i
}
console.log(i, us.value);
}
}
}
contract X2 is A, B, C, D {
uint vx2 = 7;
function showStorageInX2() public view {
UintStruct storage us;
uint i;
for (; i < 6; ++i) {
assembly {
us.slot := i
}
console.log(i, us.value);
}
}
}
可以看到, O 是 A 、B、C、D 合约的共同 parent contract,而 X2 比 X1 少继承了一个 O。
部署两个合约,分别调用两个合约中的函数 showStorageInX1() 和 showStorageInX2(),结果如下:

可以看到,X1 和 X2 的 storage layout 是一样的,所以,在多重继承定义中,其实可以省略公共 parent contract。
posted on 2024-09-12 16:29 HorseShoe2016 阅读(256) 评论() 收藏 举报
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。