github.com/badrootd/nibiru-cometbft@v0.37.5-0.20240307173500-2a75559eee9b/docs/guides/go-built-in.md (about)

     1  ---
     2  order: 2
     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 the latest version of
    85  CometBFT, `v0.37.0` in this example.
    86  
    87  ```bash
    88  go mod init kvstore
    89  go get github.com/cometbft/cometbft@v0.37.0
    90  ```
    91  
    92  After running the above commands you will see two generated files, `go.mod` and `go.sum`.
    93  The go.mod file should look similar to:
    94  
    95  ```go
    96  module github.com/me/example
    97  
    98  go 1.20
    99  
   100  require (
   101  	github.com/cometbft/cometbft v0.37.0
   102  )
   103  ```
   104  
   105  As you write the kvstore application, you can rebuild the binary by
   106  pulling any new dependencies and recompiling it.
   107  
   108  ```sh
   109  go get
   110  go build
   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  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 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 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 in the same process
   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  	"github.com/cometbft/cometbft/p2p"
   505  	"github.com/cometbft/cometbft/privval"
   506  	"github.com/cometbft/cometbft/proxy"
   507  	"log"
   508  	"os"
   509  	"os/signal"
   510  	"path/filepath"
   511  	"syscall"
   512  
   513  	"github.com/dgraph-io/badger/v3"
   514  	"github.com/spf13/viper"
   515  	cfg "github.com/cometbft/cometbft/config"
   516  	cmtflags "github.com/cometbft/cometbft/libs/cli/flags"
   517  	cmtlog "github.com/cometbft/cometbft/libs/log"
   518  	nm "github.com/cometbft/cometbft/node"
   519  )
   520  
   521  var homeDir string
   522  
   523  func init() {
   524  	flag.StringVar(&homeDir, "cmt-home", "", "Path to the CometBFT config directory (if empty, uses $HOME/.cometbft)")
   525  }
   526  
   527  func main() {
   528  	flag.Parse()
   529  	if homeDir == "" {
   530  		homeDir = os.ExpandEnv("$HOME/.cometbft")
   531  	}
   532  	config := cfg.DefaultConfig()
   533  
   534  	config.SetRoot(homeDir)
   535  
   536  	viper.SetConfigFile(fmt.Sprintf("%s/%s", homeDir, "config/config.toml"))
   537  	if err := viper.ReadInConfig(); err != nil {
   538  		log.Fatalf("Reading config: %v", err)
   539  	}
   540  	if err := viper.Unmarshal(config); err != nil {
   541  		log.Fatalf("Decoding config: %v", err)
   542  	}
   543  	if err := config.ValidateBasic(); err != nil {
   544  		log.Fatalf("Invalid configuration data: %v", err)
   545  	}
   546  
   547  	dbPath := filepath.Join(homeDir, "badger")
   548  	db, err := badger.Open(badger.DefaultOptions(dbPath))
   549  	if err != nil {
   550  		log.Fatalf("Opening database: %v", err)
   551  	}
   552  	defer func() {
   553  		if err := db.Close(); err != nil {
   554  			log.Printf("Closing database: %v", err)
   555  		}
   556  	}()
   557  
   558  	app := NewKVStoreApplication(db)
   559  
   560  	pv := privval.LoadFilePV(
   561  		config.PrivValidatorKeyFile(),
   562  		config.PrivValidatorStateFile(),
   563  	)
   564  
   565  	nodeKey, err := p2p.LoadNodeKey(config.NodeKeyFile())
   566  	if err != nil {
   567  		log.Fatalf("failed to load node's key: %v", err)
   568  	}
   569  
   570  	logger := cmtlog.NewTMLogger(cmtlog.NewSyncWriter(os.Stdout))
   571  	logger, err = cmtflags.ParseLogLevel(config.LogLevel, logger, cfg.DefaultLogLevel)
   572  	if err != nil {
   573  		log.Fatalf("failed to parse log level: %v", err)
   574  	}
   575  
   576  	node, err := nm.NewNode(
   577  		config,
   578  		pv,
   579  		nodeKey,
   580  		proxy.NewLocalClientCreator(app),
   581  		nm.DefaultGenesisDocProviderFunc(config),
   582  		nm.DefaultDBProvider,
   583  		nm.DefaultMetricsProvider(config.Instrumentation),
   584  		logger)
   585  
   586  	if err != nil {
   587  		log.Fatalf("Creating node: %v", err)
   588  	}
   589  
   590  	node.Start()
   591  	defer func() {
   592  		node.Stop()
   593  		node.Wait()
   594  	}()
   595  
   596  	c := make(chan os.Signal, 1)
   597  	signal.Notify(c, os.Interrupt, syscall.SIGTERM)
   598  	<-c
   599  }
   600  ```
   601  
   602  This is a huge blob of code, so let's break it down into pieces.
   603  
   604  First, we use [viper](https://github.com/spf13/viper) to load the CometBFT configuration files, which we will generate later:
   605  
   606  
   607  ```go
   608  	config := cfg.DefaultValidatorConfig()
   609  
   610  	config.SetRoot(homeDir)
   611  
   612  	viper.SetConfigFile(fmt.Sprintf("%s/%s", homeDir, "config/config.toml"))
   613  	if err := viper.ReadInConfig(); err != nil {
   614  		log.Fatalf("Reading config: %v", err)
   615  	}
   616  	if err := viper.Unmarshal(config); err != nil {
   617  		log.Fatalf("Decoding config: %v", err)
   618  	}
   619  	if err := config.ValidateBasic(); err != nil {
   620  		log.Fatalf("Invalid configuration data: %v", err)
   621  	}
   622  ```
   623  
   624  Next, we initialize the Badger database and create an app instance.
   625  
   626  ```go
   627  	dbPath := filepath.Join(homeDir, "badger")
   628  	db, err := badger.Open(badger.DefaultOptions(dbPath))
   629  	if err != nil {
   630  		log.Fatalf("Opening database: %v", err)
   631  	}
   632  	defer func() {
   633  		if err := db.Close(); err != nil {
   634  			log.Fatalf("Closing database: %v", err)
   635  		}
   636  	}()
   637  
   638  	app := NewKVStoreApplication(db)
   639  ```
   640  
   641  We use `FilePV`, which is a private validator (i.e. thing which signs consensus
   642  messages). Normally, you would use `SignerRemote` to connect to an external
   643  [HSM](https://kb.certus.one/hsm.html).
   644  
   645  ```go
   646  	pv := privval.LoadFilePV(
   647  		config.PrivValidatorKeyFile(),
   648  		config.PrivValidatorStateFile(),
   649  	)
   650  ```
   651  
   652  `nodeKey` is needed to identify the node in a p2p network.
   653  
   654  ```go
   655  	nodeKey, err := p2p.LoadNodeKey(config.NodeKeyFile())
   656  	if err != nil {
   657  		return nil, fmt.Errorf("failed to load node's key: %w", err)
   658  	}
   659  ```
   660  
   661  Now we have everything set up to run the CometBFT node. We construct
   662  a node by passing it the configuration, the logger, a handle to our application and
   663  the genesis information:
   664  
   665  ```go
   666  	node, err := nm.NewNode(
   667  		config,
   668  		pv,
   669  		nodeKey,
   670  		proxy.NewLocalClientCreator(app),
   671  		nm.DefaultGenesisDocProviderFunc(config),
   672  		nm.DefaultDBProvider,
   673  		nm.DefaultMetricsProvider(config.Instrumentation),
   674  		logger)
   675  
   676  	if err != nil {
   677  		log.Fatalf("Creating node: %v", err)
   678  	}
   679  ```
   680  
   681  Finally, we start the node, i.e., the CometBFT service inside our application:
   682  
   683  ```go
   684  	node.Start()
   685  	defer func() {
   686  		node.Stop()
   687  		node.Wait()
   688  	}()
   689  ```
   690  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:
   691  
   692  ```go
   693  	c := make(chan os.Signal, 1)
   694  	signal.Notify(c, os.Interrupt, syscall.SIGTERM)
   695  	<-c
   696  ```
   697  
   698  ## 1.5 Initializing and Running
   699  
   700  Our application is almost ready to run, but first we'll need to populate the CometBFT configuration files.
   701  The following command will create a `cometbft-home` directory in your project and add a basic set of configuration files in `cometbft-home/config/`.
   702  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).
   703  
   704  From the root of your project, run:
   705  
   706  ```bash
   707  go run github.com/cometbft/cometbft/cmd/cometbft@v0.37.0 init --home /tmp/cometbft-home
   708  ```
   709  
   710  You should see an output similar to the following:
   711  
   712  ```bash
   713  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
   714  I[2022-11-09|09:06:34.444] Generated node key                           module=main path=/tmp/cometbft-home/config/node_key.json
   715  I[2022-11-09|09:06:34.444] Generated genesis file                       module=main path=/tmp/cometbft-home/config/genesis.json
   716  ```
   717  
   718  Now rebuild the app:
   719  
   720  ```bash
   721  go build -mod=mod # use -mod=mod to automatically refresh the dependencies
   722  ```
   723  
   724  Everything is now in place to run your application. Run:
   725  
   726  ```bash
   727  ./kvstore -cmt-home /tmp/cometbft-home
   728  ```
   729  
   730  The application will start and you should see a continuous output starting with:
   731  
   732  ```bash
   733  badger 2022/11/09 09:08:50 INFO: All 0 tables opened in 0s
   734  badger 2022/11/09 09:08:50 INFO: Discard stats nextEmptySlot: 0
   735  badger 2022/11/09 09:08:50 INFO: Set nextTxnTs to 0
   736  I[2022-11-09|09:08:50.085] service start                                module=proxy msg="Starting multiAppConn service" impl=multiAppConn
   737  I[2022-11-09|09:08:50.085] service start                                module=abci-client connection=query msg="Starting localClient service" impl=localClient
   738  I[2022-11-09|09:08:50.085] service start                                module=abci-client connection=snapshot msg="Starting localClient service" impl=localClient
   739  ...
   740  ```
   741  
   742  More importantly, the application using CometBFT is producing blocks  🎉🎉 and you can see this reflected in the log output in lines like this:
   743  
   744  ```bash
   745  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}"
   746  I[2022-11-09|09:08:52.152] received complete proposal block             module=consensus height=2 hash=F518444C0E348270436A73FD0F0B9DFEA758286BEB29482F1E3BEA75330E825C
   747  I[2022-11-09|09:08:52.160] finalizing commit of block                   module=consensus height=2 hash=F518444C0E348270436A73FD0F0B9DFEA758286BEB29482F1E3BEA75330E825C root= num_txs=0
   748  I[2022-11-09|09:08:52.167] executed block                               module=state height=2 num_valid_txs=0 num_invalid_txs=0
   749  I[2022-11-09|09:08:52.171] committed state                              module=state height=2 num_txs=0 app_hash=
   750  ```
   751  
   752  The blocks, as you can see from the `num_valid_txs=0` part, are empty, but let's remedy that next.
   753  
   754  ## 1.6 Using the application
   755  
   756  Let's try submitting a transaction to our new application.
   757  Open another terminal window and run the following curl command:
   758  
   759  
   760  ```bash
   761  curl -s 'localhost:26657/broadcast_tx_commit?tx="cometbft=rocks"'
   762  ```
   763  
   764  If everything went well, you should see a response indicating which height the
   765  transaction was included in the blockchain.
   766  
   767  Finally, let's make sure that transaction really was persisted by the application.
   768  Run the following command:
   769  
   770  ```bash
   771  curl -s 'localhost:26657/abci_query?data="cometbft"'
   772  ```
   773  
   774  Let's examine the response object that this request returns.
   775  The request returns a `json` object with a `key` and `value` field set.
   776  
   777  ```json
   778  ...
   779  	"key": "dGVuZGVybWludA==",
   780  	"value": "cm9ja3M=",
   781  ...
   782  ```
   783  
   784  Those values don't look like the `key` and `value` we sent to CometBFT.
   785  What's going on here?
   786  
   787  The response contains a `base64` encoded representation of the data we submitted.
   788  To get the original value out of this data, we can use the `base64` command line utility:
   789  
   790  ```bash
   791  echo "cm9ja3M=" | base64 -d
   792  ```
   793  
   794  ## Outro
   795  
   796  I hope everything went smoothly and your first, but hopefully not the last,
   797  CometBFT application is up and running. If not, please [open an issue on
   798  Github](https://github.com/cometbft/cometbft/issues/new/choose).