HomeArtTechHackBlockchain

1 วัน work from home กับการสร้าง application บน blockchain ด้วย cosmos network

By Khomkrid Lerdprasert
Published in Blockchain101
September 09, 2020
3 min read
1 วัน work from home กับการสร้าง application บน blockchain ด้วย cosmos network

1 วัน work from home กับการสร้าง application บน blockchain ด้วย cosmos network

Introduction

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

Create Own Blockchain From Cosmos

เราสามารถสร้าง Blockchain ของตัวเองได้ง่ายๆบน Cosmos SDK โดยใช้ ภาษา Go โดย Cosmos จะ run อยู่บน Tendermint อีกที

Ecosystem

เริ่มต้นสร้าง Blockchain ของตัวเองเลย

“Build A Chain”

Lab 1. Polling app

Polling app

เราจะทำ application เกี่ยวกับการลงคะแนนกันครับ โดย user สามารถ

  1. ลงชื่อเข้าใช้งาน
  2. สร้างแบบสำรวจลงคะแนน
  3. ลงคะแนนเสียง
  4. ดูผลการลงคะแนน

โดยการสร้างแบบสำรวจจะมีค่าใช้จ่าย 200 token โดยต้องลงทะเบียนก่อนใช้งานระบบเท่านั้น

โดยเราจะใช้ Tool ที่ชื่อว่า Starport ในการสร้าง blockchain ขึ้นมาอย่างง่ายๆครับ โดยสามารถเข้าไปติดตั้งได้ที่นี่

Starport

โดยเจ้า Starport เนี่ยเค้าเคลมว่า เป็น tools ที่ง่ายโคตรๆในการสร้าง blockchains และโคตรๆจะ เฟรนลี่กับ developer เพื่อให้ developer ไป focus กับ business logic แทนไงล่ะ

งั้นว่าแล้วก็มาลองกันเลย ฮึ๊บๆๆ

Install Starport

npm i -g @tendermint/starport

โดยเจ้า Starport เนี่ยต้องการ Go lang version 1.14 ขึ้นไป และ Nodejs เวอร์ชั่นสูงหน่อย (ของผม verion v14.3.0)

เอาล่ะ มาเริ่มกันเลย

Get started

เริ่มต้นด้วยการใช้ 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 serve
NOTE: add --verbose flag for verbose (detailed) output.

โดยใน Directory voter จะมีโครงสร้างแบบนี้

$ls -lah
total 168
drwxr-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 .gitignore
drwxr-xr-x 5 skulltree staff 160B Sep 9 14:38 app
drwxr-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.md
drwxr-xr-x 11 skulltree staff 352B Sep 9 14:38 vue
drwxr-xr-x 3 skulltree staff 96B Sep 9 14:38 x
  1. app เก็บไฟล์ main application ของเรา
  2. cmd ใช้เก็บไฟล์ commandline สำหรับสั่งใช้งานคำสั่ง voterd และ votercli ผ่าน terminal ของเรา
  3. frontend เก็บไฟล์ web user interface ของ application เรา
  4. x เก็บไฟล์โครงสร้างการทำงานต่างๆของ blockchain application voter. ของเรา

ต่อไปเราจะทดลองสั่ง 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/

Welcome Screen
Welcome Screen

จ๊าบไหมล่ะ!! แค่ 2 command เราก็ได้ Blockchain Application ขึ้นมา Run อยู่บนเครื่องเราเรียบร้อยแล้ว

ต่อไปเรากด CTRL+C ออกมาจาก starport serve ก่อน

เราจะมาสร้าง Poll กัน!!

$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/ เราจะพับกบ!!

Add poll
Add poll

วะวะวะวว้าวววววว ตกงานแน่กรู!!! starport สร้าง poll application ขึ้นมาให้เราพร้อม field สำหรับ input title และ option ให้เรียบร้อย

ต่อไปเราจะทดสอบการสร้าง poll แรกของเราบน blockchain application เด้อ แน่นอน เราต้องมีรหัสผ่านเข้าสู่ระบบ ถึงจะสร้างได้ และเราต้องมีรหัสผ่านเข้าสู่ระบบ เราถึงจะโหวตได้ และทุกๆการ vote จะต้องเสีย 200 token

แต่เจ้า starport เค้าสร้าง account มาให้เราแล้ว 2 account โดยมีชุดรหัส mnemonic เข้าใช้งานตามภาพด้านล่าง

mnemonic
mnemonic

เราก็ copy & paste developer วางลงไปบน form ใน http://localhost:8080/ ซะ แล้วก็ sign in

mnemonic
mnemonic

เมื่อเราทำการ sign in แล้ว cosmos จะแสดงชื่อบัญชีของเรา และ balance ของเรามาให้ ซึ่งผมมีอยู่ 1000 token (ไว้หลอกขายเม่าที่ออฟฟิศ ฮาๆๆ)

account
account

จากรูปเราจะเห็นว่า แม่งยังไม่ work เว้ย เพราะอะไร???

เพราะ option มันมีแค่อันเดียว แล้วจะให้คนเลือกอะไรอ่ะ มันควรจะสร้าง option ได้หลายๆ option ดิวะ

create poll
create poll

เมื่อกด create poll ไปแล้ว object poll ของเราก็จะไปถูกเก็บบน blockchain เรียบร้อย แก้ไขไม่ได้

create poll
create poll

เดี๋ยวเราจะไปแก้โครงสร้าง block สำหรับการ vote ของเราใหม่ใน directory x/voter/types/TypePoll.go ที่เก็บโครงสร้างของ block ที่เราจะสร้างขึ้นไปเก็บไว้บน blockchain ซึ่งจะมีโครงสร้างดังนี้

  1. creator:
  2. id:
  3. title:
  4. options:

เราจะทำการเปลี่ยน options ให้เป็น array จะได้ใส่ได้หลาย options หน่อย

package types
import (
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 string
Creator 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 ของเรา

  1. frontend/src/views ข้างในจะเป็น templates ของ app เรา
  2. frontend/src/store/index.js อันนี้จะเป็นส่วนของการ ติดต่อรับส่งข้อมูลระหว่าง client และ blockchain ของเรา โดยจะมีการเรียกใช้งาน Cosmjs ที่เป็น lib ในการจัดการ wallets, creating, signing และ broadcasting transactions ในระบบ blockchain
  3. frontend/src/components พวกปุ่ม components ต่างๆ reuse ใช้งาน

โดยถ้าไล่มาจากไฟล์ 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>
<div
class="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-input
v-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
create poll

เมื่อเรากด Create Poll ไปแล้ว จะพบว่าไม่มีอะไรเกิดขึ้น นั่นเพราะเรายังไม่ได้ทำการแก้ไข user interface ในส่วนของการแสดงผลนั่นเอง แต่ว่าตัว transaction ได้ถูกส่งออกไปแล้ว สามารถเช็คได้จาก http://localhost:1317/voter/poll

create poll
create 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 นั้นๆ เป็นปุ่มให้เรากดโหวต

create poll
create 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ที่นี่


Tags

#blockchain#cosmos#tendermint#จดบันทึก

Share

Previous Article
DVWA Javascript Attacks
Khomkrid Lerdprasert

Khomkrid Lerdprasert

Full Stack Life

Related Posts

ใช้ Go Create Transaction เพื่อโอนเงินใน stellar
November 07, 2021
1 min
© 2024, All Rights Reserved.
Powered By

Quick Links

Author

Social Media