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