Stateless verification for Bitcoin handles using the Spaces protocol.
Similar to BIP-353, but replaces centralized ICANN signing keys with a permissionless trust anchor.
[dependencies]
libveritas = { git = "https://github.com/spacesprotocol/libveritas.git" }npm install @spacesprotocol/libveritasAdd to your Package.swift:
dependencies: [
.package(url: "https://github.com/spacesprotocol/libveritas-swift.git", from: "0.1.0")
]// build.gradle.kts
dependencies {
implementation("org.spacesprotocol:libveritas:0.1.0")
}npm install @spacesprotocol/react-native-libveritaspip install libveritasgo get github.com/spacesprotocol/libveritas-gouse libveritas::Veritas;
use libveritas::msg::{Message, QueryContext};
let anchors_json = std::fs::read("trust_anchors.json")?;
let veritas = Veritas::new()
.with_anchors(serde_json::from_slice(&anchors_json)?)?;
let msg_bytes = std::fs::read("message.bin")?;
let msg = Message::from_slice(&msg_bytes)?;
let ctx = QueryContext::new();
let result = veritas.verify_message(&ctx, msg)?;
for zone in &result.zones {
println!("{} -> {}", zone.handle, zone.sovereignty);
}import { readFileSync } from "fs";
import { Veritas, QueryContext, Message } from "@spacesprotocol/libveritas";
const anchors = JSON.parse(readFileSync("trust_anchors.json", "utf8"));
const msg = new Message(readFileSync("message.bin"));
const veritas = new Veritas(anchors);
const ctx = new QueryContext();
const result = veritas.verifyMessage(ctx, msg);
for (const zone of result.zones()) {
console.log(`${zone.handle()} -> ${zone.sovereignty()}`);
}import Libveritas
let anchorsJson = try String(contentsOfFile: "trust_anchors.json", encoding: .utf8)
let msgBytes = try Data(contentsOf: URL(fileURLWithPath: "message.bin"))
let anchors = try Anchors.fromJson(json: anchorsJson)
let veritas = try Veritas(anchors: anchors)
let msg = try Message(bytes: Array(msgBytes))
let ctx = QueryContext()
let result = try veritas.verifyMessage(ctx: ctx, msg: msg)
for zone in result.zones() {
print("\(zone.handle()) -> \(zone.sovereignty())")
switch zone.commitment() {
case .exists(_, _, _, let blockHeight, _):
print(" commitment at block \(blockHeight)")
case .empty:
print(" no commitment")
case .unknown:
print(" commitment unknown")
}
}import uniffi.libveritas_uniffi.*
val anchorsJson = File("trust_anchors.json").readText()
val msgBytes = File("message.bin").readBytes()
val anchors = Anchors.fromJson(anchorsJson)
val veritas = Veritas(anchors)
val msg = Message(msgBytes.toList())
val ctx = QueryContext()
val result = veritas.verifyMessage(ctx, msg)
for (zone in result.zones()) {
println("${zone.handle()} -> ${zone.sovereignty()}")
when (val c = zone.commitment()) {
is CommitmentState.Exists ->
println(" commitment at block ${c.blockHeight}")
is CommitmentState.Empty ->
println(" no commitment")
is CommitmentState.Unknown ->
println(" commitment unknown")
}
}import {
Veritas,
Anchors,
QueryContext,
Message,
} from '@spacesprotocol/react-native-libveritas';
const anchors = Anchors.fromJson(anchorsJsonString);
const veritas = new Veritas(anchors);
const ctx = new QueryContext();
const msg = new Message(messageBytes);
const result = veritas.verifyMessage(ctx, msg);
for (const zone of result.zones()) {
console.log(`${zone.handle()} -> ${zone.sovereignty()}`);
}from libveritas import Anchors, Veritas, QueryContext, Message
anchors = Anchors.from_json(anchors_json_string)
veritas = Veritas(anchors)
msg = Message(message_bytes)
ctx = QueryContext()
result = veritas.verify_message(ctx, msg)
for zone in result.zones():
print(f"{zone.handle()} -> {zone.sovereignty()}")import veritas "github.com/spacesprotocol/libveritas-go"
anchors, _ := veritas.AnchorsFromJson(anchorsJsonString)
v, _ := veritas.NewVeritas(anchors)
msg, _ := veritas.NewMessage(messageBytes)
ctx := veritas.NewQueryContext()
result, _ := v.VerifyMessage(ctx, msg)
for _, zone := range result.Zones() {
fmt.Printf("%s -> %s\n", zone.Handle(), zone.Sovereignty())
}By default, all handles in a message are verified. Use QueryContext to verify specific handles or provide previously-known zones for incremental verification:
const ctx = new QueryContext();
// Only verify specific handles
ctx.addRequest("alice@bitcoin");
// Provide a previously stored zone for context
ctx.addZone(storedZoneBytes);
const result = veritas.verifyMessage(ctx, msg);When you have multiple zone snapshots for the same handle, use is_better_than to determine which is more recent:
const better = newerZone.isBetterThan(olderZone); // trueZones can be serialized for storage with toBytes() and later restored with decodeZone().
The compiled guest program ELF binaries are available behind the elf feature flag for provers:
[dependencies]
libveritas = { version = "0.1", features = ["elf"] }use libveritas::constants::{FOLD_ELF, STEP_ELF, FOLD_ID, STEP_ID};The image IDs (FOLD_ID, STEP_ID) are always available without the feature flag.
When modifying the ZK guest programs in methods/guest/src/bin/, the baked ELF binaries and
image IDs must be updated. Run:
./update-elfs.shThis will:
- Build the guest programs reproducibly using Docker (
cargo risczero build) - Copy the compiled ELF binaries to
veritas/elfs/ - Compute the image IDs and update
veritas/src/constants.rs
Requirements: RISC Zero toolchain (cargo-risczero, r0vm) and Docker.