ActionEvaluator 상세 - planetarium/libplanet GitHub Wiki
이 문서에서는 액션이 어떤 과정을 통해 Evaluate 되는지에 대해 설명합니다.
BlockChain
에서 블록을 붙일 때, 블록 내의 트랜잭션을 붙여보고 실행합니다. 이는 IActionEvaluator.Evaluate()
함수를 호출하는 것으로부터 시작합니다.
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
의 코드를 따라서 쉽게 이해할 수 있습니다.