Beispiel #1
0
 public CommittedBlock(int blockHeight, BlockId blockId, BlockAlias alias, BlockAlias parent)
 {
     BlockId     = blockId;
     BlockHeight = blockHeight;
     Alias       = alias;
     Parent      = parent;
 }
Beispiel #2
0
        /// <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);
        }
Beispiel #3
0
        /// <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;
        }
Beispiel #4
0
        /// <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);
        }
Beispiel #5
0
        /// <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);
        }
Beispiel #6
0
        /// <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));
        }
Beispiel #7
0
        /// <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);
        }
Beispiel #8
0
 /// <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));
 }
Beispiel #9
0
 public BlockEvent(BlockAlias alias, BlockEventType type)
 {
     _value = alias.Value | (((uint)type) << Shift);
 }