EP 1: From CLI to Code — Bring Your Data to Celestia DA
ต่อยอดจากการส่ง Blob ผ่าน CLI สู่การสร้าง dApp ที่เก็บและดึงข้อมูลจาก Celestia DA

จากบทความที่แล้ว เราได้ลองไปเล่น DA บน Celestia Network ผ่าน CLI กันไปบ้างแล้วใช่ไหม ใครยังไม่ได้ลอง อ่านรายละเอียดได้จากที่นี้เลย https://celestia-thailand.hashnode.dev/submit-blob-celestia
คราวนี้เราจะนำ DA ของ Celestia ไปต่อยอดละ โดยจะนำไปพัฒนา dApp ง่ายที่พวกเราน่าจะคุ้นเคยกันอยู่ แล้วนั้นคือ TODO dApp นั้นเอง โดยข้อมูลทั้งหมดเราจะเก็บขึ้น on-chain ทั้งหมด พร้อมแล้วไปเริ่มกันเลย
เตรียมตัวกันก่อน
Celestia light node (Testnet network) จากบทความก่อนหน้า ติดตั้งให้เรียบร้อย ทำหน้าที่เป็นเหมือน Database onchain เพื่อเก็บข้อมูลของ Todo แล้วเรายังสามารถเรียกข้อมูลกลับมาเมื่อไหร่ก็ได้ โดยที่ข้อมูลนั้นจะไม่เปลี่ยนไป หรือมีใครมาแก้ไขมันได้
Go lang ติดตั้งให้เรียบร้อย เราจะใช้เป็น Logic ให้กับ Application เป็นเหมือนคนคอยจัดการข้อมูลก่อนส่งไปที่ Data Layer
เริ่มเขียนโค๊ดกัน
Initialize Golang module
go mod init todois-dapp
ติดตั้ง package
go get github.com/celestiaorg/celestia-openrpc@v0.5.0
สำหรับ packgage github.com/celestiaorg/celestia-openrpc จะมีการลง package ที่ทาง celestia เป็นคนทำขึ้นมา ดังนั้นเราจะต้องทำการ replace package เพื่อให้ Application ของเราสามารถทำงานได้ ซึ่งต้องไปแก้ไขในไฟล์ go.mod
replace (
github.com/cosmos/cosmos-sdk => github.com/celestiaorg/cosmos-sdk v1.28.2-sdk-v0.46.16
github.com/filecoin-project/dagstore => github.com/celestiaorg/dagstore v0.0.0-20230824094345-537c012aa403
github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1
github.com/ipfs/boxo => github.com/celestiaorg/boxo v0.29.0-fork
github.com/syndtr/goleveldb => github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7
github.com/tendermint/tendermint => github.com/celestiaorg/celestia-core v1.51.0-tm-v0.34.35
)
เสร็จจากตรงนี้ เราก็มีเตรียมตัวพร้อมที่จะไปเขียนในส่วนของ Logic ในการนำข้อมูลจาก TODO ขึ้น DA ของ Celestia Network
ส่งข้อมูลขึ้น DA ของ Celestia network
เริ่มจากเราต้องมี Light node ก่อน (ดูจากบทความที่แล้วได้นะ) เพื่อเป็นตัวกลางในการคุยกับ Celestia Data Availability แล้วเราจะได้ Light node endpoint มา
กำหนด Light node URL
url := "http://localhost:26658"
สร้าง Celestia client
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel()
// สร้าง Celestia Client
c, err := client.NewClient(ctx, url, "")
if err != nil {
return err
}
defer c.Close()
กำหนด Namespace ให้กับข้อมูลของเรา
// กำหนด application namespace
namespace := "todoist"
// convert เป็น byte
namespaceBytes := []byte(namespace)
// convert ให้เป้น namespace id
ns, err := share.NewBlobNamespaceV0([]byte(d.Namespace()))
if err != nil {
return err
}
ทำการสร้างข้อมูล (เป็นตัวอย่างก่อนนะ)
data := `[{ "title": "todo 1"}]`
// สร้าง blob จาก string โดยใช้เป็น v0
b, err := blob.NewBlobV0(ns, data)
if err != nil {
return err
}
เมื่อนำทุกอย่างมารวมร่างกัน
func SubmitData() uint64, error {
url := "http://localhost:26658"
token := ""
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel()
c, err := client.NewClient(ctx, url, token)
if err != nil {
return 0, err
}
defer c.Close()
ns, err := share.NewBlobNamespaceV0([]byte(d.Namespace()))
if err != nil {
return 0, err
}
// Submit the data as a blob
b, err := blob.NewBlobV0(ns, data)
if err != nil {
return 0, err
}
opts := &blob.SubmitOptions{}
height, err := c.Blob.Submit(ctx, []*blob.Blob{b}, opts)
if err != nil {
return 0, err
}
// ส่ง height กลับไป เพื่อใช้ในการดึงข้อมูล
// height = 10485
return height, nil
}
จากตรงนี้เราก็จะได้ function ในการส่งข้อมูลขึ้นไปเก็บที่ Celestia network แล้ว
อ่านข้อมูลจาก DA
กำหนด Light node URL
url := "http://localhost:26658"
สร้าง Celestia Client เหมือนเดิม
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel()
c, err := client.NewClient(ctx, url, "")
if err != nil {
return "", err
}
defer c.Close()
กำหนด Namespace ที่เราจะใช้อ่านข้อมูลจาก Celestia network
// กำหนด application namespace
namespace := "todoist"
// convert เป็น byte
namespaceBytes := []byte(namespace)
// convert ให้เป้น namespace id
ns, err := share.NewBlobNamespaceV0([]byte(d.Namespace()))
if err != nil {
return err
}
เรียกข้อมูล โดยเราจะอ้างอิงจาก Height จากขั้นตอนแรก
height := 10485
got, err := c.Blob.GetAll(ctx, height, []share.Namespace{ns})
if err != nil {
return "", err
}
if len(got) == 0 {
return "", fmt.Errorf("no blobs found")
}
data := got[0].Data
นำทุกอย่างมารวมกันอีกครั้ง
func Get() (string, error) {
url := "http://localhost:26658"
token := ""
// height จากการ submit data
height := 10485
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel()
c, err := client.NewClient(ctx, url, token)
if err != nil {
return "", err
}
defer c.Close()
ns, err := share.NewBlobNamespaceV0([]byte(d.Namespace()))
if err != nil {
return "", err
}
got, err := c.Blob.GetAll(ctx, height, []share.Namespace{ns})
if err != nil {
return "", err
}
if len(got) == 0 {
return "", fmt.Errorf("no blobs found")
}
return string(got[0].Data), nil
}
จากทั้งหมดนี้ เราจะได้ function ทั้งหมดละ ในการส่งข้อมูล เเละอ่านข้อมูลจาก DA ของ Celestia ขั้นตอนถัดไปเราจะสร้าง HTTP server (API) เพื่อเรียกใช้งาน function ดังกล่าว ลองเอาไปเล่นกันดูนะ เดียวรอบหน้าเราจะมาประกอบล่างกันต่อ
เจอกันครับ
by NUT | ContributionDAO
