github.com/badrootd/celestia-core@v0.0.0-20240305091328-aa4207a4b25d/docs/guides/go-built-in.md (about)

     1  ---
     2  order: 3
     3  ---
     4  
     5  # Creating a built-in 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  This is the approach followed in this tutorial.
    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  If that is the way you wish to proceed, use the [Creating an application in Go](./go.md) guide instead of this one.
    42  
    43  
    44  ## 1.1 Installing Go
    45  
    46  Verify that you have the latest version of Go installed (refer to the [official guide for installing Go](https://golang.org/doc/install)):
    47  
    48  ```bash
    49  $ go version
    50  go version go1.20.1 darwin/amd64
    51  ```
    52  
    53  ## 1.2 Creating a new Go project
    54  
    55  We'll start by creating a new Go project.
    56  
    57  ```bash
    58  mkdir kvstore
    59  ```
    60  
    61  Inside the example directory, create a `main.go` file with the following content:
    62  
    63  ```go
    64  package main
    65  
    66  import (
    67      "fmt"
    68  )
    69  
    70  func main() {
    71      fmt.Println("Hello, CometBFT")
    72  }
    73  ```
    74  
    75  When run, this should print "Hello, CometBFT" to the standard output.
    76  
    77  ```bash
    78  cd kvstore
    79  $ go run main.go
    80  Hello, CometBFT
    81  ```
    82  
    83  We are going to use [Go modules](https://github.com/golang/go/wiki/Modules) for
    84  dependency management, so let's start by including a dependency on this version of
    85  CometBFT.
    86  
    87  ```bash
    88  go mod init kvstore
    89  go get github.com/cometbft/cometbft
    90  go mod edit -replace github.com/cometbft/cometbft=github.com/cometbft/cometbft@v0.34.28
    91  ```
    92  
    93  After running the above commands you will see two generated files, `go.mod` and `go.sum`.
    94  The go.mod file should look similar to:
    95  
    96  ```go
    97  module github.com/me/example
    98  
    99  go 1.20
   100  
   101  require (
   102  	github.com/cometbft/cometbft v0.34.27
   103  )
   104  ```
   105  
   106  As you write the kvstore application, you can rebuild the binary by
   107  pulling any new dependencies and recompiling it.
   108  
   109  ```sh
   110  go get
   111  go build
   112  ```
   113  
   114  ## 1.3 Writing a CometBFT application
   115  
   116  CometBFT communicates with the application through the Application
   117  BlockChain Interface (ABCI). The messages exchanged through the interface are
   118  defined in the ABCI [protobuf
   119  file](https://github.com/cometbft/cometbft/blob/v0.34.x/proto/tendermint/abci/types.proto).
   120  
   121  We begin by creating the basic scaffolding for an ABCI application by
   122  creating a new type, `KVStoreApplication`, which implements the
   123  methods defined by the `abcitypes.Application` interface.
   124  
   125  Create a file called `app.go` with the following contents:
   126  
   127  ```go
   128  package main
   129  
   130  import (
   131  	abcitypes "github.com/cometbft/cometbft/abci/types"
   132  )
   133  
   134  type KVStoreApplication struct{}
   135  
   136  var _ abcitypes.Application = (*KVStoreApplication)(nil)
   137  
   138  func NewKVStoreApplication() *KVStoreApplication {
   139  	return &KVStoreApplication{}
   140  }
   141  
   142  func (app *KVStoreApplication) Info(info abcitypes.RequestInfo) abcitypes.ResponseInfo {
   143  	return abcitypes.ResponseInfo{}
   144  }
   145  
   146  func (app *KVStoreApplication) Query(query abcitypes.RequestQuery) abcitypes.ResponseQuery {
   147  	return abcitypes.ResponseQuery{}
   148  }
   149  
   150  func (app *KVStoreApplication) CheckTx(tx abcitypes.RequestCheckTx) abcitypes.ResponseCheckTx {
   151  	return abcitypes.ResponseCheckTx{}
   152  }
   153  
   154  func (app *KVStoreApplication) InitChain(chain abcitypes.RequestInitChain) abcitypes.ResponseInitChain {
   155  	return abcitypes.ResponseInitChain{}
   156  }
   157  
   158  
   159  func (app *KVStoreApplication) BeginBlock(block abcitypes.RequestBeginBlock) abcitypes.ResponseBeginBlock {
   160  	return abcitypes.ResponseBeginBlock{}
   161  }
   162  
   163  func (app *KVStoreApplication) DeliverTx(tx abcitypes.RequestDeliverTx) abcitypes.ResponseDeliverTx {
   164  	return abcitypes.ResponseDeliverTx{}
   165  }
   166  
   167  func (app *KVStoreApplication) EndBlock(block abcitypes.RequestEndBlock) abcitypes.ResponseEndBlock {
   168  	return abcitypes.ResponseEndBlock{}
   169  }
   170  
   171  func (app *KVStoreApplication) Commit() abcitypes.ResponseCommit {
   172  	return abcitypes.ResponseCommit{}
   173  }
   174  
   175  func (app *KVStoreApplication) ListSnapshots(snapshots abcitypes.RequestListSnapshots) abcitypes.ResponseListSnapshots {
   176  	return abcitypes.ResponseListSnapshots{}
   177  }
   178  
   179  func (app *KVStoreApplication) OfferSnapshot(snapshot abcitypes.RequestOfferSnapshot) abcitypes.ResponseOfferSnapshot {
   180  	return abcitypes.ResponseOfferSnapshot{}
   181  }
   182  
   183  func (app *KVStoreApplication) LoadSnapshotChunk(chunk abcitypes.RequestLoadSnapshotChunk) abcitypes.ResponseLoadSnapshotChunk {
   184  	return abcitypes.ResponseLoadSnapshotChunk{}
   185  }
   186  
   187  func (app *KVStoreApplication) ApplySnapshotChunk(chunk abcitypes.RequestApplySnapshotChunk) abcitypes.ResponseApplySnapshotChunk {
   188  	return abcitypes.ResponseApplySnapshotChunk{}
   189  }
   190  ```
   191  
   192  The types used here are defined in the CometBFT library and were added as a dependency
   193  to the project when you ran `go get`. If your IDE is not recognizing the types, go ahead and run the command again.
   194  
   195  ```bash
   196  go get github.com/cometbft/cometbft@v0.34.27
   197  ```
   198  
   199  Now go back to the `main.go` and modify the `main` function so it matches the following,
   200  where an instance of the `KVStoreApplication` type is created.
   201  
   202  ```go
   203  func main() {
   204      fmt.Println("Hello, CometBFT")
   205  
   206      _ = NewKVStoreApplication()
   207  }
   208  ```
   209  
   210  You can recompile and run the application now by running `go get` and `go build`, but it does
   211  not do anything.
   212  So let's revisit the code adding the logic needed to implement our minimal key/value store
   213  and to start it along with the CometBFT Service.
   214  
   215  
   216  ### 1.3.1 Add a persistent data store
   217  
   218  Our application will need to write its state out to persistent storage so that it
   219  can stop and start without losing all of its data.
   220  
   221  For this tutorial, we will use [BadgerDB](https://github.com/dgraph-io/badger), a
   222  fast embedded key-value store.
   223  
   224  First, add Badger as a dependency of your go module using the `go get` command:
   225  
   226  `go get github.com/dgraph-io/badger/v3`
   227  
   228  Next, let's update the application and its constructor to receive a handle to the database, as follows:
   229  
   230  ```go
   231  type KVStoreApplication struct {
   232  	db           *badger.DB
   233  	onGoingBlock *badger.Txn
   234  }
   235  
   236  var _ abcitypes.Application = (*KVStoreApplication)(nil)
   237  
   238  func NewKVStoreApplication(db *badger.DB) *KVStoreApplication {
   239  	return &KVStoreApplication{db: db}
   240  }
   241  ```
   242  
   243  The `onGoingBlock` keeps track of the Badger transaction that will update the application's state when a block
   244  is completed. Don't worry about it for now, we'll get to that later.
   245  
   246  Next, update the `import` stanza at the top to include the Badger library:
   247  
   248  ```go
   249  import(
   250  	"github.com/dgraph-io/badger/v3"
   251  	abcitypes "github.com/cometbft/cometbft/abci/types"
   252  )
   253  ```
   254  
   255  Finally, update the `main.go` file to invoke the updated constructor:
   256  
   257  ```go
   258  	_ = NewKVStoreApplication(nil)
   259  ```
   260  
   261  ### 1.3.2 CheckTx
   262  
   263  When CometBFT receives a new transaction from a client, or from another full node,
   264  CometBFT asks the application if the transaction is acceptable, using the `CheckTx` method.
   265  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.
   266  
   267  In our application, a transaction is a string with the form `key=value`, indicating a key and value to write to the store.
   268  
   269  The most basic validation check we can perform is to check if the transaction conforms to the `key=value` pattern.
   270  For that, let's add the following helper method to app.go:
   271  
   272  ```go
   273  func (app *KVStoreApplication) isValid(tx []byte) uint32 {
   274  	// check format
   275  	parts := bytes.Split(tx, []byte("="))
   276  	if len(parts) != 2 {
   277  		return 1
   278  	}
   279  
   280  	return 0
   281  }
   282  ```
   283  
   284  Now you can rewrite the `CheckTx` method to use the helper function:
   285  
   286  ```go
   287  func (app *KVStoreApplication) CheckTx(req abcitypes.RequestCheckTx) abcitypes.ResponseCheckTx {
   288  	code := app.isValid(req.Tx)
   289  	return abcitypes.ResponseCheckTx{Code: code}
   290  }
   291  ```
   292  
   293  While this `CheckTx` is simple and only validates that the transaction is well-formed,
   294  it is very common for `CheckTx` to make more complex use of the state of an application.
   295  For example, you may refuse to overwrite an existing value, or you can associate
   296  versions to the key/value pairs and allow the caller to specify a version to
   297  perform a conditional update.
   298  
   299  Depending on the checks and on the conditions violated, the function may return
   300  different values, but any response with a non-zero code will be considered invalid
   301  by CometBFT. Our `CheckTx` logic returns 0 to CometBFT when a transaction passes
   302  its validation checks. The specific value of the code is meaningless to CometBFT.
   303  Non-zero codes are logged by CometBFT so applications can provide more specific
   304  information on why the transaction was rejected.
   305  
   306  Note that `CheckTx` does not execute the transaction, it only verifies 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.
   307  
   308  
   309  Finally, make sure to add the bytes package to the `import` stanza at the top of `app.go`:
   310  
   311  ```go
   312  import(
   313  	"bytes"
   314  
   315  	"github.com/dgraph-io/badger/v3"
   316  	abcitypes "github.com/cometbft/cometbft/abci/types"
   317  )
   318  ```
   319  
   320  
   321  ### 1.3.3 BeginBlock -> DeliverTx -> EndBlock -> Commit
   322  
   323  When the CometBFT consensus engine has decided on the block, the block is transferred to the
   324  application over three ABCI method calls: `BeginBlock`, `DeliverTx`, and `EndBlock`.
   325  
   326  - `BeginBlock` is called once to indicate to the application that it is about to
   327  receive a block.
   328  - `DeliverTx` is called repeatedly, once for each application transaction that was included in the block.
   329  - `EndBlock` is called once to indicate to the application that no more transactions
   330  will be delivered to the application within this block.
   331  
   332  Note that, to implement these calls in our application we're going to make use of Badger's
   333  transaction mechanism. We will always refer to these as Badger transactions, not to
   334  confuse them with the transactions included in the blocks delivered by CometBFT,
   335  the _application transactions_.
   336  
   337  First, let's create a new Badger transaction during `BeginBlock`. All application transactions in the
   338  current block will be executed within this Badger transaction.
   339  Then, return informing CometBFT that the application is ready to receive application transactions:
   340  
   341  ```go
   342  func (app *KVStoreApplication) BeginBlock(req abcitypes.RequestBeginBlock) abcitypes.ResponseBeginBlock {
   343  	app.onGoingBlock = app.db.NewTransaction(true)
   344  	return abcitypes.ResponseBeginBlock{}
   345  }
   346  ```
   347  
   348  Next, let's modify `DeliverTx` to add the `key` and `value` to the database transaction every time our application
   349  receives a new application transaction through `RequestDeliverTx`.
   350  
   351  ```go
   352  func (app *KVStoreApplication) DeliverTx(req abcitypes.RequestDeliverTx) abcitypes.ResponseDeliverTx {
   353  	if code := app.isValid(req.Tx); code != 0 {
   354  		return abcitypes.ResponseDeliverTx{Code: code}
   355  	}
   356  
   357  	parts := bytes.SplitN(req.Tx, []byte("="), 2)
   358  	key, value := parts[0], parts[1]
   359  
   360  	if err := app.onGoingBlock.Set(key, value); err != nil {
   361  		log.Panicf("Error writing to database, unable to execute tx: %v", err)
   362  	}
   363  
   364  	return abcitypes.ResponseDeliverTx{Code: 0}
   365  }
   366  ```
   367  
   368  Note that we check the validity of the transaction _again_ during `DeliverTx`.
   369  Transactions are not guaranteed to be valid when they are delivered to an
   370  application, even if they were valid when they were proposed.
   371  This can happen if the application state is used to determine transaction
   372  validity. Application state may have changed between the initial execution of `CheckTx`
   373  and the transaction delivery in `DeliverTx` in a way that rendered the transaction
   374  no longer valid.
   375  
   376  `EndBlock` is called to inform the application that the full block has been delivered
   377  and give the application a chance to perform any other computation needed, before the
   378  effects of the transactions become permanent.
   379  
   380  Note that `EndBlock` **cannot** yet commit the Badger transaction we were building
   381  in during `DeliverTx`.
   382  Since other methods, such as `Query`, rely on a consistent view of the application's
   383  state, the application should only update its state by committing the Badger transactions
   384  when the full block has been delivered and the `Commit` method is invoked.
   385  
   386  The `Commit` method tells the application to make permanent the effects of
   387  the application transactions.
   388  Let's update the method to terminate the pending Badger transaction and
   389  persist the resulting state:
   390  
   391  ```go
   392  func (app *KVStoreApplication) Commit() abcitypes.ResponseCommit {
   393  	if err := app.onGoingBlock.Commit(); err != nil {
   394  		log.Panicf("Error writing to database, unable to commit block: %v", err)
   395  	}
   396  	return abcitypes.ResponseCommit{Data: []byte{}}
   397  }
   398  ```
   399  
   400  Finally, make sure to add the log library to the `import` stanza as well:
   401  
   402  ```go
   403  import (
   404  	"bytes"
   405  	"log"
   406  
   407  	"github.com/dgraph-io/badger/v3"
   408  	abcitypes "github.com/cometbft/cometbft/abci/types"
   409  )
   410  ```
   411  
   412  You may have noticed that the application we are writing will crash if it receives
   413  an unexpected error from the Badger database during the `DeliverTx` or `Commit` methods.
   414  This is not an accident. If the application received an error from the database, there
   415  is no deterministic way for it to make progress so the only safe option is to terminate.
   416  
   417  ### 1.3.4 Query
   418  
   419  When a client tries to read some information from the `kvstore`, the request will be
   420  handled in the `Query` method. To do this, let's rewrite the `Query` method in `app.go`:
   421  
   422  ```go
   423  func (app *KVStoreApplication) Query(req abcitypes.RequestQuery) abcitypes.ResponseQuery {
   424  	resp := abcitypes.ResponseQuery{Key: req.Data}
   425  
   426  	dbErr := app.db.View(func(txn *badger.Txn) error {
   427  		item, err := txn.Get(req.Data)
   428  		if err != nil {
   429  			if err != badger.ErrKeyNotFound {
   430  				return err
   431  			}
   432  			resp.Log = "key does not exist"
   433  			return nil
   434  		}
   435  
   436  		return item.Value(func(val []byte) error {
   437  			resp.Log = "exists"
   438  			resp.Value = val
   439  			return nil
   440  		})
   441  	})
   442  	if dbErr != nil {
   443  		log.Panicf("Error reading database, unable to execute query: %v", dbErr)
   444  	}
   445  	return resp
   446  }
   447  ```
   448  
   449  Since it reads only committed data from the store, transactions that are part of a block
   450  that is being processed are not reflected in the query result.
   451  
   452  
   453  ## 1.4 Starting an application and a CometBFT instance in the same process
   454  
   455  Now that we have the basic functionality of our application in place, let's put it all together inside of our main.go file.
   456  
   457  Change the contents of your `main.go` file to the following.
   458  
   459  ```go
   460  package main
   461  
   462  import (
   463  	"flag"
   464  	"fmt"
   465  	"github.com/cometbft/cometbft/p2p"
   466  	"github.com/cometbft/cometbft/privval"
   467  	"github.com/cometbft/cometbft/proxy"
   468  	"log"
   469  	"os"
   470  	"os/signal"
   471  	"path/filepath"
   472  	"syscall"
   473  
   474  	"github.com/dgraph-io/badger/v3"
   475  	"github.com/spf13/viper"
   476  	cfg "github.com/cometbft/cometbft/config"
   477  	cmtflags "github.com/cometbft/cometbft/libs/cli/flags"
   478  	cmtlog "github.com/cometbft/cometbft/libs/log"
   479  	nm "github.com/cometbft/cometbft/node"
   480  )
   481  
   482  var homeDir string
   483  
   484  func init() {
   485  	flag.StringVar(&homeDir, "cmt-home", "", "Path to the CometBFT config directory (if empty, uses $HOME/.cometbft)")
   486  }
   487  
   488  func main() {
   489  	flag.Parse()
   490  	if homeDir == "" {
   491  		homeDir = os.ExpandEnv("$HOME/.cometbft")
   492  	}
   493  	config := cfg.DefaultConfig()
   494  
   495  	config.SetRoot(homeDir)
   496  
   497  	viper.SetConfigFile(fmt.Sprintf("%s/%s", homeDir, "config/config.toml"))
   498  	if err := viper.ReadInConfig(); err != nil {
   499  		log.Fatalf("Reading config: %v", err)
   500  	}
   501  	if err := viper.Unmarshal(config); err != nil {
   502  		log.Fatalf("Decoding config: %v", err)
   503  	}
   504  	if err := config.ValidateBasic(); err != nil {
   505  		log.Fatalf("Invalid configuration data: %v", err)
   506  	}
   507  
   508  	dbPath := filepath.Join(homeDir, "badger")
   509  	db, err := badger.Open(badger.DefaultOptions(dbPath))
   510  	if err != nil {
   511  		log.Fatalf("Opening database: %v", err)
   512  	}
   513  	defer func() {
   514  		if err := db.Close(); err != nil {
   515  			log.Printf("Closing database: %v", err)
   516  		}
   517  	}()
   518  
   519  	app := NewKVStoreApplication(db)
   520  
   521  	pv := privval.LoadFilePV(
   522  		config.PrivValidatorKeyFile(),
   523  		config.PrivValidatorStateFile(),
   524  	)
   525  
   526  	nodeKey, err := p2p.LoadNodeKey(config.NodeKeyFile())
   527  	if err != nil {
   528  		log.Fatalf("failed to load node's key: %v", err)
   529  	}
   530  
   531  	logger := cmtlog.NewTMLogger(cmtlog.NewSyncWriter(os.Stdout))
   532  	logger, err = cmtflags.ParseLogLevel(config.LogLevel, logger, cfg.DefaultLogLevel)
   533  	if err != nil {
   534  		log.Fatalf("failed to parse log level: %v", err)
   535  	}
   536  
   537  	node, err := nm.NewNode(
   538  		config,
   539  		pv,
   540  		nodeKey,
   541  		proxy.NewLocalClientCreator(app),
   542  		nm.DefaultGenesisDocProviderFunc(config),
   543  		nm.DefaultDBProvider,
   544  		nm.DefaultMetricsProvider(config.Instrumentation),
   545  		logger)
   546  
   547  	if err != nil {
   548  		log.Fatalf("Creating node: %v", err)
   549  	}
   550  
   551  	node.Start()
   552  	defer func() {
   553  		node.Stop()
   554  		node.Wait()
   555  	}()
   556  
   557  	c := make(chan os.Signal, 1)
   558  	signal.Notify(c, os.Interrupt, syscall.SIGTERM)
   559  	<-c
   560  }
   561  ```
   562  
   563  This is a huge blob of code, so let's break it down into pieces.
   564  
   565  First, we use [viper](https://github.com/spf13/viper) to load the CometBFT configuration files, which we will generate later:
   566  
   567  
   568  ```go
   569  	config := cfg.DefaultValidatorConfig()
   570  
   571  	config.SetRoot(homeDir)
   572  
   573  	viper.SetConfigFile(fmt.Sprintf("%s/%s", homeDir, "config/config.toml"))
   574  	if err := viper.ReadInConfig(); err != nil {
   575  		log.Fatalf("Reading config: %v", err)
   576  	}
   577  	if err := viper.Unmarshal(config); err != nil {
   578  		log.Fatalf("Decoding config: %v", err)
   579  	}
   580  	if err := config.ValidateBasic(); err != nil {
   581  		log.Fatalf("Invalid configuration data: %v", err)
   582  	}
   583  ```
   584  
   585  Next, we initialize the Badger database and create an app instance.
   586  
   587  ```go
   588  	dbPath := filepath.Join(homeDir, "badger")
   589  	db, err := badger.Open(badger.DefaultOptions(dbPath))
   590  	if err != nil {
   591  		log.Fatalf("Opening database: %v", err)
   592  	}
   593  	defer func() {
   594  		if err := db.Close(); err != nil {
   595  			log.Fatalf("Closing database: %v", err)
   596  		}
   597  	}()
   598  
   599  	app := NewKVStoreApplication(db)
   600  ```
   601  
   602  We use `FilePV`, which is a private validator (i.e. thing which signs consensus
   603  messages). Normally, you would use `SignerRemote` to connect to an external
   604  [HSM](https://kb.certus.one/hsm.html).
   605  
   606  ```go
   607  	pv := privval.LoadFilePV(
   608  		config.PrivValidatorKeyFile(),
   609  		config.PrivValidatorStateFile(),
   610  	)
   611  ```
   612  
   613  `nodeKey` is needed to identify the node in a p2p network.
   614  
   615  ```go
   616  	nodeKey, err := p2p.LoadNodeKey(config.NodeKeyFile())
   617  	if err != nil {
   618  		return nil, fmt.Errorf("failed to load node's key: %w", err)
   619  	}
   620  ```
   621  
   622  Now we have everything set up to run the CometBFT node. We construct
   623  a node by passing it the configuration, the logger, a handle to our application and
   624  the genesis information:
   625  
   626  ```go
   627  	node, err := nm.NewNode(
   628  		config,
   629  		pv,
   630  		nodeKey,
   631  		proxy.NewLocalClientCreator(app),
   632  		nm.DefaultGenesisDocProviderFunc(config),
   633  		nm.DefaultDBProvider,
   634  		nm.DefaultMetricsProvider(config.Instrumentation),
   635  		logger)
   636  
   637  	if err != nil {
   638  		log.Fatalf("Creating node: %v", err)
   639  	}
   640  ```
   641  
   642  Finally, we start the node, i.e., the CometBFT service inside our application:
   643  
   644  ```go
   645  	node.Start()
   646  	defer func() {
   647  		node.Stop()
   648  		node.Wait()
   649  	}()
   650  ```
   651  
   652  The additional logic at the end of the file allows the program to catch SIGTERM. This means that the node can shut down gracefully when an operator tries to kill the program:
   653  
   654  ```go
   655  	c := make(chan os.Signal, 1)
   656  	signal.Notify(c, os.Interrupt, syscall.SIGTERM)
   657  	<-c
   658  ```
   659  
   660  ## 1.5 Initializing and Running
   661  
   662  Our application is almost ready to run, but first we'll need to populate the CometBFT configuration files.
   663  The following command will create a `cometbft-home` directory in your project and add a basic set of configuration files in `cometbft-home/config/`.
   664  For more information on what these files contain see [the configuration documentation](https://github.com/cometbft/cometbft/blob/v0.34.x/docs/core/configuration.md).
   665  
   666  From the root of your project, run:
   667  
   668  ```bash
   669  go run github.com/cometbft/cometbft/cmd/cometbft@v0.34.27 init --home /tmp/cometbft-home
   670  ```
   671  
   672  You should see an output similar to the following:
   673  
   674  ```bash
   675  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
   676  I[2022-11-09|09:06:34.444] Generated node key                           module=main path=/tmp/cometbft-home/config/node_key.json
   677  I[2022-11-09|09:06:34.444] Generated genesis file                       module=main path=/tmp/cometbft-home/config/genesis.json
   678  ```
   679  
   680  Now rebuild the app:
   681  
   682  ```bash
   683  go build -mod=mod # use -mod=mod to automatically refresh the dependencies
   684  ```
   685  
   686  Everything is now in place to run your application. Run:
   687  
   688  ```bash
   689  ./kvstore -cmt-home /tmp/cometbft-home
   690  ```
   691  
   692  The application will start and you should see a continuous output starting with:
   693  
   694  ```bash
   695  badger 2022/11/09 09:08:50 INFO: All 0 tables opened in 0s
   696  badger 2022/11/09 09:08:50 INFO: Discard stats nextEmptySlot: 0
   697  badger 2022/11/09 09:08:50 INFO: Set nextTxnTs to 0
   698  I[2022-11-09|09:08:50.085] service start                                module=proxy msg="Starting multiAppConn service" impl=multiAppConn
   699  I[2022-11-09|09:08:50.085] service start                                module=abci-client connection=query msg="Starting localClient service" impl=localClient
   700  I[2022-11-09|09:08:50.085] service start                                module=abci-client connection=snapshot msg="Starting localClient service" impl=localClient
   701  ...
   702  ```
   703  
   704  More importantly, the application using CometBFT is producing blocks  🎉🎉 and you can see this reflected in the log output in lines like this:
   705  
   706  ```bash
   707  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}"
   708  I[2022-11-09|09:08:52.152] received complete proposal block             module=consensus height=2 hash=F518444C0E348270436A73FD0F0B9DFEA758286BEB29482F1E3BEA75330E825C
   709  I[2022-11-09|09:08:52.160] finalizing commit of block                   module=consensus height=2 hash=F518444C0E348270436A73FD0F0B9DFEA758286BEB29482F1E3BEA75330E825C root= num_txs=0
   710  I[2022-11-09|09:08:52.167] executed block                               module=state height=2 num_valid_txs=0 num_invalid_txs=0
   711  I[2022-11-09|09:08:52.171] committed state                              module=state height=2 num_txs=0 app_hash=
   712  ```
   713  
   714  The blocks, as you can see from the `num_valid_txs=0` part, are empty, but let's remedy that next.
   715  
   716  ## 1.6 Using the application
   717  
   718  Let's try submitting a transaction to our new application.
   719  Open another terminal window and run the following curl command:
   720  
   721  
   722  ```bash
   723  curl -s 'localhost:26657/broadcast_tx_commit?tx="cometbft=rocks"'
   724  ```
   725  
   726  If everything went well, you should see a response indicating which height the
   727  transaction was included in the blockchain.
   728  
   729  Finally, let's make sure that transaction really was persisted by the application.
   730  Run the following command:
   731  
   732  ```bash
   733  curl -s 'localhost:26657/abci_query?data="cometbft"'
   734  ```
   735  
   736  Let's examine the response object that this request returns.
   737  The request returns a `json` object with a `key` and `value` field set.
   738  
   739  ```json
   740  ...
   741  	"key": "dGVuZGVybWludA==",
   742  	"value": "cm9ja3M=",
   743  ...
   744  ```
   745  
   746  Those values don't look like the `key` and `value` we sent to CometBFT.
   747  What's going on here?
   748  
   749  The response contains a `base64` encoded representation of the data we submitted.
   750  To get the original value out of this data, we can use the `base64` command line utility:
   751  
   752  ```bash
   753  echo "cm9ja3M=" | base64 -d
   754  ```
   755  
   756  ## Outro
   757  
   758  I hope everything went smoothly and your first, but hopefully not the last,
   759  CometBFT application is up and running. If not, please [open an issue on
   760  Github](https://github.com/cometbft/cometbft/issues/new/choose).