Example #1
0
        public async Task HashFullAsync_NonEmptyTree()
        {
            var tree = CreateMerkleTree();
            var l    = new byte[5][];

            for (byte i = 0; i < 5; i++)
            {
                var data = new byte[32];
                data[0] = i;
                await tree.AppendAsync(data);

                l[i] = _hasher.HashLeaf(data);
            }

            var rootHash = _hasher.HashNode(_hasher.HashNode(_hasher.HashNode(l[0], l[1]), _hasher.HashNode(l[2], l[3])), l[4]);

            Assert.Equal(await tree.HashFullAsync(0, 5), rootHash);
        }
        public async Task <SignedTreeHead> AppendAsync(byte[] hash)
        {
            MerkleNode leafNode = new MerkleNode(_hasher.HashLeaf(hash));
            var        session  = await _client.StartSessionAsync();

            try
            {
                //session.StartTransaction();
                var root = await _roots.Find(session, _ => true)
                           .Sort(new SortDefinitionBuilder <MerkleRoot>()
                                 .Descending(r => r.TreeSize)).Limit(1).SingleOrDefaultAsync();

                if (root is null)
                {
                    MerkleLeaf leaf = new MerkleLeaf(0, _hasher.HashLeaf(hash));
                    await _leaves.InsertOneAsync(session, leaf);

                    await _nodes.InsertOneAsync(session, leafNode);

                    root = new MerkleRoot(leafNode.Level, leafNode.Hash, 1ul, _signer.Sign(leafNode.Hash), "");
                    await _roots.InsertOneAsync(session, root);
                }
                else
                {
                    var existingLeaf = await _leaves.Find(session, n => n.Hash == leafNode.Hash).AnyAsync();

                    if (existingLeaf)
                    {
                        _logger.LogInformation("The leaf {leafHash} already exist in the tree {treeHash}. Nothing was appended.", hash.ByteToHex(), root.Hash.ByteToHex());
                        return(CreateSignedTreeHead(root));
                    }
                    else
                    {
                        var nodesToInsert = new List <MerkleNode>(root.Level + 1)
                        {
                            leafNode
                        };
                        var stack       = new Stack <MerkleNode>(root.Level + 1);
                        var currentNode = await _nodes.Find(session, n => n.Hash == root.Hash).SingleAsync();

                        MerkleNode node;
                        if (currentNode.IsFull)
                        {
                            bool isFullNode = currentNode.Level == leafNode.Level && leafNode.IsFull;
                            node = new MerkleNode(new[] { currentNode.Hash, leafNode.Hash }, currentNode.Level + 1, _hasher.HashNode(currentNode.Hash, leafNode.Hash), isFullNode);
                            nodesToInsert.Add(node);
                        }
                        else
                        {
                            do
                            {
                                var left = await _nodes.Find(session, n => n.Hash == currentNode.Left).SingleAsync();

                                stack.Push(left);
                                var right = await _nodes.Find(session, n => n.Hash == currentNode.Right).SingleAsync();

                                currentNode = right;
                            } while (!currentNode.IsFull);

                            stack.Push(currentNode);
                            stack.Push(leafNode);
                            do
                            {
                                var  right      = stack.Pop();
                                var  left       = stack.Pop();
                                bool isFullNode = left.Level == right.Level && left.IsFull && right.IsFull;
                                node = new MerkleNode(new[] { left.Hash, right.Hash }, left.Level + 1, _hasher.HashNode(left.Hash, right.Hash), isFullNode);

                                nodesToInsert.Add(node);
                                stack.Push(node);
                            } while (stack.Count != 1);
                        }

                        MerkleLeaf leaf = new MerkleLeaf(root.TreeSize, _hasher.HashLeaf(hash));
                        await _leaves.InsertOneAsync(session, leaf);

                        await _nodes.InsertManyAsync(session, nodesToInsert);

                        root = new MerkleRoot(node.Level, node.Hash, root.TreeSize + 1, _signer.Sign(node.Hash), "");
                        await _roots.InsertOneAsync(session, root);
                    }
                }

                //session.CommitTransaction();
                _logger.LogInformation("The leaf {leafHash} was appended to tree {treeHash}.", hash.ByteToHex(), root.Hash.ByteToHex());
                return(CreateSignedTreeHead(root));
            }
            catch (Exception e)
            {
                //session.AbortTransaction();
                _logger.LogError(e, "An error occurred while appending the leaf {leaf}.", hash.ByteToHex());
                throw new MerkleTreeException($"An error occurred while appending the leaf {hash.ByteToHex()}.", e);
            }
        }
