Skip to content

Commit e86a3a1

Browse files
committed
Update veritas for ptrs
1 parent 7f81aff commit e86a3a1

File tree

5 files changed

+395
-30
lines changed

5 files changed

+395
-30
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

veritas/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ crate-type = ["cdylib", "rlib"]
88

99
[dependencies]
1010
spaces_protocol = { path = "../protocol", default-features = false, features = ["std"]}
11+
spaces_ptr = { path = "../ptr", default-features = false, features = ["std"]}
1112
bincode = { version = "2.0.1", default-features = false, features = ["alloc"]}
1213
spacedb = { git = "https://github.com/spacesprotocol/spacedb", version = "0.0.7", default-features = false }
1314

veritas/README.md

Lines changed: 54 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,12 @@ Veritas is a <strong>stateless way</strong> to verify <strong><a href="https://s
1717
1818
---
1919

20-
## Javascript Example
20+
## Javascript Examples
2121

22-
### Verifying proofs
22+
### Verifying Spaces proofs
2323

2424
```javascript
25-
const veritas = new Veritas();
25+
const veritas = new Veritas("spaces");
2626

2727
// Set up a trust anchor
2828
const root = Buffer.from(
@@ -47,10 +47,10 @@ for (const {key, value: utxo} of proof.entries()) {
4747
}
4848
```
4949

50-
### Verifying messages
50+
### Verifying Spaces messages
5151

5252
```javascript
53-
const veritas = new Veritas();
53+
const veritas = new Veritas("spaces");
5454

5555
// Set up a trust anchor
5656
const anchor = Buffer.from(
@@ -86,6 +86,55 @@ console.log("- Public Key:", Buffer.from(utxo.getPublicKey()).toString("hex"));
8686
console.log("- Signature: ", sig.toString('hex'));
8787
```
8888

89+
### Verifying PTR proofs
90+
91+
```javascript
92+
const veritas = new Veritas("ptrs");
93+
94+
// Set up a PTR trust anchor
95+
const root = Buffer.from(
96+
"b55cd9bda4295a18d75f79c9d60efcd78ee2cdf661b0df4b054ff7602bc70794",
97+
"hex"
98+
);
99+
veritas.addAnchor(root);
100+
101+
const rawProof = Buffer.from(
102+
"...", // PTR proof bytes
103+
"base64"
104+
);
105+
106+
// Verify a PTR proof
107+
const proof = veritas.verifyProof(rawProof);
108+
109+
// Find a PTR by its identifier
110+
const sptr = new Sptr("sptr1...");
111+
const ptrout = proof.findPtr(sptr);
112+
if (ptrout) {
113+
console.log('✅ SPTR found');
114+
console.log('🔑 Public key: ', Buffer.from(ptrout.getPublicKey()).toString('hex'));
115+
const ptr = ptrout.getPtr();
116+
if (ptr) {
117+
console.log('📍 SPTR ID: ', ptr.getId().toString());
118+
console.log('📅 Last update: ', ptr.getLastUpdate());
119+
}
120+
}
121+
122+
// Get a commitment by its key
123+
const commitmentKey = Buffer.from("...", "hex");
124+
const commitment = proof.getCommitment(commitmentKey);
125+
if (commitment) {
126+
console.log('✅ Commitment root: ', Buffer.from(commitment.getRoot()).toString('hex'));
127+
console.log('📅 Block height: ', commitment.getBlockHeight());
128+
}
129+
130+
// Get delegated space for an SPTR
131+
const sptrKey = Buffer.from("...", "hex");
132+
const delegatedSpace = proof.getDelegation(sptrKey);
133+
if (delegatedSpace) {
134+
console.log('✅ Delegated to space: ', delegatedSpace.toString());
135+
}
136+
```
137+
89138

90139

91140
## Multiple anchors

veritas/src/lib.rs

Lines changed: 152 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -19,28 +19,42 @@ use spaces_protocol::{
1919
slabel::SLabel,
2020
SpaceOut,
2121
};
22+
use spaces_ptr::{PtrOut, Commitment, sptr::Sptr};
23+
24+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
25+
pub enum ProofType {
26+
Spaces,
27+
Ptrs,
28+
}
2229

2330
pub struct Veritas {
2431
anchors: BTreeSet<hasher::Hash>,
32+
proof_type: ProofType,
2533
ctx: Secp256k1<VerifyOnly>,
2634
}
2735

2836
pub struct Proof {
2937
root: Hash,
38+
proof_type: ProofType,
3039
inner: SubTree<Sha256Hasher>,
3140
}
3241

3342
pub struct ProofIter<'a> {
43+
proof_type: ProofType,
3444
inner: SubtreeIter<'a>,
3545
}
3646

