aelf技术点解读 | AEDPoS合约实现逻辑


在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的实现逻辑。

// <summary>
// First step of getting consensus command for any pubkey:
// to get expected consensus behaviour.
// </summary>
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 during current round period.
    if (_minerInRound.OutValue == null)
    {
        var behaviour = HandleMinerInNewRound();
        // It‘s possible HandleMinerInNewRound can‘t handle all the situations, if this method returns Nothing,
        // just go ahead. Otherwise, return it‘s result.
        if (behaviour != aelfConsensusBehaviour.Nothing)
        {
            return behaviour;
        }
    }
    else if (!_isTimeSlotPassed
    ) // Provided pubkey mined blocks during current round, and current block time is still in his time slot.
    {
        if (_minerInRound.ActualMiningTimes.Count < _maximumBlocksCount)
        {
            // Provided pubkey can keep producing tiny blocks.
            return aelfConsensusBehaviour.TinyBlock;
        }

        var blocksBeforeCurrentRound =
            _minerInRound.ActualMiningTimes.Count(t => t <= CurrentRound.GetRoundStartTime());
        // If provided pubkey is the one who terminated previous round, he can mine
        // (_maximumBlocksCount + blocksBeforeCurrentRound) blocks
        // because he has two time slots recorded in current round.
        if (CurrentRound.ExtraBlockProducerOfPreviousRound ==
            _pubkey && // Provided pubkey terminated previous round
            !CurrentRound.IsMinerListJustChanged && // & Current round isn‘t the first round of current term
            _minerInRound.ActualMiningTimes.Count.Add(1) <
            _maximumBlocksCount.Add(
                blocksBeforeCurrentRound) // & Provided pubkey hasn‘t mine enough blocks for current round.
        )
        {
            // Then provided pubkey can keep producing tiny blocks.
            return aelfConsensusBehaviour.TinyBlock;
        }
    }
    return GetConsensusBehaviourToTerminateCurrentRound();
}
// <summary>
// If this miner come to a new round, normally, there are three possible behaviour:
// UPDATE_VALUE (most common)
// TINY_BLOCK (happens if this miner is mining blocks for extra block time slot of previous round)
// NEXT_ROUND (only happens in first round)
// </summary>
// <returns></returns>
private aelfConsensusBehaviour HandleMinerInNewRound()
{
    if (
        // For first round, the expected mining time is incorrect (due to configuration),
        CurrentRound.RoundNumber == 1 &&
        // so we‘d better prevent miners‘ ain‘t first order (meanwhile he isn‘t boot miner) from mining fork blocks
        _minerInRound.Order != 1 &&
        // by postpone their mining time
        CurrentRound.FirstMiner().OutValue == null
    )
    {
        return aelfConsensusBehaviour.NextRound;
    }

    if (
        // If this miner is extra block producer of previous round,
        CurrentRound.ExtraBlockProducerOfPreviousRound == _pubkey &&
        // and currently the time is ahead of current round,
        _currentBlockTime < CurrentRound.GetRoundStartTime() &&
        // make this miner produce some tiny blocks.
        _minerInRound.ActualMiningTimes.Count < _maximumBlocksCount
    )
    {
        return aelfConsensusBehaviour.TinyBlock;
    }
    return !_isTimeSlotPassed ? aelfConsensusBehaviour.UpdateValue : aelfConsensusBehaviour.Nothing;
}

// <summary>
// Main Chain & Side Chains are different (because side chains have no election mechanism thus no NEXT_TERM behaviour).
// </summary>
// <returns></returns>
protected abstract aelfConsensusBehaviour GetConsensusBehaviourToTerminateCurrentRound();
}

