-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
269 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Text; | ||
|
||
namespace com.github.neoresearch.NeoDataStructure | ||
{ | ||
public class MPT | ||
{ | ||
private byte[] _rootHash; | ||
private readonly Dictionary<byte[], MPTNode> _db = new Dictionary<byte[], MPTNode>(); | ||
|
||
public byte[] this[byte[] key] | ||
{ | ||
// get => _rootHash == null ? null : Get(_db[_rootHash], ConvertToNibble(key)); | ||
get => _rootHash == null ? null : _rootHash; | ||
set | ||
{ | ||
var node = _rootHash == null ? null : _db[_rootHash]; | ||
if (_rootHash != null && _db.ContainsKey(_rootHash)) | ||
{ | ||
_db.Remove(_rootHash); | ||
} | ||
|
||
_rootHash = Set(node, ConvertToNibble(key), key, value); | ||
} | ||
} | ||
|
||
private byte[] ConvertToNibble(byte[] key) | ||
{ | ||
var resp = new byte[key.Length * 2]; | ||
for (var i = 0; i < key.Length; i++) | ||
{ | ||
resp[2 * i] = (byte) (key[i] / 16); | ||
resp[2 * i + 1] = (byte) (key[i] % 16); | ||
} | ||
|
||
return resp; | ||
} | ||
|
||
public string this[string key] | ||
{ | ||
get => Encoding.Default.GetString(this[Encoding.UTF8.GetBytes(key)]); | ||
set => this[Encoding.UTF8.GetBytes(key)] = Encoding.UTF8.GetBytes(value); | ||
} | ||
|
||
private byte[] Set(ExtensionNode node, byte[] path, byte[] key, byte[] value) | ||
{ | ||
/* | ||
if (path.Length == 0) | ||
{ | ||
var newNode = new BranchNode | ||
{ | ||
Key = key, | ||
Value = value | ||
}; | ||
var nodeHash = Set(newNode, node.Path, node.Key, node.Value); | ||
node = _db[nodeHash]; | ||
} | ||
else if (node.Path == path) | ||
{ | ||
node.Key = key; | ||
node.Value = value; | ||
} | ||
else if (node.Path[0] == path[0]) | ||
{ | ||
for (var pos = 0;; pos++) | ||
{ | ||
if (pos + 1 != node.Path.Length && pos + 1 != path.Length && | ||
node.Path[pos + 1] == path[pos + 1]) continue; | ||
var newNode = new MPTNode {Path = path.Take(pos + 1).ToArray()}; | ||
var innerHash = Set(new MPTNode(true), node.Path.Skip(pos + 1).ToArray(), node.Key, | ||
node.Value); | ||
var innerNode = _db[innerHash]; | ||
_db.Remove(innerHash); | ||
newNode[1] = Set(innerNode, path.Skip(pos + 1).ToArray(), key, value); | ||
node = newNode; | ||
break; | ||
} | ||
} | ||
else | ||
{ | ||
var newNode = new MPTNode {Path = path.Take(pos + 1).ToArray()}; | ||
var innerHash = Set(new MPTNode(true), node.Path.Skip(pos + 1).ToArray(), node.Key, node.Value); | ||
var innerNode = _db[innerHash]; | ||
_db.Remove(innerHash); | ||
newNode[1] = Set(innerNode, path.Skip(pos + 1).ToArray(), key, value); | ||
node = newNode; | ||
} | ||
*/ | ||
|
||
var tempHash = node.Hash(); | ||
_db[tempHash] = node; | ||
return tempHash; | ||
} | ||
|
||
private byte[] Set(LeafNode node, byte[] path, byte[] key, byte[] value) | ||
{ | ||
var tempHash = node.Hash(); | ||
_db[tempHash] = node; | ||
return tempHash; | ||
} | ||
|
||
private byte[] Set(BranchNode node, byte[] path, byte[] key, byte[] value) | ||
{ | ||
if (path.Length == 0) | ||
{ | ||
node.Key = key; | ||
node.Value = value; | ||
} | ||
else | ||
{ | ||
var innerHash = node[path[0]]; | ||
var innerNode = innerHash != null ? _db[innerHash] : null; | ||
if (innerHash != null && _db.ContainsKey(innerHash)) | ||
{ | ||
_db.Remove(innerHash); | ||
} | ||
|
||
node[path[0]] = Set(innerNode, path.Skip(1).ToArray(), key, value); | ||
} | ||
|
||
var tempHash = node.Hash(); | ||
_db[tempHash] = node; | ||
return tempHash; | ||
} | ||
|
||
private byte[] Set(MPTNode node, byte[] path, byte[] key, byte[] value) | ||
{ | ||
if (node == null) | ||
{ | ||
node = new LeafNode | ||
{ | ||
Path = path, | ||
Key = key, | ||
Value = value | ||
}; | ||
} | ||
|
||
var tempHash = node.Hash(); | ||
_db[tempHash] = node; | ||
return tempHash; | ||
} | ||
|
||
public override string ToString() => _rootHash == null ? "{}" : $"{ToString(_db[_rootHash])}"; | ||
|
||
private string ToString(MPTNode node) | ||
{ | ||
if (node is LeafNode || node is ExtensionNode) | ||
{ | ||
return node.ToString(); | ||
} | ||
|
||
var resp = new StringBuilder("{"); | ||
for (var i = 0; i < node.Length; i++) | ||
{ | ||
if (node[i] != null) | ||
{ | ||
resp.Append($"\"{i}\":{ToString(_db[node[i]])}"); | ||
} | ||
} | ||
|
||
return resp.Append("}").ToString(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
using System.Collections.Generic; | ||
using System.Text; | ||
|
||
namespace com.github.neoresearch.NeoDataStructure | ||
{ | ||
public abstract class MPTNode | ||
{ | ||
private const int BranchSize = 18; | ||
private const int ExtensionSize = 2; | ||
private const int LeafSize = 3; | ||
|
||
private readonly byte[][] _hashes; | ||
private MPTNode(int size = 0) => _hashes = new byte[size][]; | ||
|
||
public bool IsBranch => _hashes.Length == BranchSize; | ||
public bool IsExtension => _hashes.Length == ExtensionSize; | ||
public bool IsLeaf => _hashes.Length == LeafSize; | ||
|
||
|
||
public byte[] this[int index] | ||
{ | ||
get => _hashes[index]; | ||
set => _hashes[index] = value; | ||
} | ||
|
||
public byte[] Path | ||
{ | ||
get => _hashes[_hashes.Length - 2]; | ||
set => _hashes[_hashes.Length - 2] = value; | ||
} | ||
|
||
public byte[] Value | ||
{ | ||
get => _hashes[_hashes.Length - 1]; | ||
set => _hashes[_hashes.Length - 1] = value; | ||
} | ||
|
||
public byte[] Key | ||
{ | ||
get => _hashes[_hashes.Length - 2]; | ||
set => _hashes[_hashes.Length - 2] = value; | ||
} | ||
|
||
public byte[] Next | ||
{ | ||
get => _hashes[_hashes.Length - 1]; | ||
set => _hashes[_hashes.Length - 1] = value; | ||
} | ||
|
||
public byte[] Hash() | ||
{ | ||
var bytes = new List<byte>(); | ||
for (var i = 0; i < _hashes.Length; i++) | ||
{ | ||
bytes.Add((byte) i); | ||
if (_hashes[i] != null) | ||
{ | ||
bytes.AddRange(_hashes[i]); | ||
} | ||
} | ||
|
||
return new System.Security.Cryptography.SHA256Managed().ComputeHash(bytes.ToArray()); | ||
} | ||
|
||
public override string ToString() | ||
{ | ||
var resp = new StringBuilder("["); | ||
var virgula = false; | ||
for (var i = 0; i < _hashes.Length; i++) | ||
{ | ||
resp.Append(virgula ? "," : "") | ||
.Append(IsBranch ? $"{i}:" : "") | ||
.Append($"{_hashes[i].ByteToHexString(false)}"); | ||
virgula = true; | ||
} | ||
|
||
return resp.Append("]").ToString(); | ||
} | ||
|
||
public int Length => _hashes.Length; | ||
|
||
public static MPTNode BranchNode() => new MPTNode(BranchSize); | ||
public static MPTNode ExtensionNode() => new MPTNode(ExtensionSize); | ||
public static MPTNode LeafNode() => new MPTNode(LeafSize); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
namespace com.github.neoresearch.NeoDataStructureTest | ||
{ | ||
using System.Collections.Generic; | ||
using NeoDataStructure; | ||
using Xunit; | ||
|
||
public class MPTTest | ||
{ | ||
[Fact] | ||
public void OperatorEqual() | ||
{ | ||
var mp = new MPT(); | ||
Assert.False(mp == null); | ||
mp[new byte[] {0, 0, 1}] = new byte[] {0, 0, 1}; | ||
mp[new byte[] {0, 0, 2}] = new byte[] {0, 0, 2}; | ||
} | ||
} | ||
} |