public CommittedBlock(int blockHeight, BlockId blockId, BlockAlias alias, BlockAlias parent) { BlockId = blockId; BlockHeight = blockHeight; Alias = alias; Parent = parent; }
/// <summary> /// Returns whether a block is considered pruneable (i.e. it can /// be removed from the chain). The necessary criteria for this decision /// are that it is not on the main chain and that its first ancestor on /// the main chain is below or equal to the <see cref="_pruneLimit"/>. /// </summary> public bool IsPruneable(BlockAlias block) { // block is on the main chain if (!_quasiOrphans.ContainsKey(block)) { return(false); } // TODO: [vermorel] I suggest to usethe code below instead (more readable) // to be double checked //if (_quasiOrphans.TryGetValue(block, out var parent)) //{ // while (_quasiOrphans.Keys.Contains(parent)) // { // _quasiOrphans.TryGetValue(parent, out parent); // } //} // Find first ancestor on the main chain and test if it is below the limit _quasiOrphans.TryGetValue(block, out var parent); while (_quasiOrphans.Keys.Contains(parent)) { _quasiOrphans.TryGetValue(parent, out parent); } return(parent.IsPrior(_pruneLimit) || parent == _pruneLimit); }
/// <summary> /// Create an optimized lineage that knows the quasi orphans and the /// prune limit, so that the query functions <see cref="IsAncestor"/> /// and <see cref="IsPruneable"/> can easily be executed. /// </summary> public OptimizedLineage(IReadOnlyDictionary <BlockAlias, BlockAlias> quasiOrphans, BlockAlias pruneLimit) { // TODO: [vermorel] check arguments _quasiOrphans = quasiOrphans; _pruneLimit = pruneLimit; }
/// <summary> /// Appends a new block to the end of the current committedBlocks. /// The first block cannot be added via this method, /// <see cref="OpenFirstBlock"/> will have to be used instead. /// </summary> /// <returns>The alias of the added block.</returns> public UncommittedBlock OpenBlock(BlockAlias parentAlias) { // Verify if block to add is empty if (parentAlias == BlockAlias.GenesisParent) { throw new ArgumentException("Blocks cannot have GenesisParent as parent."); } var parentIsNotInCBlock = !FindCommitted(block => block.Alias == parentAlias, out var parent); // Parent has to be a valid block. var parentIsNotInUBlock = !FindUncommitted(block => block.Alias == parentAlias, out var parentUnc); if (parentIsNotInCBlock && parentIsNotInUBlock) { throw new ArgumentException($"{parentAlias} does not exist."); } // TOOD: [vermorel] Why do we have this small forest of tests? Comment needed // TODO: [vermorel] Introduce a Linq-like helper 'QuickLast' to avoid the pattern '_array[_array.Length - 1]'. BlockAlias newBlockAlias; if (_uncommitted.Count == 0) { newBlockAlias = _committed[_committed.Count - 1].Alias.GetNext(); } else if (_committed.Count == 0) { newBlockAlias = _uncommitted[_uncommitted.Count - 1].Alias.GetNext(); } else // a committed block can have a higher alias than a non-committed one if they are on different forked chains { newBlockAlias = BlockAlias.GetJoinNext(_committed[_committed.Count - 1].Alias, _uncommitted[_uncommitted.Count - 1].Alias); } var newBlockIdentifier = TempBlockId.Create(); int newBlockBlockHeight = 1 + (parentIsNotInCBlock ? parentUnc.BlockHeight : parent.BlockHeight); var newBlock = new UncommittedBlock(newBlockBlockHeight, newBlockIdentifier, newBlockAlias, parentAlias); _uncommitted.Add(newBlock); if (newBlockBlockHeight > BlockchainHeight) { BlockchainHeight = newBlockBlockHeight; } return(newBlock); }
/// <summary> /// Deletes the block from the blockchain identified /// by the alias given as argument, if it exists. /// No conditions are attached to a deletion. /// The calling party has to make sure the blockchain /// does not break by deleting the block. /// </summary> public bool DeleteBlock(BlockAlias alias) { if (RetrieveUncommittedBlock(alias, out var blockU)) { _uncommitted.Remove(blockU); RecalculateBlockchainHeight(); return(true); } if (RetrieveCommittedBlock(alias, out var blockC)) { _committed.Remove(blockC); RecalculateBlockchainHeight(); return(true); } return(false); }
/// <summary> /// If a block is recent, it can be of interest to know whether /// another block is its ancestor. /// </summary> public bool IsAncestor(BlockAlias tip, BlockAlias maybeAncestor) { // We climb down the side chain until we find the parent, // a block that is lower than the maybeAncestor or we reach the main chain var parent = tip; do { // Move up the side chain looking for an ancestor on the main branch // A block counts as its own ancestor if (parent == maybeAncestor) { return(true); } // The ancestor has to have a smaller number than anything that comes afterward. if (parent.IsPrior(maybeAncestor)) { return(false); } } while (_quasiOrphans.TryGetValue(parent, out parent)); /* * o * | * o * | * o----o <- maybeAncestorAlias * | * o <- parent * / \ * mainChain -> o o <- blockAlias */ // Here, 'block' is the most recent ancestor of the original block // on the main chain, and 'maybeAncestor < block', so the ancestor // will be an ancestor if and only if it is also on the main chain. return(!_quasiOrphans.ContainsKey(maybeAncestor)); }
/// <summary> /// Commits the uncommitted block referenced by its block alias. /// The hash ID of the future committed block has to be given as argument to the /// function. /// </summary> /// <returns></returns> public bool CommitBlock(BlockAlias alias, BlockId blockId) { if (!RetrieveUncommittedBlock(alias, out var toCommit)) { throw new ArgumentException($"ID {alias} does not exist or is already committed."); } // verify that parent of block to commit is committed itself if (!RetrieveCommittedBlock(toCommit.Parent, out _) && !blockId.Equals(BlockId.Genesis)) { throw new ArgumentException($"Parent {toCommit.Parent} is not yet committed."); } var blockToCommit = toCommit; _uncommitted.Remove(toCommit); var newBlock = new CommittedBlock(blockToCommit.BlockHeight, blockId, blockToCommit.Alias, blockToCommit.Parent); _committed.Add(newBlock); return(true); }
/// <summary> /// Returns the block metadata of the uncommitted block /// identified by the alias given as argument. /// </summary> public bool RetrieveUncommittedBlock(BlockAlias alias, out UncommittedBlock block) { return(FindUncommitted(b => b.Alias == alias, out block)); }
public BlockEvent(BlockAlias alias, BlockEventType type) { _value = alias.Value | (((uint)type) << Shift); }