Пример #1
0
        internal ActionEvaluation EvaluateBlockAction(
            Block <T> block,
            IReadOnlyList <ActionEvaluation> txActionEvaluations,
            StateCompleterSet <T> stateCompleters,
            ITrie?previousBlockStatesTrie
            )
        {
            if (block == null)
            {
                throw new ArgumentNullException(nameof(block));
            }

            if (_blockAction is null)
            {
                var message = "To evaluate block action, Policy.BlockAction must not be null.";
                throw new InvalidOperationException(message);
            }

            _logger.Debug(
                "Evaluating block action in block {blockIndex}: {block}", block?.Index, block);

            IAccountStateDelta?lastStates = null;

            if (!(txActionEvaluations is null) && txActionEvaluations.Count > 0)
            {
                lastStates = txActionEvaluations[txActionEvaluations.Count - 1].OutputStates;
            }

            Address miner = block !.Miner.GetValueOrDefault();

            if (lastStates is null)
            {
                IValue?GetState(Address a) =>
                _stateGetter(a, block.PreviousHash, stateCompleters.StateCompleter);

                FungibleAssetValue GetBalance(Address address, Currency currency) =>
                _balanceGetter(
                    address,
                    currency,
                    block.PreviousHash,
                    stateCompleters.FungibleAssetStateCompleter
                    );

                lastStates = block.ProtocolVersion > 0
                    ? new AccountStateDeltaImpl(GetState, GetBalance, miner)
                    : new AccountStateDeltaImplV0(GetState, GetBalance, miner);
            }

            return(ActionEvaluation.EvaluateActionsGradually(
                       block.PreEvaluationHash,
                       block.Index,
                       null,
                       lastStates,
                       miner,
                       miner,
                       Array.Empty <byte>(),
                       new[] { _blockAction }.ToImmutableList(),
                       previousBlockStatesTrie: previousBlockStatesTrie,
                       blockAction: true).First());
        }
Пример #2
0
        internal void RenderFastForward(
            bool render,
            Block <T> oldTip,
            Block <T> newTip,
            Block <T> branchpoint,
            IReadOnlyList <Block <T> > fastForwardPath,
            StateCompleterSet <T> stateCompleters)
        {
            if (render && ActionRenderers.Any())
            {
                _logger.Debug("Rendering actions in new chain.");

                long count = 0;
                foreach (Block <T> block in fastForwardPath)
                {
                    ImmutableList <ActionEvaluation> evaluations =
                        ActionEvaluator.Evaluate(block, stateCompleters).ToImmutableList();

                    count += RenderActions(
                        evaluations: evaluations,
                        block: block,
                        stateCompleters: stateCompleters);
                }

                _logger.Debug(
                    $"{nameof(Swap)}() completed rendering {{Count}} actions.",
                    count);

                foreach (IActionRenderer <T> renderer in ActionRenderers)
                {
                    renderer.RenderBlockEnd(oldTip, newTip);
                }
            }
        }
Пример #3
0
        internal void RenderRewind(
            bool render,
            Block <T> oldTip,
            Block <T> newTip,
            Block <T> branchpoint,
            IReadOnlyList <Block <T> > rewindPath,
            StateCompleterSet <T> stateCompleters)
        {
            if (render && ActionRenderers.Any())
            {
                // Unrender stale actions.
                _logger.Debug("Unrendering abandoned actions...");

                long count = 0;

                foreach (Block <T> block in rewindPath)
                {
                    ImmutableList <ActionEvaluation> evaluations =
                        ActionEvaluator.Evaluate(block, stateCompleters)
                        .ToImmutableList().Reverse();

                    count += UnrenderActions(
                        evaluations: evaluations,
                        block: block,
                        stateCompleters: stateCompleters);
                }

                _logger.Debug(
                    $"{nameof(Swap)}() completed unrendering {{Actions}} actions.",
                    count);
            }
        }