3747
pub enum Value {
3848
Outpoint(OutPoint),
3949
UTXO(SpaceOut),
50+
PtrUTXO(PtrOut),
51+
Commitment(Commitment),
52+
Space(SLabel),
53+
Root(hasher::Hash),
4054
Unknown(Vec<u8>),
4155
}
4256

43-
pub trait SpaceoutExt {
57+
pub trait UtxoExt {
4458
fn public_key(&self) -> Option<XOnlyPublicKey>;
4559
}
4660

@@ -58,9 +72,10 @@ pub enum Error {
5872
}
5973

6074
impl Veritas {
61-
pub fn new() -> Self {
75+
pub fn new(proof_type: ProofType) -> Self {
6276
Self {
6377
anchors: BTreeSet::new(),
78+
proof_type,
6479
ctx: Secp256k1::verification_only(),
6580
}
6681
}
@@ -76,7 +91,11 @@ impl Veritas {
7691
if !self.anchors.contains(&root) {
7792
return Err(Error::NoMatchingAnchor);
7893
}
79-
Ok(Proof { root, inner })
94+
Ok(Proof {
95+
root,
96+
proof_type: self.proof_type.clone(),
97+
inner
98+
})
8099
}
81100

82101
pub fn verify_schnorr(&self, pubkey: &[u8], digest: &[u8], sig: &[u8]) -> bool {
@@ -105,6 +124,7 @@ impl Veritas {
105124
impl Proof {
106125
pub fn iter(&self) -> ProofIter {
107126
ProofIter {
127+
proof_type: self.proof_type,
108128
inner: self.inner.iter(),
109129
}
110130
}
@@ -146,6 +166,69 @@ impl Proof {
146166
}
147167
Ok(None)
148168
}
169+
170+
/// Retrieves a PTR UTXO leaf within the subtree specified by the outpoint hash
171+
pub fn get_ptrout(&self, utxo_key: &Hash) -> Result<Option<PtrOut>, Error> {
172+
let (_, value) = match self.inner.iter().find(|(k, _)| *k == utxo_key) {
173+
None => return Ok(None),
174+
Some(kv) => kv,
175+
};
176+
let (ptrout, _): (PtrOut, _) = bincode::decode_from_slice(value, config::standard())
177+
.map_err(|_| Error::MalformedValue)?;
178+
Ok(Some(ptrout))
179+
}
180+
181+
/// Retrieves a PTR UTXO leaf containing the specified sptr
182+
pub fn find_ptr(&self, sptr: &Sptr) -> Result<Option<PtrOut>, Error> {
183+
for (_, v) in self.iter() {
184+
match v {
185+
Value::PtrUTXO(ptrout) => {
186+
if ptrout
187+
.sptr
188+
.as_ref()
189+
.is_some_and(|ptr| &ptr.id == sptr)
190+
{
191+
return Ok(Some(ptrout));
192+
}
193+
}
194+
_ => continue,
195+
}
196+
}
197+
Ok(None)
198+
}
199+
200+
/// Retrieves a commitment by its key
201+
pub fn get_commitment(&self, commitment_key: &Hash) -> Result<Option<Commitment>, Error> {
202+
let (_, value) = match self.inner.iter().find(|(k, _)| *k == commitment_key) {
203+
None => return Ok(None),
204+
Some(kv) => kv,
205+
};
206+
let (commitment, _): (Commitment, _) = bincode::decode_from_slice(value, config::standard())
207+
.map_err(|_| Error::MalformedValue)?;
208+
Ok(Some(commitment))
209+
}
210+
211+
/// Retrieves the delegated space for an SPTR
212+
pub fn get_delegation(&self, sptr_key: &Hash) -> Result<Option<SLabel>, Error> {
213+
let (_, value) = match self.inner.iter().find(|(k, _)| *k == sptr_key) {
214+
None => return Ok(None),
215+
Some(kv) => kv,
216+
};
217+
let (space, _): (SLabel, _) = bincode::decode_from_slice(value, config::standard())
218+
.map_err(|_| Error::MalformedValue)?;
219+
Ok(Some(space))
220+
}
221+
222+
/// Retrieves the latest commitment root for a space
223+
pub fn get_registry_tip(&self, registry_key: &Hash) -> Result<Option<hasher::Hash>, Error> {
224+
let (_, value) = match self.inner.iter().find(|(k, _)| *k == registry_key) {
225+
None => return Ok(None),
226+
Some(kv) => kv,
227+
};
228+
let (root, _): (hasher::Hash, _) = bincode::decode_from_slice(value, config::standard())
229+
.map_err(|_| Error::MalformedValue)?;
230+
Ok(Some(root))
231+
}
149232
}
150233

151234
impl From<spacedb::Error> for Error {
@@ -161,7 +244,16 @@ impl From<spacedb::Error> for Error {
161244
}
162245
}
163246

164-
impl SpaceoutExt for SpaceOut {
247+
impl UtxoExt for SpaceOut {
248+
fn public_key(&self) -> Option<XOnlyPublicKey> {
249+
match self.script_pubkey.is_p2tr() {
250+
true => XOnlyPublicKey::from_slice(&self.script_pubkey.as_bytes()[2..]).ok(),
251+
false => None,
252+
}
253+
}
254+
}
255+
256+
impl UtxoExt for PtrOut {
165257
fn public_key(&self) -> Option<XOnlyPublicKey> {
166258
match self.script_pubkey.is_p2tr() {
167259
true => XOnlyPublicKey::from_slice(&self.script_pubkey.as_bytes()[2..]).ok(),
@@ -175,24 +267,64 @@ impl Iterator for ProofIter<'_> {
175267

176268
fn next(&mut self) -> Option<Self::Item> {
177269
self.inner.next().map(|(k, v)| {
178-
if OutpointKey::is_valid(k) {
179-
let result = bincode::decode_from_slice(v.as_slice(), config::standard())
180-
.ok()
181-
.map(|(raw, _)| Value::UTXO(raw));
270+
match self.proof_type {
271+
ProofType::Spaces => {
272+
// Spaces proof: OutpointKey → SpaceOut, SpaceKey → OutPoint
273+
if OutpointKey::is_valid(k) {
274+
let result = bincode::decode_from_slice(v.as_slice(), config::standard())
275+
.ok()
276+
.map(|(raw, _)| Value::UTXO(raw));
277+
return (*k, result.unwrap_or(Value::Unknown(v.clone())));
278+
}
279+
if SpaceKey::is_valid(k) {
280+
let result: Option<OutPoint> =
281+
bincode::serde::decode_from_slice(v.as_slice(), config::standard())
282+
.ok()
283+
.map(|(raw, _)| raw);
284+
return result
285+
.map(|r| (*k, Value::Outpoint(r)))
286+
.unwrap_or_else(|| (*k, Value::Unknown(v.clone())));
287+
}
288+
(*k, Value::Unknown(v.clone()))
289+
}
290+
ProofType::Ptrs => {
291+
// PTR proof: Try to decode value as different PTR types
182292

183-
return (*k, result.unwrap_or(Value::Unknown(v.clone())));
184-
}
185-
if SpaceKey::is_valid(k) {
186-
let result: Option<OutPoint> =
187-
bincode::serde::decode_from_slice(v.as_slice(), config::standard())
188-
.ok()
189-
.map(|(raw, _)| raw);
190-
return result
191-
.map(|r| (*k, Value::Outpoint(r)))
192-
.unwrap_or_else(|| (*k, Value::Unknown(v.clone())));
193-
}
293+
// Try PtrOutpointKey → PtrOut
294+
let ptrout_result: Result<(PtrOut, _), _> = bincode::decode_from_slice(v.as_slice(), config::standard());
295+
if let Ok((ptrout, _)) = ptrout_result {
296+
return (*k, Value::PtrUTXO(ptrout));
297+
}
298+
299+
// Try Sptr → OutPoint
300+
let outpoint_result: Result<(OutPoint, _), _> = bincode::serde::decode_from_slice(v.as_slice(), config::standard());
301+
if let Ok((outpoint, _)) = outpoint_result {
302+
return (*k, Value::Outpoint(outpoint));
303+
}
194304

195-
(*k, Value::Unknown(v.clone()))
305+
// Try CommitmentKey → Commitment
306+
let commitment_result: Result<(Commitment, _), _> = bincode::decode_from_slice(v.as_slice(), config::standard());
307+
if let Ok((commitment, _)) = commitment_result {
308+
return (*k, Value::Commitment(commitment));
309+
}
310+
311+
// Try RegistrySptrKey → SLabel
312+
let space_result: Result<(SLabel, _), _> = bincode::decode_from_slice(v.as_slice(), config::standard());
313+
if let Ok((space, _)) = space_result {
314+
return (*k, Value::Space(space));
315+
}
316+
317+
// Try RegistryKey → Hash (root)
318+
if v.len() == 32 {
319+
let root_result: Result<(hasher::Hash, _), _> = bincode::decode_from_slice(v.as_slice(), config::standard());
320+
if let Ok((root, _)) = root_result {
321+
return (*k, Value::Root(root));
322+
}
323+
}
324+
325+
(*k, Value::Unknown(v.clone()))
326+
}
327+
}
196328
})
197329
}
198330
}

0 commit comments

Comments
 (0)