ACS:AElf Contract Standard,AElf合约标准,顾名思义,就是开发AElf智能合约时须要继承和实现的一些接口。
全部的ACS都经过protobuf的service定义。 ACS4做为ACS之一,是实现任意一种共识合约时,须要实现的一些接口。
本文主要讨论共识标准接口的可行性和AElf中为共识合约的实现所提供的接口及其余支持。
站在实现区块链共识的角度,咱们主要关心三件事:git
谁能够产生区块,如PoW共识容许每个人参与算力竞争,PoS和DPoS共识则要对此作出必定限制;
若是能够产生区块,那么应该在何时产生,或者说当前的时间能不能开始尝试广播区块;
做为一个区块链全节点,应该怎么验证这个区块的合法性。github
针对这三点,咱们很容易想到三类接口:数据库
输入公钥,判断这个公钥有没有资格产生区块,PoW共识直接返回true就能够了,DPoS可能会对大部分人返回false。
输入公钥,返回这个公钥下一次可以产生区块的时间戳,或者返回这个公钥当前能不能产生区块。
全节点获得区块头(block header)以后,输入从中提取到的共识数据,验证这个区块的1和2相关信息,即,PoW共识须要验证nonce的合法性,DPoS共识须要验证新区块的生产者身份合法性、生产者是否尊重本身的时间槽,获得验证结果。
除此之外,为了获得接口3中的输入,即区块头中的共识数据,至少还须要为区块生产者提供一个生成区块头共识数据的方法。区块链
AElf中的实践
首先,AElf的主链选择的共识属于DPoS,本文虽然说讨论的是通用共识接口,也免不了倾向于多讨论(AE)DPoS。google
其次,全部的共识合约标准上的接口,都是只读的,由于单纯获取这些数据无需改动WorldState。(WorldState是以太坊中的概念,AElf在开发中称用于存储合约的状态的数据库为State DB;除此以外还有Chain DB,用于存储区块自己,包括区块中的交易。)code
ACS4中合并了接口1和接口2,获得一个接口:orm
rpc GetConsensusCommand (google.protobuf.BytesValue) returns (ConsensusCommand) {继承
option (aelf.is_view) = true;
}接口
message ConsensusCommand {开发
int32 NextBlockMiningLeftMilliseconds = 1;// How many milliseconds left to trigger the mining of next block. int32 LimitMillisecondsOfMiningBlock = 2;// Time limit of mining next block. bytes Hint = 3;// Context of Hint is diverse according to the consensus protocol we choose, so we use bytes. google.protobuf.Timestamp ExpectedMiningTime = 4;
}
很显然,NextBlockMiningLeftMilliseconds的值取决于ExpectedMiningTime(预期出块时间)与当前时间(调用这个接口的时间)的差值。
LimitMillisecondsOfMiningBlock是系统分配给这个块用于打包的时间,决定了这个区块可以打包多少用户发送的交易(给打包用户发送的交易留了多长时间)。
Hint字段用来传递一些单独的状态,取决于选用的共识协议,在PoW中这个字段可能显得没必要要,而AEDPoS定义了一些区块类型,如Normal Block,Extra Block,只更新少许共识信息的Tiny Block。这些,都须要反应在Hint中——共识合约不只须要告知区块生产者多久之后能够着手产生区块,也须要告知他应该产生什么类型的区块,而不一样区块会更新不一样的共识信息。除此以外,引入Hint字段也能为实现其余共识协议提供了更多的可能。
这个接口分别会在链刚刚启动时、在本地Best分支上同步完一个新的区块(不管这个区块是否是本身产生的)时、区块生产者在执行本身的区块前的验证中突然发现当前时间已经超出了本身的时间槽(即调用下文的ValidateConsensusBeforeExecution接口)后被调用。
调用以后,Consensus Service会将NextBlockMiningLeftMilliseconds传入共识的调度器中,时间一到,就会去触发生产区块的逻辑。
注意,调度器中的时间是能够随时被覆盖的。事实上,每一次同步一个新的区块,调度器都会被从新初始化。
事关接口3,ACS4拆分出两个接口:
rpc ValidateConsensusBeforeExecution (google.protobuf.BytesValue) returns (ValidationResult) {
option (aelf.is_view) = true;
}
rpc ValidateConsensusAfterExecution (google.protobuf.BytesValue) returns (ValidationResult) {
option (aelf.is_view) = true;
}
message ValidationResult {
bool Success = 1; string Message = 2;
}
也就是说,AElf中,每个区块执行先后,会分别调用以上两个接口的实现对区块头进行验证。验证的逻辑位于实现ACS4的合约中,验证的数据就不得不基于State DB。由于你不能凭空去验证区块头里的共识信息对不对,共识合约里须要存储最近的共识信息,才能给最近的共识信息提供验证的支撑。
既然验证的数据基于State DB,而ACS4的接口都是只读的方法,那就须要单独生成一笔交易去更新State DB(区块链的特性,更新WorldState或者State DB必须经过执行交易来完成)。
这样,ACS4除了要提供生成区块头信息的方法以外,还须要可以返回一个(或者一组)可以更新State DB中的共识信息的交易。这个交易会做为系统交易被生成,而后在打包过程当中,最早添加进区块中。系统交易的执行会先于普通交易,这样普通交易在执行时会获取到最近更新的系统提供的信息,如hash-commit-reveal策略下的随机数。在AElf中,每一个区块会至少包含一个共识系统交易。
ACS4的最后两个与更新共识信息相关的接口以下:
rpc GetInformationToUpdateConsensus (google.protobuf.BytesValue) returns (google.protobuf.BytesValue) {
option (aelf.is_view) = true;
}
rpc GenerateConsensusTransactions (google.protobuf.BytesValue) returns (TransactionList) {
option (aelf.is_view) = true;
}
message TransactionList {
repeated aelf.Transaction Transactions = 1;
}
在AElf的kernel模块代码中,GetInformationToUpdateConsensus会在生成区块头的过程当中被调用,该接口返回的数据会做为区块头的Extra Data之一,用于收到区块的人验证共识信息。GenerateConsensusTransactions接口会在生成区块头以后,进一步生成系统交易的过程当中被调用。补充一句,ValidateConsensusAfterExecution接口本质上只是为了验证区块头中的共识信息和执行完共识系统交易后State DB中的共识信息的一致性,防止区块生产者“出尔反尔”。
ACS4的完整代码在这里。关于对本文或者ACS4或者其余AElf共识组件的建议都会被认真对待,欢迎留言、私信甚至直接去github开issue。
以上是AElf区块链共识合约标准ACS4的大体介绍,下一篇文章会介绍其中最为复杂的GetConsensusCommand接口在AEDPoS共识下的具体实现。