Пример #4
0
        internal void RenderSwap(
            bool render,
            Block <T> oldTip,
            Block <T> newTip,
            Block <T> branchpoint,
            IReadOnlyList <Block <T> > rewindPath,
            IReadOnlyList <Block <T> > fastForwardPath,
            StateCompleterSet <T> stateCompleters)
        {
            // If there is no rewind, it is not considered as a reorg.
            bool reorg = rewindPath.Count > 0;

            if (render && reorg)
            {
                foreach (IRenderer <T> renderer in Renderers)
                {
                    renderer.RenderReorg(
                        oldTip: oldTip,
                        newTip: newTip,
                        branchpoint: branchpoint);
                }
            }

            RenderRewind(
                render: render,
                oldTip: oldTip,
                newTip: newTip,
                branchpoint: branchpoint,
                rewindPath: rewindPath,
                stateCompleters: stateCompleters);

            if (render)
            {
                foreach (IRenderer <T> renderer in Renderers)
                {
                    renderer.RenderBlock(
                        oldTip: oldTip,
                        newTip: newTip);
                }
            }

            RenderFastForward(
                render: render,
                oldTip: oldTip,
                newTip: newTip,
                branchpoint: branchpoint,
                fastForwardPath: fastForwardPath,
                stateCompleters: stateCompleters);

            if (render && reorg)
            {
                foreach (IRenderer <T> renderer in Renderers)
                {
                    renderer.RenderReorgEnd(
                        oldTip: oldTip,
                        newTip: newTip,
                        branchpoint: branchpoint);
                }
            }
        }
