use serde::{Deserialize, Serialize};
pub type HexId = String;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
pub struct PeerAddress {
pub host: String,
pub port: u16,
pub kind: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
pub struct Provider {
pub peer_id: HexId,
pub addresses: Vec<PeerAddress>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
pub struct ContentRef {
pub store_id: HexId,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub root: Option<HexId>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub retrieval_key: Option<HexId>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
pub struct RedirectInfo {
pub content: ContentRef,
pub providers: Vec<Provider>,
pub redirect_depth: u64,
pub max_redirects: u64,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
pub struct GetContentParams {
pub store_id: HexId,
pub retrieval_key: HexId,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub root: Option<HexId>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub offset: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub mode: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub redirect_depth: Option<u64>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
pub struct ContentChunk {
pub ciphertext: String,
pub root: HexId,
pub complete: bool,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub next_offset: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub inclusion_proof: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub chunk_lens: Option<Vec<u64>>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub source: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub total_length: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub length: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub offset: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub program_hash: Option<HexId>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
pub struct GetAnchoredRootParams {
pub store_id: HexId,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
pub struct AnchoredRoot {
pub store_id: HexId,
pub root: HexId,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
pub struct GetCollectionParams {
pub launcher_ids: Vec<HexId>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub did: Option<HexId>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
pub struct Collection {
#[serde(skip_serializing_if = "Option::is_none", default)]
pub did: Option<HexId>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub declared_did: Option<HexId>,
pub item_count: u64,
pub resolved_count: u64,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub royalty_basis_points: Option<u64>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
pub struct ListCollectionItemsParams {
pub launcher_ids: Vec<HexId>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub offset: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub limit: Option<u64>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
pub struct NftMetadata {
#[serde(skip_serializing_if = "Option::is_none", default)]
pub edition_number: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub edition_total: Option<u64>,
#[serde(default)]
pub data_uris: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub data_hash: Option<HexId>,
#[serde(default)]
pub metadata_uris: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub metadata_hash: Option<HexId>,
#[serde(default)]
pub license_uris: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub license_hash: Option<HexId>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
pub struct CollectionItem {
pub launcher_id: HexId,
pub coin_id: HexId,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub owner_did: Option<HexId>,
pub royalty_puzzle_hash: HexId,
pub royalty_basis_points: u64,
pub owner_puzzle_hash: HexId,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub metadata: Option<NftMetadata>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
pub struct CollectionItemsPage {
pub items: Vec<CollectionItem>,
pub offset: u64,
pub limit: u64,
pub total: u64,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub next_offset: Option<u64>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
pub struct RelayStatus {
pub url: String,
pub reserved: bool,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
pub struct NetworkInfo {
#[serde(skip_serializing_if = "Option::is_none", default)]
pub peer_id: Option<HexId>,
pub network_id: String,
pub listen_addr: String,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub reflexive_addr: Option<String>,
pub candidate_addresses: Vec<String>,
pub reachability: String,
pub relay: RelayStatus,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
pub struct PeersList {
pub peers: Vec<Provider>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
pub struct AnnounceParams {
pub peer_id: HexId,
pub addresses: Vec<PeerAddress>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
pub struct AnnounceAck {
pub accepted: bool,
pub known_peers: u64,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
pub struct AvailabilityQuery {
pub store_id: HexId,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub root: Option<HexId>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub retrieval_key: Option<HexId>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
pub struct GetAvailabilityParams {
pub items: Vec<AvailabilityQuery>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
pub struct AvailabilityAnswer {
pub available: bool,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub roots: Option<Vec<HexId>>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub total_length: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub chunk_count: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub complete: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub providers: Option<Vec<Provider>>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
pub struct AvailabilityBatch {
pub items: Vec<AvailabilityAnswer>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
pub struct ListInventoryParams {
#[serde(skip_serializing_if = "Option::is_none", default)]
pub store_id: Option<HexId>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub limit: Option<u64>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(untagged)]
#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
pub enum Inventory {
ForStore {
store_id: HexId,
roots: Vec<HexId>,
},
AllStores {
stores: Vec<HexId>,
},
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
pub struct FetchRangeParams {
pub store_id: HexId,
pub root: HexId,
pub retrieval_key: HexId,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub offset: Option<u64>,
pub length: u64,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub capsule: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub redirect_depth: Option<u64>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
pub struct RangeFrame {
pub offset: u64,
pub length: u64,
pub bytes: String,
pub complete: bool,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub total_length: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub chunk_lens: Option<Vec<u64>>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub chunk_index: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub inclusion_proof: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub root: Option<HexId>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
pub struct StageParams {
pub dir: String,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub store_id: Option<HexId>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub salt: Option<HexId>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub metadata: Option<serde_json::Value>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
pub struct StageResult {
pub capsule: String,
pub store_id: HexId,
pub root: HexId,
pub module_path: String,
pub size: u64,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub content_address: Option<String>,
#[serde(default)]
pub files: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub ephemeral: Option<bool>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
pub struct CacheConfig {
pub cap_bytes: u64,
pub used_bytes: u64,
pub cache_dir: String,
pub shared: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
pub struct SetCapBytesParams {
pub cap_bytes: u64,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
pub struct SetCapBytesResult {
pub cap_bytes: u64,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
pub struct CachedCapsule {
pub capsule: String,
pub store_id: HexId,
pub root: HexId,
pub size_bytes: u64,
pub last_used_unix_ms: u64,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
pub struct CachedList {
pub cached: Vec<CachedCapsule>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
pub struct CapsuleKey {
pub store_id: HexId,
pub root: HexId,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
pub struct RemoveCachedResult {
pub removed: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
pub struct FetchAndCacheResult {
pub status: String,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub size_bytes: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub served_root: Option<HexId>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub message: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
pub struct PeerStatusSnapshot {
pub running: bool,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub peer_id: Option<HexId>,
pub network_id: String,
pub relay: RelayStatus,
pub connected_peers: u64,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub last_error: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
pub struct Health {
pub status: String,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub version: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub network_id: Option<String>,
#[serde(default)]
pub methods: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
pub struct Methods {
pub methods: Vec<String>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn content_chunk_node_profile_is_lean() {
let c = ContentChunk {
ciphertext: "AAA=".into(),
root: "ab".repeat(32),
complete: false,
next_offset: Some(3_145_728),
inclusion_proof: Some("cHJvb2Y=".into()),
chunk_lens: Some(vec![10, 20]),
source: Some("local".into()),
total_length: None,
length: None,
offset: None,
program_hash: None,
};
let v = serde_json::to_value(&c).unwrap();
assert_eq!(v["source"], "local");
assert!(
v.get("total_length").is_none(),
"node profile must omit total_length"
);
assert!(v.get("program_hash").is_none());
assert_eq!(serde_json::from_value::<ContentChunk>(v).unwrap(), c);
}
#[test]
fn content_chunk_network_profile_carries_extras() {
let c = ContentChunk {
ciphertext: "AAA=".into(),
root: "cd".repeat(32),
complete: true,
next_offset: None,
inclusion_proof: None,
chunk_lens: None,
source: None,
total_length: Some(100),
length: Some(100),
offset: Some(0),
program_hash: Some("ef".repeat(32)),
};
let v = serde_json::to_value(&c).unwrap();
assert_eq!(v["total_length"], 100);
assert_eq!(v["length"], 100);
assert!(v.get("source").is_none());
}
#[test]
fn inventory_untagged_by_shape() {
let for_store = Inventory::ForStore {
store_id: "ab".repeat(32),
roots: vec!["cd".repeat(32)],
};
let s = serde_json::to_string(&for_store).unwrap();
assert!(s.contains("\"roots\""));
assert!(!s.contains("ForStore"));
assert_eq!(serde_json::from_str::<Inventory>(&s).unwrap(), for_store);
let all = Inventory::AllStores {
stores: vec!["ef".repeat(32)],
};
let s = serde_json::to_string(&all).unwrap();
assert!(s.contains("\"stores\""));
assert_eq!(serde_json::from_str::<Inventory>(&s).unwrap(), all);
}
#[test]
fn redirect_info_shape() {
let r = RedirectInfo {
content: ContentRef {
store_id: "ab".repeat(32),
root: Some("cd".repeat(32)),
retrieval_key: Some("ef".repeat(32)),
},
providers: vec![Provider {
peer_id: "12".repeat(32),
addresses: vec![PeerAddress {
host: "::1".into(),
port: 9444,
kind: "direct".into(),
}],
}],
redirect_depth: 1,
max_redirects: 4,
};
let v = serde_json::to_value(&r).unwrap();
assert_eq!(v["redirect_depth"], 1);
assert_eq!(v["max_redirects"], 4);
assert_eq!(v["providers"][0]["addresses"][0]["host"], "::1");
assert_eq!(serde_json::from_value::<RedirectInfo>(v).unwrap(), r);
}
#[test]
fn cache_config_field_name_is_cache_dir() {
let c = CacheConfig {
cap_bytes: 1 << 30,
used_bytes: 0,
cache_dir: "/var/cache/dig".into(),
shared: true,
};
let v = serde_json::to_value(&c).unwrap();
assert!(v.get("cache_dir").is_some());
assert!(v.get("dir").is_none(), "must not use the legacy `dir` name");
}
}