Blockchain utilities for .NET applications
$ dotnet add package Evoq.BlockchainA lightweight .NET library providing utilities for blockchain integration. This package contains common types and helpers that are useful when working with any blockchain from .NET applications.
dotnet add package Evoq.Blockchain
The library is designed to support advanced verification capabilities in the future, in collaboration with wrapper libraries:
Complete Chain of Trust Verification
Intelligent Verification
This vision enables a complete end-to-end verification system that can validate the entire chain of trust from JWS signatures down to individual leaf data and back up to blockchain attestations. Work is in progress with wrapper libraries like Zipwire.ProofPack which adds attestation locators and JWS outer wrappers to create complete proof packages.
Comprehensive documentation is available in the docs directory:
// Create a Merkle tree
var tree = new MerkleTree();
// Add leaves with automatic random salts
var data = new Dictionary<string, object?>
{
{ "name", "John" },
{ "age", 30 },
{ "ssn", "123-45-6789" }
};
tree.AddJsonLeaves(data);
// Compute the root hash (required before serialization)
tree.RecomputeSha256Root();
// Create a selective disclosure version (hiding sensitive data)
Predicate<MerkleLeaf> privateSsn = leaf =>
leaf.TryReadText(out string text) && text.Contains("ssn");
// Convert to JSON with selective disclosure
string json = tree.ToJson(privateSsn);
// Parse and verify the tree
var parsedTree = MerkleTree.Parse(json);
bool isValid = parsedTree.VerifyRoot(); // Automatically uses hash function from metadata
The tree automatically selects the appropriate hash function based on the metadata:
// Verify using the hash function specified in metadata
bool isValid = tree.VerifyRoot();
// Or explicitly specify a hash function
bool isValid = tree.VerifyRoot(myCustomHashFunction);
The root hash must be computed before serialization:
// This will throw InvalidRootException if root hasn't been computed
tree.ToJson();
// Always compute the root first
tree.RecomputeSha256Root();
tree.ToJson(); // Now works
The library provides clear error messages for common issues:
try {
tree.VerifyRoot();
} catch (NotSupportedException ex) {
// Error message explains how to use custom hash functions
Console.WriteLine(ex.Message);
}
You can implement custom hash functions and select them based on the tree's metadata:
// Define a custom hash function
Hex ComputeReverseSha256Hash(byte[] data)
{
// Reverse the input bytes
byte[] reversed = new byte[data.Length];
Array.Copy(data, reversed, data.Length);
Array.Reverse(reversed);
// Hash the reversed bytes
using var sha256 = SHA256.Create();
return new Hex(sha256.ComputeHash(reversed));
}
// Create a hash function selector
HashFunction SelectHashFunction(MerkleTree tree)
{
return tree.Metadata.HashAlgorithm switch
{
"sha256" => MerkleTree.ComputeSha256Hash,
"sha256-reverse" => ComputeReverseSha256Hash,
_ => throw new NotSupportedException(
$"Hash algorithm '{tree.Metadata.HashAlgorithm}' is not supported. " +
"Please implement a custom hash function for this algorithm.")
};
}
// Use the selector to get the right hash function
var hashFunction = SelectHashFunction(tree);
bool isValid = tree.VerifyRoot(hashFunction);
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:
// v1.0 format (current default, uses "metadata" property)
var v1Json = @"{
""metadata"": { ""hashAlgorithm"": ""sha256"", ""version"": ""1.0"" },
""leaves"": [...],
""root"": ""...""
}";
// v2.0 format (JWT-style, uses "header" property with standardized values)
var v2Json = @"{
""header"": {
""alg"": ""SHA256"", // Standardized algorithm name
""typ"": ""MerkleTree+2.0"" // JWT-style type identifier
},
""leaves"": [...],
""root"": ""...""
}";
// Both formats are automatically detected
var tree = MerkleTree.Parse(v1Json); // Works with v1.0
var tree2 = MerkleTree.Parse(v2Json); // Works with v2.0
Note: The library uses
JavaScriptEncoder.UnsafeRelaxedJsonEscapingto ensure special characters in version strings (like '+' in "MerkleTree+2.0") are not escaped in the JSON output. This is particularly important for JWT-style type identifiers in v2.0 format.
This package targets .NET Standard 2.0 for maximum compatibility across:
dotnet build
dotnet test
The repository includes shell scripts to simplify the build and publishing process:
This script automates the build process:
# Make the script executable
chmod +x build.sh
# Run the build script
./build.sh
This script publishes the NuGet package to NuGet.org:
# Make the script executable
chmod +x publish.sh
# Set your NuGet API key
export NUGET_API_KEY="your-nuget-api-key"
# Run the publish script
./publish.sh
IMPORTANT: Follow the comprehensive Shipping Guide for detailed release procedures.
The release process includes:
Quick reference:
# Check current versions
grep '<Version>' src/Evoq.Blockchain/Evoq.Blockchain.csproj
git tag --list --sort=-version:refname | head -1
curl -s "https://api.nuget.org/v3/registration5-semver1/evoq.blockchain/index.json" | grep -o '"version":"[^"]*"' | tail -1
# Build and test
./build.sh
# Create tag and release
git tag -a vX.Y.Z -m "Version X.Y.Z - Description"
git push origin vX.Y.Z
# Publish to NuGet (manual upload)
# Upload artifacts/Evoq.Blockchain.X.Y.Z.nupkg to https://www.nuget.org/packages/manage/upload
See docs/SHIPPING.md for the complete process.
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
git checkout -b feature/AmazingFeature)git commit -m 'Add some AmazingFeature')git push origin feature/AmazingFeature)This project is licensed under the MIT License - see the LICENSE file for details.
Luke Puplett