
Bài 1: Tương Tác Hợp Đồng “Hello World” Bằng PyCardano
Chào mừng các bạn đến với bài học nền tảng về Lập trình Off-chain trên Cardano!
Bạn có thể đã học cách viết Smart Contract (On-chain) bằng ngôn ngữ Aiken. Nhưng một hợp đồng nằm trên blockchain sẽ trở nên vô dụng nếu không có ai tương tác với nó. Hôm nay, chúng ta sẽ học cách sử dụng Python (thư viện PyCardano) để “nói chuyện” với hợp đồng.
Chúng ta sẽ cùng nhau thực hiện một ví dụ kinh điển: “Hello World”. Bạn sẽ học cách khóa (Lock) ADA vào hợp đồng, và sau đó mở khóa (Unlock/Spend) nó bằng cách thỏa mãn đúng các điều kiện mà hợp đồng yêu cầu.
Mục Tiêu Bài Học
Trong bài này, bạn sẽ nắm được:
-
Cách đọc và hiểu logic cốt lõi của một hợp đồng Aiken.
-
Ánh xạ kiểu dữ liệu từ Aiken (On-chain) sang Python (Off-chain).
-
Xây dựng giao dịch Lock (Khóa): Gửi tiền vào Smart Contract kèm theo thẻ tên (Datum).
-
Xây dựng giao dịch Unlock (Mở khóa): Lấy tiền ra khỏi Smart Contract bằng cách cung cấp “thần chú” (Redeemer) và Chữ ký hợp lệ.
1. Phân Tích Smart Contract “Hello World” (Aiken)
Trước khi code Python, chúng ta cần biết hợp đồng yêu cầu những gì. Hãy xem file hello_world.ak đã được cập nhật cú pháp mới nhất của Aiken:
use aiken/hash.{Blake2b_224, Hash}
use aiken/list
use aiken/transaction.{Transaction}
use aiken/transaction/credential.{VerificationKey}
// 1. Định nghĩa Datum (Thẻ tên gắn kèm UTxO)
type Datum {
owner: Hash<Blake2b_224, VerificationKey>,
}
// 2. Định nghĩa Redeemer (Hành động/Thông điệp mở khóa)
type Redeemer {
msg: ByteArray,
}
// 3. Logic Validator
validator {
fn hello_world(datum: Datum, redeemer: Redeemer, context: Transaction) -> Bool {
// ĐIỀU KIỆN 1: Thông điệp mở khóa phải chính xác là "Hello, World!"
let must_say_hello =
redeemer.msg == "Hello, World!"
// ĐIỀU KIỆN 2: Giao dịch phải được ký bởi người chủ (owner) được ghi trong Datum
let must_be_signed =
list.has(context.extra_signatories, datum.owner)
must_say_hello && must_be_signed
}
}
Tóm lại: Hợp đồng này giống như một chiếc két sắt an toàn. Để mở két, bạn cần đọc đúng câu “thần chú” (Hello, World!) VÀ bạn phải chứng minh mình là chủ nhân của két sắt đó (bằng chữ ký điện tử khớp với owner đã lưu trong Datum).
2. Ánh Xạ Dữ Liệu Sang Python (PyCardano)
Để Python có thể “nói chuyện” được với Aiken, hai bên phải dùng chung một chuẩn mã hóa dữ liệu (CBOR). Trong Python, chúng ta sử dụng @dataclass kế thừa từ PlutusData để định nghĩa lại Datum và Redeemer:
from dataclasses import dataclass
from pycardano import PlutusData
# Ánh xạ cấu trúc Datum
@dataclass
class Datum(PlutusData):
CONSTR_ID = 0
owner: bytes # Public Key Hash của chủ sở hữu
# Ánh xạ cấu trúc Redeemer
@dataclass
class Redeemer(PlutusData):
CONSTR_ID = 0
msg: bytes # Thông điệp mở khóa
Lưu ý: CONSTR_ID = 0 rất quan trọng, nó báo cho trình biên dịch biết đây là cấu trúc dữ liệu đầu tiên, giúp dữ liệu sinh ra khớp hoàn hảo với cấu trúc bên Aiken.
3. Giao Dịch Khóa (Lock ADA)
Bây giờ, chúng ta sẽ xem file lock.py để gửi ADA vào két sắt (Smart Contract).
# 1. Đọc file hợp đồng đã biên dịch (plutus.json)
with open("../contract/plutus.json", "r") as f:
script_hex = json.load(f)["validators"][0]["compiledCode"]
hello_world_script = PlutusV2Script(bytes.fromhex(script_hex))
script_hash = plutus_script_hash(hello_world_script)
script_address = Address(script_hash, network=network)
# 2. Tạo Datum ghi nhận bạn là chủ sở hữu
# Dùng public key hash của ví bạn làm owner
datum = Datum(owner=payment_vkey.hash().payload)
# 3. Xây dựng giao dịch
builder = TransactionBuilder(context)
builder.add_input_address(address) # Lấy tiền từ ví của bạn
# Gửi ADA vào địa chỉ hợp đồng, đính kèm Datum
builder.add_output(
TransactionOutput(
address=script_address,
amount=5000000, # Khóa 5 ADA
datum=datum # Đặt thẻ tên (owner) của bạn vào đây
)
)
# Ký và gửi lên blockchain...
Sau khi chạy script này, 5 ADA của bạn đã bị khóa an toàn trên blockchain. Bất cứ ai cũng có thể nhìn thấy nó (vì blockchain là minh bạch), nhưng không ai có thể lấy được nếu không biết “thần chú” và không có Private Key của bạn.
4. Giao Dịch Mở Khóa (Unlock/Spend ADA)
Đã đến lúc lấy lại ADA của mình. Đây là phần thú vị nhất! Trong file unlock.py, chúng ta phải làm thỏa mãn cả 2 điều kiện mà Aiken yêu cầu.
# 1. Tìm UTxO đang nằm trên Smart Contract
utxo_to_spend = None
for utxo in context.utxos(script_address):
if utxo.output.datum:
try:
# Giải mã CBOR để xem Datum này có phải của mình không
stored_datum = Datum.from_cbor(utxo.output.datum.cbor)
if stored_datum.owner == payment_vkey.hash().payload:
utxo_to_spend = utxo
break
except Exception:
pass
# 2. Xây dựng giao dịch mở khóa
builder = TransactionBuilder(context)
builder.add_input_address(address) # Cung cấp UTxO từ ví để trả phí mạng (Fee)
# Thỏa mãn ĐIỀU KIỆN 1: Cung cấp đúng thông điệp "Hello, World!"
redeemer = Redeemer(msg=b"Hello, World!")
# Rút tiền từ hợp đồng
builder.add_script_input(
utxo=utxo_to_spend,
script=hello_world_script,
redeemer=RedeemerTag.SPEND,
redeemer_data=redeemer
)
# Thỏa mãn ĐIỀU KIỆN 2: Cung cấp chữ ký của chủ sở hữu
builder.required_signers = [payment_vkey.hash()]
# Ký và gửi lên blockchain...
Chuyện gì sẽ xảy ra nếu ta làm sai?
-
Nếu bạn đổi
msg=b"Hello, Cardano!"$\rightarrow$ Validator Aiken thấy thông điệp không khớp, và lập tức TỪ CHỐI (Reject) giao dịch của bạn. -
Nếu một hacker có script này (họ biết câu “Hello, World!”) nhưng dùng ví của họ để gửi $\rightarrow$ Validator sẽ kiểm tra danh sách người ký
context.extra_signatorieskhông khớp vớidatum.owner. Giao dịch cũng sẽ bị TỪ CHỐI.
Chỉ khi bạn đưa đúng thông điệp VÀ dùng đúng ví đã khóa để ký, tiền mới được giải phóng!
Tổng Kết
Chúc mừng! Bạn vừa hoàn thành việc giao tiếp Full-stack (On-chain to Off-chain) trên mạng lưới Cardano.
Hãy ghi nhớ 3 mảnh ghép không thể tách rời khi chi tiêu (Spend) từ một Smart Contract:
-
Script (Mã nguồn hợp đồng): Đóng vai trò là “Ổ khóa” và định ra các quy tắc.
-
Datum (Trạng thái): Lưu cùng UTxO trên mạng, đóng vai trò là “Biển tên” hoặc dữ liệu.
-
Redeemer (Hành động): Thông số từ bên ngoài truyền vào đóng vai trò là “Chìa khóa & Thần chú”.
Bạn đã vượt qua bài “Hello World”. Kiến thức về TransactionBuilder, add_script_input, cách tính phí (Fees) và ánh xạ @dataclass này chính là nền tảng cực kỳ vững chắc để chúng ta bước vào những chuẩn token phức tạp hơn như Vesting hay CIP-68 ở các bài học tiếp theo.
Hẹn gặp lại các bạn ở các dự án thực chiến tiếp theo! 🚀
Hẹn gặp lại các bạn!
Chi tiết nội source code của bài học các bạn có thể theo dõi tại đây!!!!
Chapter 3 — Lesson 1: Hello World (Aiken + PyCardano)
