在aelf的共识合约标准中,其五个接口可以分为三组:
-
对于任何一个节点,可在任意时刻,从合约中请求共识命令;
-
得到有效出块时间的节点在调度器倒计时终结之后,从合约中获得共识数据,并基于此数据生产区块。
-
节点在将某个区块添加至本地区块链时,将区块信息提交给共识合约,以进行一系列针对共识数据的验证。
请求共识命令 - GetConsensusCommand
这个方法的大致逻辑如下:
public override ConsensusCommand GetConsensusCommand(BytesValue input)
{
_processingBlockMinerPubkey = input.Value.ToHex();
if (Context.CurrentHeight < 2) return ConsensusCommandProvider.InvalidConsensusCommand;
// 如果查询不到当前轮次信息,返回一个无效的时间。
if (!TryToGetCurrentRoundInformation(out var currentRound))
return ConsensusCommandProvider.InvalidConsensusCommand;
// 如果请求共识命令的公钥不在生产节点列表中,返回一个无效时间。
if (!currentRound.IsInMinerList(_processingBlockMinerPubkey))
return ConsensusCommandProvider.InvalidConsensusCommand;
// 这条链开始运行的时间戳(大致)
var blockchainStartTimestamp = GetBlockchainStartTimestamp();
var behaviour = IsMainChain
? new MainChainConsensusBehaviourProvider(currentRound, _processingBlockMinerPubkey,
GetMaximumBlocksCount(),
Context.CurrentBlockTime, blockchainStartTimestamp, State.TimeEachTerm.Value)
.GetConsensusBehaviour()
: new SideChainConsensusBehaviourProvider(currentRound, _processingBlockMinerPubkey,
GetMaximumBlocksCount(),
Context.CurrentBlockTime).GetConsensusBehaviour();
Context.LogDebug(() =>
$"{currentRound.ToString(_processingBlockMinerPubkey)}\nArranged behaviour: {behaviour.ToString()}");
return behaviour == aelfConsensusBehaviour.Nothing
? ConsensusCommandProvider.InvalidConsensusCommand
: GetConsensusCommand(behaviour, currentRound, _processingBlockMinerPubkey, Context.CurrentBlockTime);
}
在该实现中,代码运行到获得区块链开始运行的时间戳后,获取共识命令分为两个步骤:
1.根据轮次(Round)信息和该条链是主链还是侧链,判断该公钥接下来应该生产一个什么类型的区块,这里描述为Consensus Behaviour;
2.如果能得到一个有效的Consensus Behaviour,再去进一步组装出共识命令(Consensus Command),作为结果返回。
获取Consensus Behaviour
aelf侧链和主链的区别在于,侧链不存在生产节点选举换届等事务(即Consensus Behaviour),在联合挖矿(Merged Mining)设计模式下,侧链共享主链的生产节点,因此与生产节点竞选等事务相关的合约只需要部署在主链上即可。
ConsensusBehaviourProviderBase为主链和侧链共享Consensus Behaviour的实现逻辑。
public override ConsensusCommand GetConsensusCommand(BytesValue input)
{
_processingBlockMinerPubkey = input.Value.ToHex();
if (Context.CurrentHeight < 2) return ConsensusCommandProvider.InvalidConsensusCommand;
// 如果查询不到当前轮次信息,返回一个无效的时间。
if (!TryToGetCurrentRoundInformation(out var currentRound))
return ConsensusCommandProvider.InvalidConsensusCommand;
// 如果请求共识命令的公钥不在生产节点列表中,返回一个无效时间。
if (!currentRound.IsInMinerList(_processingBlockMinerPubkey))
return ConsensusCommandProvider.InvalidConsensusCommand;
// 这条链开始运行的时间戳(大致)
var blockchainStartTimestamp = GetBlockchainStartTimestamp();
var behaviour = IsMainChain
? new MainChainConsensusBehaviourProvider(currentRound, _processingBlockMinerPubkey,
GetMaximumBlocksCount(),
Context.CurrentBlockTime, blockchainStartTimestamp, State.TimeEachTerm.Value)
.GetConsensusBehaviour()
: new SideChainConsensusBehaviourProvider(currentRound, _processingBlockMinerPubkey,
GetMaximumBlocksCount(),
Context.CurrentBlockTime).GetConsensusBehaviour();
Context.LogDebug(() =>
$"{currentRound.ToString(_processingBlockMinerPubkey)}\nArranged behaviour: {behaviour.ToString()}");
return behaviour == aelfConsensusBehaviour.Nothing
? ConsensusCommandProvider.InvalidConsensusCommand
: GetConsensusCommand(behaviour, currentRound, _processingBlockMinerPubkey, Context.CurrentBlockTime);
}
在该实现中,代码运行到获得区块链开始运行的时间戳后,获取共识命令分为两个步骤:
1.根据轮次(Round)信息和该条链是主链还是侧链,判断该公钥接下来应该生产一个什么类型的区块,这里描述为Consensus Behaviour;
2.如果能得到一个有效的Consensus Behaviour,再去进一步组装出共识命令(Consensus Command),作为结果返回。
获取Consensus Behaviour
aelf侧链和主链的区别在于,侧链不存在生产节点选举换届等事务(即Consensus Behaviour),在联合挖矿(Merged Mining)设计模式下,侧链共享主链的生产节点,因此与生产节点竞选等事务相关的合约只需要部署在主链上即可。
ConsensusBehaviourProviderBase为主链和侧链共享Consensus Behaviour的实现逻辑。
//
// First step of getting consensus command for any pubkey:
// to get expected consensus behaviour.
//
private abstract class ConsensusBehaviourProviderBase
{
protected readonly Round CurrentRound;
private readonly string _pubkey;
private readonly int _maximumBlocksCount;
private readonly Timestamp _currentBlockTime;
private readonly bool _isTimeSlotPassed;
private readonly MinerInRound _minerInRound;
protected ConsensusBehaviourProviderBase(Round currentRound, string pubkey, int maximumBlocksCount,
Timestamp currentBlockTime)
{
CurrentRound = currentRound;
_pubkey = pubkey;
_maximumBlocksCount = maximumBlocksCount;
_currentBlockTime = currentBlockTime;
_isTimeSlotPassed = CurrentRound.IsTimeSlotPassed(_pubkey, _currentBlockTime);
_minerInRound = CurrentRound.RealTimeMinersInformation[_pubkey];
}
public aelfConsensusBehaviour GetConsensusBehaviour()
{
// The most simple situation: provided pubkey isn't a miner.
if (!CurrentRound.IsInMinerList(_pubkey))
{
return aelfConsensusBehaviour.Nothing;
}
// If out value is null, it means provided pubkey hasn't mine any block du