This document defines the Rust 0.4.x protocol contract.
You implement protocol logic only. Chaincraft handles UDP networking, gossip, deduplication, storage, peer management, and pipeline orchestration.
┌──────────────────────────────────────────────────────────────────┐
│ ChaincraftNode │
│ │
│ inbound datagram │
│ ├─ control path (direct): REQUEST_DIGEST / REQUEST_MESSAGES │
│ └─ gossip path: SharedMessage │
│ ├─ dedupe check by hash │
│ ├─ process_message(msg) │
│ │ 1) validate all objects (all-or-nothing) │
│ │ 2) linear apply pipeline │
│ │ add_message(msg, frontier_state?) │
│ │ -> Option<StateMemento> │
│ │ -> emitted/fallback memento passed downstream │
│ ├─ persist only on successful pipeline │
│ └─ rebroadcast │
└──────────────────────────────────────────────────────────────────┘
Pipeline snapshot propagated object-to-object in one message pass.
use chaincraft::{StateMemento, normalize_state_memento};
let m: StateMemento = normalize_state_memento("tip_digest", Some(vec![
"tip_digest".to_string()
]));Fields:
canonical_digestfrontier_digestsrevisionmetadata(string map for reorg/catch-up hints)
ApplicationObject is the protocol contract.
#[async_trait]
pub trait ApplicationObject {
async fn is_valid(&self, message: &SharedMessage) -> Result<bool>;
async fn add_message(
&mut self,
message: SharedMessage,
frontier_state: Option<StateMemento>,
) -> Result<Option<StateMemento>>;
async fn emit_state_memento(&self) -> Result<StateMemento>;
fn get_state_digests(&self) -> Vec<String>;
// ... digest/sync/state/reset methods ...
}Rules:
is_validmust be deterministic and side-effect free.add_messagemutates state and can emit a new memento.- If
add_messageemitsNone, the runtime usesemit_state_memento().
Transport envelope exchanged over network:
message_type,data,timestamp,hash,signature, etc.
ApplicationObjectRegistry::process_message:
- Validate message against all registered objects.
- If any object rejects, abort pipeline (message rejected).
- If all valid, process objects sequentially in registration order.
- Pass
frontier_statememento from one object to the next.
This gives deterministic multi-object state transitions and supports reorg/catch-up coordination between related objects.
For both local and inbound gossip messages:
- Run validation + apply pipeline first.
- Persist and broadcast only if pipeline succeeds.
This avoids committing invalid state transitions to storage.
Non-gossip control messages (REQUEST_DIGEST, REQUEST_MESSAGES_SINCE,
responses) are direct UDP request/response messages.
They are not regular gossip fanout objects, but replayed SharedMessages from
sync responses are still routed through the same v2 pipeline.
Use merkelized objects when canonical/frontier digests matter (chain, DAG, ledger snapshots). Use non-merkelized objects for eventually-consistent queues or caches where digest proofs are unnecessary.
Core object families:
- merkelized:
MerkelizedObject,UTXOLedger,BalanceLedger,Blockchain,DAGObject,TransactionChain - non-merkelized:
NonMerkelizedObject,CacheObject,Mempool,DocumentCache
Aliases:
MerkleizedObject->MerkelizedObjectNativeSharedObject->CoreSharedObject
Protocol modules under src/examples/ implement the v2 signature:
chatroom,ecdsa_ledger,randomness_beaconslush,snowflake,snowballtendermintblockchain(memento-aware chain example)
Runnable examples under examples/ use typed wrappers and node lifecycle:
chatroom_exampleecdsa_ledger_examplerandomness_beacon_exampleslush_examplesnowflake_examplesnowball_exampleblockchain_example
use chaincraft::{ChaincraftNode, PeerId, storage::MemoryStorage, BalanceLedger};
use std::sync::Arc;
let mut node = ChaincraftNode::new(PeerId::new(), Arc::new(MemoryStorage::new()));
node.add_shared_object(Box::new(BalanceLedger::new())).await?;
node.start().await?;
node.create_shared_message_with_data(serde_json::json!({
"type": "MY_EVENT",
"payload": {"x": 42}
})).await?;- Keep
is_validstrict and side-effect free. - Return stable mementos from
add_messagewhen your object is canonical owner. - Implement
get_state_digestsfor multi-head structures (DAG/forks). - Do not reimplement sockets, gossip fanout, or node threading.