PageSourceSearch

https://docs.ignite.com/assets/js/0cd0818f.ca089cc1.js

js ignite.com collected 2026-10-03 23:19:15 UTC 35,365 bytes, 1 lines download raw bytes

1"use strict";(globalThis.webpackChunkmy_website=globalThis.webpackChunkmy_website||[]).push([[3255],{15680(e,n,t){t.d(n,{xA:()=>p,yg:()=>h});var a=t(96540);function o(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function l(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);n&&(a=a.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,a)}return t}function i(e){for(var n=1;n<arguments.length;n++){var t=null!=arguments[n]?arguments[n]:{};n%2?l(Object(t),!0).forEach((function(n){o(e,n,t[n])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(t)):l(Object(t)).forEach((function(n){Object.defineProperty(e,n,Object.getOwnPropertyDescriptor(t,n))}))}return e}function r(e,n){if(null==e)return{};var t,a,o=function(e,n){if(null==e)return{};var t,a,o={},l=Object.keys(e);for(a=0;a<l.length;a++)t=l[a],n.indexOf(t)>=0||(o[t]=e[t]);return o}(e,n);if(Object.getOwnPropertySymbols){var l=Object.getOwnPropertySymbols(e);for(a=0;a<l.length;a++)t=l[a],n.indexOf(t)>=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(o[t]=e[t])}return o}var s=a.createContext({}),c=function(e){var n=a.useContext(s),t=n;return e&&(t="function"==typeof e?e(n):i(i({},n),e)),t},p=function(e){var n=c(e.components);return a.createElement(s.Provider,{value:n},e.children)},d={inlineCode:"code",wrapper:function(e){var n=e.children;return a.createElement(a.Fragment,{},n)}},g=a.forwardRef((function(e,n){var t=e.components,o=e.mdxType,l=e.originalType,s=e.parentName,p=r(e,["components","mdxType","originalType","parentName"]),g=c(t),h=o,m=g["".concat(s,".").concat(h)]||g[h]||d[h]||l;return t?a.createElement(m,i(i({ref:n},p),{},{components:t})):a.createElement(m,i({ref:n},p))}));function h(e,n){var t=arguments,o=n&&n.mdxType;if("string"==typeof e||o){var l=t.length,i=new Array(l);i[0]=g;var r={};for(var s in n)hasOwnProperty.call(n,s)&&(r[s]=n[s]);r.originalType=e,r.mdxType="string"==typeof e?e:o,i[1]=r;for(var c=2;c<l;c++)i[c]=t[c];return a.createElement.apply(null,i)}return a.createElement.apply(null,t)}g.displayName="MDXCreateElement"},45226(e,n,t){t.r(n),t.d(n,{assets:()=>s,contentTitle:()=>i,default:()=>d,frontMatter:()=>l,metadata:()=>r,toc:()=>c});var a=t(58168),o=(t(96540),t(15680));const l={sidebar_position:7,description:"Build an understanding of how to create and send packets across blockchains and navigate between blockchains.",title:"Inter-Blockchain Communication: Basics"},i="Inter-Blockchain Communication: Basics",r={unversionedId:"guide/ibc",id:"version-v30/guide/ibc",title:"Inter-Blockchain Communication: Basics",description:"Build an understanding of how to create and send packets across blockchains and navigate between blockchains.",source:"@site/versioned_docs/version-v30/02-guide/04-ibc.md",sourceDirName:"02-guide",slug:"/guide/ibc",permalink:"/guide/ibc",draft:!1,tags:[],version:"v30",sidebarPosition:7,frontMatter:{sidebar_position:7,description:"Build an understanding of how to create and send packets across blockchains and navigate between blockchains.",title:"Inter-Blockchain Communication: Basics"},sidebar:"tutorialSidebar",previous:{title:"Running inside a Docker container",permalink:"/guide/docker"},next:{title:"State Management",permalink:"/guide/state"}},s={},c=[{value:"What is IBC?",id:"what-is-ibc",level:2},{value:"Create a blockchain",id:"create-a-blockchain",level:2},{value:"Build your blockchain app",id:"build-your-blockchain-app",level:2},{value:"Build a new blockchain",id:"build-a-new-blockchain",level:3},{value:"Scaffold the blog module inside your blockchain",id:"scaffold-the-blog-module-inside-your-blockchain",level:3},{value:"Generate CRUD actions for types",id:"generate-crud-actions-for-types",level:3},{value:"IGNITE\xae CLI Scaffold List Command Overview",id:"ignite-cli-scaffold-list-command-overview",level:3},{value:"Scaffold a sendable and interpretable IBC packet",id:"scaffold-a-sendable-and-interpretable-ibc-packet",level:3},{value:"Modify the source code",id:"modify-the-source-code",level:2},{value:"Add creator to the blog post packet",id:"add-creator-to-the-blog-post-packet",level:3},{value:"Receive the post",id:"receive-the-post",level:3},{value:"Receive the post acknowledgement",id:"receive-the-post-acknowledgement",level:3},{value:"Store information about the timed-out packet",id:"store-information-about-the-timed-out-packet",level:3},{value:"Use the IBC modules",id:"use-the-ibc-modules",level:2},{value:"Test the IBC modules",id:"test-the-ibc-modules",level:3}
1,{value:"Configure and start the relayer",id:"configure-and-start-the-relayer",level:3},{value:"Send packets",id:"send-packets",level:3},{value:"Congratulations \ud83c\udf89",id:"congratulations-",level:2}],p={toc:c};function d(e){let{components:n,...l}=e;return(0,o.yg)("wrapper",(0,a.A)({},p,l,{components:n,mdxType:"MDXLayout"}),(0,o.yg)("h1",{id:"inter-blockchain-communication-basics"},"Inter-Blockchain Communication: Basics"),(0,o.yg)("p",null,"The Inter-Blockchain Communication protocol (IBC) is an important part of the\nCosmos SDK ecosystem. The Hello World tutorial is a time-honored tradition in\ncomputer programming. This tutorial builds an understanding of how to create and\nsend packets across blockchain. This foundational knowledge helps you navigate\nbetween blockchains with the Cosmos SDK."),(0,o.yg)("p",null,(0,o.yg)("strong",{parentName:"p"},"You will learn how to")),(0,o.yg)("ul",null,(0,o.yg)("li",{parentName:"ul"},"Use IBC to create and send packets between blockchains."),(0,o.yg)("li",{parentName:"ul"},"Navigate between blockchains using the Cosmos SDK and the IGNITE\xae CLI Relayer."),(0,o.yg)("li",{parentName:"ul"},"Create a basic blog post and save the post on another blockchain.")),(0,o.yg)("h2",{id:"what-is-ibc"},"What is IBC?"),(0,o.yg)("p",null,"The Inter-Blockchain Communication protocol (IBC) allows blockchains to talk to\neach other. IBC handles transport across different sovereign blockchains. This\nend-to-end, connection-oriented, stateful protocol provides reliable, ordered,\nand authenticated communication between heterogeneous blockchains."),(0,o.yg)("p",null,"The ",(0,o.yg)("a",{parentName:"p",href:"https://ibc.cosmos.network/main/ibc/overview"},"IBC protocol in the Cosmos\nSDK")," is the standard for the\ninteraction between two blockchains. The IBCmodule interface defines how packets\nand messages are constructed to be interpreted by the sending and the receiving\nblockchain."),(0,o.yg)("p",null,"The IBC relayer lets you connect between sets of IBC-enabled chains. This\ntutorial teaches you how to create two blockchains and then start and use the\nrelayer with IGNITE\xae CLI to connect two blockchains."),(0,o.yg)("p",null,"This tutorial covers essentials like modules, IBC packets, relayer, and the\nlifecycle of packets routed through IBC."),(0,o.yg)("h2",{id:"create-a-blockchain"},"Create a blockchain"),(0,o.yg)("p",null,"Create a blockchain app with a blog module to write posts on other blockchains\nthat contain the Hello World message. For this tutorial, you can write posts for\nthe Cosmos SDK universe that contain Hello Mars, Hello Cosmos, and Hello Earth\nmessages."),(0,o.yg)("p",null,"For this simple example, create an app that contains a blog module that has a\npost transaction with title and text."),(0,o.yg)("p",null,"After you define the logic, run two blockchains that have this module installed."),(0,o.yg)("ul",null,(0,o.yg)("li",{parentName:"ul"},(0,o.yg)("p",{parentName:"li"},"The chains can send posts between each other using IBC.")),(0,o.yg)("li",{parentName:"ul"},(0,o.yg)("p",{parentName:"li"},"On the sending chain, save the ",(0,o.yg)("inlineCode",{parentName:"p"},"acknowledged")," and ",(0,o.yg)("inlineCode",{parentName:"p"},"timed out")," posts."))),(0,o.yg)("p",null,"After the transaction is acknowledged by the receiving chain, you know that the\npost is saved on both blockchains."),(0,o.yg)("ul",null,(0,o.yg)("li",{parentName:"ul"},(0,o.yg)("p",{parentName:"li"},"The sending chain has the additional data ",(0,o.yg)("inlineCode",{parentName:"p"},"postID"),".")),(0,o.yg)("li",{parentName:"ul"},(0,o.yg)("p",{parentName:"li"},"Sent posts that are acknowledged and timed out contain the title and the\ntarget chain of the post. These identifiers")),(0,o.yg)("li",{parentName:"ul"},(0,o.yg)("p",{parentName:"li"},"are visible on the parameter ",(0,o.yg)("inlineCode",{parentName:"p"},"chain"),". The following chart shows the lifecycle\nof a packet that travels through IBC."))),(0,o.yg)("p",null,(0,o.yg)("img",{alt:"The Lifecycle of an IBC packet",src:t(36522).A,width:"1974",height:"901"})),(0,o.yg)("h2",{id:"build-your-blockchain-app"},"Build your blockchain app"),(0,o.yg)("p",null,"Use IGNITE\xae CLI to scaffold the blockchain app and the blog module."),(0,o.yg)("h3",{id:"build-a-new-blockchain"},"Build a new blockchain"),(0,o.yg)("p",null,"To scaffold a new blockchain named ",(0,o.yg)("inlineCode",{parentName:"p"},"planet"),":"),(0,o.yg)("pre",null,(0,o.yg)("code",{parentName:"pre",className:"language-bash"},"ignite cosmos scaffold chain planet --no-module\ncd planet\n")),(0,o.yg)("p",null,"A new directory named ",(0,o.yg)("inlineCode",{parentName:"p"},"planet")," is created in your home directory. The ",(0,o.yg)("inlineCode",{parentName:"p"},"planet"),"\ndirectory contains a working blockchain app."),(0,o.yg)("h3",{id:"scaffold-the-blog-module-inside-your-blockchain"},"Scaffold the blog module inside your blockchain"),(0,o.yg)("p",null,"Next, use IGNITE\xae CLI to scaffold a blog module with IBC capabilities. The blog\nmodule contains the logic for creating blog posts and routing them through IBC\nto the second blockchain."),(0,o.yg)("p",null,"To scaffold a module named ",(0,o.yg)("inlineCode",{parentName:"p"},"blog"),":"),(0,o.yg)("pre",null,(0,o.yg)("code",{parentName:"pre",className:"language-bash"},"ignite cosmos scaffold module blog --ibc\n")),(0,o.yg)("p",null,"A new directory with the code for an IBC module is created in ",(0,o.yg)("inlineCode",{parentName:"p"},"planet/x/blog"),".\nModules scaffolded with the ",(0,o.yg)("inlineCode",{parentName:"p"},"--ibc")," flag include all the logic for the\nscaffolded IBC module."),(0,o.yg)("h3",{id:"generate-crud-actions-for-types"},"Generate CRUD actions for types"),(0,o.yg)("p",null,"Next, create the CRUD actions for the blog module types."),(0,o.yg)("p",null,"Use the ",(0,o.yg)("inlineCode",{parentName:"p"},"ignite cosmos scaffold list")," command to scaffold the boilerplate code for the\ncreate, read, update, and delete (CRUD) actions."),(0,o.yg)("p",null,"These ",(0,o.yg)("inlineCode",{parentName:"p"}
1,"ignite cosmos scaffold list")," commands create CRUD code for the following\ntransactions:"),(0,o.yg)("ul",null,(0,o.yg)("li",{parentName:"ul"},"Creating blog posts")),(0,o.yg)("pre",null,(0,o.yg)("code",{parentName:"pre",className:"language-bash"},"ignite cosmos scaffold list post title content creator --no-message --module blog\n")),(0,o.yg)("ul",null,(0,o.yg)("li",{parentName:"ul"},"Processing acknowledgments for sent posts")),(0,o.yg)("pre",null,(0,o.yg)("code",{parentName:"pre",className:"language-bash"},"ignite cosmos scaffold list sentPost postID:uint title chain creator --no-message --module blog\n")),(0,o.yg)("ul",null,(0,o.yg)("li",{parentName:"ul"},"Managing post timeouts")),(0,o.yg)("pre",null,(0,o.yg)("code",{parentName:"pre",className:"language-bash"},"ignite cosmos scaffold list timeoutPost title chain creator --no-message --module blog\n")),(0,o.yg)("p",null,"The scaffolded code includes proto files for defining data structures, messages,\nmessages handlers, keepers for modifying the state, and CLI commands."),(0,o.yg)("h3",{id:"ignite-cli-scaffold-list-command-overview"},"IGNITE\xae CLI Scaffold List Command Overview"),(0,o.yg)("pre",null,(0,o.yg)("code",{parentName:"pre"},"ignite cosmos scaffold list [typeName] [field1] [field2] ... [flags]\n")),(0,o.yg)("p",null,"The first argument of the ",(0,o.yg)("inlineCode",{parentName:"p"},"ignite cosmos scaffold list [typeName]")," command specifies\nthe name of the type being created. For the blog app, you created ",(0,o.yg)("inlineCode",{parentName:"p"},"post"),",\n",(0,o.yg)("inlineCode",{parentName:"p"},"sentPost"),", and ",(0,o.yg)("inlineCode",{parentName:"p"},"timeoutPost")," types."),(0,o.yg)("p",null,"The next arguments define the fields that are associated with the type. For the\nblog app, you created ",(0,o.yg)("inlineCode",{parentName:"p"},"title"),", ",(0,o.yg)("inlineCode",{parentName:"p"},"content"),", ",(0,o.yg)("inlineCode",{parentName:"p"},"postID"),", and ",(0,o.yg)("inlineCode",{parentName:"p"},"chain")," fields."),(0,o.yg)("p",null,"The ",(0,o.yg)("inlineCode",{parentName:"p"},"--module")," flag defines which module the new transaction type is added to.\nThis optional flag lets you manage multiple modules within your IGNITE\xae CLI app.\nWhen the flag is not present, the type is scaffolded in the module that matches\nthe name of the repo."),(0,o.yg)("p",null,"When a new type is scaffolded, the default behavior is to scaffold messages that\ncan be sent by users for CRUD operations. The ",(0,o.yg)("inlineCode",{parentName:"p"},"--no-message")," flag disables this\nfeature. Disable the messages option for the app since you want the posts to be\ncreated upon reception of IBC packets and not directly created from a user's\nmessages."),(0,o.yg)("h3",{id:"scaffold-a-sendable-and-interpretable-ibc-packet"},"Scaffold a sendable and interpretable IBC packet"),(0,o.yg)("p",null,"You must generate code for a packet that contains the title and the content of\nthe blog post."),(0,o.yg)("p",null,"The ",(0,o.yg)("inlineCode",{parentName:"p"},"ignite packet")," command creates the logic for an IBC packet that can be sent\nto another blockchain."),(0,o.yg)("ul",null,(0,o.yg)("li",{parentName:"ul"},(0,o.yg)("p",{parentName:"li"},"The ",(0,o.yg)("inlineCode",{parentName:"p"},"title")," and ",(0,o.yg)("inlineCode",{parentName:"p"},"content")," are stored on the target chain.")),(0,o.yg)("li",{parentName:"ul"},(0,o.yg)("p",{parentName:"li"},"The ",(0,o.yg)("inlineCode",{parentName:"p"},"postID")," is acknowledged on the sending chain."))),(0,o.yg)("p",null,"To scaffold a sendable and interpretable IBC packet:"),(0,o.yg)("pre",null,(0,o.yg)("code",{parentName:"pre",className:"language-bash"},"ignite cosmos scaffold packet ibcPost title content --ack postID:uint --module blog\n")),(0,o.yg)("p",null,"Notice the fields in the ",(0,o.yg)("inlineCode",{parentName:"p"},"ibcPost")," packet match the fields in the ",(0,o.yg)("inlineCode",{parentName:"p"},"post")," type\nthat you created earlier."),(0,o.yg)("ul",null,(0,o.yg)("li",{parentName:"ul"},(0,o.yg)("p",{parentName:"li"},"The ",(0,o.yg)("inlineCode",{parentName:"p"},"--ack")," flag defines which identifier is returned to the sending\nblockchain.")),(0,o.yg)("li",{parentName:"ul"},(0,o.yg)("p",{parentName:"li"},"The ",(0,o.yg)("inlineCode",{parentName:"p"},"--module")," flag specifies to create the packet in a particular IBC module."))),(0,o.yg)("p",null,"The ",(0,o.yg)("inlineCode",{parentName:"p"},"ignite packet")," command also scaffolds the CLI command that is capable of\nsending an IBC packet:"),(0,o.yg)("pre",null,(0,o.yg)("code",{parentName:"pre",className:"language-bash"},"planetd tx blog send-ibcPost [portID] [channelID] [title] [content]\n")),(0,o.yg)("h2",{id:"modify-the-source-code"},"Modify the source code"),(0,o.yg)("p",null,"After you create the types and transactions, you must manually insert the logic\nto manage updates in the database. Modify the source code to save the data as\nspecified earlier in this tutorial."),(0,o.yg)("h3",{id:"add-creator-to-the-blog-post-packet"},"Add creator to the blog post packet"),(0,o.yg)("p",null,"Start with the proto file that defines the structure of the IBC packet."),(0,o.yg)("p",null,"To identify the creator of the post in the receiving blockchain, add the\n",(0,o.yg)("inlineCode",{parentName:"p"},"creator")," field inside the packet. This field was not specified directly in the\ncommand because it would automatically become a parameter in the ",(0,o.yg)("inlineCode",{parentName:"p"},"SendIbcPost"),"\nCLI command."),(0,o.yg)("pre",null,(0,o.yg)("code",{parentName:"pre",className:"language-protobuf",metastring:'title="proto/planet/blog/v1/packet.proto"',title:'"proto/planet/blog/v1/packet.proto"'},"message IbcPostPacketData {\n  string title = 1;\n  string content = 2;\n  // highlight-next-line\n  string creator = 3;\n}\n")),(0,o.yg)("p",null,"To make sure the receiving chain has content on the creator of a blog post, add\nthe ",(0,o.yg)("inlineCode",{parentName:"p"},"msg.Creator")," value to the IBC ",(0,o.yg)("inlineCode",{parentName:"p"},"packet"),"."),(0,o.yg)("ul",null,(0,o.yg)("li",{parentName:"ul"},"The content of the ",(0,o.yg)("inlineCode",{parentName:"li"},"sender")," of the message is automatically included in\n",(0,o.yg)("inlineCode",{parentName:"li"},"SendIbcPost")," message."),(0,o.yg)("li",{parentName:"ul"},"The sender is verified as the signer of the message, so you can add the\n",(0,o.yg)("inlineCode",{parentName:"li"},"msg.Sender")," as the creator to the new packet"),(0,o.yg)("li",{parentName:"ul"},"before it is sent over IBC.")),(0,o.yg)("pre",null,(0,o.yg)("code",{parentName:"pre",className:"language-go",metastring:'title="x/blog/keeper/msg_server_ibc_post.go"',title:'"x/blog/keeper/msg_server_ibc_post.go"'},'package keeper\n\nfunc (k msgServer) SendIbcPost(goCtx context.Context, msg *types.MsgSendIbcPost) (*types.MsgSendIbcPostResponse, error) {\n    // validate incoming message\n    if _, err := k.addressCodec.StringToBytes(msg.Creator); err != nil {\n        return nil, errorsmod.Wrap(sdkerrors.ErrInvalidAddress, fmt.Sprintf("invalid address: %s", err))\n    }\n\n    if msg.Port == "" {\n        return nil, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "invalid packet port")\n    }\n\n    if msg.ChannelID == "" {\n        return nil, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "invalid packet channel")\n    }\n\n    if msg.TimeoutTimestamp == 0 {\n        return nil, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "invalid packet timeout")\n    }\n\n    // TODO: logic before transmitting the packet\n\n    // Construct the packet\n    var packet types.IbcPostPacketData\n\n    packet.Title = msg.Title\n    packet.Content = msg.Content\n    // highlight-next-line\n    packet.Creator = msg.Creator\n\n    // Transmit the packet\n    ctx := s
1dk.UnwrapSDKContext(goCtx)\n    _, err := k.TransmitIbcPostPacket(\n        ctx,\n        packet,\n        msg.Port,\n        msg.ChannelID,\n        clienttypes.ZeroHeight(),\n        msg.TimeoutTimestamp,\n    )\n    if err != nil {\n        return nil, err\n    }\n\n    return &types.MsgSendIbcPostResponse{}, nil\n}\n')),(0,o.yg)("h3",{id:"receive-the-post"},"Receive the post"),(0,o.yg)("p",null,"The methods for primary transaction logic are in the ",(0,o.yg)("inlineCode",{parentName:"p"},"x/blog/keeper/ibc_post.go"),"\nfile. Use these methods to manage IBC packets:"),(0,o.yg)("ul",null,(0,o.yg)("li",{parentName:"ul"},(0,o.yg)("inlineCode",{parentName:"li"},"TransmitIbcPostPacket")," is called manually to send the packet over IBC. This\nmethod also defines the logic before the packet is sent over IBC to another\nblockchain app."),(0,o.yg)("li",{parentName:"ul"},(0,o.yg)("inlineCode",{parentName:"li"},"OnRecvIbcPostPacket")," hook is automatically called when a packet is received\non the chain. This method defines the packet reception logic."),(0,o.yg)("li",{parentName:"ul"},(0,o.yg)("inlineCode",{parentName:"li"},"OnAcknowledgementIbcPostPacket")," hook is called when a sent packet is\nacknowledged on the source chain. This method defines the logic when the\npacket has been received."),(0,o.yg)("li",{parentName:"ul"},(0,o.yg)("inlineCode",{parentName:"li"},"OnTimeoutIbcPostPacket")," hook is called when a sent packet times out. This\nmethod defines the logic when the packet is not received on the target chain")),(0,o.yg)("p",null,"You must modify the source code to add the logic inside those functions so that\nthe data tables are modified accordingly."),(0,o.yg)("p",null,"On reception of the post message, create a new post with the title and the\ncontent on the receiving chain."),(0,o.yg)("p",null,"To identify the blockchain app that a message is originating from and who\ncreated the message, use an identifier in the following format:"),(0,o.yg)("p",null,(0,o.yg)("inlineCode",{parentName:"p"},"<portID>-<channelID>-<creatorAddress>")),(0,o.yg)("p",null,"Finally, the IGNITE\xae CLI-generated AppendPost function returns the ID of the new\nappended post. You can return this value to the source chain through\nacknowledgment."),(0,o.yg)("p",null,"Append the type instance as ",(0,o.yg)("inlineCode",{parentName:"p"},"PostId")," on receiving the packet:"),(0,o.yg)("ul",null,(0,o.yg)("li",{parentName:"ul"},"The context ",(0,o.yg)("inlineCode",{parentName:"li"},"ctx")," is an ",(0,o.yg)("a",{parentName:"li",href:"https://docs.cosmos.network/main/core/context#go-context-package"},"immutable data\nstructure"),"\nthat has header data from the transaction. See ",(0,o.yg)("a",{parentName:"li",href:"https://github.com/cosmos/cosmos-sdk/blob/main/types/context.go#L71"},"how the context is\ninitiated")),(0,o.yg)("li",{parentName:"ul"},"The identifier format that you defined earlier"),(0,o.yg)("li",{parentName:"ul"},"The ",(0,o.yg)("inlineCode",{parentName:"li"},"title")," is the Title of the blog post"),(0,o.yg)("li",{parentName:"ul"},"The ",(0,o.yg)("inlineCode",{parentName:"li"},"content")," is the Content of the blog post")),(0,o.yg)("p",null,"Then modify the ",(0,o.yg)("inlineCode",{parentName:"p"},"OnRecvIbcPostPacket")," keeper function with the following code:"),(0,o.yg)("pre",null,(0,o.yg)("code",{parentName:"pre",className:"language-go",metastring:'title="x/blog/keeper/ibc_post.go"',title:'"x/blog/keeper/ibc_post.go"'},"package keeper\n\nfunc (k Keeper) OnRecvIbcPostPacket(ctx sdk.Context, packet channeltypes.Packet, data types.IbcPostPacketData) (packetAck types.IbcPostPacketAck, err error) {\n    packetAck.PostId, err = k.PostSeq.Next(ctx)\n    if err != nil {\n        return packetAck, err\n    }\n    return packetAck, k.Post.Set(ctx, packetAck.PostId, types.Post{Title: data.Title, Content: data.Content})\n}\n")),(0,o.yg)("h3",{id:"receive-the-post-acknowledgement"},"Receive the post acknowledgement"),(0,o.yg)("p",null,"On the sending blockchain, store a ",(0,o.yg)("inlineCode",{parentName:"p"},"sentPost")," so you know that the post has been\nreceived on the target chain."),(0,o.yg)("p",null,"Store the title and the target to identify the post."),(0,o.yg)("p",null,"When a packet is scaffolded, the default type for the received acknowledgment\ndata is a type that identifies if the packet treatment has failed. The\n",(0,o.yg)("inlineCode",{parentName:"p"},"Acknowledgement_Error")," type is set if ",(0,o.yg)("inlineCode",{parentName:"p"},"OnRecvIbcPostPacket")," returns an error\nfrom the packet."),(0,o.yg)("pre",null,(0,o.yg)("code",{parentName:"pre",className:"language-go",metastring:'title="x/blog/keeper/ibc_post.go"',title:'"x/blog/keeper/ibc_post.go"'},'package keeper\n\nimport transfertypes "github.com/cosmos/ibc-go/v11/modules/apps/transfer/types"\n\nfunc (k Keeper) OnAcknowledgementIbcPostPacket(ctx sdk.Context, packet channeltypes.Packet, data types.IbcPostPacketData, ack channeltypes.Acknowledgement) error {\n    switch dispatchedAck := ack.Response.(type) {\n    case *channeltypes.Acknowledgement_Error:\n        // We will not treat acknowledgment error in this tutorial\n        return nil\n    case *channeltypes.Acknowledgement_Result:\n        // Decode the packet acknowledgment\n        var packetAck types.IbcPostPacketAck\n        if err := k.cdc.UnmarshalJSON(dispatchedAck.Result, &packetAck); err != nil {\n            // The counter-party module doesn\'t implement the correct acknowledgment format\n            return errors.New("cannot unmarshal acknowledgment")\n        }\n\n        seq, err := k.SentPostSeq.Next(ctx)\n        if err != nil {\n            return err\n        }\n\n        return k.SentPost.Set(ctx, seq,\n            types.SentPost{\n                PostId: packetAck.PostId,\n                Title:  data.Title,\n                Chain:  packet.DestinationPort + "-" + packet.DestinationChannel,\n            },\n        )\n    default:\n        return errors.New("the counter-party module does not implement the correct acknowledgment format")\n    }\n}\n')),(0,o.yg)("h3",{id:"store-information-about-the-timed-out-packet"},"Store information about the timed-out packet"),(0,o.yg)("p",null,"Store posts that have not been received by target chains in ",(0,o.yg)("inlineCode",{parentName:"p"},"timeoutPost"),"\nposts. This logic follows the same format as ",(0,o.yg)("inlineCode",{parentName:"p"},"sentPost"),"."),(0,o.yg)("pre",null,(0,o.yg)("code",{parentName:"pre",className:"language-go",metastring:'title="x/blog/keeper/ibc_post.go"',title:'"x/blog/keeper/ibc_post.go"'},'func (k Keeper) OnTimeoutIbcPostPacket(ctx sdk.Context, packet channeltypes.Packet, data types.IbcPostPacketData) error {\n    seq, err := k.TimeoutPostSeq.Next(ctx)\n    if err != nil {\n        return err\n    }\n\n    return k.TimeoutPost.Set(ctx, seq,\n        types.TimeoutPost{\n            Title: data.Title,\n            Chain: packet.DestinationPort + "-" + packet.DestinationChannel,\n        },\n    )\n}\n')),(0,o.yg)("p",null,"This last step completes the basic ",(0,o.yg)("inlineCode",{parentName:"p"},"blog")," module setup. The blockchain is now\nready!"),(0,o.yg)("h2",{id:"use-the-ibc-modules"},"Use the IBC modules"),(0,o.yg)("p",null,"You can now spin up the blockchain and send a blog post from one blockchain app\nto the other. Multiple terminal windows are required to complete these next\nsteps."),(0,o.yg)("h3",{id:"test-the-ibc-modules"},"Test the IBC modules"),(0,o.yg)("p",null,"To test the IBC module, start two blockchain networks on the same machine. Both\nblockchains use the same source code. Each blockchain has a unique chain ID."),(0,o.yg)("p",null,"One blockchain is named ",(0,o.yg)("inlineCode",{parentName:"p"},"earth")," and the other blockchain is named ",(0,o.yg)("inlineCode",{parentName:"p"},"mars"),"."),(0,o.yg)("p",null,"The ",(0,o.yg)("inlineCode",{parentName:"p"},"earth.yml")," and ",(0,o.yg)("inlineCode",{parentName:"p"},"mars.yml")," files are required in the project directory:"),(0,o.yg)("pre",null,(0,o.yg)("code",{parentName:"pre",className:"language-yaml",metastring:'title="earth.yml"',title:'"earth.yml"'},"version: 1\nvalidation: sovereign\nbuild:\n  proto:\n    path: proto\naccounts:\n- name: alice\n  coins:\n  - 1000token\n  - 100000000stake\n- name: bob\n  coins:\n  - 500token\n  - 100000000stake\nfaucet:\n  name: bob\n  coins:\n  - 5token\n  - 100000stake\n  host: 0.0.0.0:4500\ngenesis:\n  chain_id: earth\nvalidators:\n- name: alice\n  bonded: 100000000stake\n  home: $HOME/.earth\n")),(0,o.yg)("pre",null,(0,o.yg)("code",{parentName:"pre",className:"language-yaml",metastring:'title="mars.yml"',title:'"mars.yml"'},"version: 1\nvalidation: sovereign\nbuild:\n  proto:\n    path: proto\naccounts:\n- name: alice\n  coins:\n  - 1000token\n  - 1000000000stake\n- name: bob\n  coins:\n  - 500token\n  - 100000000stake\nfaucet:\n  name: bob\n  coins:\n  - 5token\n  - 100000stake\n  host: :4501\ngenesis:\n  chain_id: mars\nvalidators:\n- name: alice\n  bonded: 100000000stake\n  app:\n    api:\n      address: :1318\n    grpc:\n      address: :9092\n    grpc-web:\n      address: :9093\n  
1config:\n    p2p:\n      laddr: :26658\n    rpc:\n      laddr: :26659\n      pprof_laddr: :6061\n  home: $HOME/.mars\n")),(0,o.yg)("p",null,"Open a terminal window and run the following command to start the ",(0,o.yg)("inlineCode",{parentName:"p"},"earth"),"\nblockchain:"),(0,o.yg)("pre",null,(0,o.yg)("code",{parentName:"pre",className:"language-bash"},"ignite cosmos chain serve -c earth.yml\n")),(0,o.yg)("p",null,"Open a different terminal window and run the following command to start the\n",(0,o.yg)("inlineCode",{parentName:"p"},"mars")," blockchain:"),(0,o.yg)("pre",null,(0,o.yg)("code",{parentName:"pre",className:"language-bash"},"ignite cosmos chain serve -c mars.yml\n")),(0,o.yg)("p",null,"If existing relayer configurations do not exist, the command returns ",(0,o.yg)("inlineCode",{parentName:"p"},"no matches\nfound")," and no action is taken."),(0,o.yg)("h3",{id:"configure-and-start-the-relayer"},"Configure and start the relayer"),(0,o.yg)("p",null,"First, add the Hermes relayer app."),(0,o.yg)("pre",null,(0,o.yg)("code",{parentName:"pre",className:"language-bash"},"ignite app install -g github.com/ignite/apps/hermes\n")),(0,o.yg)("p",null,"If you previously used the relayer, follow these steps to remove exiting relayer\nand IGNITE\xae CLI configurations:"),(0,o.yg)("ul",null,(0,o.yg)("li",{parentName:"ul"},"Stop your blockchains and delete previous configuration files:")),(0,o.yg)("pre",null,(0,o.yg)("code",{parentName:"pre",className:"language-bash"},"ignite relayer hermes clear binaries\nignite relayer hermes clear configs\n")),(0,o.yg)("p",null,"and after configure the relayer."),(0,o.yg)("pre",null,(0,o.yg)("code",{parentName:"pre",className:"language-bash"},'ignite relayer hermes configure \\\n"earth" "http://localhost:26657" "http://localhost:9090" \\\n"mars" "http://localhost:26659" "http://localhost:9092" \\\n--chain-a-faucet "http://0.0.0.0:4500" \\\n--chain-b-faucet "http://0.0.0.0:4501" \\\n--chain-a-port-id "blog" \\\n--chain-b-port-id "blog" \\\n--channel-version "blog-1"\n')),(0,o.yg)("p",null,"When prompted, press Enter to accept the default values for ",(0,o.yg)("inlineCode",{parentName:"p"},"Chain A Account")," and\n",(0,o.yg)("inlineCode",{parentName:"p"},"Chain B Account"),"."),(0,o.yg)("p",null,"The output looks like:"),(0,o.yg)("pre",null,(0,o.yg)("code",{parentName:"pre"},"Hermes config created at /Users/danilopantani/.ignite/relayer/hermes/earth_mars\n? Chain earth doesn't have a default Hermes key. Type your mnemonic to continue or type enter to generate a new one: (optional) \nNew mnemonic generated: danger plate flavor twist chimney myself sketch assist copy expand core tattoo ignore ensure quote mean forum carbon enroll gadget immense grab early maze\nChain earth key created\nChain earth relayer wallet: cosmos1jk6wmyl880j6t9vw6umy9v8ex0yhrfwgx0vv2d\nNew balance from faucet: 100000stake,5token\n? Chain mars doesn't have a default Hermes key. Type your mnemonic to continue or type enter to generate a new one: (optional) \nNew mnemonic generated: invest box icon session lens demise purse link boss dwarf give minimum jazz eye vocal seven sunset coach express want ask version anger ranch\nChain mars key created\nChain mars relayer wallet: cosmos1x9kt37c0sutanaqwy9gxpvq5990yt0qnpqntmp\nNew balance from faucet: 100000stake,5token\nClient '07-tendermint-0' created (earth -> mars)\nClient 07-tendermint-0' created (mars -> earth)\nConnection 'earth (connection-0) <-> mars (connection-0)' created\nChannel 'earth (channel-0) <-> mars (channel-0)' created\n")),(0,o.yg)("p",null,"Now start the relayer:"),(0,o.yg)("pre",null,(0,o.yg)("code",{parentName:"pre",className:"language-bash"},'ignite relayer hermes start "earth" "mars"\n')),(0,o.yg)("h3",{id:"send-packets"},"Send packets"),(0,o.yg)("p",null,"You can now send packets and verify the received posts:"),(0,o.yg)("pre",null,(0,o.yg)("code",{parentName:"pre",className:"language-bash"},'planetd tx blog send-ibc-post blog channel-0 "Hello" "Hello Mars, I\'m Alice from Earth" --from alice --chain-id earth --home ~/.earth\n')),(0,o.yg)("p",null,"To verify that the post has been received on Mars:"),(0,o.yg)("pre",null,(0,o.yg)("code",{parentName:"pre",className:"language-bash"},"planetd q blog list-post --node tcp://localhost:26659\n")),(0,o.yg)("p",null,"The packet has been received:"),(0,o.yg)("pre",null,(0,o.yg)("code",{parentName:"pre",className:"language-yaml"},'Post:\n  - content: Hello Mars, I\'m Alice from Earth\n    creator: blog-channel-0-cosmos1aew8dk9cs3uzzgeldatgzvm5ca2k4m98xhy20x\n    id: "0"\n    title: Hello\npagination:\n  next_key: null\n  total: "1"\n')),(0,o.yg)("p",null,"To check if the packet has been acknowledged on Earth:"),(0,o.yg)("pre",null,(0,o.yg)("code",{parentName:"pre",className:"language-bash"},"planetd q blog list-sent-post\n")),(0,o.yg)("p",null,"Output:"),(0,o.yg)("pre",null,(0,o.yg)("code",{parentName:"pre",className:"language-yaml"},'SentPost:\n  - chain: blog-channel-0\n    creator: cosmos1aew8dk9cs3uzzgeldatgzvm5ca2k4m98xhy20x\n    id: "0"\n    postID: "0"\n    title: Hello\npagination:\n  next_key: null\n  total: "1"\n')),(0,o.yg)("p",null,"To test timeout, set the timeout time of a packet to 1 nanosecond, verify that\nthe packet is timed out, and check the timed-out posts:"),(0,o.yg)("pre",null,(0,o.yg)("code",{parentName:"pre",className:"language-bash"},'planetd tx blog send-ibc-post blog channel-0 "Sorry" "Sorry Mars, you will never see this post" --from alice --chain-id earth --home ~/.earth --packet-timeout-timestamp 1\n')),(0,o.yg)("p",null,"Check the timed-out posts:"),(0,o.yg)("pre",null,(0,o.yg)("code",{parentName:"pre",className:"language-bash"},"planetd q blog list-timeout-post\n")),(0,o.yg)("p",null,"Results:"),(0,o.yg)("pre",null,(0,o.yg)("code",{parentName:"pre",className:"language-yaml"},'TimeoutPost:\n  - chain: blog-channel-0\n    creator: cosmos1fhpcsxn0g8uask73xpcgwxlfxtuunn3ey5ptjv\n    id: "0"\n    title: Sorry\npagination:\n  next_key: null\n  total: "2"\n')),(0,o.yg)("p",null,"You can also send a post from Mars:"),(0,o.yg)("pre",null,(0,o.yg)("code",{parentName:"pre",className:"language-bash"},'planetd tx blog send-ibc-post blog channel-0 "Hello" "Hello Earth, I\'m Alice from Mars" --from alice --chain-id mars --home ~/.mars --node tcp://localhost:26659\n')),(0,o.yg)("p",null,"List post on Earth:"),(0,o.yg)("pre",null,(0,o.yg)("code",{parentName:"pre",className:"language-bash"},"planetd q blog list-post\n")),(0,o.yg)("p",null,"Results:"),(0,o.yg)("pre",null,(0,o.yg)("code",{parentName:"pre",className:"language-yaml"},'Post:\n  - content: Hello Earth, I\'m Alice from Mars\n    creator: blog-channel-0-cosmos1xtpx43l826348s59au24p22pxg6q248638q2tf\n    id: "0"\n    title: Hello\npagination:\n  next_key: null\n  total: "1"\n')),(0,o.yg)("h2",{id:"congratulations-"},"Congratulations \ud83c\udf89"),(0,o.yg)("p",null,"By completing this tutorial, you've learned to use the Inter-Blockchain\nCommunication protocol (IBC)."),(0,o.yg)("p",null,"Here's what you accomplished in this tutorial:"),(0,o.yg)("ul",null,(0,o.yg)("li",{parentName:"ul"},"Built two Hello blockchain apps as IBC modules"),(0,o.yg)("li",{parentName:"ul"},"Modified the generated code to add CRUD action logic"),(0,o.yg)("li",{parentName:"ul"},"Configured and used the IGNITE\xae CLI relayer to connect two blockchains with\neach other"),(0,o.yg)("li",{parentName:"ul"},"Transferred IBC packets from one blockchain to another")))}d.isMDXComponent=!0},36522(e,n,t){t.d(n,{A:()=>a});const a=t.p+"assets/images/packet_sendpost-250db83c05d6472196790d0f04514173.png"}}]);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.