先大致介绍一下AEDPoS共识的设计机制和区块生产时序。众所周知,在DPoS系列共识下,区块由一系列被选举出来的代理节点进行生产,而AEDPoS的区块生产周期以轮次(Round)为单位,每一轮中,每个生产节点被随机分配一个时间槽(Time Slot),只有在被分配时间槽中生产区块才被认为是合法的。生产节点分配顺序由上一轮中各自产生的随机数确定(为验证上一轮的随机数,需要生产节点在上一轮公布一个随机数的哈希值,再在本轮公布上一轮的随机数,验证随机数的哈希值与上一轮公布的哈希值是否相等即可验证,这一验证机制也被用于AEDPoS共识Commitment Scheme随机数的验证上)。每一轮最后一个生产节点时间槽之后,包含一个额外时间槽,用来生产额外区块,该区块用于对下一轮信息进行初始化。
因此生产节点获取Consensus Behaviour的实现逻辑就比较清晰(主链启动后的第一轮默认时间为1970年,需要做特殊处理,此处略过):
1.如果该节点本轮还未生产过区块:
a.如果当前时间已经过了被分配的时间槽,就先不确定Consensus Behaviour,返回Nothing;
b.如果当前时间还未到被分配的时间槽,返回Consensus Behaviour,称之为UpdateValue,因为本质上Consensus Behaviour是用来更新共识数据的。
2.如果该节点本轮已经生产过区块,则判断能不能生产小块(每个时间槽可以连续生产8个区块):
a.若能,则Consensus Behaviour执行TinyBlock;
b.若不能,就给当前节点分配一个终结本轮的时间槽:
i.如果当前节点恰好是本轮预先指定的额外区块生产节点(Extra Block Producer),那么就将本轮自带的最后一个时间槽之后的一个时间槽分配给该生产节点;
ii.如果当前节点不是指定的额外区块生产节点,在本轮自带的最后一个时间槽之后,按照其在本轮的次序,给予分配相应次序的时间槽——如果指定的额外区块生产者因为分叉或掉线而未能按时出块的话,那么就有机会轮到当前节点来终结本轮。
判断是否错过时间槽的方法位于Round中:

public bool IsTimeSlotPassed(string publicKey, Timestamp currentBlockTime)
{
var miningInterval = GetMiningInterval();
if (!RealTimeMinersInformation.ContainsKey(publicKey)) return false;
var minerInRound = RealTimeMinersInformation[publicKey];
if (RoundNumber != 1)
{
return minerInRound.ExpectedMiningTime + new Duration {Seconds = miningInterval.Div(1000)} <
currentBlockTime;
}

var actualStartTimes = FirstMiner().ActualMiningTimes;
if (actualStartTimes.Count == 0)
{
    return false;
}

var actualStartTime = actualStartTimes.First();
var runningTime = currentBlockTime - actualStartTime;
var expectedOrder = runningTime.Seconds.Div(miningInterval.Div(1000)).Add(1);
return minerInRound.Order < expectedOrder;
}

对于侧链而言,要终结本轮,直接返回NextRound即可:

// <summary>// Simply return NEXT_ROUND for side chain.// </summary>// <returns></returns>protected override aelfConsensusBehaviour GetConsensusBehaviourToTerminateCurrentRound() => aelfConsensusBehaviour.NextRound;
生产节点换届周期设计为7天,因此要根据时间来判断是否进入下一轮竞选,此时Consensus Behaviour执行NextTerm:
// <summary>// The blockchain start timestamp is incorrect during the first round,// don‘t worry, we can return NextRound without hesitation.// Besides, return only NextRound for single node running.// </summary>// <returns></returns>protected override aelfConsensusBehaviour GetConsensusBehaviourToTerminateCurrentRound() => CurrentRound.RoundNumber == 1 || // Return NEXT_ROUND in first round. !CurrentRound.NeedToChangeTerm(_blockchainStartTimestamp, CurrentRound.TermNumber, _timeEachTerm) || CurrentRound.RealTimeMinersInformation.Keys.Count == 1 // Return NEXT_ROUND for single node. ? aelfConsensusBehaviour.NextRound : aelfConsensusBehaviour.NextTerm;
其中,NeedToChangeTerm判定规则为:三分之二以上节点的出块最新时间戳相较上次换届时间戳差达7天之后,则返回true。

