use bitcoin::hashes::Hash as BitcoinHash;
use bitcoin::key::Keypair;
use bitcoin::key::rand::Rng;
use bitcoin::secp256k1::Secp256k1;
use bitcoin::secp256k1::rand;
use bitcoin::{BlockHash, OutPoint, ScriptBuf, Txid};
use borsh::{BorshDeserialize, BorshSerialize};
use libveritas::cert::{
Certificate, HandleOut, HandleSubtree, KeyHash, NumsSubtree, Signature, SpacesSubtree, Witness,
};
use libveritas::msg::{self, Message, QueryContext};
use libveritas::{ProvableOption, SovereigntyState, Veritas, Zone, hash_signable_message};
use risc0_zkvm::{FakeReceipt, InnerReceipt, Receipt, ReceiptClaim};
use spacedb::Sha256Hasher;
use spacedb::subtree::{ProofType, SubTree, ValueOrHash};
use spaces_nums::constants::COMMITMENT_FINALITY_INTERVAL;
use spaces_nums::num_id::NumId;
use spaces_nums::snumeric::SNumeric;
use spaces_nums::{
CommitmentKey, CommitmentTipKey, DelegatorKey, FullNumOut, Num, NumOut, NumOutpointKey,
RootAnchor, rolling_hash,
};
use spaces_protocol::constants::ChainAnchor;
use spaces_protocol::hasher::{KeyHasher, OutpointKey, SpaceKey};
use spaces_protocol::slabel::SLabel;
use spaces_protocol::sname::{SName, Subname};
use spaces_protocol::{Covenant, FullSpaceOut, Space, SpaceOut};
use std::collections::HashMap;
use std::str::FromStr;
fn sname(s: &str) -> SName {
SName::from_str(s).unwrap()
}
fn slabel(s: &str) -> SLabel {
SLabel::from_str(s).unwrap()
}
fn label(s: &str) -> Subname {
Subname::from_str(s).unwrap()
}
fn sign_zone(zone: &Zone, keypair: &Keypair) -> Signature {
let msg = hash_signable_message(&zone.signing_bytes());
let secp = Secp256k1::new();
let sig = secp.sign_schnorr_no_aux_rand(&msg, keypair);
Signature(sig.serialize())
}
#[derive(BorshSerialize, BorshDeserialize)]
pub struct EncodableOutpoint(
#[borsh(
serialize_with = "borsh_utils::serialize_outpoint",
deserialize_with = "borsh_utils::deserialize_outpoint"
)]
pub OutPoint,
);
fn gen_p2tr_spk() -> (ScriptBuf, Keypair) {
use bitcoin::opcodes::all::OP_PUSHNUM_1;
use bitcoin::script::Builder;
let secp = Secp256k1::new();
let (secret_key, public_key) = secp.generate_keypair(&mut rand::thread_rng());
let keypair = Keypair::from_secret_key(&secp, &secret_key);
let (xonly, _parity) = public_key.x_only_public_key();
let script = Builder::new()
.push_opcode(OP_PUSHNUM_1)
.push_slice(xonly.serialize())
.into_script();
(script, keypair)
}
#[derive(Clone)]
pub struct TestSpace {
pub fso: FullSpaceOut,
pub keypair: Keypair,
}
#[derive(Clone)]
pub struct TestNum {
pub fso: FullNumOut,
pub keypair: Keypair,
}
impl TestSpace {
pub fn new(name: &str, block_height: u32) -> Self {
let mut rng = rand::thread_rng();
let mut txid_bytes = [0u8; 32];
rng.fill(&mut txid_bytes);
let txid = Txid::from_slice(&txid_bytes).expect("valid txid");
let n: u32 = rng.r#gen();
let (script_pubkey, keypair) = gen_p2tr_spk();
let fso = FullSpaceOut {
txid,
spaceout: SpaceOut {
n: n as usize,
space: Some(Space {
name: slabel(name),
covenant: Covenant::Transfer {
expire_height: block_height + spaces_protocol::constants::RENEWAL_INTERVAL,
data: None,
},
}),
value: Default::default(),
script_pubkey,
},
};
TestSpace { fso, keypair }
}
pub fn label(&self) -> SLabel {
self.fso
.spaceout
.space
.as_ref()
.expect("valid space")
.name
.clone()
}
pub fn outpoint_key(&self) -> OutpointKey {
OutpointKey::from_outpoint::<KeyHash>(self.fso.outpoint())
}
pub fn script_pubkey(&self) -> ScriptBuf {
self.fso.spaceout.script_pubkey.clone()
}
pub fn space_key(&self) -> SpaceKey {
SpaceKey::from(KeyHash::hash(self.label().as_ref()))
}
pub fn spaceout_bytes(&self) -> Vec<u8> {
borsh::to_vec(&self.fso.spaceout).expect("valid")
}
pub fn outpoint_bytes(&self) -> Vec<u8> {
borsh::to_vec(&EncodableOutpoint(self.fso.outpoint())).expect("valid")
}
}
impl TestNum {
pub fn new(genesis_spk: ScriptBuf, block_height: u32) -> Self {
let num_id = NumId::from_spk::<KeyHash>(genesis_spk);
let mut rng = rand::thread_rng();
let mut txid_bytes = [0u8; 32];
rng.fill(&mut txid_bytes);
let txid = Txid::from_slice(&txid_bytes).expect("valid txid");
let n: u32 = rng.r#gen();
let (script_pubkey, keypair) = gen_p2tr_spk();
let fso = FullNumOut {
txid,
numout: NumOut {
n: n as usize,
num: Num {
id: num_id,
name: SNumeric::new(0, 0, 0),
data: None,
last_update: block_height,
},
value: Default::default(),
script_pubkey,
},
};
TestNum { fso, keypair }
}
pub fn id(&self) -> NumId {
self.fso.numout.num.id
}
pub fn outpoint_key(&self) -> NumOutpointKey {
NumOutpointKey::from_outpoint::<KeyHash>(self.fso.outpoint())
}
pub fn numout_bytes(&self) -> Vec<u8> {
borsh::to_vec(&self.fso.numout).expect("valid")
}
pub fn outpoint_bytes(&self) -> Vec<u8> {
borsh::to_vec(&EncodableOutpoint(self.fso.outpoint())).expect("valid")
}
}
#[derive(Clone)]
pub struct TestChain {
pub spaces_tree: SubTree<Sha256Hasher>,
pub nums_tree: SubTree<Sha256Hasher>,
pub spaces: HashMap<SLabel, TestSpace>,
pub nums: HashMap<NumId, TestNum>,
pub block_height: u32,
}
pub struct TestHandleTree {
pub space: SLabel,
pub ds: TestDelegatedSpace,
pub handle_tree: SubTree<Sha256Hasher>,
pub commitments: Vec<TestCommitmentBundle>,
pub staged: HashMap<Subname, StagedHandle>,
}
pub struct StagedHandle {
pub handle: TestHandle,
pub signature: Signature,
}
pub struct TestCommitmentBundle {
root: [u8; 32],
handles: HashMap<Subname, TestHandle>,
handle_tree: SubTree<Sha256Hasher>,
receipt: Option<Receipt>,
}
impl Default for TestChain {
fn default() -> Self {
Self::new()
}
}
impl TestChain {
pub fn new() -> Self {
Self {
spaces_tree: SubTree::empty(),
nums_tree: SubTree::empty(),
spaces: Default::default(),
nums: Default::default(),
block_height: 0,
}
}
pub fn increase_time(&mut self, n: u32) {
self.block_height += n;
}
pub fn add_space(&mut self, name: &str) -> TestSpace {
let space = TestSpace::new(name, self.block_height);
assert!(!self.spaces.contains_key(&space.label()));
self.spaces_tree
.insert(
space.outpoint_key().into(),
ValueOrHash::Value(space.spaceout_bytes()),
)
.expect("insert space");
self.spaces_tree
.insert(
space.space_key().into(),
ValueOrHash::Value(space.outpoint_bytes()),
)
.expect("insert outpoint");
self.spaces.insert(space.label(), space.clone());
space
}
pub fn current_root_anchor(&self) -> RootAnchor {
let spaces_root = self.spaces_tree.compute_root().expect("spaces root");
let nums_root = self.nums_tree.compute_root().expect("nums root");
let block_hash =
BlockHash::from_byte_array(rolling_hash::<KeyHash>(spaces_root, nums_root));
RootAnchor {
spaces_root,
nums_root: Some(nums_root),
block: ChainAnchor {
hash: block_hash,
height: self.block_height,
},
}
}
pub fn add_num(&mut self, genesis_spk: ScriptBuf) -> TestNum {
let num = TestNum::new(genesis_spk, self.block_height);
assert!(!self.nums.contains_key(&num.id()));
self.nums_tree
.insert(
num.outpoint_key().into(),
ValueOrHash::Value(num.numout_bytes()),
)
.expect("insert num");
self.nums_tree
.insert(num.id().into(), ValueOrHash::Value(num.outpoint_bytes()))
.expect("insert outpoint");
self.nums.insert(num.id(), num.clone());
num
}
pub fn add_space_with_delegation(&mut self, name: &str) -> TestDelegatedSpace {
let space = self.add_space(name);
let num = self.add_num(space.script_pubkey());
let delegator_key = DelegatorKey::from_id::<KeyHash>(num.id());
self.nums_tree
.insert(
delegator_key.into(),
ValueOrHash::Value(space.label().as_ref().to_vec()),
)
.expect("insert delegator key");
TestDelegatedSpace { space, ptr: num }
}
pub fn insert_commitment(
&mut self,
ds: &TestDelegatedSpace,
root: [u8; 32],
) -> spaces_nums::Commitment {
let prev_finalized = self.rollback_to_finalized_commitment(&ds.space.label());
let commitment = match prev_finalized {
None => spaces_nums::Commitment {
state_root: root,
prev_root: None,
rolling_hash: root,
block_height: self.block_height,
},
Some(prev) => spaces_nums::Commitment {
state_root: root,
prev_root: Some(prev.state_root),
rolling_hash: rolling_hash::<KeyHash>(prev.rolling_hash, root),
block_height: self.block_height,
},
};
let commitment_key = CommitmentKey::new::<KeyHash>(&ds.space.label(), root);
let commitment_bytes = borsh::to_vec(&commitment).expect("valid");
self.nums_tree
.insert(commitment_key.into(), ValueOrHash::Value(commitment_bytes))
.expect("insert commitment");
let registry_key = CommitmentTipKey::from_slabel::<KeyHash>(&ds.space.label());
self.nums_tree
.update(
registry_key.into(),
ValueOrHash::Value(commitment.state_root.to_vec()),
)
.expect("insert registry");
commitment
}
pub fn get_commitment(
&self,
space: &SLabel,
root: Option<[u8; 32]>,
) -> Option<spaces_nums::Commitment> {
let root = match root {
Some(root) => Some(root),
None => {
let registry_key = CommitmentTipKey::from_slabel::<KeyHash>(space);
let rkh: [u8; 32] = registry_key.into();
self.nums_tree
.iter()
.find(|(k, _)| **k == rkh)
.map(|(_, v)| {
let mut h = [0u8; 32];
h.copy_from_slice(v);
h
})
}
}?;
let commitment_key = CommitmentKey::new::<KeyHash>(space, root);
let ckh: [u8; 32] = commitment_key.into();
self.nums_tree
.iter()
.find(|(k, _)| **k == ckh)
.map(|(_, v)| {
let commitment: spaces_nums::Commitment =
borsh::from_slice(v).expect("valid commitment");
commitment
})
}
pub fn rollback_to_finalized_commitment(
&mut self,
space: &SLabel,
) -> Option<spaces_nums::Commitment> {
let commitment = self.get_commitment(space, None)?;
if commitment.is_finalized(self.block_height) {
return Some(commitment);
}
let registry_key = CommitmentTipKey::from_slabel::<KeyHash>(space);
let commitment_key = CommitmentKey::new::<KeyHash>(space, commitment.state_root);
let mut nums_tree = self.nums_tree.clone();
nums_tree = nums_tree.delete(®istry_key.into()).expect("delete");
nums_tree = nums_tree.delete(&commitment_key.into()).expect("delete");
self.nums_tree = nums_tree;
let prev_root = commitment.prev_root?;
let finalized = self.get_commitment(space, Some(prev_root))?;
self.nums_tree
.update(
registry_key.into(),
ValueOrHash::Value(finalized.state_root.to_vec()),
)
.expect("update");
Some(finalized)
}
}
pub struct TestHandle {
pub name: Subname,
pub genesis_spk: ScriptBuf,
pub keypair: Keypair,
}
impl TestHandleTree {
pub fn new(ds: &TestDelegatedSpace) -> Self {
Self {
space: ds.space.label(),
ds: ds.clone(),
handle_tree: SubTree::empty(),
commitments: vec![],
staged: Default::default(),
}
}
pub fn add_handle(&mut self, name: &str) {
let label = label(name);
let label_hash = KeyHash::hash(label.as_slabel().as_ref());
assert!(
!self
.handle_tree
.contains(&label_hash)
.expect("complete tree"),
"already exists"
);
assert!(!self.staged.contains_key(&label), "already staged");
let (genesis_spk, keypair) = gen_p2tr_spk();
let handle = TestHandle {
name: label,
genesis_spk: genesis_spk.clone(),
keypair,
};
let h = sname(&format!("{}{}", name, self.space));
let num_id = Some(NumId::from_spk::<KeyHash>(genesis_spk.clone()));
let zone = Zone {
anchor: 0,
sovereignty: SovereigntyState::Dependent,
canonical: h.clone(),
handle: h,
alias: None,
script_pubkey: genesis_spk,
fallback_records: sip7::RecordSet::default(),
records: sip7::RecordSet::default(),
delegate: ProvableOption::Unknown,
commitment: ProvableOption::Unknown,
num_id,
};
let signature = sign_zone(&zone, &self.ds.ptr.keypair);
let staged = StagedHandle { handle, signature };
self.staged.insert(staged.handle.name.clone(), staged);
}
pub fn commit(&mut self, chain: &mut TestChain) {
assert!(!self.staged.is_empty(), "no handles to commit");
let initial_root = self.handle_tree.compute_root().expect("compute root");
let handles: HashMap<Subname, TestHandle> = std::mem::take(&mut self.staged)
.into_iter()
.map(|(k, v)| (k, v.handle))
.collect();
for (_, handle) in handles.iter() {
let handle_key = KeyHash::hash(handle.name.as_slabel().as_ref());
let handle_out = HandleOut {
name: handle.name.as_slabel().clone(),
spk: handle.genesis_spk.clone(),
};
self.handle_tree
.insert(handle_key, ValueOrHash::Value(handle_out.to_vec()))
.expect("insert handle");
}
let final_root = self.handle_tree.compute_root().expect("compute root");
let onchain_commitment = chain.insert_commitment(&self.ds, final_root);
let receipt = if onchain_commitment.prev_root.is_some() {
let commitment = libveritas_zk::guest::Commitment {
policy_step: libveritas::constants::STEP_ID,
policy_fold: libveritas::constants::FOLD_ID,
initial_root,
final_root,
rolling_hash: onchain_commitment.rolling_hash,
kind: libveritas_zk::guest::CommitmentKind::Fold,
};
let words = risc0_zkvm::serde::to_vec(&commitment).expect("serialize commitment");
let journal_bytes: Vec<u8> = words.iter().flat_map(|w| w.to_le_bytes()).collect();
let receipt_claim =
ReceiptClaim::ok(libveritas::constants::FOLD_ID, journal_bytes.clone());
Some(Receipt::new(
InnerReceipt::Fake(FakeReceipt::new(receipt_claim)),
journal_bytes,
))
} else {
None
};
self.commitments.push(TestCommitmentBundle {
root: final_root,
handles,
handle_tree: self.handle_tree.clone(),
receipt,
})
}
pub fn build_message(
&self,
chain: &TestChain,
commitment_idx: usize,
handle_names: &[&str],
anchor: &ChainAnchor,
) -> Message {
let tcb = &self.commitments[commitment_idx];
let spaces_keys: Vec<[u8; 32]> = vec![
self.ds.space.outpoint_key().into(),
self.ds.space.space_key().into(),
];
let mut nums_keys: Vec<[u8; 32]> =
vec![self.ds.ptr.outpoint_key().into(), self.ds.ptr.id().into()];
nums_keys.push(CommitmentTipKey::from_slabel::<KeyHash>(&self.space).into());
nums_keys.push(CommitmentKey::new::<KeyHash>(&self.space, tcb.root).into());
let mut handle_keys: Vec<[u8; 32]> = Vec::new();
let mut handles: Vec<msg::Handle> = Vec::new();
for &name in handle_names {
let l = label(name);
let label_hash = KeyHash::hash(l.as_slabel().as_ref());
handle_keys.push(label_hash);
let handle = self.commitments[..=commitment_idx]
.iter()
.find_map(|c| c.handles.get(&l))
.expect("handle must exist in a previous commitment");
let handle_num_id = NumId::from_spk::<KeyHash>(handle.genesis_spk.clone());
nums_keys.push(handle_num_id.into());
handles.push(msg::Handle {
name: l,
genesis_spk: handle.genesis_spk.clone(),
records: None,
signature: None, });
}
let spaces_proof = chain
.spaces_tree
.prove(&spaces_keys, ProofType::Standard)
.expect("prove spaces");
let nums_proof = chain
.nums_tree
.prove(&nums_keys, ProofType::Standard)
.expect("prove nums");
let handles_proof = tcb
.handle_tree
.prove(&handle_keys, ProofType::Standard)
.expect("prove handles");
Message {
chain: msg::ChainProof {
anchor: *anchor,
spaces: SpacesSubtree(spaces_proof),
nums: NumsSubtree(nums_proof),
},
spaces: vec![msg::Bundle {
subject: self.space.clone(),
receipt: tcb.receipt.clone(),
epochs: vec![msg::Epoch {
tree: HandleSubtree(handles_proof),
handles,
}],
records: None,
delegate_records: None,
}],
}
}
pub fn build_temporary_message(
&self,
chain: &TestChain,
commitment_idx: usize,
handle_name: &str,
anchor: &ChainAnchor,
) -> Message {
let tcb = &self.commitments[commitment_idx];
let staged = self
.staged
.get(&label(handle_name))
.expect("handle must be staged");
let spaces_keys: Vec<[u8; 32]> = vec![
self.ds.space.outpoint_key().into(),
self.ds.space.space_key().into(),
];
let mut nums_keys: Vec<[u8; 32]> =
vec![self.ds.ptr.outpoint_key().into(), self.ds.ptr.id().into()];
nums_keys.push(CommitmentTipKey::from_slabel::<KeyHash>(&self.space).into());
nums_keys.push(CommitmentKey::new::<KeyHash>(&self.space, tcb.root).into());
let handle_num_id = NumId::from_spk::<KeyHash>(staged.handle.genesis_spk.clone());
nums_keys.push(handle_num_id.into());
let handle_key = KeyHash::hash(staged.handle.name.as_slabel().as_ref());
let handle_keys: Vec<[u8; 32]> = vec![handle_key];
let spaces_proof = chain
.spaces_tree
.prove(&spaces_keys, ProofType::Standard)
.expect("prove spaces");
let nums_proof = chain
.nums_tree
.prove(&nums_keys, ProofType::Standard)
.expect("prove nums");
let handles_proof = tcb
.handle_tree
.prove(&handle_keys, ProofType::Standard)
.expect("prove handles exclusion");
Message {
chain: msg::ChainProof {
anchor: *anchor,
spaces: SpacesSubtree(spaces_proof),
nums: NumsSubtree(nums_proof),
},
spaces: vec![msg::Bundle {
subject: self.space.clone(),
receipt: tcb.receipt.clone(),
epochs: vec![msg::Epoch {
tree: HandleSubtree(handles_proof),
handles: vec![msg::Handle {
name: staged.handle.name.clone(),
genesis_spk: staged.handle.genesis_spk.clone(),
records: None,
signature: Some(staged.signature),
}],
}],
records: None,
delegate_records: None,
}],
}
}
}
#[derive(Clone)]
pub struct TestDelegatedSpace {
pub space: TestSpace,
pub ptr: TestNum,
}
struct Fixture {
finalized_chain: TestChain,
latest_chain: TestChain,
handles: TestHandleTree,
finalized_anchor: RootAnchor,
latest_anchor: RootAnchor,
}
impl Fixture {
fn new() -> Self {
let mut chain = TestChain::new();
let ds = chain.add_space_with_delegation("@bitcoin");
let mut handles = TestHandleTree::new(&ds);
handles.add_handle("alice");
handles.add_handle("bob");
handles.commit(&mut chain);
chain.increase_time(COMMITMENT_FINALITY_INTERVAL + 1);
let finalized_anchor = chain.current_root_anchor();
let finalized_chain = chain.clone();
chain.increase_time(1);
handles.add_handle("charlie");
handles.commit(&mut chain);
let latest_anchor = chain.current_root_anchor();
handles.add_handle("staged");
Fixture {
finalized_chain,
latest_chain: chain,
handles,
finalized_anchor,
latest_anchor,
}
}
fn veritas(&self) -> Veritas {
let anchors = vec![self.latest_anchor.clone(), self.finalized_anchor.clone()];
Veritas::new().with_anchors(anchors).expect("valid anchors")
}
fn finalized_message(&self, handles: &[&str]) -> Message {
self.handles.build_message(
&self.finalized_chain,
0,
handles,
&self.finalized_anchor.block,
)
}
fn pending_message(&self, handles: &[&str]) -> Message {
self.handles
.build_message(&self.latest_chain, 1, handles, &self.latest_anchor.block)
}
fn temporary_message(&self, handle_name: &str) -> Message {
self.handles.build_temporary_message(
&self.latest_chain,
1,
handle_name,
&self.latest_anchor.block,
)
}
}
#[test]
fn verify_root_finalized() {
let f = Fixture::new();
let veritas = f.veritas();
let ctx = QueryContext::new();
let result = veritas
.verify_with_options(&ctx, f.finalized_message(&[]), libveritas::VERIFY_DEV_MODE)
.expect("verify");
assert_eq!(result.zones.len(), 1);
let zone = &result.zones[0];
assert_eq!(zone.handle, sname("@bitcoin"));
assert!(matches!(zone.sovereignty, SovereigntyState::Sovereign));
let ProvableOption::Exists { value: c } = &zone.commitment else {
panic!("expected commitment Exists");
};
assert_eq!(c.onchain.state_root, f.handles.commitments[0].root);
assert!(c.receipt_hash.is_none()); assert!(matches!(zone.delegate, ProvableOption::Exists { .. }));
}
#[test]
fn verify_leaf_finalized() {
let f = Fixture::new();
let veritas = f.veritas();
let ctx = QueryContext::new();
let result = veritas
.verify_with_options(
&ctx,
f.finalized_message(&["alice"]),
libveritas::VERIFY_DEV_MODE,
)
.expect("verify");
assert_eq!(result.zones.len(), 2);
let alice = result
.zones
.iter()
.find(|z| z.handle == sname("alice@bitcoin"))
.expect("alice");
assert!(matches!(alice.sovereignty, SovereigntyState::Sovereign));
let result = veritas
.verify_with_options(
&ctx,
f.finalized_message(&["bob"]),
libveritas::VERIFY_DEV_MODE,
)
.expect("verify");
let bob = result
.zones
.iter()
.find(|z| z.handle == sname("bob@bitcoin"))
.expect("bob");
assert!(matches!(bob.sovereignty, SovereigntyState::Sovereign));
}
#[test]
fn verify_root_pending() {
let f = Fixture::new();
let veritas = f.veritas();
let ctx = QueryContext::new();
let result = veritas
.verify_with_options(&ctx, f.pending_message(&[]), libveritas::VERIFY_DEV_MODE)
.expect("verify");
assert_eq!(result.zones.len(), 1);
let zone = &result.zones[0];
assert!(matches!(zone.sovereignty, SovereigntyState::Sovereign));
let ProvableOption::Exists { value: c } = &zone.commitment else {
panic!("expected commitment Exists");
};
assert_eq!(c.onchain.state_root, f.handles.commitments[1].root);
assert!(c.receipt_hash.is_some()); }
#[test]
fn verify_leaf_pending() {
let f = Fixture::new();
let veritas = f.veritas();
let ctx = QueryContext::new();
let result = veritas
.verify_with_options(
&ctx,
f.pending_message(&["charlie"]),
libveritas::VERIFY_DEV_MODE,
)
.expect("verify");
let charlie = result
.zones
.iter()
.find(|z| z.handle == sname("charlie@bitcoin"))
.expect("charlie");
assert!(matches!(charlie.sovereignty, SovereigntyState::Pending));
}
#[test]
fn verify_leaf_across_anchors() {
let f = Fixture::new();
let veritas = f.veritas();
let ctx = QueryContext::new();
let result = veritas
.verify_with_options(
&ctx,
f.pending_message(&["alice"]),
libveritas::VERIFY_DEV_MODE,
)
.expect("verify");
let alice = result
.zones
.iter()
.find(|z| z.handle == sname("alice@bitcoin"))
.expect("alice");
assert_eq!(alice.handle, sname("alice@bitcoin"));
}
#[test]
fn verify_leaf_temporary() {
let f = Fixture::new();
let veritas = f.veritas();
let ctx = QueryContext::new();
let result = veritas
.verify_with_options(
&ctx,
f.temporary_message("staged"),
libveritas::VERIFY_DEV_MODE,
)
.expect("verify");
let staged = result
.zones
.iter()
.find(|z| z.handle == sname("staged@bitcoin"))
.expect("staged");
assert_eq!(staged.handle, sname("staged@bitcoin"));
assert!(matches!(staged.sovereignty, SovereigntyState::Dependent));
}
#[test]
fn verify_with_request_filter() {
let f = Fixture::new();
let veritas = f.veritas();
let mut ctx = QueryContext::new();
ctx.add_request(sname("alice@bitcoin"));
let result = veritas
.verify_with_options(
&ctx,
f.finalized_message(&["alice", "bob"]),
libveritas::VERIFY_DEV_MODE,
)
.expect("verify");
assert_eq!(result.zones.len(), 1);
assert_eq!(result.zones[0].handle, sname("alice@bitcoin"));
}
#[test]
fn verify_with_cached_parent_zone() {
let f = Fixture::new();
let veritas = f.veritas();
let ctx = QueryContext::new();
let result = veritas
.verify_with_options(&ctx, f.finalized_message(&[]), libveritas::VERIFY_DEV_MODE)
.expect("verify");
let parent_zone = result.zones[0].clone();
let ctx = QueryContext::from_zones(vec![parent_zone]);
let result = veritas
.verify_with_options(
&ctx,
f.finalized_message(&["alice"]),
libveritas::VERIFY_DEV_MODE,
)
.expect("verify");
let alice = result
.zones
.iter()
.find(|z| z.handle == sname("alice@bitcoin"))
.expect("alice");
assert_eq!(alice.handle, sname("alice@bitcoin"));
}
#[test]
fn verify_uses_better_cached_zone() {
let f = Fixture::new();
let veritas = f.veritas();
let cached_zone = Zone {
anchor: 0, sovereignty: SovereigntyState::Dependent,
canonical: sname("alice@bitcoin"),
handle: sname("alice@bitcoin"),
alias: None,
script_pubkey: ScriptBuf::new(),
fallback_records: sip7::RecordSet::default(),
records: sip7::RecordSet::default(),
delegate: ProvableOption::Unknown,
commitment: ProvableOption::Unknown,
num_id: None,
};
let ctx = QueryContext::from_zones(vec![cached_zone.clone()]);
let result = veritas
.verify_with_options(
&ctx,
f.finalized_message(&["alice"]),
libveritas::VERIFY_DEV_MODE,
)
.expect("verify");
let alice = result
.zones
.iter()
.find(|z| z.handle == sname("alice@bitcoin"))
.expect("alice");
assert!(alice.anchor > 0);
assert!(matches!(alice.sovereignty, SovereigntyState::Sovereign));
}
#[test]
fn certificate_iterator() {
let f = Fixture::new();
let veritas = f.veritas();
let ctx = QueryContext::new();
let result = veritas
.verify_with_options(
&ctx,
f.finalized_message(&["alice", "bob"]),
libveritas::VERIFY_DEV_MODE,
)
.expect("verify");
let certs: Vec<Certificate> = result.certificates().collect();
assert_eq!(certs.len(), 3);
assert_eq!(certs[0].subject, sname("@bitcoin"));
assert!(matches!(certs[0].witness, Witness::Root { .. }));
let alice_cert = certs
.iter()
.find(|c| c.subject == sname("alice@bitcoin"))
.expect("alice cert");
assert!(matches!(alice_cert.witness, Witness::Leaf { .. }));
let bob_cert = certs
.iter()
.find(|c| c.subject == sname("bob@bitcoin"))
.expect("bob cert");
assert!(matches!(bob_cert.witness, Witness::Leaf { .. }));
}
#[test]
fn certificate_iterator_leaves_only() {
let f = Fixture::new();
let veritas = f.veritas();
let mut ctx = QueryContext::new();
ctx.add_request(sname("alice@bitcoin"));
let result = veritas
.verify_with_options(
&ctx,
f.finalized_message(&["alice"]),
libveritas::VERIFY_DEV_MODE,
)
.expect("verify");
let certs: Vec<Certificate> = result.certificates().collect();
assert_eq!(certs.len(), 1);
assert_eq!(certs[0].subject, sname("alice@bitcoin"));
assert!(matches!(certs[0].witness, Witness::Leaf { .. }));
}