private bool GetProof(ref MPTNode node, byte[] path, HashSet <byte[]> proof) { switch (node) { case LeafNode leafNode: { if (path.Length == 0) { proof.Add(leafNode.Encode()); return(true); } break; } case HashNode hashNode: { if (hashNode.IsEmptyNode) { break; } var new_node = Resolve(hashNode); if (new_node is null) { break; } node = new_node; return(GetProof(ref node, path, proof)); } case BranchNode branchNode: { proof.Add(branchNode.Encode()); if (path.Length == 0) { return(GetProof(ref branchNode.Children[BranchNode.ChildCount - 1], path, proof)); } return(GetProof(ref branchNode.Children[path[0]], path.Skip(1), proof)); } case ExtensionNode extensionNode: { if (path.AsSpan().StartsWith(extensionNode.Key)) { proof.Add(extensionNode.Encode()); return(GetProof(ref extensionNode.Next, path.Skip(extensionNode.Key.Length), proof)); } break; } } return(false); }
private bool TryGet(ref MPTNode node, byte[] path, out byte[] value) { switch (node) { case LeafNode leafNode: { if (path.Length == 0) { value = (byte[])leafNode.Value.Clone(); return(true); } break; } case HashNode hashNode: { if (hashNode.IsEmptyNode) { break; } var newNode = Resolve(hashNode); if (newNode is null) { break; } node = newNode; return(TryGet(ref node, path, out value)); } case BranchNode branchNode: { if (path.Length == 0) { return(TryGet(ref branchNode.Children[BranchNode.ChildCount - 1], path, out value)); } return(TryGet(ref branchNode.Children[path[0]], path.Skip(1), out value)); } case ExtensionNode extensionNode: { if (path.AsSpan().StartsWith(extensionNode.Key)) { return(TryGet(ref extensionNode.Next, path.Skip(extensionNode.Key.Length), out value)); } break; } } value = Array.Empty <byte>(); return(false); }
public MPTReadOnlyTrie(UInt256 root, IKVReadOnlyStore store) { if (store is null) { throw new System.ArgumentNullException(); } this.rodb = new MPTReadOnlyDb(store); if (root is null) { this.root = HashNode.EmptyNode(); } else { this.root = new HashNode(root); } }
private bool Put(ref MPTNode node, byte[] path, MPTNode val) { switch (node) { case LeafNode leafNode: { if (val is LeafNode v) { if (path.Length == 0) { node = v; db.Put(node); return(true); } var branch = new BranchNode(); branch.Children[BranchNode.ChildCount - 1] = leafNode; Put(ref branch.Children[path[0]], path.Skip(1), v); db.Put(branch); node = branch; return(true); } return(false); } case ExtensionNode extensionNode: { if (path.AsSpan().StartsWith(extensionNode.Key)) { var result = Put(ref extensionNode.Next, path.Skip(extensionNode.Key.Length), val); if (result) { extensionNode.SetDirty(); db.Put(extensionNode); } return(result); } var prefix = extensionNode.Key.CommonPrefix(path); var pathRemain = path.Skip(prefix.Length); var keyRemain = extensionNode.Key.Skip(prefix.Length); var son = new BranchNode(); MPTNode grandSon1 = HashNode.EmptyNode(); MPTNode grandSon2 = HashNode.EmptyNode(); Put(ref grandSon1, keyRemain.Skip(1), extensionNode.Next); son.Children[keyRemain[0]] = grandSon1; if (pathRemain.Length == 0) { Put(ref grandSon2, pathRemain, val); son.Children[BranchNode.ChildCount - 1] = grandSon2; } else { Put(ref grandSon2, pathRemain.Skip(1), val); son.Children[pathRemain[0]] = grandSon2; } db.Put(son); if (prefix.Length > 0) { var exNode = new ExtensionNode() { Key = prefix, Next = son, }; db.Put(exNode); node = exNode; } else { node = son; } return(true); } case BranchNode branchNode: { bool result; if (path.Length == 0) { result = Put(ref branchNode.Children[BranchNode.ChildCount - 1], path, val); } else { result = Put(ref branchNode.Children[path[0]], path.Skip(1), val); } if (result) { branchNode.SetDirty(); db.Put(branchNode); } return(result); } case HashNode hashNode: { MPTNode newNode; if (hashNode.IsEmptyNode) { if (path.Length == 0) { newNode = val; } else { newNode = new ExtensionNode() { Key = path, Next = val, }; db.Put(newNode); } node = newNode; if (val is LeafNode) { db.Put(val); } return(true); } newNode = Resolve(hashNode); if (newNode is null) { return(false); } node = newNode; return(Put(ref node, path, val)); } default: throw new System.InvalidOperationException("Invalid node type."); } }
private bool TryDelete(ref MPTNode node, byte[] path) { switch (node) { case LeafNode _: { if (path.Length == 0) { node = HashNode.EmptyNode(); return(true); } return(false); } case ExtensionNode extensionNode: { if (path.AsSpan().StartsWith(extensionNode.Key)) { var result = TryDelete(ref extensionNode.Next, path.Skip(extensionNode.Key.Length)); if (!result) { return(false); } if (extensionNode.Next is HashNode hashNode && hashNode.IsEmptyNode) { node = extensionNode.Next; return(true); } if (extensionNode.Next is ExtensionNode en) { extensionNode.Key = extensionNode.Key.Concat(en.Key); extensionNode.Next = en.Next; } extensionNode.SetDirty(); db.Put(extensionNode); return(true); } return(false); } case BranchNode branchNode: { bool result; if (path.Length == 0) { result = TryDelete(ref branchNode.Children[BranchNode.ChildCount - 1], path); } else { result = TryDelete(ref branchNode.Children[path[0]], path.Skip(1)); } if (!result) { return(false); } List <byte> childrenIndexes = new List <byte>(); for (int i = 0; i < BranchNode.ChildCount; i++) { if (branchNode.Children[i] is HashNode hn && hn.IsEmptyNode) { continue; } childrenIndexes.Add((byte)i); } if (childrenIndexes.Count > 1) { branchNode.SetDirty(); db.Put(branchNode); return(true); } var lastChildIndex = childrenIndexes[0]; var lastChild = branchNode.Children[lastChildIndex]; if (lastChildIndex == BranchNode.ChildCount - 1) { node = lastChild; return(true); } if (lastChild is HashNode hashNode) { lastChild = Resolve(hashNode); if (lastChild is null) { throw new System.ArgumentNullException("Invalid hash node"); } } if (lastChild is ExtensionNode exNode) { exNode.Key = Helper.Concat(childrenIndexes.ToArray(), exNode.Key); exNode.SetDirty(); db.Put(exNode); node = exNode; return(true); } var newNode = new ExtensionNode() { Key = childrenIndexes.ToArray(), Next = lastChild, }; node = newNode; db.Put(newNode); return(true); } case HashNode hashNode: { if (hashNode.IsEmptyNode) { return(true); } var new_node = Resolve(hashNode); if (new_node is null) { return(false); } node = new_node; return(TryDelete(ref node, path)); } default: return(false); } }