Cosmos คือ Internet of blockchain ที่มีหลายๆ blockchain อยูร่วมกัน ถูกสร้างบน Consensus ข้อตกลงแบบ Byzantine Fault Tolerance (BFT)
BFT ก็คือ Consensus รูปแบบหนึ่งหรือข้อตกลงร่วมกัน ระหว่าง node แต่ละ node ของ blockchain
นั่นเพราะว่า blockchain ทำงานแบบ decentralized ก็คือมันไม่ได้เก็บข้อมูลไว้ที่ server แค่ตัวเดียว แต่มันทำการกระจายไปยัง server แต่ละ node ทั่วโลก
และแต่ละ node ก็ทำงานของมันไป ของใคร ก็ของมัน ทีนี้ปัญหามันจะเกิดขึ้นมาเมื่อ แต่ละ node มีการ update data เราจะรู้ได้ยังไงล่ะว่าข้อมูลชุดไหน ถูกต้อง เราจึงต้องมี Consensus ข้อตกลงร่วมกันเกิดขึ้นมา
ใน Cosmos นั้นเป็น Consensus ข้อตกลงแบบ Byzantine Fault Tolerance (BFT) กับ PoS โดยจะใช้การ vote ผ่าน validator node อย่างน้อย 2/3 node โดยแต่ละ validator node จะมีเสียงในการ vote ที่ไม่เท่ากัน ตามแต่เราจะ config
เราสามารถสร้าง Blockchain ของตัวเองได้ง่ายๆบน Cosmos SDK โดยใช้ ภาษา Go โดย Cosmos จะ run อยู่บน Tendermint อีกที
เริ่มต้นสร้าง Blockchain ของตัวเองเลย
เราจะทำ application เกี่ยวกับการลงคะแนนกันครับ โดย user สามารถ
โดยการสร้างแบบสำรวจจะมีค่าใช้จ่าย 200 token โดยต้องลงทะเบียนก่อนใช้งานระบบเท่านั้น
โดยเราจะใช้ Tool ที่ชื่อว่า Starport ในการสร้าง blockchain ขึ้นมาอย่างง่ายๆครับ โดยสามารถเข้าไปติดตั้งได้ที่นี่
โดยเจ้า Starport เนี่ยเค้าเคลมว่า เป็น tools ที่ง่ายโคตรๆในการสร้าง blockchains และโคตรๆจะ เฟรนลี่กับ developer เพื่อให้ developer ไป focus กับ business logic แทนไงล่ะ
งั้นว่าแล้วก็มาลองกันเลย ฮึ๊บๆๆ
npm i -g @tendermint/starport
โดยเจ้า Starport เนี่ยต้องการ Go lang version 1.14 ขึ้นไป และ Nodejs เวอร์ชั่นสูงหน่อย (ของผม verion v14.3.0)
เอาล่ะ มาเริ่มกันเลย
เริ่มต้นด้วยการใช้ command line starport app ตามด้วย github.com/username/ชื่อapplication ของเรา
$starport app github.com/aofiee/voter⭐️ Successfully created a Cosmos app 'voter'.👉 Get started with the following commands:% cd voter% starport serveNOTE: add --verbose flag for verbose (detailed) output.
โดยใน Directory voter จะมีโครงสร้างแบบนี้
$ls -lahtotal 168drwxr-xr-x 12 skulltree staff 384B Sep 9 14:38 .drwxr-xr-x 9 skulltree staff 288B Sep 9 14:38 ..drwxr-xr-x 6 skulltree staff 192B Sep 9 14:39 .git-rw-r--r-- 1 skulltree staff 51B Sep 9 14:38 .gitignoredrwxr-xr-x 5 skulltree staff 160B Sep 9 14:38 appdrwxr-xr-x 4 skulltree staff 128B Sep 9 14:38 cmd-rw-r--r-- 1 skulltree staff 172B Sep 9 14:38 config.yml-rw-r--r-- 1 skulltree staff 570B Sep 9 14:38 go.mod-rw-r--r-- 1 skulltree staff 67K Sep 9 14:38 go.sum-rw-r--r-- 1 skulltree staff 1.0K Sep 9 14:38 readme.mddrwxr-xr-x 11 skulltree staff 352B Sep 9 14:38 vuedrwxr-xr-x 3 skulltree staff 96B Sep 9 14:38 x
ต่อไปเราจะทดลองสั่ง run blockchain ของเราได้ด้วยคำสั่งด้านล่างใน base directory ที่ starport สร้างขึ้นมา
$starport serve📦 Installing dependencies...🛠️ Building the app...🙂 Created an account. Password (mnemonic): embrace never write feed moment maximum universe equal differ ride endless unfair tide message hotel prepare team visit pull kind smile behave cash critic🙂 Created an account. Password (mnemonic): copper palace rent tackle crater dinner sniff peace burger fossil good leg black hat ice twelve answer aim potato swallow pelican broccoli eager jeans🌍 Running a Cosmos 'voter' app with Tendermint at http://localhost:26657.🌍 Running a server at http://localhost:1317 (LCD)🚀 Get started: http://localhost:12345/
ระบบจะทำการ Install dependencies ต่างๆและ run application เราขึ้นมาที่ http://localhost:12345/
จ๊าบไหมล่ะ!! แค่ 2 command เราก็ได้ Blockchain Application ขึ้นมา Run อยู่บนเครื่องเราเรียบร้อยแล้ว
ต่อไปเรากด CTRL+C ออกมาจาก starport serve ก่อน
$starport type poll title options🎉 Created a type `poll`.
เมื่อสร้างเสร็จแล้วสั่ง $starport serve อีกครั้ง
$starport serve📦 Installing dependencies...🛠️ Building the app...🙂 Created an account. Password (mnemonic): catalog comfort permit target canyon split copy message friend online medal island strong mountain border robot favorite title venture stove sun stairs word fiction🙂 Created an account. Password (mnemonic): club ripple brief social sheriff weather park sausage hour shop volume physical picture catalog traffic assault face trouble wild help cactus swallow void horror🌍 Running a Cosmos 'voter' app with Tendermint at http://localhost:26657.🌍 Running a server at http://localhost:1317 (LCD)🚀 Get started: http://localhost:12345/
เมื่อเราเข้าไปที่ http://localhost:8080/ เราจะพับกบ!!
วะวะวะวว้าวววววว ตกงานแน่กรู!!! starport สร้าง poll application ขึ้นมาให้เราพร้อม field สำหรับ input title และ option ให้เรียบร้อย
ต่อไปเราจะทดสอบการสร้าง poll แรกของเราบน blockchain application เด้อ แน่นอน เราต้องมีรหัสผ่านเข้าสู่ระบบ ถึงจะสร้างได้ และเราต้องมีรหัสผ่านเข้าสู่ระบบ เราถึงจะโหวตได้ และทุกๆการ vote จะต้องเสีย 200 token
แต่เจ้า starport เค้าสร้าง account มาให้เราแล้ว 2 account โดยมีชุดรหัส mnemonic เข้าใช้งานตามภาพด้านล่าง
เราก็ copy & paste developer วางลงไปบน form ใน http://localhost:8080/ ซะ แล้วก็ sign in
เมื่อเราทำการ sign in แล้ว cosmos จะแสดงชื่อบัญชีของเรา และ balance ของเรามาให้ ซึ่งผมมีอยู่ 1000 token (ไว้หลอกขายเม่าที่ออฟฟิศ ฮาๆๆ)
จากรูปเราจะเห็นว่า แม่งยังไม่ work เว้ย เพราะอะไร???
เพราะ option มันมีแค่อันเดียว แล้วจะให้คนเลือกอะไรอ่ะ มันควรจะสร้าง option ได้หลายๆ option ดิวะ
เมื่อกด create poll ไปแล้ว object poll ของเราก็จะไปถูกเก็บบน blockchain เรียบร้อย แก้ไขไม่ได้
เดี๋ยวเราจะไปแก้โครงสร้าง block สำหรับการ vote ของเราใหม่ใน directory x/voter/types/TypePoll.go ที่เก็บโครงสร้างของ block ที่เราจะสร้างขึ้นไปเก็บไว้บน blockchain ซึ่งจะมีโครงสร้างดังนี้
เราจะทำการเปลี่ยน options ให้เป็น array จะได้ใส่ได้หลาย options หน่อย
package typesimport (sdk "github.com/cosmos/cosmos-sdk/types")type Poll struct {Creator sdk.AccAddress `json:"creator" yaml:"creator"`ID string `json:"id" yaml:"id"`Title string `json:"title" yaml:"title"`Options string `json:"options" yaml:"options"`}
โดยเราจะทำการแก้ไขบรรทัดนี้ ด้วยการเปลี่ยน type ตัวแปรของ Options จาก string ให้เป็น array string แทน
Options string `json:"options" yaml:"options"`
จะได้เป็น
Options []string `json:"options" yaml:"options"`
บักห่าขั่วมึงเอ้ยยยยยยยย แก้นิดเดียว -_-’
แต่ไปเราก็ต้องไปแก้ไฟล์ทุกไฟล์ที่มีการใช้ struct Options อันเก่าที่เป็น string โดยแก้ให้เป็น array string ให้หมด
ไป ไป ไป!!!
x/voter/types/MsgCreatePoll.go
type MsgCreatePoll struct {ID stringCreator sdk.AccAddress `json:"creator" yaml:"creator"`Title string `json:"title" yaml:"title"`Options []string `json:"options" yaml:"options"`}...func NewMsgCreatePoll(creator sdk.AccAddress, title string, options []string) MsgCreatePoll {return MsgCreatePoll{ID: uuid.New().String(),Creator: creator,Title: title,Options: options,}}
และทำการแก้ไขไฟล์ x/voter/client/rest/txPoll.go
type createPollRequest struct {BaseReq rest.BaseReq `json:"base_req"`Creator string `json:"creator"`Title string `json:"title"`Options []string `json:"options"`}
ต่อไปก็ไฟล์ x/voter/client/cli/txPoll.go ซึ่งเป็น file สำหรับการสั่งงานสร้าง poll ผ่าน commandline ด้วยคำสั่ง
$votercli tx voter create-poll "Text editors" "Emacs" "Vim" --from user1
โดย params ที่รับค่ามาใน code txPoll.go จะเป็นการรับค่า string params ที่ 2 มาเก็บเป็นค่า options
argsOptions := string(args[1])
เป็น
argsOptions := args[1:len(args)]
ซึ่งจะเป็นการบอกว่า params ที่ 2 เป็นต้นไปถือว่าเป็น options
หลังจากมีการแก้ไขเรียบร้อยแล้วเราจะสั่ง
starport serve
และจะไปทำการแก้ไข user interface กันต่อในส่วนของ frontend ซึ่งในตัวอย่างจะเขียนด้วย Vue Js ซึ่งผมเขียนไม่เป็น!!!
ช่างแม่ง!!! มั่วไป
คราวนี้เราจะ focus ที่ 3 directory ของเราในการแก้ไข user interface ของเรา
โดยถ้าไล่มาจากไฟล์ vue/src/views/Index.vue
<template><div><app-layout><app-text type="h1">voter</app-text><wallet /><type-list /></app-layout></div></template>
เราจะต้องไปทำการแก้ไข vue/src/components/TypeItem.vue ข้างในจะเป็นแบบนี้
<template><div><app-text type="h2">List of {{ value.type }} items</app-text><div class="item" v-for="instance in instanceList" :key="instance.id"><div class="item__field" v-for="(value, key) in instance" :key="key"><div class="item__field__key">{{ key }}:</div><div class="item__field__value">{{ value }}</div></div></div><divclass="card__empty"v-if="instanceList.length < 1">There are no {{ value.type }} items yet. Create one using the form below.</div><app-text type="h2">New {{ value.type }}</app-text><div v-for="field in value.fields" :key="field"><app-inputv-model="fields[field]"type="text":placeholder="title(field)":disabled="flight"/><div v-for="(option,key) in options" :key="key"><app-input placeholder="Option" v-model="option.title" /></div></div><app-button @click.native="add">Add option</app-button><button:class="['button', `button__valid__${!!valid && !flight && hasAddress}`]"@click="submit">Create {{ value.type }}<div class="button__label" v-if="flight"><div class="button__label__icon"><icon-refresh /></div>Sending transaction...</div></button></div></template><style scoped>button {background: none;border: none;color: rgba(0, 125, 255);padding: 0;font-size: inherit;font-weight: 800;font-family: inherit;text-transform: uppercase;margin-top: 0.5rem;cursor: pointer;transition: opacity 0.1s;letter-spacing: 0.03em;transition: color 0.25s;display: inline-flex;align-items: center;}.item {box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1);margin-bottom: 1rem;padding: 1rem;border-radius: 0.5rem;overflow: hidden;}.item__field {display: grid;line-height: 1.5;grid-template-columns: 15% 1fr;grid-template-rows: 1fr;word-break: break-all;}.item__field__key {color: rgba(0, 0, 0, 0.25);word-break: keep-all;overflow: hidden;}button:focus {opacity: 0.85;outline: none;}.button.button__valid__true:active {opacity: 0.65;}.button__label {display: inline-flex;align-items: center;}.button__label__icon {height: 1em;width: 1em;margin: 0 0.5em 0 0.5em;fill: rgba(0, 0, 0, 0.25);animation: rotate linear 4s infinite;}.button.button__valid__false {color: rgba(0, 0, 0, 0.25);cursor: not-allowed;}.card__empty {margin-bottom: 1rem;border: 1px dashed rgba(0, 0, 0, 0.1);padding: 1rem;display: flex;align-items: center;justify-content: center;box-sizing: border-box;border-radius: 8px;color: rgba(0, 0, 0, 0.25);text-align: center;min-height: 8rem;}@keyframes rotate {from {transform: rotate(0);}to {transform: rotate(-360deg);}}@media screen and (max-width: 980px) {.narrow {padding: 0;}}</style><script>export default {props: ["value"],data: function () {return {fields: {},flight: false,options: [],};},created() {(this.value.fields || []).forEach((field) => {this.$set(this.fields, field, "");});},computed: {hasAddress() {return !!this.$store.state.account.address;},instanceList() {return this.$store.state.data[this.value.type] || [];},valid() {return Object.values(this.fields).every((el) => {return el.trim().length > 0;});},},methods: {title(string) {return string.charAt(0).toUpperCase() + string.slice(1);},add() {this.options = [...this.options, { title: "" }];},async submit() {if (this.valid && !this.flight && this.hasAddress) {this.flight = true;//const payload = { type: this.value.type, body: this.fields };const payload = {type: this.value.type,body: {title: this.fields["title"],options: this.options.map((o) => o.title),},};await this.$store.dispatch("entitySubmit", payload);await this.$store.dispatch("entityFetch", payload);this.flight = false;Object.keys(this.fields).forEach((f) => {this.fields[f] = "";});}},},};</script>
และทำการแก้ไขไฟล์ vue/src/store/app.js
module.exports = {types: [// this line is used by starport scaffolding{ type: "poll", fields: ["title",] },],};
โดยเจ้า file PollForm Component จะเป็นการสร้าง input title และ options แบบเพิ่มได้ไม่จำกัดขึ้นมา โดยเราสามารถใส่ ผลโหวต option ได้เรื่อยๆด้วยการ click ที่ Add option
เมื่อเรากด Create Poll ไปแล้ว จะพบว่าไม่มีอะไรเกิดขึ้น นั่นเพราะเรายังไม่ได้ทำการแก้ไข user interface ในส่วนของการแสดงผลนั่นเอง แต่ว่าตัว transaction ได้ถูกส่งออกไปแล้ว สามารถเช็คได้จาก http://localhost:1317/voter/poll
{"height": "0","result": [{"creator": "cosmos1e9f7hpz9rv4dd77dwy65wj9pmazysappmp5708","id": "ccca5218-b405-4b4b-bb9c-0b6f8224bfb5","title": "ถ้า Harley Davidson Sportster 48 แบตหมดจำทำยังไง","options": ["ซื้อ BMW GSA 1250 มาขับแทนไปก่อน","ซื้อ Harley Street Glide มาขับวันสองวันค่อยว่ากัน","ซื้อ KLX 230 ไปขับเข้าป่าแก้เครียด"]}]}
เอาใหม่
ด้วยคำสั่ง
$starport type vote pollID value
เมื่อเรากลับไปดูที่ไฟล์ vue/src/store/app.js จะพบว่ามี code ถูก gen เพิ่มขึ้นมาจากเดิมที่เราเข้าไปแก้ไขไว้ คือมี type vote เพิ่มขึ้นมา โดยมี fields 2 fields คือ pollID และ value
module.exports = {types: [// this line is used by starport scaffolding{ type: "vote", fields: ["pollID", "value", ] },{ type: "poll", fields: ["title",] },],};
โดยผมจะเข้า comment บรรทัดที่เพิ่มขึ้นมาก่อน { type: “vote”, fields: [“pollID”, “value”, ] }, เพื่อไม่ให้ code มันดึง fields ที่เพิ่งเพิ่มเข้ามาเอ ามา render ใหม่ (ขี้เกียจเขียน code ใหม่ ใช้ hack code มันเอาเพื่อทดสอบไปก่อน)
และต่อมาผมจะไปสร้าง view ชื่อว่า PollList ไว้ที่ vue/src/components/PollList.vue
<template><div><div v-for="poll in polls"><app-text type="h2">Poll {{ poll.title }}</app-text><app-radio-item@click.native="submit(poll.id, option)"v-for="option in poll.options":value="option"/><app-text type="subtitle">Results: {{ results(poll.id) }}</app-text></div></div></template><script>export default {data() {return {selected: ""};},computed: {polls() {return this.$store.state.data.poll || [];},votes() {return this.$store.state.data.vote || [];}},methods: {results(id) {const results = this.votes.filter(v => v.pollID === id);return this.$lodash.countBy(results, "value");},async submit(pollID, value) {const type = { type: "vote" };const body = { pollID, value };await this.$store.dispatch("entitySubmit", { ...type, body });await this.$store.dispatch("entityFetch", type);}}};</script>
ซึ่งเจ้า PollList components เนี่ย จะมีหน้าที่ลิส โพลทุกโพลที่มี ในระบบขึ้นมาแสดงพร้อม options ของ poll นั้นๆ เป็นปุ่มให้เรากดโหวต
เมื่อเรากด vote แล้วจะได้ข้อมูลผลการ result ของทุก account กลับมา ว่ามีใคร vote อะไรไปแล้วบ้าง
โดยดูโหลทั้งหมดได้จาก http://localhost:1317/voter/poll
และผลโวตทั้งหมดจาก http://localhost:1317/voter/vote
{"height": "0","result": [{"creator": "cosmos1peytdk6e2q79va84k24uheseq70vlfp0w3l2x2","id": "146889af-841b-4fca-b832-b7a57c3cae67","pollID": "3fe18a26-5667-4dee-bd2a-ac7fc214c043","value": "ซื้อ Harley Street Glide มาขับวันสองวันค่อยว่ากัน"},{"creator": "cosmos1dn569eqfmr2nx0p27q0k7rlpp76rpmer0vf77y","id": "84c29042-b04b-434c-a3b8-32a794c0fa23","pollID": "3fe18a26-5667-4dee-bd2a-ac7fc214c043","value": "ซื้อ KLX 230 ไปขับเข้าป่าแก้เครียด"}]}
ถือว่าโอเคสำหรับครึ่งวันแรกในการนั่ง research tool สำหรับการสร้าง blockchain และ dApp ขึ้นมา นับว่า cosmos.network ใช้งานง่ายพอสมควรสำหรับการสร้าง application blockchain ขึ้นมาบน tendermint ยังดีที่พอจะเขียน go กับ javascript มาบ้าง เลยพอจะแก้ไขโค้ดตามได้
ช่วงนี้เรียนทั้ง Security และ Blockchain มันก็จะมึนๆหน่อย
ตัวอย่างในบทความนี้ ทำตามและแก้ไขนิดหน่อยตาม Cosmos.Network”
รอบหน้ามาว่ากันต่อ จนกว่าจะ deploy ขึ้นมาได้
ลุยยยยยยยยยยยยยยยยยยยยยยยยยยยยยยยยยยยย
โหลดSource Codeที่นี่
Quick Links
Legal Stuff