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).