ActionEvaluator 상세 - planetarium/libplanet GitHub Wiki

이 문서에서는 액션이 어떤 과정을 통해 Evaluate 되는지에 대해 설명합니다.

BlockChain 에서 블록을 붙일 때, 블록 내의 트랜잭션을 붙여보고 실행합니다. 이는 IActionEvaluator.Evaluate() 함수를 호출하는 것으로부터 시작합니다.

ActionEvaluator.cs#L99-L177

        public IReadOnlyList<ICommittedActionEvaluation> Evaluate(
            IPreEvaluationBlock block,
            HashDigest<SHA256>? baseStateRootHash)
        {
            if (block.ProtocolVersion < BlockMetadata.PBFTProtocolVersion)
            {
                throw new BlockProtocolVersionNotSupportedException(
                    $"The native implementation does not support an evaluation of a block " +
                    $"#{block.Index} pre-evaluation hash {block.PreEvaluationHash} " +
                    $"with protocol version less than {BlockMetadata.PBFTProtocolVersion}: " +
                    $"{block.ProtocolVersion}",
                    block.ProtocolVersion);
            }

            _logger.Information(
                "Evaluating actions in the block #{BlockIndex} " +
                "pre-evaluation hash {PreEvaluationHash}...",
                block.Index,
                ByteUtil.Hex(block.PreEvaluationHash.ByteArray)
            );
            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();
            try
            {
                IWorld previousState = _stateStore.GetWorld(baseStateRootHash);
                previousState = _stateStore.MigrateWorld(previousState, block.ProtocolVersion);

                var evaluations = ImmutableList<ActionEvaluation>.Empty;
                if (_policyActionsRegistry.BeginBlockActions.Length > 0)
                {
                    evaluations = evaluations.AddRange(EvaluatePolicyBeginBlockActions(
                        block, previousState
                    ));
                    previousState = evaluations.Last().OutputState;
                }

                evaluations = evaluations.AddRange(
                    EvaluateBlock(block, previousState).ToImmutableList()
                );

                if (_policyActionsRegistry.EndBlockActions.Length > 0)
                {
                    previousState = evaluations.Count > 0
                        ? evaluations.Last().OutputState
                        : previousState;
                    evaluations = evaluations.AddRange(EvaluatePolicyEndBlockActions(
                        block, previousState
                    ));
                }

                var committed = ToCommittedEvaluation(block, evaluations, baseStateRootHash);
                return committed;
            }
            catch (Exception e)
            {
                const string errorMessage =
                    "Failed to evaluate block #{BlockIndex} pre-evaluation hash " +
                    "pre-evaluation has {PreEvaluationHash}";
                _logger.Error(
                    e,
                    errorMessage,
                    block.Index,
                    ByteUtil.Hex(block.PreEvaluationHash.ByteArray));
                throw;
            }
            finally
            {
                _logger
                    .ForContext("Tag", "Metric")
                    .ForContext("Subtag", "BlockEvaluationDuration")
                    .Information(
                        "Actions in {TxCount} transactions for block #{BlockIndex} " +
                        "pre-evaluation hash {PreEvaluationHash} evaluated in {DurationMs} ms",
                        block.Transactions.Count,
                        block.Index,
                        ByteUtil.Hex(block.PreEvaluationHash.ByteArray),
                        stopwatch.ElapsedMilliseconds);
            }
        }

몇 가지 측정을 위한 메서드나 검증을 제외하면 크게 다음의 구조로 동작합니다.

직전 블록을 붙이고 난 뒤의 `IWorld`를 `IStateStore`에서 가져오기 > 액션에 `IWorld`를 담은 `IActionContext`구조체를 전달하여 `Execute` 실행 > 변환된 `IWorld`를 다음 액션에 전달 > ... > 모든 액션이 종료되면 최종적으로 변형된 `IWorld`를 `IStateStore`에 Commit

액션은 다음의 순서로 실행됩니다.

1. PolicyActionRegistry.BeginBlockActions
2-1. PolicyActionRegistry.BeginTxActions
2-2. 트랜잭션 내부의 액션들
2-3. PolciyActionRegistry.EndTxActions
...
n. PolicyActionRegistry.EndBlockActions

PolicyActionRegistry에 여러 정책적인 액션들을 담을 수 있습니다. 이 액션들은 트랜잭션 형태로 배포되지 않으며 각각 블록, 트랜잭션의 서명자가 서명을 대신합니다.

실제 실행 과정은 ActionEvaluator.Evaluate 의 코드를 따라서 쉽게 이해할 수 있습니다.

⚠️ **GitHub.com Fallback** ⚠️