public int MinersCountOfConsent => RealTimeMinersInformation.Count.Mul(2).Div(3).Add(1);//<summary>// Change term if two thirds of miners latest ActualMiningTime meets threshold of changing term.// </summary>// <param name="blockchainStartTimestamp"></param>// <param name="currentTermNumber"></param>// <param name="timeEachTerm"></param>// <returns></returns>public bool NeedToChangeTerm(Timestamp blockchainStartTimestamp, long currentTermNumber, long timeEachTerm){ return RealTimeMinersInformation.Values .Where(m => m.ActualMiningTimes.Any()) .Select(m => m.ActualMiningTimes.Last()) .Count(t => IsTimeToChangeTerm(blockchainStartTimestamp, t, currentTermNumber, timeEachTerm)) >= MinersCountOfConsent;}// <summary>// If timeEachTerm == 7:// 1, 1, 1 => 0 != 1 - 1 => false// 1, 2, 1 => 0 != 1 - 1 => false// 1, 8, 1 => 1 != 1 - 1 => true => term number will be 2// 1, 9, 2 => 1 != 2 - 1 => false// 1, 15, 2 => 2 != 2 - 1 => true => term number will be 3.// </summary>// <param name="blockchainStartTimestamp"></param>// <param name="termNumber"></param>// <param name="blockProducedTimestamp"></param>// <param name="timeEachTerm"></param>// <returns></returns>private static bool IsTimeToChangeTerm(Timestamp blockchainStartTimestamp, Timestamp blockProducedTimestamp, long termNumber, long timeEachTerm){ return (blockProducedTimestamp - blockchainStartTimestamp).Seconds.Div(timeEachTerm) != termNumber - 1;}

组装Consensus Command

获取到Consensus Behaviour之后,便可以基于此组装Consensus Command:

// <summary>
// aelf Consensus Behaviour is changeable in this method when
// this miner should skip his time slot more precisely.
// </summary>
// <param name="behaviour"></param>
// <param name="currentRound"></param>
// <param name="pubkey"></param>
// <param name="currentBlockTime"></param>
// <returns></returns>
private ConsensusCommand GetConsensusCommand(aelfConsensusBehaviour behaviour, Round currentRound,
string pubkey, Timestamp currentBlockTime = null)
{
if (SolitaryMinerDetection(currentRound, pubkey))
return ConsensusCommandProvider.InvalidConsensusCommand;

if (currentRound.RoundNumber == 1 && behaviour != aelfConsensusBehaviour.TinyBlock)
    return new ConsensusCommandProvider(new FirstRoundCommandStrategy(currentRound, pubkey,
        currentBlockTime, behaviour)).GetConsensusCommand();

Context.LogDebug(() => $"Params to get command: {behaviour}, {pubkey}, {currentBlockTime}");
switch (behaviour)
{
    case aelfConsensusBehaviour.UpdateValue:
        TryToGetPreviousRoundInformation(out var previousRound);
        return new ConsensusCommandProvider(new NormalBlockCommandStrategy(currentRound, pubkey,
            currentBlockTime, previousRound.RoundId)).GetConsensusCommand();

    case aelfConsensusBehaviour.NextRound:
    case aelfConsensusBehaviour.NextTerm:
        return new ConsensusCommandProvider(
                new TerminateRoundCommandStrategy(currentRound, pubkey, currentBlockTime,
                    behaviour == aelfConsensusBehaviour.NextTerm))
            .GetConsensusCommand();

    case aelfConsensusBehaviour.TinyBlock:
    {
        var consensusCommand =
            new ConsensusCommandProvider(new TinyBlockCommandStrategy(currentRound, pubkey,
                currentBlockTime, GetMaximumBlocksCount())).GetConsensusCommand();
        if (consensusCommand.Hint ==
            new aelfConsensusHint {Behaviour = aelfConsensusBehaviour.NextRound}.ToByteString())
        {
            Context.LogDebug(() => "Re-arranged behaviour from TinyBlock to NextRound.");
        }

        return consensusCommand;
    }
}

return ConsensusCommandProvider.InvalidConsensusCommand;

}
在组装Consensus Command之前需做一个判断:如果最近三轮都只有当前一个生产节点在产生区块,则说明网络或者整条链一定出了问题,那么将直接暂停出块,需等待同步网络上其他区块而重新Trigger共识服务。

