示例#1
0
        internal void Append(
            Block <T> block,
            DateTimeOffset currentTime,
            bool evaluateActions,
            bool renderActions
            )
        {
            if (!evaluateActions && renderActions)
            {
                throw new ArgumentException(
                          $"{nameof(renderActions)} option requires {nameof(evaluateActions)} " +
                          "to be turned on.",
                          nameof(renderActions)
                          );
            }

            _rwlock.EnterUpgradeableReadLock();
            try
            {
                InvalidBlockException e =
                    Policy.ValidateNextBlock(this, block);

                if (!(e is null))
                {
                    throw e;
                }

                HashDigest <SHA256>?tip = Store.IndexBlockHash(Id, -1);

                var nonceDeltas = new Dictionary <Address, long>();

                // block.Transactions have already been sorted by
                // the tx nounce order when the block was created
                foreach (Transaction <T> tx1 in block.Transactions)
                {
                    Address txSigner = tx1.Signer;
                    nonceDeltas.TryGetValue(txSigner, out var nonceDelta);

                    long expectedNonce = nonceDelta + Store.GetTxNonce(Id, txSigner);

                    if (!expectedNonce.Equals(tx1.Nonce))
                    {
                        throw new InvalidTxNonceException(
                                  tx1.Id,
                                  expectedNonce,
                                  tx1.Nonce,
                                  "Transaction nonce is invalid."
                                  );
                    }

                    nonceDeltas[txSigner] = nonceDelta + 1;
                }

                _rwlock.EnterWriteLock();
                try
                {
                    Block <T> prevTip = Tip;
                    Blocks[block.Hash] = block;
                    foreach (KeyValuePair <Address, long> pair in nonceDeltas)
                    {
                        Store.IncreaseTxNonce(Id, pair.Key, pair.Value);
                    }

                    Store.AppendIndex(Id, block.Hash);
                    var tipChangedEventArgs = new TipChangedEventArgs
                    {
                        PreviousIndex = prevTip?.Index,
                        PreviousHash  = prevTip?.Hash,
                        Index         = block.Index,
                        Hash          = block.Hash,
                    };
                    TipChanged?.Invoke(this, tipChangedEventArgs);
                    ISet <TxId> txIds = block.Transactions
                                        .Select(t => t.Id)
                                        .ToImmutableHashSet();

                    Store.UnstageTransactionIds(txIds);
                }
                finally
                {
                    _rwlock.ExitWriteLock();
                }
            }
            finally
            {
                _rwlock.ExitUpgradeableReadLock();
            }

            if (evaluateActions)
            {
                ExecuteActions(block, renderActions);
            }
        }
示例#2
0
        // FIXME it's very dangerous because replacing Id means
        // ALL blocks (referenced by MineBlock(), etc.) will be changed.
        // we need to add a synchronization mechanism to handle this correctly.
        internal void Swap(BlockChain <T> other, bool render)
        {
            // Finds the branch point.
            Block <T> topmostCommon = null;

            if (render && !(Tip is null || other.Tip is null))
            {
                long shorterHeight =
                    Math.Min(this.LongCount(), other.LongCount()) - 1;
                for (
                    Block <T> t = this[shorterHeight], o = other[shorterHeight];
                    t.PreviousHash is HashDigest <SHA256> tp &&
                    o.PreviousHash is HashDigest <SHA256> op;
                    t = Blocks[tp], o = other.Blocks[op]
                    )
                {
                    if (t.Equals(o))
                    {
                        topmostCommon = t;
                        break;
                    }
                }
            }

            if (render)
            {
                // Unrender stale actions.
                for (
                    Block <T> b = Tip;
                    !(b is null) && b.Index > (topmostCommon?.Index ?? -1) &&
                    b.PreviousHash is HashDigest <SHA256> ph;
                    b = Blocks[ph]
                    )
                {
                    List <ActionEvaluation> evaluations = b.EvaluateActionsPerTx(a =>
                                                                                 GetStates(new[] { a }, b.PreviousHash).GetValueOrDefault(a))
                                                          .Select(te => te.Item2).ToList();

                    if (Policy.BlockAction is IAction)
                    {
                        evaluations.Add(EvaluateBlockAction(b, evaluations));
                    }

                    evaluations.Reverse();

                    foreach (var evaluation in evaluations)
                    {
                        evaluation.Action.Unrender(
                            evaluation.InputContext,
                            evaluation.OutputStates
                            );
                    }
                }
            }

            try
            {
                _rwlock.EnterWriteLock();

                var tipChangedEventArgs = new TipChangedEventArgs
                {
                    PreviousHash  = Tip?.Hash,
                    PreviousIndex = Tip?.Index,
                    Hash          = other.Tip.Hash,
                    Index         = other.Tip.Index,
                };
                Guid obsoleteId = Id;
                Id = other.Id;
                Store.SetCanonicalChainId(Id);
                Blocks = new BlockSet <T>(Store);
                TipChanged?.Invoke(this, tipChangedEventArgs);
                Transactions = new TransactionSet <T>(Store);
                Store.DeleteChainId(obsoleteId);
            }
            finally
            {
                _rwlock.ExitWriteLock();
            }

            if (render)
            {
                // Render actions that had been behind.
                IEnumerable <Block <T> > blocksToRender =
                    topmostCommon is Block <T> branchPoint
                        ? this.SkipWhile(b => b.Index <= branchPoint.Index)
                        : this;

                foreach (Block <T> b in blocksToRender)
                {
                    List <ActionEvaluation> evaluations = b.EvaluateActionsPerTx(a =>
                                                                                 GetStates(new[] { a }, b.PreviousHash).GetValueOrDefault(a))
                                                          .Select(te => te.Item2).ToList();

                    if (Policy.BlockAction is IAction)
                    {
                        evaluations.Add(EvaluateBlockAction(b, evaluations));
                    }

                    foreach (var evaluation in evaluations)
                    {
                        evaluation.Action.Render(
                            evaluation.InputContext,
                            evaluation.OutputStates
                            );
                    }
                }
            }
        }
