智能合约代码编写:
contract Students {
struct StudentInfo {
uint32 _studentId;
bytes32 _studentName;
}
mapping (uint32 => StudentInfo) private _studentMapping;
function addStudent(uint32 studentId, bytes32 studentName) public returns(bool){
//TODO:
}
}
这种写法,代码全部在一个智能合约中,如果现有的智能合约已经不能满足业务诉求,比如类型为uint32字段需升级为为uint64,或者合约中添加一个新的字段,比如sex,那这个智能合约就没有用了,需要重新部署。但因为重新部署,合约地址变了,无法访问到之前的数据。
一种做法是对合约进行分层,将业务逻辑和数据分离,如下所示:
contract StudentController {
mapping (uint32 => address) private _studentMapping;
function addStudent(uint32 studentId, bytes32 studentName) public returns(bool){
//TODO:
}
}
contract Student {
uint32 _studentId;
bytes32 _studentName;
//uint8 sex;
}
这种写法使得逻辑和数据分离,当需要新增一个性别sex字段时,原始数据可以编写两个StudentController合约,通过版本区分,新的Student采用新的逻辑,需要业务层面做兼容性处理,其最大的问题是对于原有数据的交互性操作,需要跨合约完成,非常不方便,比如查询所有学生信息。
我们再次进行分层,多出一个map层,专门用于合约数据管理,即使业务逻辑层和数据层都出现问题,也没有关系,只需要重新编写业务逻辑层和数据层,并对原有数据进行特殊处理就可以做到兼容。不过,这种做法需要提前在数据合约中做好版本控制(version),针对不同的数据,采用不同的逻辑。
这种做法最大的好处是数据全部保存在StudentMap中,数据合约和逻辑合约的变更都不会影响到数据,且在后续的升级中,可以通过一个controller合约做到对新老数据的兼容,如下所示:
contract StudentController {
mapping (uint32 => address) private _studentMapping;
constructor(address studentMapping) public {
_studentMapping = studentMapping;
}
function addStudent(uint version, uint32 studentId, bytes32 studentName, uint8 sex) public returns(bool){
if(version == 1){
//TODO
}else if(version == 2){
//TODO
}
}
}
contract StudentMap {
mapping (uint32 => address) private _studentMapping;
function getStudentMap() public constant returns(address){
return _studentMapping;
}
}
contract Student {
uint8 version;
uint32 _studentId;
bytes32 _studentName;
//uint8 sex;
}
统一接口
智能合约尽管具备很多高级语言的特性,但是本身还是存在很多限制。对于业务的精准处理,需要采用Event事件进行跟踪,对于不同的合约和方法,可以编写不同的Event事件,如下:
PS:你也可以采用require的方式进行处理,不过require方式不支持动态变量,每个require处理后需要填入特定的报错内容,在SDK层面耦合性太重,且不便于扩展。
contract StudentController {
//other code
event addStudentSuccessEvent(...); //省略参数,下同
event addStudentFailEvent(...);
function addStudent(bytes32 studentId, bytes32 studentName) public returns(bool){
if(add success){
addStudentSuccessEvent(...);
return true;
}else {
addStudentFailEvent(...);
return false;
}
}
}