//<summary>
// If current miner mined blocks only himself for 2 rounds,
// just stop and waiting to execute other miners‘ blocks.
// </summary>
// <param name="currentRound"></param>
// <param name="pubkey"></param>
// <returns></returns>
private bool SolitaryMinerDetection(Round currentRound, string pubkey)
{
var isAlone = false;
// Skip this detection until 4th round.
if (currentRound.RoundNumber > 3 && currentRound.RealTimeMinersInformation.Count > 2)
{
// Not single node.
var minedMinersOfCurrentRound = currentRound.GetMinedMiners();
isAlone = minedMinersOfCurrentRound.Count == 0;
// If only this node mined during previous round, stop mining.
if (TryToGetPreviousRoundInformation(out var previousRound) && isAlone)
{
var minedMiners = previousRound.GetMinedMiners();
isAlone = minedMiners.Count == 1 &&
minedMiners.Select(m => m.Pubkey).Contains(pubkey);
}

    // check one further round.
    if (isAlone && TryToGetRoundInformation(previousRound.RoundNumber.Sub(1),
            out var previousPreviousRound))
    {
        var minedMiners = previousPreviousRound.GetMinedMiners();
        isAlone = minedMiners.Count == 1 &&
                  minedMiners.Select(m => m.Pubkey).Contains(pubkey);
    }
}

return isAlone;

}
接下来,就可以根据传入的Consensus Behaviour来选择对应的策略:

FirstRoundCommandStrategy

    (针对第一轮的特殊处理策略)

NormalBlockCommandStrategy

    (对应UpdateValue)

TerminateRoundCommandStrategy

    (对应NextRound和NextTerm)

TinyBlockCommandStrategy

     (对应TinyBlock) 

