EP 4: Seal กับ On-Chain Access Control
จัดการสิทธิการเข้าถึงเนื้อหาแบบกระจายศูนย์ด้วย Seal

Search for a command to run...
จัดการสิทธิการเข้าถึงเนื้อหาแบบกระจายศูนย์ด้วย Seal

No comments yet. Be the first to comment.
A developer's overview of the Sui Payment Stack: components, roles, and the problems it solves

Understanding How DeepBook Predict Works Under the Hood

Understanding How Prediction Markets Work — DeepBook Predict vs Polymarket vs Kalshi

Build an AI Chatbot with Groq on Sui Encrypted Messaging

Setup, Create Encrypted Channel, and Chat via CLI on Sui Testnet

สวัสดีครับ เดินทางมาถึงตอนที่ 4 แล้วกับซี่รีย์ Walrus + Seal ถ้าหากใครยังไม่ได้อ่าน อ่านย้อนหลังได้ที่นี้นะครับ
EP 1: https://onthemove.contributiondao.com/ep-1-walrus-and-seal
EP 2: https://onthemove.contributiondao.com/ep-2-walrus-programmable-storage
EP 3: https://onthemove.contributiondao.com/ep-3-walrus-seal-send-secret-message
สำหรับตอนนี้ เรามาเขียนในส่วนของ Move Smart Contract กันนะ เพราะจากตอนที่แล้ว เราได้เขียน Seal ไว้เรียบร้อยแล้ว แต่ขาดในส่วนของ Smart Contract ในการจัดการเรื่องสิทธิในการเข้าถึง
โดย Seal จะปกป้องข้อมูลของเราจากผู้ใช้งานทั่วไป ด้วยระบบป้องกันดังต่อไปนี้
Client-Side Encryption: หลักการสำคัญของ Seal คือข้อมูลที่ละเอียดอ่อนจะถูกเข้ารหัส บนอุปกรณ์ของเราเอง ก่อนที่จะถูกส่งออกไปที่อื่นๆ โดยไม่มีเซิร์ฟเวอร์ แม้แต่ key servers ของ Seal เอง ก็จะไม่เห็นข้อมูล plaintext ของเราเลย
Threshold Encryption: เพื่อหลีกเลี่ยงความเสี่ยงจากการจัดการกุญแจแบบรวมศูนย์ Seal ได้ใช้ Threshold Encryption แทนที่จะมีกุญแจถอดรหัสเพียงอันเดียว ซึ่งกุญแจนั้นจะถูกแบ่งออกเป็นหลาย shares และกระจายไปยังเครือข่าย Decentralized Key Servers ที่เป็นอิสระต่อกัน หากต้องการสร้างกุญแจ ต้นฉบับขึ้นมาใหม่เพื่อถอดรหัส จะต้องมี key servers จำนวนหนึ่ง (เช่น 2 ใน 3) ทำงานร่วมกัน ซึ่งขั้นตอนนี่จะช่วยขจัด single point of failure ออกไปได้เลยนั้นเอง
On-Chain Access Control: นี่คือส่วนที่ล้ำสมัยที่สุดของสถาปัตยกรรม Seal! โดยตรรกะที่กำหนดว่าใครได้รับอนุญาตให้ถอดรหัสข้อมูลนั้นไม่ได้ถูก hard-coded อยู่ใน off-chain server แต่ถูกกำหนดไว้ใน Move Smart Contract นั้นเอง ซึ่งข้อมูลนี้จะถูกเปิดเผยต่อสาธารณะและตรวจสอบได้บน Sui Network! สิ่งนี้ทำให้เราสามารถสร้าง Access Policies ที่ละเอียดและสามารถ Programmable ได้ เช่น เช่น token-gating (อนุญาตเฉพาะผู้ถือ NFT บางประเภท), time-locks หรือ role-based permissions ที่ซับซ้อน
มาเริ่มกันเลย ต่อไปนี้เราจะลงในส่วนของ Technical แล้วนะ ในคือการเขียน Move Smart Contract หากใครยังไม่ได้เตรียมเครื่องไม้ เครื่องมือ ให้ย้อนกลับไปดูบทความด้านล่างนี้นะ
โดย Move Smart Contract ที่เราจะเขียนกันต่อไปนี้ จะมีหัวใจหลักของ Seal อยู่ที่ฟังก์ชั่น seal_approve ซึ่ง
จุดเชื่อมต่อที่สำคัญสำหรับนักพัฒนาคือ entry function ที่มีชื่อเฉพาะใน Move module ของเรา: seal_approve ฟังก์ชันนี้ทำหน้าที่เป็น On-Chain Gatekeeper สำหรับคำขอถอดรหัสทั้งหลาย!
ซึ่งเราจะทดสอบเขียน secret_message.move contract ของเรา กันมาเริ่มกันทีละ step กันเลยครับ
สร้างโปรเจ็คขึ้นมาใหม่
sui move new secret_message
แก้ไข source code ในไฟล์ secret_message.move
module secret_message::message {
// Import เฉพาะตัวที่ต้องใช้จริง (module ส่วนใหญ่ Sui prelude มีให้แล้ว)
use sui::object::UID;
use sui::tx_context::TxContext;
use std::string::String;
/// Error ที่จะเกิดขึ้นเมื่อผู้ใช้ที่ไม่ได้รับอนุญาตพยายามเข้าถึงข้อความลับ
const ENoAccess: u64 = 0;
/// โครงสร้างของ SecretMessage ซึ่งเป็น Policy Object
///
/// - Policy นี้ไม่เก็บข้อความจริงบน-chain
/// - ตัวข้อความจะถูกเข้ารหัสและเก็บบน Walrus (off-chain)
/// - On-chain จะเก็บเฉพาะ metadata และสิทธิ์การเข้าถึง
///
/// ฟิลด์:
/// - `sender`: address ของผู้สร้าง (ผู้ส่งข้อความลับ)
/// - `recipient`: address ของผู้รับที่ได้รับอนุญาต
/// - `walrus_blob_id`: ID ของ blob ที่เก็บ encrypted content บน Walrus
public struct SecretMessage has key, store {
id: UID,
sender: address,
recipient: address,
walrus_blob_id: String,
}
/// ฟังก์ชันสำหรับสร้าง SecretMessage ใหม่
///
/// ขั้นตอน:
/// 1. ผู้ส่งเรียกฟังก์ชันนี้พร้อมระบุผู้รับ และ Walrus blob ID
/// 2. ระบบสร้าง Policy Object ใหม่ (SecretMessage)
/// 3. ส่ง Policy Object ไปยัง wallet ของผู้รับ
///
/// หมายเหตุ:
/// - ผู้รับเป็นคนเดียวที่ถือ object นี้ → สอดคล้องกับ access control
public fun create_secret_message(
recipient: address,
walrus_blob_id: String,
ctx: &mut TxContext
) {
let message_policy = SecretMessage {
id: object::new(ctx),
sender: ctx.sender(),
recipient,
walrus_blob_id,
};
// โอน Policy Object ไปที่ผู้รับ → เพื่อให้เป็นเจ้าของสิทธิ์การถอดรหัส
transfer::public_transfer(message_policy, recipient);
}
/// ฟังก์ชันหลักสำหรับทำงานร่วมกับ Seal (Decentralized Access Control Service)
///
/// Concept:
/// - Seal Key Servers จะจำลองการเรียกฟังก์ชันนี้ในขั้นตอน Verify Access
/// - ถ้าฟังก์ชันนี้ "ผ่านเงื่อนไข" → Key Servers จะอนุญาตให้ user รับ key share
/// - ถ้าไม่ผ่าน → จะไม่ปลดล็อก key share
///
/// มันคือ "On-chain Access Control Policy"
/// ใช้เงื่อนไขบน-chain ตรวจสอบว่าผู้ที่ขอถอดรหัสเป็น "recipient" ตัวจริงหรือไม่
///
/// พารามิเตอร์:
/// - `_`: ค่า vector<u8> ที่ Seal ต้องส่งมา (แต่ในที่นี้ไม่ใช้งาน)
/// - `policy`: reference ไปยัง SecretMessage ของข้อความนั้น
/// - `ctx`: ใช้ตรวจสอบว่า user ที่เรียกคือใคร
public fun seal_approve(
_: vector<u8>,
policy: &SecretMessage,
ctx: &TxContext
) {
// อนุญาตเฉพาะผู้รับที่กำหนดไว้ใน policy เท่านั้น
// ถ้า ctx.sender() ไม่ใช่ recipient → throw error
assert!(ctx.sender() == policy.recipient, ENoAccess);
}
}
Deploy Move Smart Contract
เราเริ่มจากการทดสอบ Build กันก่อนนะ ว่าติดปัญหาอะไรไหม อย่าลืมสลับไปใช้งาน Testnet นะครับ
sui move build
หากทุกอย่างเรียบร้อยก็เตรียมไปสู่ขั้นตอนการ Deploy ไปยัง SUI Testnet ได้เลย
sui client publish --gas-budget 100000000
เราจะได้หน้าตาประมาณนี้นะ
╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
│ Object Changes │
├──────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Created Objects: │
│ ┌── │
│ │ ObjectID: 0x3e841ab9ffb9dd26ef128631030e2fed4ce19d40e2be1521e760fd4c2dbfe18b │
│ │ Sender: 0xc1a83e6ed8a891cc891208e4288290013ed3f1ebf1576eede3f2c7bc4c56e75c │
│ │ Owner: Account Address ( 0xc1a83e6ed8a891cc891208e4288290013ed3f1ebf1576eede3f2c7bc4c56e75c ) │
│ │ ObjectType: 0x2::package::UpgradeCap │
│ │ Version: 349180711 │
│ │ Digest: 4zJPCm9Xab7VkY2bfsH347FwFcPQo74rYN75Qz9dddzk │
│ └── │
│ Mutated Objects: │
│ ┌── │
│ │ ObjectID: 0x0e77d7f999871c0a82f098699b0128715ade05416531deee0902f8a52bee4f9e │
│ │ Sender: 0xc1a83e6ed8a891cc891208e4288290013ed3f1ebf1576eede3f2c7bc4c56e75c │
│ │ Owner: Account Address ( 0xc1a83e6ed8a891cc891208e4288290013ed3f1ebf1576eede3f2c7bc4c56e75c ) │
│ │ ObjectType: 0x2::coin::Coin<0x2::sui::SUI> │
│ │ Version: 349180711 │
│ │ Digest: G8GFetS11dJDiDPYJvnvVaJ3kZnvy3ec31CPJfVSZTnu │
│ └── │
│ Published Objects: │
│ ┌── │
│ │ PackageID: 0x04c285b7f84f9db5cbef6e3c0a48a086463fa618a45ddc14154179fbde533c34 │
│ │ Version: 1 │
│ │ Digest: AFZExHfnygn3MqDCkfK1Hh9Ht5aaSxuGHu3bovjvbMhG │
│ │ Modules: message │
│ └── │
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
╭───────────────────────────────────────────────────────────────────────────────────────────────────╮
│ Balance Changes │
├───────────────────────────────────────────────────────────────────────────────────────────────────┤
│ ┌── │
│ │ Owner: Account Address ( 0xc1a83e6ed8a891cc891208e4288290013ed3f1ebf1576eede3f2c7bc4c56e75c ) │
│ │ CoinType: 0x2::sui::SUI │
│ │ Amount: -8488280 │
│ └── │
╰───────────────────────────────────────────────────────────────────────────────────────────────────╯
ซึ่งสิ่งที่สำคัญคือ หลังจากที่เราได้ทำการ Deploy สำเร็จเเล้ว เราจะเห็น PackageID เป็น String ยาวๆ 0x... ให้เก็บ PackageID นั้นเอาไว้ เพราะเราจะต้องนำไปใช้ใน EP 3 ซึ่งยังจำกันได้ใช่ไหมว่า ค่าของ PackageID เรายังเป็นค่าที่ยังไม่ได้กำหนด ซึ่งทำให้ตัวอย่างของเราทำงานไม่ได้
ซึ่งนี้ที่ PackageID ของเราคือ
0x04c285b7f84f9db5cbef6e3c0a48a086463fa618a45ddc14154179fbde533c34
เรียบร้อยครับ ตอนนี้เราได้ PackageID ที่พร้อมจะนำไปใช้งานแล้ว ก่อนที่เราจะไปกันต่อ ขอธิบายเพิ่มเกี่ยวกับการทำงานของ seal_approve กันสักนิดนึง โดยหลักการจะมีประมาณนี้นะ
ผู้ใช้ต้องการถอดรหัส: Client application ของผู้ใช้ (เช่น เว็บไซต์ Only… ของเรา) ต้องการเข้าถึงคอนเทนต์ลับ
จำลองธุรกรรม: แทนที่จะส่ง transaction ไปรันบน Sui Network ทันที Client application จะสร้าง Sui transaction ที่เรียกฟังก์ชัน seal_approve นี้ (แต่ยังไม่ execute จริง!) แล้วส่ง serialized bytes ของ transaction นี้ไปให้ Seal Key Servers
Key Servers ตรวจสอบ: Key Servers หลายตัว จะทำการจำลองการรัน transaction นี้บน Sui แยกกัน
ปล่อย Key Shares (ถ้าผ่าน): ถ้าการจำลองสำเร็จ (คือ assert! ในฟังก์ชัน seal_approve เป็นจริง เช่น ctx.sender() คือ recipient ที่ระบุไว้) Key Server นั้นๆ ก็จะปล่อย key share ของตนออกมา
ปฏิเสธ (ถ้าไม่ผ่าน): แต่ถ้าการจำลองล้มเหลว (เช่น assert! เป็นเท็จ เพราะผู้ที่พยายามถอดรหัสไม่ใช่ recipient ที่ถูกต้อง) Key Server ก็จะปฏิเสธที่จะให้ key share
Sui ถูกใช้เป็น decentralized และ verifiable access-control oracle ได้อย่างชาญฉลาด! ข้อมูลที่เข้ารหัสสามารถเก็บไว้ที่ไหนก็ได้ — ไม่ว่าจะเป็น Walrus หรือเครือข่ายอื่น—เพราะฟังก์ชัน seal_approve ทำหน้าที่เป็น public, immutable, composable API สำหรับตรวจสอบสิทธิ์การเข้าถึงบน chain โดยตรง
พูดง่ายๆ คือ: ตัว blockchain กลายเป็น access-control engine ที่ทุกระบบเชื่อถือร่วมกันได้ เป็นดีไซน์ที่ทั้งเรียบง่ายและทรงพลังมากๆ เลยใช่ไหมล่ะ?
เราเตรียมทุกอย่างพร้อมแล้ว สัปดาห์เราจะมาประกอบร่างสุดท้ายกันแล้ว อย่าลืมไปทดสอบ Deploy กันเด้อ แล้วเจอกันใหม่นะ