Example #3
0
        public IntegrityResult VerifyConsistency(ulong oldSize, ulong newSize, byte[] oldRoot, byte[] newRoot, byte[][] proof)
        {
            if (oldSize < 0)
            {
                throw new ArgumentOutOfRangeException("Negative tree size", nameof(oldSize));
            }

            if (newSize < 0)
            {
                throw new ArgumentOutOfRangeException("Negative tree size", nameof(newSize));
            }

            if (oldSize > newSize)
            {
                throw new ArgumentOutOfRangeException($"Older tree has bigger size ({oldSize} vs {newSize}), did you supply inputs in the wrong order?", nameof(oldSize));
            }

            if (oldSize == newSize)
            {
                if (oldRoot.SequenceEqual(newRoot))
                {
                    if (proof.Length != 0)
                    {
                        _logger.LogWarning("Trees are identical, ignoring proof");
                    }

                    return(IntegrityResult.Succeeded());
                }
                else
                {
                    return(IntegrityResult.DifferentHashSameSize());
                }
            }

            if (oldSize == 0)
            {
                if (proof.Length != 0)
                {
                    return(IntegrityResult.ProofTooLong());
                }

                return(IntegrityResult.Succeeded());
            }

            var node     = oldSize - 1;
            var lastNode = newSize - 1;

            while ((node & 1) != 0)
            {
                node     /= 2;
                lastNode /= 2;
            }

            var proofQueue = new Queue <byte[]>(proof);

            while (proofQueue.Count != 0)
            {
                byte[] p;

                byte[] computedOldHash;
                byte[] computedNewHash;
                byte[] nextHode;
                if (node != 0)
                {
                    if (!proofQueue.TryDequeue(out p !))
                    {
                        return(IntegrityResult.ProofTooShort());
                    }

                    computedNewHash = computedOldHash = p;
                }
                else
                {
                    computedNewHash = computedOldHash = oldRoot;
                }

                while (node != 0)
                {
                    if ((node & 1) != 0)
                    {
                        if (!proofQueue.TryDequeue(out p !))
                        {
                            return(IntegrityResult.ProofTooShort());
                        }

                        nextHode        = p;
                        computedOldHash = _hasher.HashNode(nextHode, computedOldHash);
                        computedNewHash = _hasher.HashNode(nextHode, computedNewHash);
                    }
                    else if (node < lastNode)
                    {
                        if (!proofQueue.TryDequeue(out p !))
                        {
                            return(IntegrityResult.ProofTooShort());
                        }

                        computedNewHash = _hasher.HashNode(computedNewHash, p);
                    }

                    node     /= 2;
                    lastNode /= 2;
                }

                while (lastNode != 0)
                {
                    if (!proofQueue.TryDequeue(out p !))
                    {
                        return(IntegrityResult.ProofTooShort());
                    }

                    computedNewHash = _hasher.HashNode(computedNewHash, p);
                    lastNode       /= 2;
                }

                if (!computedNewHash.SequenceEqual(newRoot))
                {
                    return(IntegrityResult.HashMismatch(newRoot, computedNewHash));
                }
                else if (!computedOldHash.SequenceEqual(oldRoot))
                {
                    return(IntegrityResult.HashMismatch(oldRoot, computedOldHash));
                }
            }

            if (proofQueue.Count > 0)
            {
                return(IntegrityResult.ProofTooLong());
            }

            return(IntegrityResult.Succeeded());
        }