我们通过NormalBlockCommandStrategy(对应UpdateValue)的策略看其实现逻辑:
public override ConsensusCommand GetAEDPoSConsensusCommand(){ var arrangedMiningTime = MiningTimeArrangingService.ArrangeNormalBlockMiningTime(CurrentRound, Pubkey, CurrentBlockTime); return new ConsensusCommand { Hint = new aelfConsensusHint { Behaviour = aelfConsensusBehaviour.UpdateValue, RoundId = CurrentRound.RoundId, PreviousRoundId = _previousRoundId }.ToByteString(), ArrangedMiningTime = arrangedMiningTime, // Cancel mining after time slot of current miner because of the task queue. MiningDueTime = CurrentRound.GetExpectedMiningTime(Pubkey).AddMilliseconds(MiningInterval), LimitMillisecondsOfMiningBlock = DefaultBlockMiningLimit }; }
该策略通过获取预计出块时间,来组装执行一个ConsensusCommand实例。其余的策略实现逻辑也大致如此。

生成共识数据 - GetConsensusExtraData & GenerateConsensusTransactions

在节点获得Consensus Command之后,便会根据其中的ArrangedMiningTime信息更新本地的共识调度器;倒计时结束后,即开始产生区块。区块中的共识信息位于两个位置,分别位于区块头(Block Header)中的额外数据(一个二进制数组的列表,其中之一为共识数据),和一个共识交易作为系统交易,其哈希值保存在区块头中,交易相关数据保存在区块体(Block Body)中。
其中,区块头中的信息经由调用GetConsensusExtraData产生,共识交易由调用GenerateConsensusTransactions产生。
AEDPoS对于二者的实现,都不可避免地要使用下面的方法:
private BytesValue GetConsensusBlockExtraData(BytesValue input, bool isGeneratingTransactions = false)
{
var triggerInformation = new aelfConsensusTriggerInformation();
triggerInformation.MergeFrom(input.Value);

Assert(triggerInformation.Pubkey.Any(), "Invalid pubkey.");

if (!TryToGetCurrentRoundInformation(out var currentRound))
{
    Assert(false, "Failed to get current round information.");
}

var publicKeyBytes = triggerInformation.Pubkey;
var pubkey = publicKeyBytes.ToHex();

var information = new aelfConsensusHeaderInformation();
switch (triggerInformation.Behaviour)
{
    case aelfConsensusBehaviour.UpdateValue:
        information = GetConsensusExtraDataToPublishOutValue(currentRound, pubkey,
            triggerInformation);
        if (!isGeneratingTransactions)
        {
            information.Round = information.Round.GetUpdateValueRound(pubkey);
        }

        break;

    case aelfConsensusBehaviour.TinyBlock:
        information = GetConsensusExtraDataForTinyBlock(currentRound, pubkey,
            triggerInformation);
        break;

    case aelfConsensusBehaviour.NextRound:
        information = GetConsensusExtraDataForNextRound(currentRound, pubkey,
            triggerInformation);
        break;

    case aelfConsensusBehaviour.NextTerm:
        information = GetConsensusExtraDataForNextTerm(pubkey, triggerInformation);
        break;
}

if (!isGeneratingTransactions)
{
    information.Round.DeleteSecretSharingInformation();
}

return information.ToBytesV

首先反序列化输入的信息需通过aelfConsensusTriggerInformation实例化,再根据其中的Consensus Behaviour分别去获取不同的可更新的共识数据。
这次我们看一下NextRound对应的方法:
private aelfConsensusHeaderInformation GetConsensusExtraDataForNextRound(Round currentRound,
string pubkey, aelfConsensusTriggerInformation triggerInformation)
{
if (!GenerateNextRoundInformation(currentRound, Context.CurrentBlockTime, out var nextRound))
{
Assert(false, "Failed to generate next round information.");
}

if (!nextRound.RealTimeMinersInformation.Keys.Contains(pubkey))
{
    return new aelfConsensusHeaderInformation
    {
        SenderPubkey = pubkey.ToByteString(),
        Round = nextRound,
        Behaviour = triggerInformation.Behaviour
    };
}

RevealSharedInValues(currentRound, pubkey);

nextRound.RealTimeMinersInformation[pubkey].ProducedBlocks =
    nextRound.RealTimeMinersInformation[pubkey].ProducedBlocks.Add(1);
Context.LogDebug(() => $"Mined blocks: {nextRound.GetMinedBlocks()}");
nextRound.ExtraBlockProducerOfPreviousRound = pubkey;

nextRound.RealTimeMinersInformation[pubkey].ProducedTinyBlocks = 1;
nextRound.RealTimeMinersInformation[pubkey].ActualMiningTimes
    .Add(Context.CurrentBlockTime);

return new aelfConsensusHeaderInformation
{
    SenderPubkey = pubkey.ToByteString(),
    Round = nextRound,
    Behaviour = triggerInformation.Behaviour

};
}
基于本轮所更新的共识数据,生成下一轮的信息,然后更新几个区块生产信息。
GetConsensusExtraData直接使用这个方法的返回值即可,GenerateConsensusTransactions则需对返回值进行再次处理,生成一个共识交易(GetConsensusBlockExtraData的bool类型的参数用来指示为生成交易时填充更为详尽的信息):

public override TransactionList GenerateConsensusTransactions(BytesValue input)
{
var triggerInformation = new aelfConsensusTriggerInformation();
triggerInformation.MergeFrom(input.Value);
// Some basic checks.
Assert(triggerInformation.Pubkey.Any(),
"Data to request consensus information should contain public key.");

var pubkey = triggerInformation.Pubkey;
var consensusInformation = new aelfConsensusHeaderInformation();
consensusInformation.MergeFrom(GetConsensusBlockExtraData(input, true).Value);
var transactionList = GenerateTransactionListByExtraData(consensusInformation, pubkey);
return transactionList;

}

验证共识消息 - ValidateConsensusBeforeExecution

& GenerateConsensusTransactions

对于一个区块的验证,依然要基于当前区块的生产动机——Consensus Behaviour。在ValidateConsensusBeforeExecution实现中,需根据Consensus Behaviour的不同添加不同的IHeaderInformationValidationProvider。IHeaderInformationValidationProvider目前有以下几种:

ContinuousBlocksValidationProvider(用来阻止节点连续产生过多区块)
LibInformationValidationProvider(用来验证不可逆转块的信息是否正确)
MiningPermissionValidationProvider(用来验证该节点是否有出块权限)
NextRoundMiningOrderValidationProvider(用来验证下一轮的出块顺序是否正确)
RoundTerminateValidationProvider(用来验证下一轮基本信息是否正确)
TimeSlotValidationProvider(用来验证区块是否生产自正确时间槽)
UpdateValueValidationProvider(用来验证各个节点更新的共识信息是否合法)

// <summary>
// This method will be executed before executing a block.
// </summary>
// <param name="extraData"></param>
// <returns></returns>
private ValidationResult ValidateBeforeExecution(aelfConsensusHeaderInformation extraData)
{
// According to current round information:
if (!TryToGetCurrentRoundInformation(out var baseRound))
{
return new ValidationResult {Success = false, Message = "Failed to get current round information."};
}

if (extraData.Behaviour == aelfConsensusBehaviour.UpdateValue)
{
    baseRound.RecoverFromUpdateValue(extraData.Round, extraData.SenderPubkey.ToHex());
}

if (extraData.Behaviour == aelfConsensusBehaviour.TinyBlock)
{
    baseRound.RecoverFromTinyBlock(extraData.Round, extraData.SenderPubkey.ToHex());
}

var validationContext = new ConsensusValidationContext
{
    BaseRound = baseRound,
    CurrentTermNumber = State.CurrentTermNumber.Value,
    CurrentRoundNumber = State.CurrentRoundNumber.Value,
    PreviousRound = TryToGetPreviousRoundInformation(out var previousRound) ? previousRound : new Round(),
    LatestPubkeyToTinyBlocksCount = State.LatestPubkeyToTinyBlocksCount.Value,
    ExtraData = extraData
};
/* Ask several questions: */
// Add basic providers at first.
var validationProviders = new List<IHeaderInformationValidationProvider>
{
    // Is sender in miner list (of base round)?
    new MiningPermissionValidationProvider(),
    // Is this block produced in proper time?
    new TimeSlotValidationProvider(),
    // Is sender produced too many blocks at one time?
    new ContinuousBlocksValidationProvider()
};

switch (extraData.Behaviour)
{
    case aelfConsensusBehaviour.UpdateValue:
        validationProviders.Add(new UpdateValueValidationProvider());
        // Is confirmed lib height and lib round number went down? (Which should not happens.)
        validationProviders.Add(new LibInformationValidationProvider());
        break;
    case aelfConsensusBehaviour.NextRound:
        // Is sender‘s order of next round correct?
        validationProviders.Add(new NextRoundMiningOrderValidationProvider());
        validationProviders.Add(new RoundTerminateValidationProvider());
        break;
    case aelfConsensusBehaviour.NextTerm:
        validationProviders.Add(new RoundTerminateValidationProvider());
        break;
}

var service = new HeaderInformationValidationService(validationProviders);

Context.LogDebug(() => $"Validating behaviour: {extraData.Behaviour.ToString()}");

var validationResult = service.ValidateInformation(validationContext);

if (validationResult.Success == false)
{
    Context.LogDebug(() => $"Consensus Validation before execution failed : {validationResult.Message}");
}

return validationResult;

}
ValidateConsensusAfterExecution的实现只需要检查共识交易执行后所实际更新的共识信息(的重要部分)与Block Header Extra Data中的共识信息是否一致:

public override ValidationResult ValidateConsensusAfterExecution(BytesValue input)
{
var headerInformation = new aelfConsensusHeaderInformation();
headerInformation.MergeFrom(input.Value);
if (TryToGetCurrentRoundInformation(out var currentRound))
{
if (headerInformation.Behaviour == aelfConsensusBehaviour.UpdateValue)
{
headerInformation.Round =
currentRound.RecoverFromUpdateValue(headerInformation.Round,
headerInformation.SenderPubkey.ToHex());
}

    if (headerInformation.Behaviour == aelfConsensusBehaviour.TinyBlock)
    {
        headerInformation.Round =
            currentRound.RecoverFromTinyBlock(headerInformation.Round,
                headerInformation.SenderPubkey.ToHex());
    }

    var isContainPreviousInValue = !currentRound.IsMinerListJustChanged;
    if (headerInformation.Round.GetHash(isContainPreviousInValue) !=
        currentRound.GetHash(isContainPreviousInValue))
    {
        Context.LogDebug(() => $"Round information of block header:\n{headerInformation.Round}");
        Context.LogDebug(() => $"Round information of executing result:\n{currentRound}");
        return new ValidationResult
        {
            Success = false, Message = "Current round information is different with consensus extra data."
        };
    }
}

return new ValidationResult {Success = true}

}
由于区块头额外信息中的共识信息是被简化过的(如并不包含Secret Share相关信息),ValidateConsensusAfterExecution需要将被简化的内容进行适度补全,最终直接验证StateDB中的本轮信息和补全后的信息是否完全一致,而验证本身也只关心比较重要的共识数据,会对其他多余的数据做一定裁剪,这就是是为何需要RecoverFromUpdateValue、RecoverFromTinyBlock和在GetHash中调用GetCheckableRound这几个方法。

原文地址:https://blog.51cto.com/14239384/2484043

时间: 2024-08-04 22:25:16

aelf技术点解读 | AEDPoS合约实现逻辑的相关文章

技术点解读 | aelf共识标准设计

区块链系统共识:去中心化的共识 本质上,区块链系统是一个分布式系统,但是与普遍的分布式系统不同.普遍的分布式系统,其意义在于:面对增长的业务量,用多台机器承载垂直拆分或水平拆分后的业务场景,增大系统容量:根据业务的关键程度,消除单点故障,加强系统可用性.当一个区块链系统承担的业务场景复杂如普遍的分布式系统时,当然也需要做如上的考虑.但是区块链系统之所以应当被人重视,是因为它能够解决存在作恶节点情况下的数据一致性的问题,也就是拜占庭将军问题.区块链世界中,不存在所谓的中心化服务器,其是由所有爱好者

Cocos2d-x游戏开发技术精解读书摘要(2016-5-27 10:52)

 Cocos2d-x游戏开发技术精解 刘剑卓 著 2013年6月第1版 chap2 Cocos2d-x引擎的开发环境 2.1跨平台的开发 2.2建立开发环境 2.2.1 PC开发环境 2.2.2 Android开发环境 2.2.3 iOS开发环境 2.3引擎中的混合编译 2.3.1 Java与C++的混合编译 2.3.2 Objective-C与C++的混合编译 2.4引擎的起点 2.4.1应用程序入口 2.4.2引擎应用入口 2.5丰富的示例程序 2.5.1 TestCpp示例项目 2.5

宜人蜂巢技术点解读

YEP是致力于为中国金融科技行业提供信用评估.风险控制和精准获客的金融科技能力共享平台. 宜人贷借款已使用YEP的智能分发平台,为合作伙伴推荐更适合其产品的用户. 同时,YEP以宜人蜂巢为代表,从2017年开始为市场和行业赋能,以数据科学驱动风控. 宜人蜂巢旨在通过数据科学驱动互联网风控,让信用释放更多价值.宜人蜂巢提供基于独创的非结构化解析引擎Nestor打造的多维多端实时保真数据获取服务:结合大数据.机器学习技术构建的反欺诈服务:通过深度数据挖掘.特征化工程构建千余维度特征的用户信用报告.

iSCSI网络存储技术-实例解读

1 iSCSI介绍 网络存储服务器主要有三种解决方案--DAS直连存储,SAN区域网路存储 ,NAS网络附加存储,san和nas的主要区别在于,nas共享的是文件系统,san共享的是块设备. iSCSI是一种基于TCP/IP 的协议,用来建立和管理IP存储设备.主机和客户机等之间的相互连接,并创建存储区域网络(SAN).SAN 使得SCSI 协议应用于高速数据传输网络成为可能,这种传输以数据块级别(block-level)在多个数据存储网络间进行.SCSI 结构基于C/S模式,其通常应用环境是:

小程序音视频能力技术负责人解读“小程序直播”

策划 / LiveVideoStack 责编 / 包研 一夜之间,"小程序+直播"成为多媒体开发者热议的话题.从底层技术实现到接口开放程度,是否绑定腾讯云?价格体系?低延迟性能如何?......一连串的问题背后是开发者乃至整个生态对"小程序+直播"的关注.LiveVideoStack邀请到小程序音视频能力的技术负责人常青,就开发者关注的各种问题进行了解答.如果您还有新的问题,请在在文末留言或邮件至[email protected]. 另外,我们还发起了针对"

4K超清,2500万人在线,猫晚直播技术全解读

2018天猫双11已经过去一周,各路快递也在快马加鞭送到大家手中.但对于剁手党而言,天猫双11也不仅仅是简单意义上的"买买买",更是一场边看边玩的狂欢盛宴. 作为双11的必备节目,今年的猫晚通过优酷.浙江卫视.东方卫视进行了全程网络直播和电视直播,吸引了超过全球超过2.4亿人收看.猫晚期间,优酷基于阿里云最新的广播级高可靠直播方案,为近2500万的观众带来了超高清.流畅的观看体验. 大家一定还记得今年俄罗斯世界杯期间,阿里云承包了全网70%的直播流量,其实,本次猫晚直播解决方案带来了全

商超行业微信小程序开发定制一般多少钱 (行业技术人员解读)

商超行业微信小程序开发多少钱?如果想要开发一个商超行业微信小程序大概得 需要多少钱呢?随着时代的发展小程序已经逐渐取代了很多传统APP的存在. 越来越多的品牌和企业个人都将小程序的开发作为首要目标,这也足以证明小程 序的优势是非常大的.那么下面我们就来说一说小程序开发大概需要多少钱. 一.模板开发模板开发具体步骤 模板小程序开发多少钱?首先在小程序开发之前必须搞清楚自己的需求,如果你 想要开发的小程序功能比较普遍,跟市面上的小程序基本相似,而且同样的商业 形态还很多的话,比方说网店.商城.分销系

小程序定制开发一般需要多少钱 (专业技术人员解读)

随着时代的发展越来越多的企业和个人都把小程序的开发作为首要目标,这也足以证 明小程序的优势是非常大的.一般常见的电商小程序.餐饮小程序.旅游小程序企业 展示小程序等,每种小程序都有它特定的功能.比如电商小程序需要点单.优惠活动 .预约.支付等等功能,那么你选择的小程序里是否有这些功能?所以在开发小程序 之前你必须想好需要哪些功能. 1.电商类小程序 不用多说,就是将商品图片价格上传到小程序上面,通过展示销售形成订单,获取收益.其中有卖母婴类产品的.零食类的.服装类的等等. 2.预定类小程序 比如

区块链的跨链技术介绍完整版

如果说共识机制是区块链的灵魂核心,那么对于区块链特别是联盟链及私链来看,跨链技术就是实现价值网络的关键,它是把联盟链从分散单独的孤岛中拯救出来的良药,是区块链向外拓展和连接的桥梁. 自比特币七年前诞生以来,数以百计的竞争币被开发出来,有着各种新的优势和特性,但比特币的霸主地位依然屹立不倒,而很多复制竞争币却湮灭在历史中.虽然比特币有不少缺点和限制,但比特币却又是最去中心化.最多分布节点.最公平区块链,从数字货币地位.节点数量.去中心的权威等方面比特币还是很有优势.同时,类似以太坊.比特股的区块链