示例#3
0
        // FIXME it's very dangerous because replacing Id means
        // ALL blocks (referenced by MineBlock(), etc.) will be changed.
        // we need to add a synchronization mechanism to handle this correctly.
        internal void Swap(BlockChain <T> other, bool render)
        {
            if (other?.Tip is null)
            {
                throw new ArgumentException(
                          $"The chain to be swapped is invalid. Id: {other?.Id}, Tip: {other?.Tip}",
                          nameof(other));
            }

            _logger.Debug(
                "Swapping block chain. (from: {fromChainId}) (to: {toChainId})", Id, other.Id);

            // Finds the branch point.
            Block <T> topmostCommon = null;

            if (render && !(Tip is null))
            {
                long shorterHeight =
                    Math.Min(Count, other.Count) - 1;
                for (
                    Block <T> t = this[shorterHeight], o = other[shorterHeight];
                    t.PreviousHash is HashDigest <SHA256> tp &&
                    o.PreviousHash is HashDigest <SHA256> op;
                    t = this[tp], o = other[op]
                    )
                {
                    if (t.Equals(o))
                    {
                        topmostCommon = t;
                        break;
                    }
                }
            }

            _logger.Debug(
                "Branchpoint is {branchPoint} (at {index})", topmostCommon, topmostCommon?.Index);

            if (render)
            {
                _logger.Debug("Unrendering abandoned actions...");

                // Unrender stale actions.
                for (
                    Block <T> b = Tip;
                    !(b is null) && b.Index > (topmostCommon?.Index ?? -1) &&
                    b.PreviousHash is HashDigest <SHA256> ph;
                    b = this[ph]
                    )
                {
                    List <ActionEvaluation> evaluations = EvaluateActions(b).ToList();
                    evaluations.Reverse();

                    foreach (var evaluation in evaluations)
                    {
                        _logger.Debug("Unrender action {action}", evaluation.Action);
                        evaluation.Action.Unrender(
                            evaluation.InputContext,
                            evaluation.OutputStates
                            );
                    }
                }

                _logger.Debug($"Unrender for {nameof(Swap)}() is completed.");
            }

            try
            {
                _rwlock.EnterWriteLock();

                var tipChangedEventArgs = new TipChangedEventArgs
                {
                    PreviousHash  = Tip?.Hash,
                    PreviousIndex = Tip?.Index,
                    Hash          = other.Tip.Hash,
                    Index         = other.Tip.Index,
                };
                Guid obsoleteId = Id;
                Id = other.Id;
                Store.SetCanonicalChainId(Id);
                _blocks = new BlockSet <T>(Store);
                TipChanged?.Invoke(this, tipChangedEventArgs);
                _transactions = new TransactionSet <T>(Store);
                Store.DeleteChainId(obsoleteId);
            }
            finally
            {
                _rwlock.ExitWriteLock();
            }

            if (render)
            {
                _logger.Debug("Rendering actions in new chain");

                // Render actions that had been behind.
                long startToRenderIndex = topmostCommon is Block <T> branchPoint
                    ? branchPoint.Index + 1
                    : 0;

                RenderBlocks(startToRenderIndex);
                _logger.Debug($"Render for {nameof(Swap)}() is completed.");
            }
        }