Data trong Aiken
Bài học này hướng dẫn cách làm việc với kiểu Data - nền tảng serialization trong Cardano.
Mục tiêu học tập
- Hiểu kiểu Data và vai trò trong Cardano
- Nắm cách upcast và downcast
- Serialize/deserialize custom types
- Tối ưu kích thước dữ liệu on-chain
Data là gì?
Data là kiểu dữ liệu tổng quát trong Plutus - mọi dữ liệu on-chain đều là Data:
┌─────────────────────────────────────────────────────────────┐
│ PLUTUS DATA │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ │
│ │ Integer │ Số nguyên │
│ └─────────────┘ │
│ ┌─────────────┐ │
│ │ ByteArray │ Mảng bytes │
│ └─────────────┘ │
│ ┌─────────────┐ │
│ │ List │ Danh sách Data │
│ └─────────────┘ │
│ ┌─────────────┐ │
│ │ Map │ Key-value pairs │
│ └─────────────┘ │
│ ┌─────────────┐ │
│ │ Constr │ Constructor với tag + fields │
│ └─────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
Upcast - Chuyển đổi lên Data
Mọi kiểu Aiken đều có thể chuyển sang Data (an toàn):
lib/data_demo.ak
fn upcast_examples() {
// Int -> Data
let int_data: Data = 42
// ByteArray -> Data
let bytes_data: Data = #"abc123"
// List -> Data
let list_data: Data = [1, 2, 3]
// Custom type -> Data
let user = User { name: "Alice", age: 25 }
let user_data: Data = user
int_data
}
Sử dụng builtin.serialise_data
use aiken/builtin
fn serialize_example() {
let user = User { name: "Alice", age: 25 }
// Serialize sang CBOR bytes
let cbor_bytes = builtin.serialise_data(user)
cbor_bytes
}
Downcast - Chuyển đổi xuống từ Data
Chuyển từ Data về kiểu cụ thể - có thể thất bại:
Với expect (fail nếu không match)
fn downcast_with_expect(data: Data) {
// Sẽ fail nếu data không phải User
expect user: User = data
user.name
}
Với if/is (an toàn)
fn downcast_with_if(data: Data) -> Option<User> {
if data is user: User {
Some(user)
} else {
None
}
}
Với pattern matching
fn process_data(data: Data) -> Int {
// Downcast thủ công
when data is {
// Integer
n: Int -> n
// ByteArray
_: ByteArray -> 0
// Không match
_ -> -1
}
}
Custom Types và Data Encoding
Encoding mặc định (Constr)
type User {
name: ByteArray,
age: Int,
}
// Encoded as: Constr 0 [<name>, <age>]
Tùy chỉnh constructor tag
@tag(100)
type SpecialUser {
name: ByteArray,
age: Int,
}
// Encoded as: Constr 100 [<name>, <age>]
Encoding dạng List
@list
type Point {
x: Int,
y: Int,
}
// Encoded as: List [<x>, <y>]
// Nhẹ hơn Constr!
Encoding cho Enum
type Status {
Pending // Constr 0 []
Active // Constr 1 []
Completed // Constr 2 []
}
// Custom tags
type CustomStatus {
@tag(10)
Pending
@tag(20)
Active
@tag(30)
Completed
}
Sơ đồ Data flow
┌─────────────────────────────────────────────────────────────┐
│ DATA FLOW │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ Custom Type │ ──────▶ │ Data │ ──── On-chain │
│ │ (Aiken) │ Upcast │ (Plutus) │ │
│ └─────────────┘ └─────────────┘ │
│ ▲ │ │
│ │ │ │
│ │ Downcast │ │
│ │ (expect/if is) │ │
│ │ ▼ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ Custom Type │ ◀────── │ CBOR │ ──── Storage │
│ │ (Aiken) │ │ (Bytes) │ │
│ └─────────────┘ └─────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
Bước tiếp theo
Trong bài tiếp theo, chúng ta sẽ học về Unit Testing - cách viết và chạy tests trong Aiken.