Cosmos SDK 공식 튜토리얼을 정리한 내용으로, 최신 내용은 Cosmos SDK 공식 문서에서 확인 할 수 있습니다.
Hello World 는 컴퓨터 프로그래밍의 첫 발을 떼는 오랜 전통과 같습니다. InterBlockChain 커뮤티케이션 프로토콜은 Cosmos SDK 생태계의 중요한 부분입니다. 패킷을 만들고 블록체인을 통해 전송하는 방법의 학습을 통해 Cosmos SDK를 통해 블록체인간 통신을 이해하는데 도움이 될 것입니다.
학습할 내용
Cosmos SDK는 블록체인 앱을 만드는 프레임워크로 Cosmos SDK를 통해 개발자는 다른 블록체인과 상호작용할 수 있는 자신만의 블록체인을 쉽게 구현할 수 있습니다. Cosmos SDK의 IBC 모듈은 두 블록체인간의 상호작용을 위한 표준 입니다, IBC 모듈은 패킷과 메시지 송수신을 위한 방법을 정의합니다. Cosmos IBC relayer 패키지를 사용하여 IBC를 지원하는 블록체인간에 연결을 할 수 있습니다. 이 튜토리얼에서는 두 개의 블록체인을 만든다음 Starport의 relayer를 이용해 두 블록체인을 연결하는 방법에 대해 설명합니다. 이 튜토리얼 에서는 module, IBC 패킷, relayer 및 IBC를 통해 라우팅된 패킷의 lifecycle과 같은 필수 사항을 다룹니다
이 튜토리얼은 Starport v0.15.0 버전을 사용합니다. Sterport는 블록체인을 쉽게 구성할 수 있도록 지원해주는 도구 입니다.
starport
설치 방법은 다음과 같으며, /usr/local/bin
의 위치에 설치됩니다:
curl https://get.starport.network/starport@v0.15.0! | bash
Starport v0.15.0 버전은 browser-based IDE에서도 사용할 수 있습니다. 자세한 설치 옵션은, Starport 설치 안내 페이지에서 확인할 수 있습니다.
블로그 모듈이 포함된 블록체인 앱을 만들고, IBC 모듈을 통해 Hello World 메시지가 포함된 게시물을 다른 블록체인에 작성합니다
이번 예제에서는, 제목(title)과 본문(text)을 게시(post)하는 모듈인 blog 모듈을 가진 블록체인을 만들어 볼것 입니다.
구조를 정의한 후, 이 모듈이 설치된 두 개의 블록체인을 실행합니다.
acknowledged
)과 타임 아웃(timed out
) 상태를 저장한다.수신 블록체인에의해 트랜젝션이 승인되면 게시물은 양쪽 블록체인에 저장됩니다.
PostID
라는 데이터를 추가로 가지고 있습니다.chain
변수에서 확인할 수 있습니다. 아래 그림을 통해 IBC를 통과하는 패킷의 lifecycle을 확인할 수 있습니다.plant
이라는 이름의 새로운 블록체인 프로젝트를 생성합니다.
starport app github.com/user/planet
cd planet
동작하는 블록체인 코드가 포함된, planet 이라는 이름의 디렉토리가 생성 되었습니다.
이제 생성된 프로젝트에 IBC 기능이 포함된 blog
모듈을 생성합니다.
모듈을 생성할 때 --ibc
옵션을 통해 모듈을 생성할 때 IBC 로직이 포함시킬 수 있다.
starport module create blog --ibc
IBC 모듈이 포함된 디렉토리가 planet/x/blog
에 생성 된것을 확인할 수 있다
블로그 모듈에는 블로그 게시물을 만들어 IBC를 통해 다른 블록체인으로 라우틸할 수 있는 로직이 포함되어 있습니다.
starport type
명령어를 이용해 블록체인에 CRUD(Creating, Reading, Updating, Deleting) 하기위한 boilerplate 코드를 생성할 수 있습니다.
이 모듈에는 다음과 같은 로직을 포함합니다:starport type post title content --module blog
starport type sentPost postID title chain --module blog
starport type timedoutPost title chain --module blog
게시물 관리하기 위한 CRUD starport type
을 통해 세 가지 타입을 생성 했습니다. 생성된 코드에는 데이터 구조를 정의하는 proto 파일, message, message handler, state 변경을 위한 keeper 및 CLI 명령이 포함되어 있습니다.
starport type 명령의 첫 번째 인자는 생성하려는 type의 이름, 다음 인자는 type과 관련된 필드를 정의합니다.
starport에서 --module
옵션을 통해 새로운 트랜젝션 type을 추가할 모듈을 지정하게 됩니다.
해당 옵션을 지정하지 않으면 프로젝트와 동일한 모듈에 생성되게 됩니다.
생성된 IBC 패킷은 블로그 게시물의 제목과 내용을 포함하며, 전송과 해석이 가능한 코드를 포함합니다.
제목과 내용은 대상 블록체인에 저장되고, 발신 블록체인에서는 postID
가 저장됩니다.
starport type
명령은 새로운 트랜젝션 type을 생성하는 반면, starport packet
명령은 다른 블록체인에 전송될 수 있는 IBC 패킷에 대한 로직을 생성합니다.
starport packet ibcPost title content --ack postID --module blog
ibcPost
패킷의 필드가 이전에 만든 post
type의 필드와 동일합니다.
--ack
: 송신 블록체인으로 반환되는 식별자를 정의--module
: 패킷이 생성될 IBC 모듈starport packet
명령을 통해 IBC 패킷을 전송할 수 있는 CLI 명령도 생성할 수 있습니다.
planetd tx blog send-ibcPost [portID] [channelD] [title] [content]
type과 transaction을 생성한 후 데이터 테이블의 업데이트를 관리하는 로직을 수동으로 작성해야 합니다. 소스코드를 수정하여 이 튜토리얼의 앞 부분에서 지정한대로 데이터를 저장하도록 합니다.
IBC 패킷의 구조를 정의하는 프로토콜부터 시작합니다.
수신 블록체인엠서 게시물의 작성자를 식별하려면 패킷 내부에 작정자 필드를 추가해야 합니다
이 필드는 자동으로 SendIbcPost
CLI 명령에서 매개 변수가 되기 때문에 명령어에 필드를 지정하지 않았습니다.
// planet/proto/blog/packet.proto
// this line is used by starport scaffolding # ibc/packet/proto/message
// IbcPostPacketData defines a struct for the packet payload
message IbcPostPacketData {
string title = 1;
string content = 2;
string creator = 3; // < --- 게시물 작성자 필드 추가
}
수신 블록체인에 블로그 게시물의 작성자에 대한 내용이 있는지 확인하려면 IBC packet
에 이 값을 추가합니다.
메시지 sender
의 내용은 SendIbcPost
메시지에 자동으로 포함되며, 메시지를 서명한 사람이 보낸 사람으로 지정할 수 있습니다.
새로운 패킷이 IBC를 통해 전송되기 전에 생성자로 msg.Sender
를 추가할 수 있습니다.
// planet/x/blog/keeper/msg_server_ibcPost.go
// Construct the packet
var packet types.IbcPostPacketData
packet.Title = msg.Title
packet.Content = msg.Content
packet.Creator = msg.Sender // < --- 메시지 서명자를 보낸 사람으로 지정
// Transmit the packet
err := k.TransmitIbcPostPacket(
ctx,
packet,
msg.Port,
msg.ChannelID,
clienttypes.ZeroHeight(),
msg.TimeoutTimestamp,
)
일차적인 트랜젝션 로직은 planet/x/blog/keeper/ibcPost.go
파일에 있습니다. 다음과 같은 메소드를 통해 IBC 패킷을 관리합니다.
TransmitIbcPostPacket
: IBC를 통해 패킷을 보내기 위해 수동으로 호출, 패킷이 IBC를 통해 다른 블록체인에 전송되기 전 로직을 정의OnRecvIbcPostPacket
: 블록체인에서 패킷이 수실될 때 자동으로 호출, 패킷 수신 로직을 정의OnAcknowledgementIbcPostPacket
: 보낸 패킷이 소스 블록체인에서 승인될 때 호출, 패킷이 수신되었을 때의 로직을 정의OnTimeoutIbcPostPacket
: 전송된 패킷이 타임아웃 날 때 호출, 패킷이 대상 블록체인에 수신되지 않을때의 로직을 정의데이터 테이블이 적절히 수정되도록 소스코드를 수정하여 해당 함수 내에 로직을 추가해야 합니다 메시지 수신 시 수신 블록체인에 제목과 내용이 포함된 새로운 게시물을 생성합니다. 메시지가 원본이고 메시지를 작성한 사용자를 식별하려면 다은 형식과 같은 식별자 정보를 사용합니다.
<portID>-<channelID>-<creatorAddress>
마지막으로 starport에 의해 생성된 AddPost 함수는 새로 추가된 게시물의 ID를 반환하도록 합니다 승인을 통해 이 값을 소스 블록체인에 반환 할 수 있습니다.
패킷을 수신할 때 type 인스턴스에 PostID
를 추가합니다:
ctx
는 트랜젝션의 헤더 데이터를 가진 불변 데이터 구조. 컨텍스트 시작 방법 보기title
: 블로그 게시물의 제목content
: 블로그 게시물의 내용ibcPost.go
파일에서, "errors"
아래에 잇는 "strconv"
를 가져온 후, OnRecvIbcPostPacket
를 다음과 같이 수정합니다.
// planet/x/blog/keeper/ibcPost.go
// OnRecvIbcPostPacket processes packet reception
func (k Keeper) OnRecvIbcPostPacket(ctx sdk.Context, packet channeltypes.Packet, data types.IbcPostPacketData) (packetAck types.IbcPostPacketAck, err error) {
// 수신 시 패킷 데이터 확인
if err := data.ValidateBasic(); err != nil {
return packetAck, err
}
id := k.AppendPost(
ctx,
packet.SourcePort+"-"+packet.SourceChannel+"-"+data.Creator,
data.Title,
data.Content,
)
packetAck.PostID = strconv.FormatUint(id, 10)
return packetAck, nil
}
송신 블록체인에서 sentPost
를 저장하여 해당 게시물이 대상 블록체인에서 수신되었음을 확인할 수 있다.
게시물을 식별하기 위해 제목과 내용을 저장합니다
When a packet is scaffolded, the default type for the received acknowledgment data is a type that identifies if the packet treatment has failed. The Acknowledgement_Error
type is set if OnRecvIbcPostPacket
returns an error from the packet.
// OnAcknowledgementIbcPostPacket : 수신 블록체인에서 수신된 패킷을 확인 (성공 또는 실패 응답)
func (k Keeper) OnAcknowledgementIbcPostPacket(ctx sdk.Context, packet channeltypes.Packet, data types.IbcPostPacketData, ack channeltypes.Acknowledgement) error {
switch dispatchedAck := ack.Response.(type) {
case *channeltypes.Acknowledgement_Error:
// 이 튜토리얼에서는 승인 오류는 처리하지 않음
return nil
case *channeltypes.Acknowledgement_Result:
// 승인된 패킷 디코드
var packetAck types.IbcPostPacketAck
err := packetAck.Unmarshal(dispatchedAck.Result)
if err != nil {
// 송신 모듈이 전송한 패킷이 올바른 형식이 아님
return errors.New("cannot unmarshal acknowledgment")
}
k.AppendSentPost(
ctx,
data.Creator,
packetAck.PostID,
data.Title,
packet.DestinationPort+"-"+packet.DestinationChannel,
)
return nil
default:
// 송신 모듈이 전송한 패킷이 올바른 형식이 아님
return errors.New("invalid acknowledgment format")
}
}
대상 체인이 받지 못한 게시물은 timedoutPost
에 저장한다. 이 로직은 sentPos
와 동일한 형식을 따릅니다.
// OnTimeoutIbcPostPacket: timeout으로 인해 패킷이 전송되지 않은 경우 응답
func (k Keeper) OnTimeoutIbcPostPacket(ctx sdk.Context, packet channeltypes.Packet, data types.IbcPostPacketData) error {
k.AppendTimedoutPost(
ctx,
data.Creator,
data.Title,
packet.DestinationPort+"-"+packet.DestinationChannel,
)
return nil
}
이것으로 기본적인 blog
모듈 설정을 완료 했습니다.
블록체인은 준비 되었습니다.
블록체인을 가동하고 한 블록체인에서 다른 블록체인으로 블로그 게시물을 보냅니다.
동일한 머신에서 두 개의 블록체인 네트워크를 실행합니다. 두 블록체인 모두 동일한 소스코드를 사용합니다. 각 블록체인은 고유한 체인 ID로 구분됩니다.
블록체인의 이름은 각각 earth
와 mars
라고 지정합니다.
프로젝트 디렉토리에 다음과 같이 파일을 생성합니다.
planet/earth.yml
accounts:
- name: alice
coins: ["1000token", "100000000stake"]
- name: bob
coins: ["500token"]
validator:
name: alice
staked: "100000000stake"
faucet:
name: bob
coins: ["5token"]
genesis:
chain_id: "earth"
init:
home: "$HOME/.earth"
planet/mars.yml
accounts:
- name: alice
coins: ["1000token", "1000000000stake"]
- name: bob
coins: ["500token"]
validator:
name: alice
staked: "100000000stake"
faucet:
host: ":4501"
name: bob
coins:
- "5token"
host:
rpc: ":26659"
p2p: ":26658"
prof: ":6061"
grpc: ":9091"
api: ":1318"
frontend: ":8081"
dev-ui: ":12346"
genesis:
chain_id: "mars"
init:
home: "$HOME/.mars"
터미널에서 다음과 같은 명령어를 이용해 “earth” 블록체인을 실행합니다.
starport serve -c earth.yml
다른 터미널에서 다음과 같은 명령어를 이용해 “mars” 블록체인을 실행합니다.
starport serve -c mars.yml
먼저 starport에서 configure
명령과 --advanced
옵션을 이용해 relayer를 설정합니다.
starport relayer configure --advanced --source-rpc "http://0.0.0.0:26657" --source-faucet "http://0.0.0.0:4500" --source-port "blog" --source-version "blog-1" --target-rpc "http://0.0.0.0:26659" --target-faucet "http://0.0.0.0:4501" --target-port "blog" --target-version "blog-1"
화면에 다음과 같은 정보를 확인할 수 있습니다.
---------------------------------------------
Setting up chains
---------------------------------------------
🔐 Account on "source" is "cosmos1xcxgzq75yrxzd0tu2kwmwajv7j550dkj7m00za"
|· received coins from a faucet
|· (balance: 5token)
🔐 Account on "target" is "cosmos1nxg8e4mfp5v7sea6ez23a65rvy0j59kayqr8cx"
|· received coins from a faucet
|· (balance: 5token)
⛓ Configured chains: earth-mars
그런 다음 별도의 터미널에서 relayer 프로세스를 시작합니다
starport relayer connect
결과:
🔌 Linked chains with 1 paths.
---------------------------------------------
Chains by paths
---------------------------------------------
earth-mars:
earth > (port: blog) (channel: channel-0)
mars > (port: blog) (channel: channel-0)
---------------------------------------------
Listening and relaying packets between chains...
---------------------------------------------
이제 패킷을 보내고 수신된 게시물을 확인할 수 있습니다.
planetd tx blog send-ibcPost blog channel-0 "Hello" "Hello Mars, I'm Alice from Earth" --from alice --chain-id earth --home ~/.earth
mars에서 게시물이 수신되었는지 확인
planetd q blog list-post --node tcp://localhost:26659
패킷 수신 확인
Post:
- content: Hello Mars, I'm Alice from Earth
creator: blog-channel-0-cosmos1aew8dk9cs3uzzgeldatgzvm5ca2k4m98xhy20x
id: "0"
title: Hello
pagination:
next_key: null
total: "1"
패킷이 earth에서 확인 되는지 확인
planetd q blog list-sentPost
출력내용:
SentPost:
- chain: blog-channel-0
creator: cosmos1aew8dk9cs3uzzgeldatgzvm5ca2k4m98xhy20x
id: "0"
postID: "0"
title: Hello
pagination:
next_key: null
total: "1"
타임아웃을 테스트하려면 패킷의 타임아웃 시간을 1 나노초로 설정하고 패킷이 타임아웃이 되는지 확인하고 타임아웃된 게시물을 확인
planetd tx blog send-ibcPost blog channel-0 "Sorry" "Sorry Mars, you will never see this post" --from alice --chain-id earth --home ~/.earth --packet-timeout-timestamp 1
타임아웃 게시물 확인
planetd q blog list-timedoutPost
결과확인:
TimedoutPost:
- chain: blog-channel-0
creator: cosmos1fhpcsxn0g8uask73xpcgwxlfxtuunn3ey5ptjv
id: "0"
title: Sorry
pagination:
next_key: null
total: "2"
mars에서 게시물 확인
planetd tx blog send-ibcPost blog channel-0 "Hello" "Hello Earth, I'm Alice from Mars" --from alice --chain-id mars --home ~/.mars --node tcp://localhost:26659
earth에서 게시물 목록 조회
planetd q blog list-post
결과 확인
Post:
- content: Hello Earth, I'm Alice from Mars
creator: blog-channel-0-cosmos1xtpx43l826348s59au24p22pxg6q248638q2tf
id: "0"
title: Hello
pagination:
next_key: null
total: "1"
튜토리얼을 완료 하였습니다. Cosmos SDK용 IBC 모듈의 빌드하고, 자체 블록체인 빌드, IBC(Interblockchain Communication Protocol)를 사용하는 방법을 배웠습니다.
이 튜토리얼에서 작업한 내용