Пример #5
0
        internal long RenderActions(
            IReadOnlyList <ActionEvaluation> evaluations,
            Block <T> block,
            StateCompleterSet <T> stateCompleters)
        {
            DateTimeOffset startTime = DateTimeOffset.UtcNow;

            _logger.Debug(
                "Rendering actions in block #{BlockIndex} {BlockHash}...",
                block.Index,
                block.Hash);

            if (evaluations is null)
            {
                evaluations = ActionEvaluator.Evaluate(block, stateCompleters);
            }

            long count = 0;

            foreach (var evaluation in evaluations)
            {
                foreach (IActionRenderer <T> renderer in ActionRenderers)
                {
                    if (evaluation.Exception is null)
                    {
                        renderer.RenderAction(
                            evaluation.Action,
                            evaluation.InputContext.GetUnconsumedContext(),
                            evaluation.OutputStates);
                    }
                    else
                    {
                        renderer.RenderActionError(
                            evaluation.Action,
                            evaluation.InputContext.GetUnconsumedContext(),
                            evaluation.Exception);
                    }

                    count++;
                }
            }

            TimeSpan renderDuration = DateTimeOffset.Now - startTime;

            _logger
            .ForContext("Tag", "Metric")
            .ForContext("Subtag", "BlockRenderDuration")
            .Debug(
                "Finished rendering {RenderCount} renders for actions in " +
                "block #{BlockIndex} {BlockHash} in {DurationMs:F0}ms.",
                count,
                block.Index,
                block.Hash,
                renderDuration.TotalMilliseconds);
            return(count);
        }
        /// <summary>
        /// Recalculates and complements all <i>missing</i> block states including and upto given
        /// <paramref name="blockHash"/> starting from the last known states in
        /// <see cref="StateStore"/> before <paramref name="blockHash"/> if the states are missing
        /// for <paramref name="blockHash"/>.
        /// </summary>
        /// <param name="blockHash">The starting point for searching backwards.</param>
        internal void ComplementLatestBlockStates(BlockHash blockHash)
        {
            _logger.Verbose("Complementing latest block states upto {BlockHash}...", blockHash);

            // Prevent recursive trial to recalculate & complement incomplete block states by
            // mistake; if the below code works as intended, these state completers must never
            // be invoked.
            StateCompleterSet <T> stateCompleters = StateCompleterSet <T> .Reject;
            Stack <BlockHash>     stack           = new Stack <BlockHash>();
            BlockHash?            pointer         = blockHash;

            while (pointer is { } p)
            {
                HashDigest <SHA256>?stateRootHash = Store.GetStateRootHash(p);
                if (stateRootHash is { } h&& StateStore.ContainsStateRoot(h))
                {
                    break;
                }
Пример #7
0
        internal IReadOnlyList <ActionEvaluation> EvaluateActions(
            Block <T> block,
            StateCompleterSet <T> stateCompleters
            )
        {
            AccountStateGetter   stateGetter;
            AccountBalanceGetter balanceGetter;

            if (block.PreviousHash is null)
            {
                stateGetter   = _ => null;
                balanceGetter = (a, c) => new FungibleAssetValue(c);
            }
            else
            {
                stateGetter = a => _stateGetter(
                    a,
                    block.PreviousHash,
                    stateCompleters.StateCompleter
                    );
                balanceGetter = (address, currency) => _balanceGetter(
                    address,
                    currency,
                    block.PreviousHash,
                    stateCompleters.FungibleAssetStateCompleter
                    );
            }

            ITrie?previousBlockStatesTrie =
                !(_trieGetter is null) && block.PreviousHash is { } h
                    ? _trieGetter(h)
                    : null;

            ImmutableList <ActionEvaluation> txEvaluations = block
                                                             .Evaluate(
                DateTimeOffset.UtcNow, stateGetter, balanceGetter, previousBlockStatesTrie)
                                                             .ToImmutableList();

            return(_blockAction is null
                ? txEvaluations
                : txEvaluations.Add(
                       EvaluateBlockAction(
                           block, txEvaluations, stateCompleters, previousBlockStatesTrie)));
        }
Пример #8
0
        internal long UnrenderActions(
            IReadOnlyList <ActionEvaluation> evaluations,
            Block <T> block,
            StateCompleterSet <T> stateCompleters)
        {
            _logger.Debug(
                "Unrender actions in block #{BlockIndex} {BlockHash}", block?.Index, block?.Hash);

            if (evaluations is null)
            {
                evaluations =
                    ActionEvaluator.Evaluate(block, stateCompleters)
                    .Reverse().ToImmutableList();
            }

            long count = 0;

            foreach (ActionEvaluation evaluation in evaluations)
            {
                foreach (IActionRenderer <T> renderer in ActionRenderers)
                {
                    if (evaluation.Exception is null)
                    {
                        renderer.UnrenderAction(
                            evaluation.Action,
                            evaluation.InputContext.GetUnconsumedContext(),
                            evaluation.OutputStates);
                    }
                    else
                    {
                        renderer.UnrenderActionError(
                            evaluation.Action,
                            evaluation.InputContext.GetUnconsumedContext(),
                            evaluation.Exception);
                    }
                }

                count++;
            }

            return(count);
        }
Пример #9
0
        /// <summary>
        /// Render actions of the given <paramref name="block"/>.
        /// </summary>
        /// <param name="evaluations"><see cref="ActionEvaluation"/>s of the block.  If it is
        /// <c>null</c>, evaluate actions of the <paramref name="block"/> again.</param>
        /// <param name="block"><see cref="Block{T}"/> to render actions.</param>
        /// <param name="stateCompleters">The strategy to complement incomplete block states.
        /// <see cref="StateCompleterSet{T}.Recalculate"/> by default.</param>
        /// <returns>The number of actions rendered.</returns>
        internal long RenderActions(
            IReadOnlyList <ActionEvaluation> evaluations,
            Block <T> block,
            StateCompleterSet <T> stateCompleters)
        {
            _logger.Debug("Render actions in block {blockIndex}: {block}", block?.Index, block);

            if (evaluations is null)
            {
                evaluations = ActionEvaluator.Evaluate(block, stateCompleters);
            }

            long count = 0;

            foreach (var evaluation in evaluations)
            {
                foreach (IActionRenderer <T> renderer in ActionRenderers)
                {
                    if (evaluation.Exception is null)
                    {
                        renderer.RenderAction(
                            evaluation.Action,
                            evaluation.InputContext.GetUnconsumedContext(),
                            evaluation.OutputStates);
                    }
                    else
                    {
                        renderer.RenderActionError(
                            evaluation.Action,
                            evaluation.InputContext.GetUnconsumedContext(),
                            evaluation.Exception);
                    }

                    count++;
                }
            }

            return(count);
        }
        /// <summary>
        /// Recalculates and complements all <i>missing</i> block states including and upto given
        /// <paramref name="blockHash"/> starting from the genesis block.
        /// </summary>
        /// <param name="blockHash">The inclusive limit of target hash to terminate complementation.
        /// </param>
        /// <remarks>
        /// <para>
        /// If a complementation of the entire blockchain is needed, call with the tip hash of the
        /// <see cref="BlockChain{T}"/>.
        /// </para>
        /// <para>
        /// Unlike <see cref="RecalculateBlockStates"/>, this method skips recalculations if states
        /// are found for intermediate blocks. This may not be fully secure if states for
        /// blocks in <see cref="IStateStore"/> are somehow corrupted.
        /// </para>
        /// </remarks>
        internal void ComplementAllBlockStates(BlockHash blockHash)
        {
            _logger.Verbose("Complementing all block states upto {BlockHash}...", blockHash);

            // Prevent recursive trial to recalculate & complement incomplete block states by
            // mistake; if the below code works as intended, these state completers must never
            // be invoked.
            StateCompleterSet <T> stateCompleters = StateCompleterSet <T> .Reject;

            // Calculates and fills the incomplete states on the fly.
            foreach (BlockHash hash in BlockHashes)
            {
                Block <T> block = this[hash];
                if (StateStore.ContainsStateRoot(block.StateRootHash))
                {
                    continue;
                }

                IReadOnlyList <ActionEvaluation> evaluations = ActionEvaluator.Evaluate(
                    block,
                    stateCompleters);

                _rwlock.EnterWriteLock();
                try
                {
                    SetStates(block, evaluations);
                }
                finally
                {
                    _rwlock.ExitWriteLock();
                }

                if (blockHash.Equals(hash))
                {
                    break;
                }
            }
        }
Пример #11
0
        internal System.Action Swap(
            BlockChain <T> other,
            bool render,
            StateCompleterSet <T>?stateCompleters = null)
        {
            if (other is null)
            {
                throw new ArgumentNullException(nameof(other));
            }

            // As render/unrender processing requires every step's states from the branchpoint
            // to the new/stale tip, incomplete states need to be complemented anyway...
            StateCompleterSet <T> completers = stateCompleters ?? StateCompleterSet <T> .Recalculate;

            if (Tip.Equals(other.Tip))
            {
                // If it's swapped for a chain with the same tip, it means there is no state change.
                // Hence render is unnecessary.
                render = false;
            }
            else
            {
                _logger.Debug(
                    "The blockchain was reorged from " +
                    "{OldChainId} (#{OldTipIndex} {OldTipHash}) " +
                    "to {NewChainId} (#{NewTipIndex} {NewTipHash}).",
                    Id,
                    Tip.Index,
                    Tip.Hash,
                    other.Id,
                    other.Tip.Index,
                    other.Tip.Hash);
            }

            System.Action renderSwap = () => { };

            _rwlock.EnterUpgradeableReadLock();
            try
            {
                // Finds the branch point.
                Block <T> branchpoint = FindTopCommon(this, other);

                if (branchpoint is null)
                {
                    const string msg =
                        "A chain cannot be reorged into a heterogeneous chain with a " +
                        "different genesis.";
                    throw new InvalidGenesisBlockException(Genesis.Hash, other.Genesis.Hash, msg);
                }

                _logger.Debug(
                    "The branchpoint is #{BranchpointIndex} {BranchpointHash}.",
                    branchpoint.Index,
                    branchpoint
                    );

                Block <T> oldTip = Tip, newTip = other.Tip;

                ImmutableList <Block <T> > rewindPath      =
                    GetRewindPath(this, branchpoint.Hash);
                ImmutableList <Block <T> > fastForwardPath =
                    GetFastForwardPath(other, branchpoint.Hash);

                // If there is no rewind, it is not considered as a reorg.
                bool reorg = rewindPath.Count > 0;

                _rwlock.EnterWriteLock();
                try
                {
                    IEnumerable <long> LongRange(long start, long count)
                    {
                        for (long i = 0; i < count; i++)
                        {
                            yield return(start + i);
                        }
                    }

                    ImmutableHashSet <Transaction <T> >
                    GetTxsWithRange(BlockChain <T> chain, Block <T> start, Block <T> end) =>
                    LongRange(start.Index + 1, end.Index - start.Index)
                    .SelectMany(index => chain[index].Transactions)
                    .ToImmutableHashSet();

                    // It assumes reorg is small size. If it was big, this may be heavy task.
                    ImmutableHashSet <Transaction <T> > txsToStage =
                        GetTxsWithRange(this, branchpoint, Tip);
                    ImmutableHashSet <Transaction <T> > txsToUnstage =
                        GetTxsWithRange(other, branchpoint, other.Tip);
                    foreach (Transaction <T> tx in txsToStage)
                    {
                        StagePolicy.Stage(this, tx);
                    }

                    Guid obsoleteId = Id;
                    Id = other.Id;
                    Store.SetCanonicalChainId(Id);
                    _blocks = new BlockSet <T>(Policy.GetHashAlgorithm, Store);
                    foreach (Transaction <T> tx in txsToUnstage)
                    {
                        StagePolicy.Unstage(this, tx.Id);
                    }

                    TipChanged?.Invoke(this, (oldTip, newTip));

                    Store.DeleteChainId(obsoleteId);
                }
                finally
                {
                    _rwlock.ExitWriteLock();
                }

                renderSwap = () => RenderSwap(
                    render: render,
                    oldTip: oldTip,
                    newTip: newTip,
                    branchpoint: branchpoint,
                    rewindPath: rewindPath,
                    fastForwardPath: fastForwardPath,
                    stateCompleters: completers);
            }
            finally
            {
                _rwlock.ExitUpgradeableReadLock();
            }

            return(renderSwap);
        }
Пример #12
0
        // FIXME it's very dangerous because replacing Id means
        // ALL blocks (referenced by MineBlock(), etc.) will be changed.
        internal System.Action Swap(
            BlockChain <T> other,
            bool render,
            StateCompleterSet <T>?stateCompleters = null)
        {
            if (other is null)
            {
                throw new ArgumentNullException(nameof(other));
            }

            // As render/unrender processing requires every step's states from the branchpoint
            // to the new/stale tip, incomplete states need to be complemented anyway...
            StateCompleterSet <T> completers = stateCompleters ?? StateCompleterSet <T> .Recalculate;

            if (Tip.Equals(other.Tip))
            {
                // If it's swapped for a chain with the same tip, it means there is no state change.
                // Hence render is unnecessary.
                render = false;
            }
            else
            {
                _logger.Debug(
                    "The blockchain was reorged from " +
                    "{OldChainId} (#{OldTipIndex} {OldTipHash}) " +
                    "to {NewChainId} (#{NewTipIndex} {NewTipHash}).",
                    Id,
                    Tip.Index,
                    Tip.Hash,
                    other.Id,
                    other.Tip.Index,
                    other.Tip.Hash);
            }

            System.Action renderSwap = () => { };

            _rwlock.EnterUpgradeableReadLock();
            try
            {
                // Finds the branch point.
                Block <T> branchpoint = FindTopCommon(this, other);

                if (branchpoint is null)
                {
                    const string msg =
                        "A chain cannot be reorged into a heterogeneous chain with a " +
                        "different genesis.";
                    throw new InvalidGenesisBlockException(Genesis.Hash, other.Genesis.Hash, msg);
                }

                _logger.Debug(
                    "The branchpoint is #{BranchpointIndex} {BranchpointHash}.",
                    branchpoint.Index,
                    branchpoint
                    );

                Block <T> oldTip = Tip, newTip = other.Tip;

                ImmutableList <Block <T> > rewindPath      =
                    GetRewindPath(this, branchpoint.Hash);
                ImmutableList <Block <T> > fastForwardPath =
                    GetFastForwardPath(other, branchpoint.Hash);

                // If there is no rewind, it is not considered as a reorg.
                bool reorg = rewindPath.Count > 0;

                _rwlock.EnterWriteLock();
                try
                {
                    IEnumerable <Transaction <T> >
                    GetTxsWithRange(BlockChain <T> chain, Block <T> start, Block <T> end)
                    => Enumerable
                    .Range((int)start.Index + 1, (int)(end.Index - start.Index))
                    .SelectMany(x =>
                    {
                        // FIXME: Change the type of IBlockContent<T>.Transactions to
                        // IImmutableSet<Transaction<T>>, and define a distinct property
                        // to Block<T> for this ordering.
                        Block <T> block = chain[x];
                        return(ActionEvaluator <T> .OrderTxsForEvaluation(
                                   block.ProtocolVersion,
                                   block.Transactions,
                                   block.PreEvaluationHash
                                   ));
                    });

                    // It assumes reorg is small size. If it was big, this may be heavy task.
                    ImmutableHashSet <Transaction <T> > unstagedTxs =
                        GetTxsWithRange(this, branchpoint, Tip).ToImmutableHashSet();
                    ImmutableHashSet <Transaction <T> > stageTxs =
                        GetTxsWithRange(other, branchpoint, other.Tip).ToImmutableHashSet();
                    ImmutableHashSet <Transaction <T> > restageTxs = unstagedTxs.Except(stageTxs);
                    foreach (Transaction <T> restageTx in restageTxs)
                    {
                        StagePolicy.Stage(this, restageTx);
                    }

                    Guid obsoleteId = Id;
                    Id = other.Id;
                    Store.SetCanonicalChainId(Id);
                    _blocks = new BlockSet <T>(Policy.GetHashAlgorithm, Store);
                    TipChanged?.Invoke(this, (oldTip, newTip));

                    Store.DeleteChainId(obsoleteId);
                }
                finally
                {
                    _rwlock.ExitWriteLock();
                }

                renderSwap = () => RenderSwap(
                    render: render,
                    oldTip: oldTip,
                    newTip: newTip,
                    branchpoint: branchpoint,
                    rewindPath: rewindPath,
                    fastForwardPath: fastForwardPath,
                    stateCompleters: completers);
            }
            finally
            {
                _rwlock.ExitUpgradeableReadLock();
            }

            return(renderSwap);
        }