public byte[] RecalculateHash(ulong root) { var node = GetNodeById(root); if (node is null) { return new byte[] {} } ; switch (node) { case InternalNode internalNode: List <byte[]> childrenHashes = new List <byte[]>(); foreach (var child in internalNode.Children) { childrenHashes.Add(GetNodeById(child).Hash); } return(childrenHashes .Zip(InternalNode.GetChildrenLabels(internalNode.ChildrenMask), (bytes, i) => new[] { i }.Concat(bytes)) .SelectMany(bytes => bytes) .KeccakBytes()); case LeafNode leafNode: return(leafNode.KeyHash.Length.ToBytes().Concat(leafNode.KeyHash).Concat(leafNode.Value).KeccakBytes()); } return(new byte[] {}); }
private ulong ModifyInternalNode(ulong id, InternalNode node, byte h, ulong value, byte[]?valueHash) { if (value == 0 && node.GetChildByHash(h) != 0 && node.Children.Count() == 2) { // we have to handle case when one of two children is deleted and internal node is folded to leaf var secondChild = node.Children.First(child => child != node.GetChildByHash(h)); // fold only if secondChild is also a leaf if (GetNodeById(secondChild).Type == NodeType.Leaf) { return(secondChild); } } if (value != 0 && node.GetChildByHash(h) != 0 && node.Children.Count() == 1 && GetNodeById(value).Type == NodeType.Leaf) { return(value); } var modified = InternalNode.ModifyChildren( node, h, value, node.Children.Select(id => GetNodeById(id)?.Hash ?? throw new InvalidOperationException()), valueHash ); if (modified == null) { return(0u); } var newId = _versionFactory.NewVersion(); _nodeCache[newId] = modified; return(newId); }
public ulong InsertAllNodes(ulong root, IDictionary <ulong, IHashTrieNode> allTrieNodes) { if (root == 0) { return(0); } if (allTrieNodes.TryGetValue(root, out var node)) { switch (node) { case InternalNode internalNode: List <byte[]> childrenHash = new List <byte[]>(); List <ulong> children = new List <ulong>(); var childrenMask = internalNode.ChildrenMask; foreach (var child in internalNode.Children) { ulong childRoot = InsertAllNodes(child, allTrieNodes); var childNode = GetNodeById(childRoot); children.Add(childRoot); childrenHash.Add(childNode.Hash); } var newInternalNodeId = _versionFactory.NewVersion(); _nodeCache[newInternalNodeId] = new InternalNode(childrenMask, children, childrenHash); return(newInternalNodeId); case LeafNode leafNode: var newLeafNodeId = _versionFactory.NewVersion(); _nodeCache[newLeafNodeId] = leafNode; return(newLeafNodeId); } } else { throw new InvalidOperationException(); } return(0); }
private ulong SplitLeafNode( ulong id, LeafNode leafNode, int height, IReadOnlyList <byte> keyHash, IEnumerable <byte> value ) { var firstFragment = HashFragment(leafNode.KeyHash, height); var secondFragment = HashFragment(keyHash, height); if (firstFragment != secondFragment) { var secondSon = NewLeafNode(keyHash, value); var secondSonHash = GetNodeById(secondSon)?.Hash; if (secondSonHash is null) { throw new InvalidOperationException(); } var newId = _versionFactory.NewVersion(); _nodeCache[newId] = InternalNode.WithChildren( new[] { id, secondSon }, new[] { firstFragment, secondFragment }, new[] { leafNode.Hash, secondSonHash } ); return(newId); } else { var son = SplitLeafNode(id, leafNode, height + 1, keyHash, value); var sonHash = GetNodeById(son)?.Hash; if (sonHash is null) { throw new InvalidOperationException(); } var newId = _versionFactory.NewVersion(); _nodeCache[newId] = InternalNode.WithChildren(new[] { son }, new[] { firstFragment }, new[] { sonHash }); return(newId); } }
public static IHashTrieNode?ModifyChildren( InternalNode node, byte h, ulong value, IEnumerable <byte[]> childrenHashes, byte[]?valueHash ) { if (node == null) { throw new ArgumentNullException(nameof(node)); } var was = node.GetChildByHash(h); if (was == value) { return(node); } List <byte[]> newHashes; var newNode = new InternalNode(); var pos = (int)BitsUtils.PositionOf(node.ChildrenMask, h); if (was == 0) { if (valueHash is null) { throw new ArgumentNullException(nameof(valueHash)); } newNode._children = new ulong[node._children.Length + 1]; for (var i = 0; i <= node._children.Length; ++i) { newNode._children[i] = i < pos ? node._children[i] : (i == pos ? value : node._children[i - 1]); } newNode.ChildrenMask = node.ChildrenMask | (1u << h); newHashes = childrenHashes.ToList(); newHashes.Insert(pos, valueHash); newNode.UpdateHash(newHashes, GetChildrenLabels(newNode.ChildrenMask)); return(newNode); } if (value == 0) { if (node._children.Length == 1) { return(null); } newNode._children = new ulong[node._children.Length - 1]; for (var i = 0; i + 1 < node._children.Length; ++i) { newNode._children[i] = i < pos ? node._children[i] : node._children[i + 1]; } newNode.ChildrenMask = node.ChildrenMask ^ (1u << h); newHashes = childrenHashes.ToList(); newHashes.RemoveAt(pos); newNode.UpdateHash(newHashes, GetChildrenLabels(newNode.ChildrenMask)); return(newNode); } newNode._children = node._children.ToArray(); newNode.ChildrenMask = node.ChildrenMask; newNode._children[pos] = value; newHashes = childrenHashes.ToList(); newHashes[pos] = valueHash ?? throw new ArgumentNullException(nameof(valueHash)); newNode.UpdateHash(newHashes, GetChildrenLabels(newNode.ChildrenMask)); return(newNode); }