github.com/KYVENetwork/cometbft/v38@v38.0.3/docs/guides/go.md (about)

     1  ---
     2  order: 1
     3  ---
     4  
     5  # Creating an application in Go
     6  
     7  ## Guide Assumptions
     8  
     9  This guide is designed for beginners who want to get started with a CometBFT
    10  application from scratch. It does not assume that you have any prior
    11  experience with CometBFT.
    12  
    13  CometBFT is a service that provides a Byzantine Fault Tolerant consensus engine
    14  for state-machine replication. The replicated state-machine, or "application", can be written
    15  in any language that can send and receive protocol buffer messages in a client-server model.
    16  Applications written in Go can also use CometBFT as a library and run the service in the same
    17  process as the application.
    18  
    19  By following along this tutorial you will create a CometBFT application called kvstore,
    20  a (very) simple distributed BFT key-value store.
    21  The application will be written in Go and
    22  some understanding of the Go programming language is expected.
    23  If you have never written Go, you may want to go through [Learn X in Y minutes
    24  Where X=Go](https://learnxinyminutes.com/docs/go/) first, to familiarize
    25  yourself with the syntax.
    26  
    27  Note: Please use the latest released version of this guide and of CometBFT.
    28  We strongly advise against using unreleased commits for your development.
    29  
    30  ### Built-in app vs external app
    31  
    32  On the one hand, to get maximum performance you can run your application in
    33  the same process as the CometBFT, as long as your application is written in Go.
    34  [Cosmos SDK](https://github.com/cosmos/cosmos-sdk) is written
    35  this way.
    36  If that is the way you wish to proceed, use the [Creating a built-in application in Go](./go-built-in.md) guide instead of this one.
    37  
    38  On the other hand, having a separate application might give you better security
    39  guarantees as two processes would be communicating via established binary protocol.
    40  CometBFT will not have access to application's state.
    41  This is the approach followed in this tutorial.
    42  
    43  ## 1.1 Installing Go
    44  
    45  Verify that you have the latest version of Go installed (refer to the [official guide for installing Go](https://golang.org/doc/install)):
    46  
    47  ```bash
    48  $ go version
    49  go version go1.21.1 darwin/amd64
    50  ```
    51  
    52  ## 1.2 Creating a new Go project
    53  
    54  We'll start by creating a new Go project.
    55  
    56  ```bash
    57  mkdir kvstore
    58  ```
    59  
    60  Inside the example directory, create a `main.go` file with the following content:
    61  
    62  ```go
    63  package main
    64  
    65  import (
    66      "fmt"
    67  )
    68  
    69  func main() {
    70      fmt.Println("Hello, CometBFT")
    71  }
    72  ```
    73  
    74  When run, this should print "Hello, CometBFT" to the standard output.
    75  
    76  ```bash
    77  cd kvstore
    78  $ go run main.go
    79  Hello, CometBFT
    80  ```
    81  
    82  We are going to use [Go modules](https://github.com/golang/go/wiki/Modules) for
    83  dependency management, so let's start by including a dependency on the latest version of
    84  CometBFT, `v0.38.0` in this example.
    85  
    86  ```bash
    87  go mod init kvstore
    88  go get github.com/cometbft/cometbft@v0.38.0
    89  ```
    90  
    91  After running the above commands you will see two generated files, `go.mod` and `go.sum`.
    92  The go.mod file should look similar to:
    93  
    94  ```go
    95  module kvstore
    96  
    97  go 1.21.1
    98  
    99  require (
   100  github.com/cometbft/cometbft v0.38.0
   101  )
   102  ```
   103  
   104  XXX: CometBFT `v0.38.0` uses a slightly outdated `gogoproto` library, which
   105  may fail to compile with newer Go versions. To avoid any compilation errors,
   106  upgrade `gogoproto` manually:
   107  
   108  ```bash
   109  go get github.com/cosmos/gogoproto@v1.4.11
   110  ```
   111  
   112  As you write the kvstore application, you can rebuild the binary by
   113  pulling any new dependencies and recompiling it.
   114  
   115  ```bash
   116  go get
   117  go build
   118  ```
   119  
   120  ## 1.3 Writing a CometBFT application
   121  
   122  CometBFT communicates with the application through the Application
   123  BlockChain Interface (ABCI). The messages exchanged through the interface are
   124  defined in the ABCI [protobuf
   125  file](https://github.com/KYVENetwork/cometbft/v38/blob/v0.38.x/proto/tendermint/abci/types.proto).
   126  
   127  We begin by creating the basic scaffolding for an ABCI application by
   128  creating a new type, `KVStoreApplication`, which implements the
   129  methods defined by the `abcitypes.Application` interface.
   130  
   131  Create a file called `app.go` with the following contents:
   132  
   133  ```go
   134  package main
   135  
   136  import (
   137      abcitypes "github.com/KYVENetwork/cometbft/v38/abci/types"
   138      "context"
   139  )
   140  
   141  type KVStoreApplication struct{}
   142  
   143  var _ abcitypes.Application = (*KVStoreApplication)(nil)
   144  
   145  func NewKVStoreApplication() *KVStoreApplication {
   146      return &KVStoreApplication{}
   147  }
   148  
   149  func (app *KVStoreApplication) Info(_ context.Context, info *abcitypes.RequestInfo) (*abcitypes.ResponseInfo, error) {
   150      return &abcitypes.ResponseInfo{}, nil
   151  }
   152  
   153  func (app *KVStoreApplication) Query(_ context.Context, req *abcitypes.RequestQuery) (*abcitypes.ResponseQuery, error) {
   154      return &abcitypes.ResponseQuery{}, nil
   155  }
   156  
   157  func (app *KVStoreApplication) CheckTx(_ context.Context, check *abcitypes.RequestCheckTx) (*abcitypes.ResponseCheckTx, error) {
   158      return &abcitypes.ResponseCheckTx{Code: code}, nil
   159  }
   160  
   161  func (app *KVStoreApplication) InitChain(_ context.Context, chain *abcitypes.RequestInitChain) (*abcitypes.ResponseInitChain, error) {
   162      return &abcitypes.ResponseInitChain{}, nil
   163  }
   164  
   165  func (app *KVStoreApplication) PrepareProposal(_ context.Context, proposal *abcitypes.RequestPrepareProposal) (*abcitypes.ResponsePrepareProposal, error) {
   166      return &abcitypes.ResponsePrepareProposal{}, nil
   167  }
   168  
   169  func (app *KVStoreApplication) ProcessProposal(_ context.Context, proposal *abcitypes.RequestProcessProposal) (*abcitypes.ResponseProcessProposal, error) {
   170      return &abcitypes.ResponseProcessProposal{}, nil
   171  }
   172  
   173  func (app *KVStoreApplication) FinalizeBlock(_ context.Context, req *abcitypes.RequestFinalizeBlock) (*abcitypes.ResponseFinalizeBlock, error) {
   174      return &abcitypes.ResponseFinalizeBlock{}, nil
   175  }
   176  
   177  func (app KVStoreApplication) Commit(_ context.Context, commit *abcitypes.RequestCommit) (*abcitypes.ResponseCommit, error) {
   178      return &abcitypes.ResponseCommit{}, nil
   179  }
   180  
   181  func (app *KVStoreApplication) ListSnapshots(_ context.Context, snapshots *abcitypes.RequestListSnapshots) (*abcitypes.ResponseListSnapshots, error) {
   182      return &abcitypes.ResponseListSnapshots{}, nil
   183  }
   184  
   185  func (app *KVStoreApplication) OfferSnapshot(_ context.Context, snapshot *abcitypes.RequestOfferSnapshot) (*abcitypes.ResponseOfferSnapshot, error) {
   186      return &abcitypes.ResponseOfferSnapshot{}, nil
   187  }
   188  
   189  func (app *KVStoreApplication) LoadSnapshotChunk(_ context.Context, chunk *abcitypes.RequestLoadSnapshotChunk) (*abcitypes.ResponseLoadSnapshotChunk, error) {
   190      return &abcitypes.ResponseLoadSnapshotChunk{}, nil
   191  }
   192  
   193  func (app *KVStoreApplication) ApplySnapshotChunk(_ context.Context, chunk *abcitypes.RequestApplySnapshotChunk) (*abcitypes.ResponseApplySnapshotChunk, error) {
   194  
   195      return &abcitypes.ResponseApplySnapshotChunk{Result: abcitypes.ResponseApplySnapshotChunk_ACCEPT}, nil
   196  }
   197  
   198  func (app KVStoreApplication) ExtendVote(_ context.Context, extend *abcitypes.RequestExtendVote) (*abcitypes.ResponseExtendVote, error) {
   199      return &abcitypes.ResponseExtendVote{}, nil
   200  }
   201  
   202  func (app *KVStoreApplication) VerifyVoteExtension(_ context.Context, verify *abcitypes.RequestVerifyVoteExtension) (*abcitypes.ResponseVerifyVoteExtension, error) {
   203      return &abcitypes.ResponseVerifyVoteExtension{}, nil
   204  }
   205  ```
   206  
   207  The types used here are defined in the CometBFT library and were added as a dependency
   208  to the project when you ran `go get`. If your IDE is not recognizing the types, go ahead and run the command again.
   209  
   210  ```bash
   211  go get github.com/cometbft/cometbft@v0.38.0
   212  ```
   213  
   214  Now go back to the `main.go` and modify the `main` function so it matches the following,
   215  where an instance of the `KVStoreApplication` type is created.
   216  
   217  ```go
   218  func main() {
   219      fmt.Println("Hello, CometBFT")
   220  
   221      _ = NewKVStoreApplication()
   222  }
   223  ```
   224  
   225  You can recompile and run the application now by running `go get` and `go build`, but it does
   226  not do anything.
   227  So let's revisit the code adding the logic needed to implement our minimal key/value store
   228  and to start it along with the CometBFT Service.
   229  
   230  ### 1.3.1 Add a persistent data store
   231  
   232  Our application will need to write its state out to persistent storage so that it
   233  can stop and start without losing all of its data.
   234  
   235  For this tutorial, we will use [BadgerDB](https://github.com/dgraph-io/badger), a
   236  a fast embedded key-value store.
   237  
   238  First, add Badger as a dependency of your go module using the `go get` command:
   239  
   240  `go get github.com/dgraph-io/badger/v3`
   241  
   242  Next, let's update the application and its constructor to receive a handle to the database, as follows:
   243  
   244  ```go
   245  type KVStoreApplication struct {
   246      db           *badger.DB
   247      onGoingBlock *badger.Txn
   248  }
   249  
   250  var _ abcitypes.Application = (*KVStoreApplication)(nil)
   251  
   252  func NewKVStoreApplication(db *badger.DB) *KVStoreApplication {
   253      return &KVStoreApplication{db: db}
   254  }
   255  ```
   256  
   257  The `onGoingBlock` keeps track of the Badger transaction that will update the application's state when a block
   258  is completed. Don't worry about it for now, we'll get to that later.
   259  
   260  Next, update the `import` stanza at the top to include the Badger library:
   261  
   262  ```go
   263  import(
   264      "github.com/dgraph-io/badger/v3"
   265      abcitypes "github.com/KYVENetwork/cometbft/v38/abci/types"
   266  )
   267  ```
   268  
   269  Finally, update the `main.go` file to invoke the updated constructor:
   270  
   271  ```go
   272  _ = NewKVStoreApplication(nil)
   273  ```
   274  
   275  ### 1.3.2 CheckTx
   276  
   277  When CometBFT receives a new transaction from a client, or from another full node,
   278  CometBFT asks the application if the transaction is acceptable, using the `CheckTx` method.
   279  Invalid transactions will not be shared with other nodes and will not become part of any blocks and, therefore, will not be executed by the application.
   280  
   281  In our application, a transaction is a string with the form `key=value`, indicating a key and value to write to the store.
   282  
   283  The most basic validation check we can perform is to check if the transaction conforms to the `key=value` pattern.
   284  For that, let's add the following helper method to app.go:
   285  
   286  ```go
   287  func (app *KVStoreApplication) isValid(tx []byte) uint32 {
   288      // check format
   289      parts := bytes.Split(tx, []byte("="))
   290      if len(parts) != 2 {
   291          return 1
   292      }
   293      return 0
   294  }
   295  ```
   296  
   297  Now you can rewrite the `CheckTx` method to use the helper function:
   298  
   299  ```go
   300  func (app *KVStoreApplication) CheckTx(_ context.Context, check *abcitypes.RequestCheckTx) (*abcitypes.ResponseCheckTx, error) {
   301      code := app.isValid(check.Tx)
   302      return &abcitypes.ResponseCheckTx{Code: code}, nil
   303  }
   304  ```
   305  
   306  While this `CheckTx` is simple and only validates that the transaction is well-formed,
   307  it is very common for `CheckTx` to make more complex use of the state of an application.
   308  For example, you may refuse to overwrite an existing value, or you can associate
   309  versions to the key/value pairs and allow the caller to specify a version to
   310  perform a conditional update.
   311  
   312  Depending on the checks and on the conditions violated, the function may return
   313  different values, but any response with a non-zero code will be considered invalid
   314  by CometBFT. Our `CheckTx` logic returns 0 to CometBFT when a transaction passes
   315  its validation checks. The specific value of the code is meaningless to CometBFT.
   316  Non-zero codes are logged by CometBFT so applications can provide more specific
   317  information on why the transaction was rejected.
   318  
   319  Note that `CheckTx` does not execute the transaction, it only verifies that that the transaction could be executed. We do not know yet if the rest of the network has agreed to accept this transaction into a block.
   320  
   321  Finally, make sure to add the bytes package to the `import` stanza at the top of `app.go`:
   322  
   323  ```go
   324  import(
   325      "bytes"
   326  
   327      "github.com/dgraph-io/badger/v3"
   328      abcitypes "github.com/KYVENetwork/cometbft/v38/abci/types"
   329  )
   330  ```
   331  
   332  ### 1.3.3 FinalizeBlock
   333  
   334  When the CometBFT consensus engine has decided on the block, the block is transferred to the
   335  application via the `FinalizeBlock` method.
   336  `FinalizeBlock` is an ABCI method introduced in CometBFT `v0.38.0`. This replaces the functionality provided previously (pre-`v0.38.0`) by the combination of ABCI methods `BeginBlock`, `DeliverTx`, and `EndBlock`.
   337  `FinalizeBlock`'s parameters are an aggregation of those in `BeginBlock`, `DeliverTx`, and `EndBlock`.
   338  
   339  This method is responsible for executing the block and returning a response to the consensus engine.
   340  Providing a single `FinalizeBlock` method to signal the finalization of a block simplifies the ABCI interface and increases flexibility in the execution pipeline.
   341  
   342  The `FinalizeBlock` method executes the block, including any necessary transaction processing and state updates, and returns a `ResponseFinalizeBlock` object which contains any necessary information about the executed block.
   343  
   344  **Note:** `FinalizeBlock` only prepares the update to be made and does not change the state of the application. The state change is actually committed in a later stage i.e. in `commit` phase.
   345  
   346  Note that, to implement these calls in our application we're going to make use of Badger's transaction mechanism. We will always refer to these as Badger transactions, not to confuse them with the transactions included in the blocks delivered by CometBFT, the _application transactions_.
   347  
   348  First, let's create a new Badger transaction during `FinalizeBlock`. All application transactions in the current block will be executed within this Badger transaction.
   349  Next, let's modify `FinalizeBlock` to add the `key` and `value` to the database transaction every time our application processes a new application transaction from the list received through `RequestFinalizeBlock`.
   350  
   351  Note that we check the validity of the transaction _again_ during `FinalizeBlock`.
   352  
   353  ```go
   354  func (app *KVStoreApplication) FinalizeBlock(_ context.Context, req *abcitypes.RequestFinalizeBlock) (*abcitypes.ResponseFinalizeBlock, error) {
   355      var txs = make([]*abcitypes.ExecTxResult, len(req.Txs))
   356  
   357      app.onGoingBlock = app.db.NewTransaction(true)
   358      for i, tx := range req.Txs {
   359          if code := app.isValid(tx); code != 0 {
   360              log.Printf("Error in tx in if")
   361              txs[i] = &abcitypes.ExecTxResult{Code: code}
   362          } else {
   363              parts := bytes.SplitN(tx, []byte("="), 2)
   364              key, value := parts[0], parts[1]
   365              log.Printf("Adding key %s with value %s", key, value)
   366  
   367              if err := app.onGoingBlock.Set(key, value); err != nil {
   368                  log.Panicf("Error writing to database, unable to execute tx: %v", err)
   369              }
   370              log.Printf("Successfully added key %s with value %s", key, value)
   371  
   372              txs[i] = &abcitypes.ExecTxResult{}
   373          }
   374      }
   375  
   376      return &abcitypes.ResponseFinalizeBlock{
   377          TxResults:        txs,
   378      }, nil
   379  }
   380  ```
   381  
   382  Transactions are not guaranteed to be valid when they are delivered to an application, even if they were valid when they were proposed.
   383  
   384  This can happen if the application state is used to determine transaction validity. The application state may have changed between the initial execution of `CheckTx` and the transaction delivery in `FinalizeBlock` in a way that rendered the transaction no longer valid.
   385  
   386  **Note** that `FinalizeBlock` cannot yet commit the Badger transaction we were building during the block execution.
   387  
   388  Other methods, such as `Query`, rely on a consistent view of the application's state, the application should only update its state by committing the Badger transactions when the full block has been delivered and the `Commit` method is invoked.
   389  
   390  The `Commit` method tells the application to make permanent the effects of the application transactions.
   391  Let's update the method to terminate the pending Badger transaction and persist the resulting state:
   392  
   393  ```go
   394  func (app KVStoreApplication) Commit(_ context.Context, commit *abcitypes.RequestCommit) (*abcitypes.ResponseCommit, error) {
   395      return &abcitypes.ResponseCommit{}, app.onGoingBlock.Commit()
   396  }
   397  ```
   398  
   399  Finally, make sure to add the log library to the `import` stanza as well:
   400  
   401  ```go
   402  import (
   403      "bytes"
   404      "log"
   405  
   406      "github.com/dgraph-io/badger/v3"
   407      abcitypes "github.com/KYVENetwork/cometbft/v38/abci/types"
   408  )
   409  ```
   410  
   411  You may have noticed that the application we are writing will crash if it receives
   412  an unexpected error from the Badger database during the `FinalizeBlock` or `Commit` methods.
   413  This is not an accident. If the application received an error from the database, there
   414  is no deterministic way for it to make progress so the only safe option is to terminate.
   415  
   416  ### 1.3.4 Query
   417  
   418  When a client tries to read some information from the `kvstore`, the request will be
   419  handled in the `Query` method. To do this, let's rewrite the `Query` method in `app.go`:
   420  
   421  ```go
   422  func (app *KVStoreApplication) Query(_ context.Context, req *abcitypes.RequestQuery) (*abcitypes.ResponseQuery, error) {
   423      resp := abcitypes.ResponseQuery{Key: req.Data}
   424  
   425      dbErr := app.db.View(func(txn *badger.Txn) error {
   426          item, err := txn.Get(req.Data)
   427          if err != nil {
   428              if err != badger.ErrKeyNotFound {
   429                  return err
   430              }
   431              resp.Log = "key does not exist"
   432              return nil
   433          }
   434  
   435          return item.Value(func(val []byte) error {
   436              resp.Log = "exists"
   437              resp.Value = val
   438              return nil
   439          })
   440      })
   441      if dbErr != nil {
   442          log.Panicf("Error reading database, unable to execute query: %v", dbErr)
   443      }
   444      return &resp, nil
   445  }
   446  ```
   447  
   448  Since it reads only committed data from the store, transactions that are part of a block
   449  that is being processed are not reflected in the query result.
   450  
   451  ### 1.3.5 PrepareProposal and ProcessProposal
   452  
   453  `PrepareProposal` and `ProcessProposal` are methods introduced in CometBFT v0.37.0
   454  to give the application more control over the construction and processing of transaction blocks.
   455  
   456  When CometBFT sees that valid transactions (validated through `CheckTx`) are available to be
   457  included in blocks, it groups some of these transactions and then gives the application a chance
   458  to modify the group by invoking `PrepareProposal`.
   459  
   460  The application is free to modify the group before returning from the call, as long as the resulting set
   461  does not use more bytes than `RequestPrepareProposal.max_tx_bytes`.
   462  For example, the application may reorder, add, or even remove transactions from the group to improve the
   463  execution of the block once accepted.
   464  
   465  In the following code, the application simply returns the unmodified group of transactions:
   466  
   467  ```go
   468  func (app *KVStoreApplication) PrepareProposal(_ context.Context, proposal *abcitypes.RequestPrepareProposal) (*abcitypes.ResponsePrepareProposal, error) {
   469      return &abcitypes.ResponsePrepareProposal{Txs: proposal.Txs}, nil
   470  }
   471  ```
   472  
   473  Once a proposed block is received by a node, the proposal is passed to the
   474  application to determine its validity before voting to accept the proposal.
   475  
   476  This mechanism may be used for different reasons, for example to deal with blocks manipulated
   477  by malicious nodes, in which case the block should not be considered valid.
   478  
   479  The following code simply accepts all proposals:
   480  
   481  ```go
   482  func (app *KVStoreApplication) ProcessProposal(_ context.Context, proposal *abcitypes.RequestProcessProposal) (*abcitypes.ResponseProcessProposal, error) {
   483      return &abcitypes.ResponseProcessProposal{Status: abcitypes.ResponseProcessProposal_ACCEPT}, nil
   484  }
   485  ```
   486  
   487  ## 1.4 Starting an application and a CometBFT instance
   488  
   489  Now that we have the basic functionality of our application in place, let's put
   490  it all together inside of our `main.go` file.
   491  
   492  Change the contents of your `main.go` file to the following.
   493  
   494  ```go
   495  package main
   496  
   497  import (
   498      "flag"
   499      "fmt"
   500      abciserver "github.com/KYVENetwork/cometbft/v38/abci/server"
   501      "log"
   502      "os"
   503      "os/signal"
   504      "path/filepath"
   505      "syscall"
   506  
   507      "github.com/dgraph-io/badger/v3"
   508      cmtlog "github.com/KYVENetwork/cometbft/v38/libs/log"
   509  )
   510  
   511  var homeDir string
   512  var socketAddr string
   513  
   514  func init() {
   515      flag.StringVar(&homeDir, "kv-home", "", "Path to the kvstore directory (if empty, uses $HOME/.kvstore)")
   516      flag.StringVar(&socketAddr, "socket-addr", "unix://example.sock", "Unix domain socket address (if empty, uses \"unix://example.sock\"")
   517  }
   518  
   519  func main() {
   520      flag.Parse()
   521      if homeDir == "" {
   522          homeDir = os.ExpandEnv("$HOME/.kvstore")
   523      }
   524  
   525      dbPath := filepath.Join(homeDir, "badger")
   526      db, err := badger.Open(badger.DefaultOptions(dbPath))
   527      if err != nil {
   528          log.Fatalf("Opening database: %v", err)
   529      }
   530  
   531      defer func() {
   532          if err := db.Close(); err != nil {
   533              log.Fatalf("Closing database: %v", err)
   534          }
   535      }()
   536  
   537      app := NewKVStoreApplication(db)
   538      logger := cmtlog.NewTMLogger(cmtlog.NewSyncWriter(os.Stdout))
   539  
   540      server := abciserver.NewSocketServer(socketAddr, app)
   541      server.SetLogger(logger)
   542  
   543      if err := server.Start(); err != nil {
   544          fmt.Fprintf(os.Stderr, "error starting socket server: %v", err)
   545  
   546          os.Exit(1)
   547      }
   548      defer server.Stop()
   549  
   550      c := make(chan os.Signal, 1)
   551      signal.Notify(c, os.Interrupt, syscall.SIGTERM)
   552      <-c
   553  }
   554  ```
   555  
   556  This is a huge blob of code, so let's break it down into pieces.
   557  
   558  First, we initialize the Badger database and create an app instance:
   559  
   560  ```go
   561  dbPath := filepath.Join(homeDir, "badger")
   562  db, err := badger.Open(badger.DefaultOptions(dbPath))
   563  if err != nil {
   564      log.Fatalf("Opening database: %v", err)
   565  }
   566  defer func() {
   567      if err := db.Close(); err != nil {
   568          log.Fatalf("Closing database: %v", err)
   569      }
   570  }()
   571  
   572  app := NewKVStoreApplication(db)
   573  ```
   574  
   575  Then we start the ABCI server and add some signal handling to gracefully stop
   576  it upon receiving SIGTERM or Ctrl-C. CometBFT will act as a client,
   577  which connects to our server and send us transactions and other messages.
   578  
   579  ```go
   580  server := abciserver.NewSocketServer(socketAddr, app)
   581  server.SetLogger(logger)
   582  
   583  if err := server.Start(); err != nil {
   584      fmt.Fprintf(os.Stderr, "error starting socket server: %v", err)
   585      os.Exit(1)
   586  }
   587  defer server.Stop()
   588  
   589  c := make(chan os.Signal, 1)
   590  signal.Notify(c, os.Interrupt, syscall.SIGTERM)
   591  <-c
   592  ```
   593  
   594  ## 1.5 Initializing and Running
   595  
   596  Our application is almost ready to run, but first we'll need to populate the CometBFT configuration files.
   597  The following command will create a `cometbft-home` directory in your project and add a basic set of configuration files in `cometbft-home/config/`.
   598  For more information on what these files contain see [the configuration documentation](https://github.com/KYVENetwork/cometbft/v38/blob/v0.38.x/docs/core/configuration.md).
   599  
   600  From the root of your project, run:
   601  
   602  ```bash
   603  go run github.com/KYVENetwork/cometbft/v38/cmd/cometbft@v0.38.0 init --home /tmp/cometbft-home
   604  ```
   605  
   606  You should see an output similar to the following:
   607  
   608  ```bash
   609  I[2023-04-25|09:06:34.444] Generated private validator                  module=main keyFile=/tmp/cometbft-home/config/priv_validator_key.json stateFile=/tmp/cometbft-home/data/priv_validator_state.json
   610  I[2023-04-25|09:06:34.444] Generated node key                           module=main path=/tmp/cometbft-home/config/node_key.json
   611  I[2023-04-25|09:06:34.444] Generated genesis file                       module=main path=/tmp/cometbft-home/config/genesis.json
   612  ```
   613  
   614  Now rebuild the app:
   615  
   616  ```bash
   617  go build -mod=mod # use -mod=mod to automatically refresh the dependencies
   618  ```
   619  
   620  Everything is now in place to run your application. Run:
   621  
   622  ```bash
   623  ./kvstore -kv-home /tmp/badger-home
   624  ```
   625  
   626  The application will start and you should see an output similar to the following:
   627  
   628  ```bash
   629  badger 2023-04-25 17:01:28 INFO: All 0 tables opened in 0s
   630  badger 2023-04-25 17:01:28 INFO: Discard stats nextEmptySlot: 0
   631  badger 2023-04-25 17:01:28 INFO: Set nextTxnTs to 0
   632  I[2023-04-25|17:01:28.726] service start                                msg="Starting ABCIServer service" impl=ABCIServer
   633  I[2023-04-25|17:01:28.726] Waiting for new connection...
   634  ```
   635  
   636  Then we need to start CometBFT service and point it to our application.
   637  Open a new terminal window and cd to the same folder where the app is running.
   638  Then execute the following command:
   639  
   640  ```bash
   641  go run github.com/KYVENetwork/cometbft/v38/cmd/cometbft@v0.38.0 node --home /tmp/cometbft-home --proxy_app=unix://example.sock
   642  ```
   643  
   644  This should start the full node and connect to our ABCI application, which will be
   645  reflected in the application output.
   646  
   647  ```sh
   648  I[2023-04-25|17:07:08.124] service start                                msg="Starting ABCIServer service" impl=ABCIServer
   649  I[2023-04-25|17:07:08.124] Waiting for new connection...
   650  I[2023-04-25|17:08:12.702] Accepted a new connection
   651  I[2023-04-25|17:08:12.703] Waiting for new connection...
   652  I[2023-04-25|17:08:12.703] Accepted a new connection
   653  I[2023-04-25|17:08:12.703] Waiting for new connection...
   654  ```
   655  
   656  Also, the application using CometBFT Core is producing blocks  🎉🎉 and you can see this reflected in the log output of the service in lines like this:
   657  
   658  ```bash
   659  I[2023-04-25|09:08:52.147] received proposal                            module=consensus proposal="Proposal{2/0 (F518444C0E348270436A73FD0F0B9DFEA758286BEB29482F1E3BEA75330E825C:1:C73D3D1273F2, -1) AD19AE292A45 @ 2023-04-25T12:08:52.143393Z}"
   660  I[2023-04-25|09:08:52.147] received proposal                            module=consensus proposal="Proposal{2/0 (F518444C0E348270436A73FD0F0B9DFEA758286BEB29482F1E3BEA75330E825C:1:C73D3D1273F2, -1) AD19AE292A45 @ 2023-04-25T12:08:52.143393Z}"
   661  I[2023-04-25|09:08:52.152] received complete proposal block             module=consensus height=2 hash=F518444C0E348270436A73FD0F0B9DFEA758286BEB29482F1E3BEA75330E825C
   662  I[2023-04-25|09:08:52.160] finalizing commit of block                   module=consensus height=2 hash=F518444C0E348270436A73FD0F0B9DFEA758286BEB29482F1E3BEA75330E825C root= num_txs=0
   663  I[2023-04-25|09:08:52.167] executed block                               module=state height=2 num_valid_txs=0 num_invalid_txs=0
   664  I[2023-04-25|09:08:52.171] committed state                              module=state height=2 num_txs=0 app_hash=
   665  ```
   666  
   667  The blocks, as you can see from the `num_valid_txs=0` part, are empty, but let's remedy that next.
   668  
   669  ## 1.6 Using the application
   670  
   671  Let's try submitting a transaction to our new application.
   672  Open another terminal window and run the following curl command:
   673  
   674  ```bash
   675  curl -s 'localhost:26657/broadcast_tx_commit?tx="cometbft=rocks"'
   676  ```
   677  
   678  If everything went well, you should see a response indicating which height the
   679  transaction was included in the blockchain.
   680  
   681  Finally, let's make sure that transaction really was persisted by the application.
   682  Run the following command:
   683  
   684  ```bash
   685  curl -s 'localhost:26657/abci_query?data="cometbft"'
   686  ```
   687  
   688  Let's examine the response object that this request returns.
   689  The request returns a `json` object with a `key` and `value` field set.
   690  
   691  ```json
   692  ...
   693      "key": "dGVuZGVybWludA==",
   694      "value": "cm9ja3M=",
   695  ...
   696  ```
   697  
   698  Those values don't look like the `key` and `value` we sent to CometBFT.
   699  What's going on here?
   700  
   701  The response contains a `base64` encoded representation of the data we submitted.
   702  To get the original value out of this data, we can use the `base64` command line utility:
   703  
   704  ```bash
   705  echo "cm9ja3M=" | base64 -d
   706  ```
   707  
   708  ## Outro
   709  
   710  Hope you could run everything smoothly. If you have any difficulties running through this tutorial, reach out to us via [discord](https://discord.com/invite/cosmosnetwork) or open a new [issue](https://github.com/KYVENetwork/cometbft/v38/issues/new/choose) on Github.