Compact ngôn ngữ midnight là ngôn ngữ smart contract ưu tiên quyền riêng tư đầu tiên được thiết kế chuyên biệt cho Midnight Network — một partner chain của Cardano tập trung vào bảo vệ dữ liệu. Khác với Solidity hay Rust (Solana) nơi quyền riêng tư là suy nghĩ sau cùng (cần mixer, ZK-rollup riêng), Compact xây dựng privacy by design ngay tại tầng ngôn ngữ: bạn viết code TypeScript-like, compiler tự động tách thành ba phần — ledger công khai, circuit zero-knowledge, và witness cục bộ — khiến dữ liệu nhạy cảm không bao giờ chạm chuỗi khối trừ khi bạn tường minh cho phép.
Tóm tắt nhanh: Compact = TypeScript syntax + ZK tự động + 3 ngữ cảnh (ledger/circuit/witness) + compile-time privacy guard. Dành cho dev muốn xây dApp bảo mật (voting bí mật, sealed-bid auction, DeFi confidential) mà không cần học toán học ZK phức tạp.
Compact là gì? Ngôn ngữ smart contract ưu tiên quyền riêng tư cho Midnight
Midnight Network ra đời như một partner chain của Cardano, sử dụng consensus Kachina (dựa trên Ouroboros) và token ADA làm gas. Mục tiêu: cho phép các dApp xử lý dữ liệu nhạy cảm — y tế, tài chính, nhận dạng, bầu cử — trên chuỗi khối công khai mà không tiết lộ dữ liệu gốc.
Compact là ngôn ngữ lập trình trung tâm của Midnight. Đặc điểm nổi bật:
| Đặc tính | Mô tả |
|---|---|
| Syntax | TypeScript-like (cú pháp quen thuộc, dễ onboard) |
| Type system | Strong + static + bounded (kích thước cố định tại compile-time) |
| ZK generation | Tự động từ circuit code — không viết R1CS/Circom tay |
| Privacy model | 3 ngữ cảnh: ledger (public), circuit (ZK), witness (private/local) |
| Compile targets | JS/TS (TX construction), ZKIR (proving keys), JSON (contract interface) |
| Tooling | compact CLI, VS Code extension, Vim support, source maps |
Midnight compact được phát triển dưới egida của Linux Foundation Decentralized Trust (LFDT-Minokawa) — repo chính tại github.com/LFDT-Minokawa/compact (1,035+ commits, Apache-2.0). Compiler viết bằng Chez Scheme (qua Nix), CLI bằng Rust, spec bằng Agda.
Kiến trúc ba ngữ cảnh độc đáo: Ledger — Circuit — Witness
Đây là cốt lõi khiến Compact khác biệt. Mọi hợp đồng Compact đều hoạt động song song trên ba "thế giới":
`mermaid graph TB subgraph "Người dùng (Local Machine)" W[Witness
TypeScript callback
Dữ liệu riêng tư
Không bao giờ on-chain] end subgraph "Zero-Knowledge Layer" C[Circuit
Mạch ZK bounded
Chứng minh tính đúng đắn
Compiler sinh tự động] end subgraph "Midnight Ledger (On-chain)" L[Ledger
Trạng thái công khai
Replicated across nodes
Ai cũng đọc được] end W -->|Private input| C C -->|ZK Proof + Public output| L L -->|Public state| C `
1. Ledger (Sổ cái công khai) — "Database everyone can read"
- Khai báo bằng
export ledger fieldName: Type; - Các kiểu ledger-state:
Counter,List,Map, các kiểu primitive - State được replicate trên tất cả node, bất biến, ai cũng query được
- Ví dụ:
export ledger authority: Bytes<32>;— public key của người nắm quyền
2. Circuit (Mạch ZK) — "API endpoint có chứng minh toán học"
- Khai báo bằng
export circuit name(params): ReturnType { ... } - Entry points của smart contract — user gọi qua transaction
- Bounded execution: mọi loop, recursion, allocation có giới hạn compile-time
- Compiler tự động dịch sang ZKIR → proving key / verifier key
- Có thể
pure(chỉ đọc, không sửa ledger) hoặc stateful
3. Witness (Chứng nhân cục bộ) — "Secure enclave của user"
- Khai báo bằng
witness name(params): ReturnType;— chỉ signature, không body - Implementation nằm ở TypeScript DApp chạy trên máy user (browser, mobile, backend)
- Dữ liệu witness không bao giờ rời thiết bị user — compiler enforce này
- Mental model:
async functionmà ZK prover "await" — nhưng runtime là prover, không phải browser
Analogy cho dev VN:
- Ledger = PostgreSQL public replica
- Circuit = API endpoint + ZK proof (client không thể gian lận input)
- Witness = localStorage / Secure Enclave / YubiKey — private key không bao giờ lên server
Hệ thống kiểu dữ liệu bounded: An toàn tại compile-time
Compact không có any, không có dynamic allocation, không có recursion. Mọi kiểu đều có kích thước cố định — compiler bác bỏ code có thể gây unbounded computation (độ phức tạp không xác định), điều cần thiết cho ZK circuit.
Primitive Types
| Kiểu | Mô tả | Ví dụ | |
|---|---|---|---|
Boolean | true/false | true | |
Field | Số nguyên modulo prime field của ZK system | 123 as Field | |
Uint | Unsigned integer bounded [m, n) | Uint<0..100> | |
Uint | Sized unsigned integer (n bits) | Uint<64> ≡ Uint<0..2^64> | |
Bytes | Byte vector cố định n bytes | Bytes<32> | |
Vector | Tuple n phần tử cùng kiểu T | Vector<3, Field> | |
[T1, T2, ...] | Tuple heterogeneous | [Uint<32>, Boolean] | |
Opaque<"string"\ | "Uint8Array"> | Opaque value, hash trong ZK, raw trong ledger | Opaque<"string"> |
Program-Defined Types
`compact
// Struct - nominal typing (tên quan trọng, không phải shape)
struct UserProfile {
id: Uint<64>;
name: Bytes<64>;
isActive: Boolean;
}
// Enum - state machine rõ ràng enum ProposalState { DRAFT, ACTIVE, PASSED, REJECTED }
// Contract type - structural interface (duck typing cho cross-contract) contract TokenInterface { circuit transfer(to: Bytes<32>, amount: Uint<64>): Boolean; circuit balanceOf(account: Bytes<32>): Uint<64>; } contract implements TokenInterface; // Compiler verify `
Generics: Type Params (T) + Numeric Params (#N)
`compact
module M`
#Nprefix phân biệt numeric param vs type param- Phải fully specialize tại use site — generic type không phải valid type
Subtyping cho Uint
Uint<0..100> là subtype của Uint<0..1000> — an toàn widening, compiler cho phép implicit upcast. Downcast cần explicit check (runtime guard auto-generated).
Privacy by design: disclose(), witness, và round counter
Đây là nơi Compact thực sự phát huy sức mạnh — privacy không phải option, là default.
disclose(): Compile-time guard cho private → public
`compact
witness secretKey(): Bytes<32>; // Private: chỉ user biết
circuit publicKey(round: Field, sk: Bytes<32>): Bytes<32> { return persistentHash
export circuit set(v: Uint<64>): [] { const sk = secretKey(); // Fetch private key locally const pk = publicKey(round, sk); // Derive trong ZK proof authority = disclose(pk); // ✅ EXPLICIT: "Tôi CHỌN publish pk" value = disclose(v); // ✅ EXPLICIT: "Tôi CHỌN publish value" state = State.SET; } `
- Bất kỳ giá trị nào derived từ witness không thể gán vào ledger trừ khi wrap
disclose() - Compiler bác bỏ tại compile-time nếu bạn quên
disclose()— không phải runtime bug - Giống
strict: truetrong TypeScript: bắt bạn tường minh intent, ngăn "accidental leak"
Witness Pattern: Private Input Không Bao Giờ On-Chain
`compact
witness secretKey(): Bytes<32>; // Declaration only
// TypeScript DApp implements: const secretKey = async (): Promise`
assert() để verify witness output (vd: assert(authority == pk))
Round Counter: Phá Vỡ Linkability (Giống Rotating MAC / JWT Rotation)
`compact
export ledger round: Counter; // Tăng 1 mỗi lần clear
export circuit clear(): [] { const sk = secretKey(); const pk = publicKey(round, sk); // Round nhập vào hash assert(authority == pk); state = State.UNSET; round.increment(1); // Next round → khác pk hoàn toàn } `
- Không có round: cùng
secretKey→ cùngauthority→ observer link được transactions - Có round: mỗi
clear→round++→pkmới → unlinkable - Real-world analogy: MAC address randomization (WiFi), JWT refresh token rotation, Tor circuit rotation — cùng nguyên lý: thay đổi identifier định kỳ để ngăn correlation attack
Commitment Schemes: Sealed-Bid Auction / Hash-commit-reveal
`compact
// CompactStandardLibrary cung cấp:
circuit transientHash`
- Commit phase:
commit = persistentCommit(bid, nonce)→ publishcommiton ledger - Reveal phase: disclose
bid+nonce→ verifier checkpersistentCommit(bid, nonce) == commit - Nonce không được reuse — nếu reuse, attacker link được commitments cùng giá trị
Vòng đời biên dịch: Từ .compact đến JavaScript + ZKIR + Contract Info
` ┌─────────────────┐ │ contract.compact │ └────────┬────────┘ │ compact compile [--skip-zk] output/ ▼ ┌────────────────────────────────────────────────────────────┐ │ COMPILER OUTPUT │ ├──────────────────┬──────────────────┬──────────────────────┤ │ .js / .d.ts │ *.zkir │ contract.json │ │ (TX construction│ (ZK Intermediate│ (Interface metadata)│ │ + type defs) │ Representation)│ │ └──────────────────┴──────────────────┴──────────────────────┘ `
CLI Workflow Hàng Ngày
`bash
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/midnightntwrk/compact/releases/latest/download/compact-installer.sh | sh
compact update # Latest compiler
compact update 0.29.0 # Specific version
compact list # Available versions
compact compile --skip-zk examples/counter.compact output/
compact compile examples/counter.compact output/
`
Output Files
| File | Mục đích |
|---|---|
contract.js | JS implementation: circuit functions, TX builders, type guards |
contract.d.ts | TypeScript definitions — type-safe call từ TS |
contract.zkir | ZK intermediate representation → proving/verifier keys |
contract.json | Contract info: exported circuits, types, version, metadata |
contract.js.map | Source map — debug Compact source trong Chrome DevTools |
Tip: Dùng --skip-zk khi dev (compile ~seconds vs minutes). Chỉ full compile khi testnet/mainnet deploy.
So sánh: Compact vs Solidity vs Rust (Solana) vs Circom vs Noir
| Feature | Compact | Solidity | Rust (Solana) | Circom | Noir |
|---|---|---|---|---|---|
| Privacy model | Built-in (3 contexts) | External (mixers, Aztec) | External (Elusiv, ZK) | Manual circuits | Built-in (similar) |
| Syntax | TypeScript-like | JS-like | Rust | DSL (C-like) | Rust-like |
| ZK generation | Tự động từ circuit | Manual (Solidity + Circom) | Manual (Rust + RISC Zero) | Manual R1CS | Tự động (Rust-like) |
| Type safety | Strong + bounded | Weak (dynamic) | Strong | Strong | Strong |
| Bounded execution | Enforced compiler | No (gas limit runtime) | No (compute budget) | Manual constraints | Enforced |
| Private data guard | disclose() compile-time | No | No | Manual | Similar |
| Vietnamese docs | Bài này (first!) | Có nhiều | Có | Không | Ít |
| Learning curve | Thấp (nếu biết TS) | Thấp | Trung bình (Rust) | Cao (math-heavy) | Trung bình |
| Ecosystem maturity | Early (testnet) | Mature | Mature | Mature (tooling) | Growing |
Khi nào chọn Compact?
- Cần privacy native cho user data (y tế, tài chính, voting, identity)
- Team đã biết TypeScript — onboard nhanh
- Muốn ZK không viết circuit tay — focus business logic
- Target Midnight/Cardano ecosystem — ADA gas, Kachina consensus
Khi nào KHÔNG chọn Compact?
- Cần DeFi composability ngay hôm nay (Midnight testnet, TVL thấp)
- Cần tooling mature (Hardhat, Foundry, Anchor equivalent chưa có)
- Team chỉ biết Solidity/Rust, không muốn học paradigm mới
Bắt đầu nhanh: Cài đặt và hợp đồng Counter đầu tiên
1. Cài đặt
`bash
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/midnightntwrk/compact/releases/latest/download/compact-installer.sh | sh
compact update # Cài latest toolchain
`
2. Hello World: Counter Contract
Tạo filecounter.compact:
`compact
pragma language_version 0.16;
import CompactStandardLibrary;
export ledger round: Counter;
export circuit increment(): [] { round.increment(1); } `
3. Biên dịch
`bash
compact compile --skip-zk counter.compact output/
`
4. Gọi từ TypeScript
`typescript
import { createCounterContract } from './output/counter.js';
import { MidnightProvider } from '@midnight-ntwrk/midnight-js-provider';
const provider = new MidnightProvider('wss://testnet.midnight.network'); const contract = createCounterContract(contractAddress, provider);
// Call circuit const tx = await contract.circuits.increment().proveAndSubmit(); console.log('Transaction submitted:', tx.hash); `
5. VS Code Extension
- Cài từ marketplace: "Compact" (publisher: LFDT-Minokawa)
- Syntax highlighting, hover types, go-to-definition, compile-on-save
Ví dụ thực tế: Private Voting (Bỏ phiếu bí mật) trên Midnight
Bài toán
DAO Việt Nam cần bầu cử hội đồng quản trị. Yêu cầu:- Tính bí mật: Không ai biết ai vote cho ai (không retaliation)
- Tính minh bạch: Kết quả tally công khai, verifiable
- Chống gian lận: Mỗi user vote 1 lần, không double-vote
Giải pháp Compact
`compact
pragma language_version 0.16;
import CompactStandardLibrary;
enum VoteChoice { YES, NO, ABSTAIN }
export ledger tallyYes: Uint<64>; export ledger tallyNo: Uint<64>; export ledger tallyAbstain: Uint<64>; export ledger voted: Map
witness voteChoice(): VoteChoice; // Private: user chọn YES/NO/ABSTAIN witness voterNullifier(): Bytes<32>; // Private: unique per election per user witness merkleProof(): Vector<32, Bytes<32>>; // Private: Merkle proof of eligibility
circuit verifyMerkleProof(leaf: Bytes<32>, proof: Vector<32, Bytes<32>>, root: Bytes<32>): Boolean { // Verify Merkle proof trong ZK circuit // (Implementation dùng persistentHash iterative) }
export circuit vote(): [] { const choice = voteChoice(); const nullifier = voterNullifier(); const proof = merkleProof(); // 1. Check eligibility (ZK proof) assert(verifyMerkleProof(nullifier, proof, merkleRoot), "Not eligible"); // 2. Check nullifier chưa dùng (prevent double-vote) assert(!voted.get(nullifier).orElse(false), "Already voted"); // 3. Tally (private choice → public aggregate) if (choice == VoteChoice.YES) { tallyYes = tallyYes + 1; } else if (choice == VoteChoice.NO) { tallyNo = tallyNo + 1; } else { tallyAbstain = tallyAbstain + 1; } // 4. Mark nullifier used voted.set(nullifier, true); } `
Luồng DApp (TypeScript)
`typescript
// User side (browser)
async function castVote(choice: VoteChoice) {
// 1. Generate nullifier = hash(secretKey, electionId)
const nullifier = await generateNullifier(userSecretKey, electionId);
// 2. Get Merkle proof từ eligibility tree (off-chain service)
const merkleProof = await fetchMerkleProof(nullifier);
// 3. Call witness implementations
const witnesses = {
voteChoice: () => choice,
voterNullifier: () => nullifier,
merkleProof: () => merkleProof,
};
// 4. Prove & submit (midnight-js handles ZK proving)
const tx = await contract.circuits.vote(witnesses).proveAndSubmit();
return tx;
}
`
Ứng dụng thực tế tại Việt Nam
- Bầu cử nội bộ công ty: Nhân viên vote kín đáo, kết quả透明
- DAO quản trị quỹ cộng đồng: Thành viên vote phân bổ ngân sách không lo bị chèn ép
- Hội đồng quản trị startup: Founders/Investors vote quyết định chiến lược bí mật
- Thẩm định dự án công: Các chuyên gia chấm điểm ẩn danh, kết quả công khai
Các Pattern Thường Gặp và Best Practices
1. State Machine với Enum
`compact
enum EscrowState { CREATED, FUNDED, RELEASED, REFUNDED, DISPUTED }
export ledger state: EscrowState;
export circuit fund(): [] { assert(state == EscrowState.CREATED); state = EscrowState.FUNDED; } `
- Tại sao: Enum forces exhaustive handling, compiler cảnh báo missing cases
2. Counter cho Replay Protection & Unlinkability
`compact
export ledger nonce: Counter;
// Mỗi circuit call: nonce.increment(1);
// Dùng trong hash: persistentHash([data, nonce])
`
- Ngăn replay attack (cùng input → khác output nhờ nonce)
- Cùng secretKey → khác commitment mỗi lần (unlinkability)
3. Commitment Scheme cho Sealed-Bid Auction
`compact
witness bidAmount(): Uint<64>;
witness bidNonce(): Bytes<32>;
export ledger commitments: Map
export circuit commitBid(bidder: Bytes<32>): [] { const amount = bidAmount(); const nonce = bidNonce(); const commit = persistentCommit(amount, nonce); commitments.set(bidder, commit); }
export circuit revealBid(bidder: Bytes<32>): [] { const amount = bidAmount(); // Same witness, user reveals const nonce = bidNonce(); const commit = commitments.get(bidder).unwrap(); assert(persistentCommit(amount, nonce) == commit, "Invalid reveal"); // Process bid... } `
4. Contract Types cho Cross-Contract Calls (Limitations)
`compact
contract Token {
circuit transfer(to: Bytes<32>, amount: Uint<64>): Boolean;
}
export ledger token: Token; // Reference to deployed contract
export circuit payToken(to: Bytes<32>, amount: Uint<64>): [] { // Current limitation: circuits calling witnesses CANNOT satisfy contract types // Workaround: separate pure view circuits for reads token.transfer(to, amount); } `
Lưu ý: Compiler hiện tại không cho phép circuit gọi witness satisfy contract type. Đọc doc/compact-reference.mdx section "Cross-contract calls" để biết chi tiết.
5. Error Messages Thân Thiện
`compact
export circuit withdraw(amount: Uint<64>): [] {
assert(balance >= amount, "Insufficient balance: requested ${amount}, available ${balance}");
assert(amount > 0, "Withdraw amount must be positive");
balance = balance - amount;
}
`
assert(condition, "message")→ message xuất hiện trong TX failure log- Giúp user/debugger hiểu lỗi ngay lập tức
Hệ sinh thái công cụ và tài nguyên
| Công cụ | Link | Mô tả |
|---|---|---|
| Compact CLI | github.com/midnightntwrk/compact/releases | compact update, compile, list, self update |
| VS Code Extension | Marketplace / editor-support/vsc | Syntax highlight, type hover, compile task |
| Vim Support | editor-support/vim | Syntax, indent, compiler integration |
| CompactStandardLibrary | doc/compact-reference.mdx#standard-library | Hash, commitment, vector ops, math |
| Examples | github.com/LFDT-Minokawa/compact/tree/main/examples | Counter, Lock, Bulletin Board, Voting |
| Writing Guide | doc/writing.mdx | Walkthrough từ zero đến full contract |
| Language Reference | doc/compact-reference.mdx | Spec đầy đủ (3800+ lines) |
| Midnight Dev Docs | docs.midnight.network | Network info, deployment, midnight-js |
| Discord Community | discord.gg/midnight | Dev support, announcements |
| GitHub Discussions | github.com/LFDT-Minokawa/compact/discussions | Q&A, feature requests, RFC |
Roadmap & Trạng Thái (2025)
- Testnet: Active (faucet, explorer, RPC endpoints)
- Mainnet: Roadmap Q4 2025
- Compiler: Version 0.16+ (language_version pragma)
- Breaking changes: Có thể xảy ra pre-1.0 — pin version trong CI/CD
FAQ
1. Compact khác gì so với Solidity?
Compact có privacy native (3 ngữ cảnh), ZK tự động, TypeScript syntax, bounded types. Solidity public-by-default, cần external tooling cho ZK (Circom, Noir), dynamic types, gas-based execution bounds.2. Có cần biết toán học ZK không?
Không. Compiler xử lý R1CS/PLONK/Halo2 conversion. Bạn chỉ cần hiểu: witness = private input, circuit = logic cần chứng minh, ledger = public state, disclose() = opt-in publish.3. Triển khai lên mainnet khi nào?
Midnight testnet đang active (2024-2025). Mainnet roadmap Q4 2025. Theo dõimidnight.network và Discord để cập nhật.
4. Tương tác với Cardano như thế nào?
Midnight là partner chain của Cardano:- Dùng ADA làm gas (bridge từ Cardano)
- Consensus Kachina (dựa trên Ouroboros Praos)
- Có thể read Cardano state qua cross-chain bridge (đang phát triển)
5. Học đường cong dốc không?
Dễ nếu biết TypeScript. Khái niệm mới chỉ 3:witness, disclose(), bounded types. Nếu biết Rust + Circom → dễ hơn. Nếu chỉ biết Solidity → cần shift mindset: "public by default" → "private by default".
6. Debug ZK circuit như thế nào?
- Dùng
--skip-zkcho logic testing (JS output chạy bình thường) - Source maps (
contract.js.map) → debug Compact source trong Chrome DevTools console.logtrong witness implementation (TypeScript side)- Midnight-js tester:
circuit.prove(witnesses)→ inspect proof locally trước khi submit
7. Gas/Fees thế nào?
- Dùng ADA (bridge từ Cardano)
- Fee tính theo proof size + TX size (khác Ethereum gas model)
- Testnet có faucet miễn phí
8. Có thể upgrade contract không?
Không trực tiếp (immutable deployment). Pattern: proxy contract + versioned logic (giống OpenZeppelin proxy) — deploy contract mới, migrate state qua admin circuit.Kết luận
Compact ngôn ngữ midnight mang来了 một paradigm mới cho smart contract development: privacy không phải add-on, mà là default. Với TypeScript-like syntax, ZK generation tự động, và compile-time privacy guards (disclose()), Compact hạ thấp barrier entry cho ZK development xuống mức bất kỳ TypeScript developer nào cũng có thể onboard trong vài giờ.
Đối với lập trình viên Việt Nam — nơi TypeScript thống trị, nhạy cảm về privacy (luật bảo vệ dữ liệu 13/2023), và có truyền thống mạnh về outsourcing blockchain (Axie, Coin98, Kyber) — Compact là cơ hội lớn để dẫn đầu wave privacy-preserving dApps trên Cardano/Midnight ecosystem.
Bước tiếp theo:
compact updatevà compileexamples/counter.compact- Đọc
doc/writing.mdxend-to-end - Tham gia Discord Midnight — hỏi trực tiếp core team
- Build một mini dApp: private voting, sealed-bid auction, hoặc confidential token transfer
Resources:
📖 [Writing a Contract](https://github.com/LFDT-Minokawa/compact/blob/main/doc/writing.mdx)
📚 [Language Reference](https://github.com/LFDT-Minokawa/compact/blob/main/doc/compact-reference.mdx)
💻 [Examples](https://github.com/LFDT-Minokawa/compact/tree/main/examples)
💬 [Discord](https://discord.gg/midnight)
🐙 [GitHub](https://github.com/LFDT-Minokawa/compact)
Bài viết này được tạo dựa trên tài liệu chính thức từ LFDT-Minokawa/compact (Apache-2.0), Midnight Foundation docs, và phân tích gốc của tác giả. Mã nguồn ví dụ lấy từ repo chính thức. Cập nhật: 2026-08-19.
