Skip to content

Commit d40ae35

Browse files
committed
feat: add v2.0 format support with JWT-style headers
1 parent 24ca451 commit d40ae35

File tree

8 files changed

+524
-67
lines changed

8 files changed

+524
-67
lines changed

README.md

Lines changed: 99 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ var data = new Dictionary<string, object?>
3838
};
3939
tree.AddJsonLeaves(data);
4040

41-
// Compute the root hash
41+
// Compute the root hash (required before serialization)
4242
tree.RecomputeSha256Root();
4343

4444
// Create a selective disclosure version (hiding sensitive data)
@@ -47,6 +47,104 @@ Predicate<MerkleLeaf> privateSsn = leaf =>
4747

4848
// Convert to JSON with selective disclosure
4949
string json = tree.ToJson(privateSsn);
50+
51+
// Parse and verify the tree
52+
var parsedTree = MerkleTree.Parse(json);
53+
bool isValid = parsedTree.VerifyRoot(); // Automatically uses hash function from metadata
54+
```
55+
56+
### Merkle Tree Features
57+
58+
#### Automatic Hash Function Selection
59+
The tree automatically selects the appropriate hash function based on the metadata:
60+
```csharp
61+
// Verify using the hash function specified in metadata
62+
bool isValid = tree.VerifyRoot();
63+
64+
// Or explicitly specify a hash function
65+
bool isValid = tree.VerifyRoot(myCustomHashFunction);
66+
```
67+
68+
#### Root Hash Computation
69+
The root hash must be computed before serialization:
70+
```csharp
71+
// This will throw InvalidRootException if root hasn't been computed
72+
tree.ToJson();
73+
74+
// Always compute the root first
75+
tree.RecomputeSha256Root();
76+
tree.ToJson(); // Now works
77+
```
78+
79+
#### Error Handling
80+
The library provides clear error messages for common issues:
81+
```csharp
82+
try {
83+
tree.VerifyRoot();
84+
} catch (NotSupportedException ex) {
85+
// Error message explains how to use custom hash functions
86+
Console.WriteLine(ex.Message);
87+
}
88+
```
89+
90+
#### Custom Hash Functions
91+
You can implement custom hash functions and select them based on the tree's metadata:
92+
```csharp
93+
// Define a custom hash function
94+
Hex ComputeReverseSha256Hash(byte[] data)
95+
{
96+
// Reverse the input bytes
97+
byte[] reversed = new byte[data.Length];
98+
Array.Copy(data, reversed, data.Length);
99+
Array.Reverse(reversed);
100+
101+
// Hash the reversed bytes
102+
using var sha256 = SHA256.Create();
103+
return new Hex(sha256.ComputeHash(reversed));
104+
}
105+
106+
// Create a hash function selector
107+
HashFunction SelectHashFunction(MerkleTree tree)
108+
{
109+
return tree.Metadata.HashAlgorithm switch
110+
{
111+
"sha256" => MerkleTree.ComputeSha256Hash,
112+
"sha256-reverse" => ComputeReverseSha256Hash,
113+
_ => throw new NotSupportedException(
114+
$"Hash algorithm '{tree.Metadata.HashAlgorithm}' is not supported. " +
115+
"Please implement a custom hash function for this algorithm.")
116+
};
117+
}
118+
119+
// Use the selector to get the right hash function
120+
var hashFunction = SelectHashFunction(tree);
121+
bool isValid = tree.VerifyRoot(hashFunction);
122+
```
123+
124+
#### Version Support
125+
The parser automatically detects the version format of the JSON. The library currently defaults to v1.0 format, but also supports the newer v2.0 format which uses JWT-style headers:
126+
127+
```csharp
128+
// v1.0 format (current default, uses "metadata" property)
129+
var v1Json = @"{
130+
""metadata"": { ""hashAlgorithm"": ""sha256"", ""version"": ""1.0"" },
131+
""leaves"": [...],
132+
""root"": ""...""
133+
}";
134+
135+
// v2.0 format (JWT-style, uses "header" property with standardized values)
136+
var v2Json = @"{
137+
""header"": {
138+
""alg"": ""SHA256"", // Standardized algorithm name
139+
""typ"": ""MerkleTree+2.0"" // JWT-style type identifier
140+
},
141+
""leaves"": [...],
142+
""root"": ""...""
143+
}";
144+
145+
// Both formats are automatically detected
146+
var tree = MerkleTree.Parse(v1Json); // Works with v1.0
147+
var tree2 = MerkleTree.Parse(v2Json); // Works with v2.0
50148
```
51149

52150
## Target Frameworks
Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,37 @@
11
namespace Evoq.Blockchain.Merkle;
22

3+
/// <summary>
4+
/// Contains the version strings for the Merkle tree.
5+
/// </summary>
6+
public static class MerkleTreeVersionStrings
7+
{
8+
/// <summary>
9+
/// The version string for the Merkle tree version 1.0.
10+
/// </summary>
11+
public const string V1_0 = "1.0";
12+
13+
/// <summary>
14+
/// The version string for the Merkle tree version 2.0.
15+
/// </summary>
16+
public const string V2_0 = "MerkleTree+2.0";
17+
}
18+
19+
/// <summary>
20+
/// Contains the hash algorithm strings for the Merkle tree.
21+
/// </summary>
22+
public static class MerkleTreeHashAlgorithmStrings
23+
{
24+
/// <summary>
25+
/// The hash algorithm string for the SHA-256 hash algorithm.
26+
/// </summary>
27+
public const string Sha256Legacy = "sha256";
28+
29+
/// <summary>
30+
/// The hash algorithm string for the SHA-256 hash algorithm using JWT style.
31+
/// </summary>
32+
public const string Sha256 = "SHA256";
33+
}
34+
335
/// <summary>
436
/// Contains metadata about a Merkle tree.
537
/// </summary>
@@ -8,10 +40,10 @@ public class MerkleMetadata
840
/// <summary>
941
/// Gets or sets the hash algorithm used in the Merkle tree.
1042
/// </summary>
11-
public string HashAlgorithm { get; set; } = "none";
43+
public string HashAlgorithm { get; set; } = MerkleTreeHashAlgorithmStrings.Sha256;
1244

1345
/// <summary>
1446
/// Gets or sets the version of the Merkle tree implementation.
1547
/// </summary>
16-
public string Version { get; set; } = "1.0";
48+
public string Version { get; set; } = MerkleTreeVersionStrings.V1_0;
1749
}

0 commit comments

Comments
 (0)