Initialize repository snapshot
This commit is contained in:
693
docs/skill/CODE_TEMPLATES.md
Normal file
693
docs/skill/CODE_TEMPLATES.md
Normal file
@@ -0,0 +1,693 @@
|
||||
# VaultMesh Code Templates
|
||||
|
||||
## Rust Templates
|
||||
|
||||
### Core Types
|
||||
|
||||
```rust
|
||||
// Receipt Header
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ReceiptHeader {
|
||||
pub receipt_type: String,
|
||||
pub timestamp: DateTime<Utc>,
|
||||
pub root_hash: String,
|
||||
pub tags: Vec<String>,
|
||||
}
|
||||
|
||||
// Receipt Metadata
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ReceiptMeta {
|
||||
pub scroll: Scroll,
|
||||
pub sequence: u64,
|
||||
pub anchor_epoch: Option<u64>,
|
||||
pub proof_path: Option<String>,
|
||||
}
|
||||
|
||||
// Generic Receipt
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Receipt<T> {
|
||||
#[serde(flatten)]
|
||||
pub header: ReceiptHeader,
|
||||
#[serde(flatten)]
|
||||
pub meta: ReceiptMeta,
|
||||
#[serde(flatten)]
|
||||
pub body: T,
|
||||
}
|
||||
|
||||
// Scroll Enum
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum Scroll {
|
||||
Drills,
|
||||
Compliance,
|
||||
Guardian,
|
||||
Treasury,
|
||||
Mesh,
|
||||
OffSec,
|
||||
Identity,
|
||||
Observability,
|
||||
Automation,
|
||||
PsiField,
|
||||
Federation,
|
||||
Governance,
|
||||
}
|
||||
|
||||
impl Scroll {
|
||||
pub fn jsonl_path(&self) -> &'static str {
|
||||
match self {
|
||||
Scroll::Drills => "receipts/drills/drill_runs.jsonl",
|
||||
Scroll::Compliance => "receipts/compliance/oracle_answers.jsonl",
|
||||
Scroll::Guardian => "receipts/guardian/anchor_events.jsonl",
|
||||
Scroll::Treasury => "receipts/treasury/treasury_events.jsonl",
|
||||
Scroll::Mesh => "receipts/mesh/mesh_events.jsonl",
|
||||
Scroll::OffSec => "receipts/offsec/offsec_events.jsonl",
|
||||
Scroll::Identity => "receipts/identity/identity_events.jsonl",
|
||||
Scroll::Observability => "receipts/observability/observability_events.jsonl",
|
||||
Scroll::Automation => "receipts/automation/automation_events.jsonl",
|
||||
Scroll::PsiField => "receipts/psi/psi_events.jsonl",
|
||||
Scroll::Federation => "receipts/federation/federation_events.jsonl",
|
||||
Scroll::Governance => "receipts/governance/governance_events.jsonl",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn root_file(&self) -> &'static str {
|
||||
match self {
|
||||
Scroll::Drills => "ROOT.drills.txt",
|
||||
Scroll::Compliance => "ROOT.compliance.txt",
|
||||
Scroll::Guardian => "ROOT.guardian.txt",
|
||||
Scroll::Treasury => "ROOT.treasury.txt",
|
||||
Scroll::Mesh => "ROOT.mesh.txt",
|
||||
Scroll::OffSec => "ROOT.offsec.txt",
|
||||
Scroll::Identity => "ROOT.identity.txt",
|
||||
Scroll::Observability => "ROOT.observability.txt",
|
||||
Scroll::Automation => "ROOT.automation.txt",
|
||||
Scroll::PsiField => "ROOT.psi.txt",
|
||||
Scroll::Federation => "ROOT.federation.txt",
|
||||
Scroll::Governance => "ROOT.governance.txt",
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### DID Types
|
||||
|
||||
```rust
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
|
||||
pub struct Did(String);
|
||||
|
||||
impl Did {
|
||||
pub fn new(did_type: DidType, identifier: &str) -> Self {
|
||||
Did(format!("did:vm:{}:{}", did_type.as_str(), identifier))
|
||||
}
|
||||
|
||||
pub fn parse(s: &str) -> Result<Self, DidParseError> {
|
||||
if !s.starts_with("did:vm:") {
|
||||
return Err(DidParseError::InvalidPrefix);
|
||||
}
|
||||
Ok(Did(s.to_string()))
|
||||
}
|
||||
|
||||
pub fn as_str(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum DidType {
|
||||
Node,
|
||||
Human,
|
||||
Agent,
|
||||
Service,
|
||||
Mesh,
|
||||
}
|
||||
|
||||
impl DidType {
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
DidType::Node => "node",
|
||||
DidType::Human => "human",
|
||||
DidType::Agent => "agent",
|
||||
DidType::Service => "service",
|
||||
DidType::Mesh => "mesh",
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Hash Utilities
|
||||
|
||||
```rust
|
||||
use blake3::Hasher;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct VmHash(String);
|
||||
|
||||
impl VmHash {
|
||||
pub fn blake3(data: &[u8]) -> Self {
|
||||
let hash = blake3::hash(data);
|
||||
VmHash(format!("blake3:{}", hash.to_hex()))
|
||||
}
|
||||
|
||||
pub fn from_json<T: Serialize>(value: &T) -> Result<Self, serde_json::Error> {
|
||||
let json = serde_json::to_vec(value)?;
|
||||
Ok(Self::blake3(&json))
|
||||
}
|
||||
|
||||
pub fn hex(&self) -> &str {
|
||||
self.0.strip_prefix("blake3:").unwrap_or(&self.0)
|
||||
}
|
||||
|
||||
pub fn as_str(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
pub fn merkle_root(hashes: &[VmHash]) -> VmHash {
|
||||
if hashes.is_empty() {
|
||||
return VmHash::blake3(b"empty");
|
||||
}
|
||||
if hashes.len() == 1 {
|
||||
return hashes[0].clone();
|
||||
}
|
||||
|
||||
let mut current_level: Vec<VmHash> = hashes.to_vec();
|
||||
|
||||
while current_level.len() > 1 {
|
||||
let mut next_level = Vec::new();
|
||||
for chunk in current_level.chunks(2) {
|
||||
let combined = if chunk.len() == 2 {
|
||||
format!("{}{}", chunk[0].hex(), chunk[1].hex())
|
||||
} else {
|
||||
format!("{}{}", chunk[0].hex(), chunk[0].hex())
|
||||
};
|
||||
next_level.push(VmHash::blake3(combined.as_bytes()));
|
||||
}
|
||||
current_level = next_level;
|
||||
}
|
||||
|
||||
current_level.remove(0)
|
||||
}
|
||||
```
|
||||
|
||||
### Engine Template
|
||||
|
||||
```rust
|
||||
// Template for new engine implementation
|
||||
pub struct MyEngine {
|
||||
db: DatabasePool,
|
||||
receipts_path: PathBuf,
|
||||
}
|
||||
|
||||
impl MyEngine {
|
||||
pub fn new(db: DatabasePool, receipts_path: PathBuf) -> Self {
|
||||
MyEngine { db, receipts_path }
|
||||
}
|
||||
|
||||
pub async fn create_contract(&self, params: CreateParams) -> Result<Contract, EngineError> {
|
||||
let contract = Contract {
|
||||
id: generate_id("contract"),
|
||||
title: params.title,
|
||||
created_at: Utc::now(),
|
||||
// ... domain-specific fields
|
||||
};
|
||||
|
||||
// Store contract
|
||||
self.store_contract(&contract).await?;
|
||||
|
||||
Ok(contract)
|
||||
}
|
||||
|
||||
pub async fn execute(&mut self, contract_id: &str) -> Result<State, EngineError> {
|
||||
let contract = self.load_contract(contract_id).await?;
|
||||
let mut state = State::new(&contract);
|
||||
|
||||
// Execute steps
|
||||
for step in &contract.steps {
|
||||
state.execute_step(step).await?;
|
||||
}
|
||||
|
||||
// Seal with receipt
|
||||
let receipt = self.seal(&contract, &state).await?;
|
||||
|
||||
Ok(state)
|
||||
}
|
||||
|
||||
async fn seal(&self, contract: &Contract, state: &State) -> Result<Receipt<MyReceipt>, EngineError> {
|
||||
let receipt_body = MyReceipt {
|
||||
contract_id: contract.id.clone(),
|
||||
status: state.status.clone(),
|
||||
// ... domain-specific fields
|
||||
};
|
||||
|
||||
let root_hash = VmHash::from_json(&receipt_body)?;
|
||||
|
||||
let receipt = Receipt {
|
||||
header: ReceiptHeader {
|
||||
receipt_type: "my_receipt_type".to_string(),
|
||||
timestamp: Utc::now(),
|
||||
root_hash: root_hash.as_str().to_string(),
|
||||
tags: vec!["my_engine".to_string()],
|
||||
},
|
||||
meta: ReceiptMeta {
|
||||
scroll: Scroll::MyScroll,
|
||||
sequence: 0,
|
||||
anchor_epoch: None,
|
||||
proof_path: None,
|
||||
},
|
||||
body: receipt_body,
|
||||
};
|
||||
|
||||
self.append_receipt(&receipt).await?;
|
||||
|
||||
Ok(receipt)
|
||||
}
|
||||
|
||||
async fn append_receipt<T: Serialize>(&self, receipt: &Receipt<T>) -> Result<(), EngineError> {
|
||||
let scroll_path = self.receipts_path.join(Scroll::MyScroll.jsonl_path());
|
||||
|
||||
let mut file = OpenOptions::new()
|
||||
.create(true)
|
||||
.append(true)
|
||||
.open(&scroll_path)?;
|
||||
|
||||
let json = serde_json::to_string(receipt)?;
|
||||
writeln!(file, "{}", json)?;
|
||||
|
||||
// Update Merkle root
|
||||
self.update_merkle_root().await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Prometheus Metrics
|
||||
|
||||
```rust
|
||||
use prometheus::{Counter, CounterVec, Histogram, HistogramVec, Gauge, GaugeVec, Opts, Registry};
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
lazy_static! {
|
||||
pub static ref REGISTRY: Registry = Registry::new();
|
||||
|
||||
pub static ref RECEIPTS_TOTAL: CounterVec = CounterVec::new(
|
||||
Opts::new("vaultmesh_receipts_total", "Total receipts by scroll"),
|
||||
&["scroll", "type"]
|
||||
).unwrap();
|
||||
|
||||
pub static ref OPERATION_DURATION: HistogramVec = HistogramVec::new(
|
||||
prometheus::HistogramOpts::new(
|
||||
"vaultmesh_operation_duration_seconds",
|
||||
"Operation duration"
|
||||
).buckets(vec![0.001, 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0]),
|
||||
&["operation"]
|
||||
).unwrap();
|
||||
|
||||
pub static ref ACTIVE_OPERATIONS: GaugeVec = GaugeVec::new(
|
||||
Opts::new("vaultmesh_active_operations", "Active operations"),
|
||||
&["type"]
|
||||
).unwrap();
|
||||
}
|
||||
|
||||
pub fn register_metrics() {
|
||||
REGISTRY.register(Box::new(RECEIPTS_TOTAL.clone())).unwrap();
|
||||
REGISTRY.register(Box::new(OPERATION_DURATION.clone())).unwrap();
|
||||
REGISTRY.register(Box::new(ACTIVE_OPERATIONS.clone())).unwrap();
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Python Templates
|
||||
|
||||
### CLI Command Group
|
||||
|
||||
```python
|
||||
import click
|
||||
import json
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
@click.group()
|
||||
def my_engine():
|
||||
"""My Engine - Description"""
|
||||
pass
|
||||
|
||||
@my_engine.command("create")
|
||||
@click.option("--title", required=True, help="Title")
|
||||
@click.option("--config", type=click.Path(exists=True), help="Config file")
|
||||
def create(title: str, config: str):
|
||||
"""Create a new contract."""
|
||||
contract_id = f"contract-{datetime.utcnow().strftime('%Y%m%d%H%M%S')}"
|
||||
|
||||
contract = {
|
||||
"id": contract_id,
|
||||
"title": title,
|
||||
"created_at": datetime.utcnow().isoformat() + "Z",
|
||||
}
|
||||
|
||||
if config:
|
||||
with open(config) as f:
|
||||
contract.update(json.load(f))
|
||||
|
||||
# Store contract
|
||||
contract_path = Path(f"cases/my_engine/{contract_id}/contract.json")
|
||||
contract_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
with open(contract_path, "w") as f:
|
||||
json.dump(contract, f, indent=2)
|
||||
|
||||
click.echo(f"✓ Contract created: {contract_id}")
|
||||
|
||||
@my_engine.command("execute")
|
||||
@click.argument("contract_id")
|
||||
def execute(contract_id: str):
|
||||
"""Execute a contract."""
|
||||
# Load contract
|
||||
contract_path = Path(f"cases/my_engine/{contract_id}/contract.json")
|
||||
with open(contract_path) as f:
|
||||
contract = json.load(f)
|
||||
|
||||
# Execute (implementation specific)
|
||||
state = {"status": "completed"}
|
||||
|
||||
# Emit receipt
|
||||
receipt = emit_receipt(
|
||||
scroll="my_scroll",
|
||||
receipt_type="my_receipt_type",
|
||||
body={
|
||||
"contract_id": contract_id,
|
||||
"status": state["status"],
|
||||
},
|
||||
tags=["my_engine"]
|
||||
)
|
||||
|
||||
click.echo(f"✓ Executed: {contract_id}")
|
||||
click.echo(f" Receipt: {receipt['root_hash'][:20]}...")
|
||||
|
||||
@my_engine.command("query")
|
||||
@click.option("--status", help="Filter by status")
|
||||
@click.option("--from", "from_date", help="From date")
|
||||
@click.option("--to", "to_date", help="To date")
|
||||
@click.option("--format", "output_format", default="table", type=click.Choice(["table", "json", "csv"]))
|
||||
def query(status: str, from_date: str, to_date: str, output_format: str):
|
||||
"""Query receipts."""
|
||||
filters = {}
|
||||
if status:
|
||||
filters["status"] = status
|
||||
if from_date:
|
||||
filters["from_date"] = from_date
|
||||
if to_date:
|
||||
filters["to_date"] = to_date
|
||||
|
||||
receipts = load_receipts("my_scroll", filters)
|
||||
|
||||
if output_format == "json":
|
||||
click.echo(json.dumps(receipts, indent=2))
|
||||
else:
|
||||
click.echo(f"Found {len(receipts)} receipts")
|
||||
for r in receipts:
|
||||
click.echo(f" {r.get('timestamp', '')[:19]} | {r.get('type', '')}")
|
||||
```
|
||||
|
||||
### Receipt Utilities
|
||||
|
||||
```python
|
||||
import json
|
||||
import hashlib
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
def emit_receipt(scroll: str, receipt_type: str, body: dict, tags: list[str]) -> dict:
|
||||
"""Create and emit a receipt to the appropriate scroll."""
|
||||
receipt = {
|
||||
"schema_version": "2.0.0",
|
||||
"type": receipt_type,
|
||||
"timestamp": datetime.utcnow().isoformat() + "Z",
|
||||
"tags": tags,
|
||||
**body
|
||||
}
|
||||
|
||||
# Compute root hash
|
||||
receipt_json = json.dumps(receipt, sort_keys=True)
|
||||
root_hash = f"blake3:{hashlib.blake3(receipt_json.encode()).hexdigest()}"
|
||||
receipt["root_hash"] = root_hash
|
||||
|
||||
# Append to scroll
|
||||
scroll_path = Path(f"receipts/{scroll}/{scroll}_events.jsonl")
|
||||
scroll_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
with open(scroll_path, "a") as f:
|
||||
f.write(json.dumps(receipt) + "\n")
|
||||
|
||||
# Update Merkle root
|
||||
update_merkle_root(scroll)
|
||||
|
||||
return receipt
|
||||
|
||||
def load_receipts(scroll: str, filters: Optional[dict] = None) -> list[dict]:
|
||||
"""Load and filter receipts from a scroll."""
|
||||
scroll_path = Path(f"receipts/{scroll}/{scroll}_events.jsonl")
|
||||
|
||||
if not scroll_path.exists():
|
||||
return []
|
||||
|
||||
receipts = []
|
||||
with open(scroll_path) as f:
|
||||
for line in f:
|
||||
receipt = json.loads(line.strip())
|
||||
|
||||
if filters:
|
||||
match = True
|
||||
for key, value in filters.items():
|
||||
if key == "from_date":
|
||||
if receipt.get("timestamp", "") < value:
|
||||
match = False
|
||||
elif key == "to_date":
|
||||
if receipt.get("timestamp", "") > value:
|
||||
match = False
|
||||
elif key == "type":
|
||||
if receipt.get("type") not in (value if isinstance(value, list) else [value]):
|
||||
match = False
|
||||
elif receipt.get(key) != value:
|
||||
match = False
|
||||
|
||||
if match:
|
||||
receipts.append(receipt)
|
||||
else:
|
||||
receipts.append(receipt)
|
||||
|
||||
return receipts
|
||||
|
||||
def update_merkle_root(scroll: str):
|
||||
"""Recompute and update Merkle root for a scroll."""
|
||||
scroll_path = Path(f"receipts/{scroll}/{scroll}_events.jsonl")
|
||||
root_file = Path(f"receipts/ROOT.{scroll}.txt")
|
||||
|
||||
if not scroll_path.exists():
|
||||
root_file.write_text("blake3:empty")
|
||||
return
|
||||
|
||||
hashes = []
|
||||
with open(scroll_path) as f:
|
||||
for line in f:
|
||||
receipt = json.loads(line.strip())
|
||||
hashes.append(receipt.get("root_hash", ""))
|
||||
|
||||
if not hashes:
|
||||
root_file.write_text("blake3:empty")
|
||||
return
|
||||
|
||||
# Simple merkle root (production would use proper tree)
|
||||
combined = "".join(h.replace("blake3:", "") for h in hashes)
|
||||
root = f"blake3:{hashlib.blake3(combined.encode()).hexdigest()}"
|
||||
root_file.write_text(root)
|
||||
|
||||
def verify_receipt(receipt_hash: str, scroll: str) -> bool:
|
||||
"""Verify a receipt exists and is valid."""
|
||||
receipts = load_receipts(scroll, {"root_hash": receipt_hash})
|
||||
return len(receipts) > 0
|
||||
```
|
||||
|
||||
### MCP Server Template
|
||||
|
||||
```python
|
||||
from mcp.server import Server
|
||||
from mcp.types import Tool, TextContent
|
||||
import json
|
||||
|
||||
server = Server("my-engine")
|
||||
|
||||
@server.tool()
|
||||
async def my_operation(
|
||||
param1: str,
|
||||
param2: int = 10,
|
||||
) -> str:
|
||||
"""
|
||||
Description of what this tool does.
|
||||
|
||||
Args:
|
||||
param1: Description of param1
|
||||
param2: Description of param2
|
||||
|
||||
Returns:
|
||||
Description of return value
|
||||
"""
|
||||
# Verify caller capabilities
|
||||
caller = await get_caller_identity()
|
||||
await verify_capability(caller, "required_capability")
|
||||
|
||||
# Perform operation
|
||||
result = perform_operation(param1, param2)
|
||||
|
||||
# Emit receipt
|
||||
await emit_tool_call_receipt(
|
||||
tool="my_operation",
|
||||
caller=caller,
|
||||
params={"param1": param1, "param2": param2},
|
||||
result_hash=result.hash,
|
||||
)
|
||||
|
||||
return json.dumps(result.to_dict(), indent=2)
|
||||
|
||||
@server.tool()
|
||||
async def my_query(
|
||||
filter_param: str = None,
|
||||
limit: int = 50,
|
||||
) -> str:
|
||||
"""
|
||||
Query operation description.
|
||||
|
||||
Args:
|
||||
filter_param: Optional filter
|
||||
limit: Maximum results
|
||||
|
||||
Returns:
|
||||
Query results
|
||||
"""
|
||||
caller = await get_caller_identity()
|
||||
await verify_capability(caller, "view_capability")
|
||||
|
||||
results = query_data(filter_param, limit)
|
||||
|
||||
return json.dumps([r.to_dict() for r in results], indent=2)
|
||||
|
||||
def main():
|
||||
import asyncio
|
||||
from mcp.server.stdio import stdio_server
|
||||
|
||||
async def run():
|
||||
async with stdio_server() as (read_stream, write_stream):
|
||||
await server.run(
|
||||
read_stream,
|
||||
write_stream,
|
||||
server.create_initialization_options(),
|
||||
)
|
||||
|
||||
asyncio.run(run())
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Property Test Templates
|
||||
|
||||
### Rust (proptest)
|
||||
|
||||
```rust
|
||||
use proptest::prelude::*;
|
||||
|
||||
proptest! {
|
||||
/// Receipts roundtrip through serialization
|
||||
#[test]
|
||||
fn receipt_roundtrip(receipt in arb_receipt()) {
|
||||
let json = serde_json::to_string(&receipt)?;
|
||||
let restored: Receipt<serde_json::Value> = serde_json::from_str(&json)?;
|
||||
prop_assert_eq!(receipt.header.root_hash, restored.header.root_hash);
|
||||
}
|
||||
|
||||
/// Hash is deterministic
|
||||
#[test]
|
||||
fn hash_deterministic(data in prop::collection::vec(any::<u8>(), 0..1000)) {
|
||||
let hash1 = VmHash::blake3(&data);
|
||||
let hash2 = VmHash::blake3(&data);
|
||||
prop_assert_eq!(hash1, hash2);
|
||||
}
|
||||
|
||||
/// Different data produces different hashes
|
||||
#[test]
|
||||
fn different_data_different_hash(
|
||||
data1 in prop::collection::vec(any::<u8>(), 1..100),
|
||||
data2 in prop::collection::vec(any::<u8>(), 1..100)
|
||||
) {
|
||||
prop_assume!(data1 != data2);
|
||||
let hash1 = VmHash::blake3(&data1);
|
||||
let hash2 = VmHash::blake3(&data2);
|
||||
prop_assert_ne!(hash1, hash2);
|
||||
}
|
||||
}
|
||||
|
||||
fn arb_receipt() -> impl Strategy<Value = Receipt<serde_json::Value>> {
|
||||
(
|
||||
"[a-z]{5,20}", // receipt_type
|
||||
any::<i64>().prop_map(|ts| DateTime::from_timestamp(ts.abs() % 2000000000, 0).unwrap()),
|
||||
prop::collection::vec("[a-z]{3,10}", 0..5), // tags
|
||||
).prop_map(|(receipt_type, timestamp, tags)| {
|
||||
Receipt {
|
||||
header: ReceiptHeader {
|
||||
receipt_type,
|
||||
timestamp,
|
||||
root_hash: "blake3:placeholder".to_string(),
|
||||
tags,
|
||||
},
|
||||
meta: ReceiptMeta {
|
||||
scroll: Scroll::Drills,
|
||||
sequence: 0,
|
||||
anchor_epoch: None,
|
||||
proof_path: None,
|
||||
},
|
||||
body: serde_json::json!({"test": true}),
|
||||
}
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
### Python (hypothesis)
|
||||
|
||||
```python
|
||||
from hypothesis import given, strategies as st
|
||||
import json
|
||||
|
||||
@given(st.dictionaries(st.text(min_size=1, max_size=20), st.text(max_size=100), max_size=10))
|
||||
def test_receipt_roundtrip(body):
|
||||
"""Receipts survive JSON roundtrip."""
|
||||
receipt = emit_receipt("test", "test_type", body, ["test"])
|
||||
|
||||
json_str = json.dumps(receipt)
|
||||
restored = json.loads(json_str)
|
||||
|
||||
assert receipt["root_hash"] == restored["root_hash"]
|
||||
assert receipt["type"] == restored["type"]
|
||||
|
||||
@given(st.binary(min_size=1, max_size=1000))
|
||||
def test_hash_deterministic(data):
|
||||
"""Hash is deterministic."""
|
||||
hash1 = hashlib.blake3(data).hexdigest()
|
||||
hash2 = hashlib.blake3(data).hexdigest()
|
||||
assert hash1 == hash2
|
||||
|
||||
@given(
|
||||
st.binary(min_size=1, max_size=100),
|
||||
st.binary(min_size=1, max_size=100)
|
||||
)
|
||||
def test_different_data_different_hash(data1, data2):
|
||||
"""Different data produces different hashes."""
|
||||
if data1 == data2:
|
||||
return # Skip if same
|
||||
|
||||
hash1 = hashlib.blake3(data1).hexdigest()
|
||||
hash2 = hashlib.blake3(data2).hexdigest()
|
||||
assert hash1 != hash2
|
||||
```
|
||||
Reference in New Issue
Block a user