// 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) { // Finds the branch point. Block <T> topmostCommon = 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; } } // 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] ) { var actions = b.EvaluateActionsPerTx(a => GetStates(new[] { a }, b.PreviousHash).GetValueOrDefault(a) ).Reverse(); foreach (var(_, evaluation) in actions) { evaluation.Action.Unrender( evaluation.InputContext, evaluation.OutputStates ); } } try { _rwlock.EnterWriteLock(); Id = other.Id; Blocks = new BlockSet <T>(Store); Transactions = new TransactionSet <T>(Store); } finally { _rwlock.ExitWriteLock(); } // 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) { var actions = b.EvaluateActionsPerTx(a => GetStates(new[] { a }, b.PreviousHash).GetValueOrDefault(a) ); foreach (var(_, evaluation) in actions) { evaluation.Action.Render( evaluation.InputContext, evaluation.OutputStates ); } } }
// 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 ); } } } }
// 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) { _logger.Debug( "Swaping block chain. (from: {fromChainId}) (to: {toChainId})", Id, other.Id); // Finds the branch point. Block <T> topmostCommon = null; if (render && !(Tip is null || other.Tip is null)) { long shorterHeight = Math.Min(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 = 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 = b.EvaluateActionsPerTx(a => GetState(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) { _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."); } }