Skip to main content
Source: Pinaivu-AI/contracts. Three modules, each building on the one before it:
pinaivu::enclave   ← root of trust: register PCRs + attested pubkey

       │ verify_signature<ENCLAVE, P>()

pinaivu::receipts  ← typed payload shape that matches Rust's BCS encoding

       │ verify_completion_receipt(...)

pinaivu::vault     ← treasury + settle(payee, amount, signature)

pinaivu::enclave

  • EnclaveConfig<ENCLAVE> — shared object holding the expected PCR triple. The admin holding Cap<ENCLAVE> rotates PCRs after every reproducible build via update_pcrs.
  • Enclave<ENCLAVE> — shared object created by register_enclave after a fresh NSM attestation document is verified against the config’s PCRs. Stores the Ed25519 pubkey extracted from that document.
  • verify_signature<ENCLAVE, P: drop>(enclave, intent, timestamp_ms, payload, signature) — wraps the payload in an IntentMessage, BCS-encodes it, and verifies with Sui’s ed25519 precompile.

pinaivu::receipts

  • Payout { sui_address: address, amount: u64 }
  • ReceiptPayload { request_id: vector<u8>, aggregated_output_hash: vector<u8>, payouts: vector<Payout> }
  • verify_completion_receipt(enclave, timestamp_ms, request_id, hash, payouts, &signature) — convenience wrapper that calls enclave::verify_signature::<ENCLAVE, ReceiptPayload>(...) with the reserved INTENT_ROUTING_RECEIPT = 1 intent scope.

pinaivu::vault

A treasury model — Pinaivu pre-funds a shared Balance<T> per supported coin type. Clients do not deposit per request (see the honest centralization caveat about this being an intentional simplification, not the long-term design).
Vault<phantom T> {
    treasury: Balance<T>,
    settled:  Table<(request_id || payee), bool>,  // dedupe
}

public fun new_vault<T>(ctx)
public fun top_up<T>(vault, payment)          // Pinaivu funds the pool
public fun settle<T>(
    vault, enclave,
    request_id, payee, amount,
    timestamp_ms, aggregated_output_hash, payouts,
    signature,
    ctx,
)
public fun treasury_balance<T>(vault): u64
settle aborts unless:
  1. (request_id, payee) hasn’t been settled before
  2. verify_completion_receipt(...) returns true (signature valid)
  3. (payee, amount) appears in the payouts list
  4. The treasury has enough balance left
Emits TreasuryToppedUp + Settled events for the off-chain explorer to index.

Why settle is safe even if the coordinator operator key leaks

The operator key (held by the in-enclave sidecar) signs and submits the settle() PTB but does not authorise the disbursement. The disbursement is authorised by the separate enclave-attested Ed25519 key (registered via register_enclave) which signs the ReceiptPayload BCS bytes. The sidecar cannot forge that signature — only the coordinator running attested code can. A compromised operator key can pay gas to submit valid receipts faster, or DOS the vault by submitting bogus PTBs (they all abort without moving money). It cannot drain funds. This split is the practical expression of the Sui pillar described in Decentralization & verifiability model.

Deploy outline

# 1. Publish
cd contracts && sui client publish --gas-budget 200000000
# → PackageId, Cap<ENCLAVE>, EnclaveConfig<ENCLAVE> object ids

# 2. Set real PCRs (reproducible build emits coordinator.pcrs)
sui client call --package $PKG --module enclave --function update_pcrs \
  --type-args "$PKG::enclave::ENCLAVE" \
  --args $CONFIG $CAP <pcr0_hex> <pcr1_hex> <pcr2_hex>

# 3. One vault per coin type (SUI shown)
sui client call --package $PKG --module vault --function new_vault \
  --type-args "0x2::sui::SUI"

# 4. Top up
sui client call --package $PKG --module vault --function top_up \
  --type-args "0x2::sui::SUI" --args $VAULT_ID $FUNDING_COIN_ID

Coordinator registration

Registration is authoritative on the EC2 host, not inside the enclave. The in-enclave path still runs in parallel (capped retries) for redundancy, but its calls go through the sidecar’s HTTPS bridge to Sui RPC and tend to lose a race against update_pcrs: when an enclave’s NSM attestation contains PCRs that differ from EnclaveConfig.pcrs on-chain, register_enclave aborts with EInvalidPcrs. The host-side path runs update_pcrs first and therefore always converges. The host’s scripts/register-coordinator.sh:
  1. Parses out/coordinator.pcrs.
  2. Calls enclave::update_pcrs so the on-chain EnclaveConfig<ENCLAVE> matches this build’s PCRs.
  3. Fetches the live enclave’s NSM document from GET /get_attestation.
  4. Converts the attestation bytes into a Sui CLI PTB vector literal.
  5. Chains 0x2::nitro_attestation::load_nitro_attestationpinaivu::enclave::register_enclave<ENCLAVE>(config, cap, doc) in a single transaction.
  6. Extracts the new Enclave<ENCLAVE> shared object id from the transaction’s object changes.
  7. Persists PINAIVU_ENCLAVE_OBJECT_ID so the next enclave boot picks it up automatically.
  8. Pushes the new id into the currently running coordinator via an authenticated admin endpoint, so vault::settle calls work in the same deploy that registered.

Why Sui

Why this is built on Move + Nautilus + Walrus specifically, not “a chain”

Glossary

PCR, NSM, EIF, IntentMessage, and every other term used on this page