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); } }
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()); }