github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/sdk/baseapp_test.go (about) 1 package sdk 2 3 import ( 4 "bytes" 5 "encoding/binary" 6 "fmt" 7 "log/slog" 8 "os" 9 "reflect" 10 "testing" 11 12 "github.com/stretchr/testify/assert" 13 "github.com/stretchr/testify/require" 14 15 "github.com/gnolang/gno/tm2/pkg/amino" 16 abci "github.com/gnolang/gno/tm2/pkg/bft/abci/types" 17 bft "github.com/gnolang/gno/tm2/pkg/bft/types" 18 dbm "github.com/gnolang/gno/tm2/pkg/db" 19 "github.com/gnolang/gno/tm2/pkg/db/memdb" 20 "github.com/gnolang/gno/tm2/pkg/sdk/testutils" 21 "github.com/gnolang/gno/tm2/pkg/std" 22 "github.com/gnolang/gno/tm2/pkg/store/dbadapter" 23 "github.com/gnolang/gno/tm2/pkg/store/iavl" 24 store "github.com/gnolang/gno/tm2/pkg/store/types" 25 ) 26 27 var ( 28 baseKey = store.NewStoreKey("base") // in all test apps 29 mainKey = store.NewStoreKey("main") // in all test apps 30 ) 31 32 type ( 33 msgCounter = testutils.MsgCounter 34 msgCounter2 = testutils.MsgCounter2 35 msgNoRoute = testutils.MsgNoRoute 36 ) 37 38 const ( 39 routeMsgCounter = testutils.RouteMsgCounter 40 routeMsgCounter2 = testutils.RouteMsgCounter2 41 ) 42 43 // txInt: used as counter in incrementing counter tests, 44 // or as how much gas will be consumed in antehandler 45 // (depending on anteHandler used in tests) 46 func newTxCounter(txInt int64, msgInts ...int64) std.Tx { 47 msgs := make([]std.Msg, len(msgInts)) 48 49 for i, msgInt := range msgInts { 50 msgs[i] = msgCounter{msgInt, false} 51 } 52 53 tx := std.Tx{Msgs: msgs} 54 setCounter(&tx, txInt) 55 setFailOnHandler(&tx, false) 56 return tx 57 } 58 59 func defaultLogger() *slog.Logger { 60 logger := slog.New(slog.NewTextHandler(os.Stdout, nil)) 61 62 return logger.With("module", "sdk/app") 63 } 64 65 func newBaseApp(name string, db dbm.DB, options ...func(*BaseApp)) *BaseApp { 66 logger := defaultLogger() 67 app := NewBaseApp(name, logger, db, baseKey, mainKey, options...) 68 app.MountStoreWithDB(baseKey, dbadapter.StoreConstructor, nil) 69 app.MountStoreWithDB(mainKey, iavl.StoreConstructor, nil) 70 return app 71 } 72 73 // simple one store baseapp 74 func setupBaseApp(t *testing.T, options ...func(*BaseApp)) *BaseApp { 75 t.Helper() 76 77 db := memdb.NewMemDB() 78 app := newBaseApp(t.Name(), db, options...) 79 require.Equal(t, t.Name(), app.Name()) 80 err := app.LoadLatestVersion() 81 require.Nil(t, err) 82 return app 83 } 84 85 func TestMountStores(t *testing.T) { 86 t.Parallel() 87 88 app := setupBaseApp(t) 89 90 // check both stores 91 store1 := app.cms.GetCommitStore(baseKey) 92 require.NotNil(t, store1) 93 store2 := app.cms.GetCommitStore(mainKey) 94 require.NotNil(t, store2) 95 } 96 97 // Test that we can make commits and then reload old versions. 98 // Test that LoadLatestVersion actually does. 99 func TestLoadVersion(t *testing.T) { 100 t.Parallel() 101 102 pruningOpt := SetPruningOptions(store.PruneSyncable) 103 name := t.Name() 104 db := memdb.NewMemDB() 105 app := newBaseApp(name, db, pruningOpt) 106 107 // make a cap key and mount the store 108 err := app.LoadLatestVersion() // needed to make stores non-nil 109 require.Nil(t, err) 110 111 emptyCommitID := store.CommitID{} 112 113 // fresh store has zero/empty last commit 114 lastHeight := app.LastBlockHeight() 115 lastID := app.LastCommitID() 116 require.Equal(t, int64(0), lastHeight) 117 require.Equal(t, emptyCommitID, lastID) 118 119 // execute a block, collect commit ID 120 header := &bft.Header{ChainID: "test-chain", Height: 1} 121 app.BeginBlock(abci.RequestBeginBlock{Header: header}) 122 res := app.Commit() 123 commitID1 := store.CommitID{1, res.Data} 124 125 // execute a block, collect commit ID 126 header = &bft.Header{ChainID: "test-chain", Height: 2} 127 app.BeginBlock(abci.RequestBeginBlock{Header: header}) 128 res = app.Commit() 129 commitID2 := store.CommitID{2, res.Data} 130 131 // reload with LoadLatestVersion 132 app = newBaseApp(name, db, pruningOpt) 133 err = app.LoadLatestVersion() 134 require.Nil(t, err) 135 testLoadVersionHelper(t, app, int64(2), commitID2) 136 137 // reload with LoadVersion, see if you can commit the same block and get 138 // the same result 139 app = newBaseApp(name, db, pruningOpt) 140 err = app.LoadVersion(1) 141 require.Nil(t, err) 142 testLoadVersionHelper(t, app, int64(1), commitID1) 143 app.BeginBlock(abci.RequestBeginBlock{Header: header}) 144 app.Commit() 145 testLoadVersionHelper(t, app, int64(2), commitID2) 146 } 147 148 func TestAppVersionSetterGetter(t *testing.T) { 149 t.Parallel() 150 151 pruningOpt := SetPruningOptions(store.PruneSyncable) 152 name := t.Name() 153 db := memdb.NewMemDB() 154 app := newBaseApp(name, db, pruningOpt) 155 156 require.Equal(t, "", app.AppVersion()) 157 res := app.Query(abci.RequestQuery{Path: ".app/version"}) 158 require.True(t, res.IsOK()) 159 require.Equal(t, "", string(res.Value)) 160 161 versionString := "1.0.0" 162 app.SetAppVersion(versionString) 163 require.Equal(t, versionString, app.AppVersion()) 164 res = app.Query(abci.RequestQuery{Path: ".app/version"}) 165 require.True(t, res.IsOK()) 166 require.Equal(t, versionString, string(res.Value)) 167 } 168 169 func TestLoadVersionInvalid(t *testing.T) { 170 t.Parallel() 171 172 pruningOpt := SetPruningOptions(store.PruneSyncable) 173 name := t.Name() 174 db := memdb.NewMemDB() 175 app := newBaseApp(name, db, pruningOpt) 176 177 err := app.LoadLatestVersion() 178 require.Nil(t, err) 179 180 // require error when loading an invalid version 181 err = app.LoadVersion(-1) 182 require.Error(t, err) 183 184 header := &bft.Header{ChainID: "test-chain", Height: 1} 185 app.BeginBlock(abci.RequestBeginBlock{Header: header}) 186 res := app.Commit() 187 commitID1 := store.CommitID{1, res.Data} 188 189 // create a new app with the stores mounted under the same cap key 190 app = newBaseApp(name, db, pruningOpt) 191 192 // require we can load the latest version 193 err = app.LoadVersion(1) 194 require.Nil(t, err) 195 testLoadVersionHelper(t, app, int64(1), commitID1) 196 197 // require error when loading an invalid version 198 err = app.LoadVersion(2) 199 require.Error(t, err) 200 } 201 202 func testLoadVersionHelper(t *testing.T, app *BaseApp, expectedHeight int64, expectedID store.CommitID) { 203 t.Helper() 204 205 lastHeight := app.LastBlockHeight() 206 lastID := app.LastCommitID() 207 require.Equal(t, expectedHeight, lastHeight) 208 require.Equal(t, expectedID, lastID) 209 } 210 211 func TestOptionFunction(t *testing.T) { 212 t.Parallel() 213 214 db := memdb.NewMemDB() 215 bap := newBaseApp("starting name", db, testChangeNameHelper("new name")) 216 require.Equal(t, bap.name, "new name", "BaseApp should have had name changed via option function") 217 } 218 219 func testChangeNameHelper(name string) func(*BaseApp) { 220 return func(bap *BaseApp) { 221 bap.name = name 222 } 223 } 224 225 // Test that Info returns the latest committed state. 226 func TestInfo(t *testing.T) { 227 t.Parallel() 228 229 db := memdb.NewMemDB() 230 app := newBaseApp(t.Name(), db) 231 232 // ----- test an empty response ------- 233 reqInfo := abci.RequestInfo{} 234 res := app.Info(reqInfo) 235 236 // should be empty 237 assert.Equal(t, "", res.AppVersion) 238 assert.Equal(t, t.Name(), string(res.Data)) 239 assert.Equal(t, int64(0), res.LastBlockHeight) 240 require.Equal(t, []uint8(nil), res.LastBlockAppHash) 241 242 // ----- test a proper response ------- 243 // TODO 244 } 245 246 func TestBaseAppOptionSeal(t *testing.T) { 247 t.Parallel() 248 249 app := setupBaseApp(t) 250 251 require.Panics(t, func() { 252 app.SetName("") 253 }) 254 require.Panics(t, func() { 255 app.SetAppVersion("") 256 }) 257 require.Panics(t, func() { 258 app.SetDB(nil) 259 }) 260 require.Panics(t, func() { 261 app.SetCMS(nil) 262 }) 263 require.Panics(t, func() { 264 app.SetInitChainer(nil) 265 }) 266 require.Panics(t, func() { 267 app.SetBeginBlocker(nil) 268 }) 269 require.Panics(t, func() { 270 app.SetEndBlocker(nil) 271 }) 272 require.Panics(t, func() { 273 app.SetAnteHandler(nil) 274 }) 275 } 276 277 func TestSetMinGasPrices(t *testing.T) { 278 t.Parallel() 279 280 minGasPrices, err := ParseGasPrices("5000stake/10gas") 281 require.Nil(t, err) 282 db := memdb.NewMemDB() 283 app := newBaseApp(t.Name(), db, SetMinGasPrices("5000stake/10gas")) 284 require.Equal(t, minGasPrices, app.minGasPrices) 285 } 286 287 func TestInitChainer(t *testing.T) { 288 t.Parallel() 289 290 name := t.Name() 291 // keep the db and logger ourselves so 292 // we can reload the same app later 293 db := memdb.NewMemDB() 294 app := newBaseApp(name, db) 295 296 // set a value in the store on init chain 297 key, value := []byte("hello"), []byte("goodbye") 298 var initChainer InitChainer = func(ctx Context, req abci.RequestInitChain) abci.ResponseInitChain { 299 store := ctx.Store(mainKey) 300 store.Set(key, value) 301 return abci.ResponseInitChain{} 302 } 303 304 query := abci.RequestQuery{ 305 Path: ".store/main/key", 306 Data: key, 307 } 308 309 // initChainer is nil - nothing happens 310 app.InitChain(abci.RequestInitChain{ChainID: "test-chain"}) 311 res := app.Query(query) 312 require.Equal(t, 0, len(res.Value)) 313 314 // set initChainer and try again - should see the value 315 app.SetInitChainer(initChainer) 316 317 // stores are mounted and private members are set - sealing baseapp 318 err := app.LoadLatestVersion() // needed to make stores non-nil 319 require.Nil(t, err) 320 require.Equal(t, int64(0), app.LastBlockHeight()) 321 322 app.InitChain(abci.RequestInitChain{AppState: nil, ChainID: "test-chain-id"}) // must have valid JSON genesis file, even if empty 323 324 // assert that chainID is set correctly in InitChain 325 chainID := app.deliverState.ctx.ChainID() 326 require.Equal(t, "test-chain-id", chainID, "ChainID in deliverState not set correctly in InitChain") 327 328 chainID = app.checkState.ctx.ChainID() 329 require.Equal(t, "test-chain-id", chainID, "ChainID in checkState not set correctly in InitChain") 330 331 app.Commit() 332 res = app.Query(query) 333 require.Equal(t, int64(1), app.LastBlockHeight()) 334 require.Equal(t, value, res.Value) 335 336 // reload app 337 app = newBaseApp(name, db) 338 app.SetInitChainer(initChainer) 339 err = app.LoadLatestVersion() // needed to make stores non-nil 340 require.Nil(t, err) 341 require.Equal(t, int64(1), app.LastBlockHeight()) 342 343 // ensure we can still query after reloading 344 res = app.Query(query) 345 require.Equal(t, value, res.Value) 346 347 // commit and ensure we can still query 348 header := &bft.Header{ChainID: "test-chain", Height: app.LastBlockHeight() + 1} 349 app.BeginBlock(abci.RequestBeginBlock{Header: header}) 350 app.Commit() 351 352 res = app.Query(query) 353 require.Equal(t, value, res.Value) 354 } 355 356 type testTxData struct { 357 FailOnAnte bool 358 Counter int64 359 } 360 361 func getFailOnAnte(tx Tx) bool { 362 var testdata testTxData 363 amino.MustUnmarshalJSON([]byte(tx.Memo), &testdata) 364 return testdata.FailOnAnte 365 } 366 367 func setFailOnAnte(tx *Tx, fail bool) { 368 var testdata testTxData 369 if tx.Memo == "" { 370 tx.Memo = "{}" 371 } 372 amino.MustUnmarshalJSON([]byte(tx.Memo), &testdata) 373 testdata.FailOnAnte = fail 374 tx.Memo = string(amino.MustMarshalJSON(testdata)) 375 } 376 377 func getCounter(tx Tx) int64 { 378 var testdata testTxData 379 amino.MustUnmarshalJSON([]byte(tx.Memo), &testdata) 380 return testdata.Counter 381 } 382 383 func setCounter(tx *Tx, counter int64) { 384 var testdata testTxData 385 if tx.Memo == "" { 386 tx.Memo = "{}" 387 } 388 amino.MustUnmarshalJSON([]byte(tx.Memo), &testdata) 389 testdata.Counter = counter 390 tx.Memo = string(amino.MustMarshalJSON(testdata)) 391 } 392 393 func setFailOnHandler(tx *Tx, fail bool) { 394 for i, msg := range tx.Msgs { 395 tx.Msgs[i] = msgCounter{msg.(msgCounter).Counter, fail} 396 } 397 } 398 399 func anteHandlerTxTest(t *testing.T, capKey store.StoreKey, storeKey []byte) AnteHandler { 400 t.Helper() 401 402 return func(ctx Context, tx std.Tx, simulate bool) (newCtx Context, res Result, abort bool) { 403 store := ctx.Store(capKey) 404 if getFailOnAnte(tx) { 405 res.Error = ABCIError(std.ErrInternal("ante handler failure")) 406 return newCtx, res, true 407 } 408 409 res = incrementingCounter(t, store, storeKey, getCounter(tx)) 410 newCtx = ctx 411 return 412 } 413 } 414 415 type testHandler struct { 416 process func(Context, Msg) Result 417 query func(Context, abci.RequestQuery) abci.ResponseQuery 418 } 419 420 func (th testHandler) Process(ctx Context, msg Msg) Result { 421 return th.process(ctx, msg) 422 } 423 424 func (th testHandler) Query(ctx Context, req abci.RequestQuery) abci.ResponseQuery { 425 return th.query(ctx, req) 426 } 427 428 func newTestHandler(proc func(Context, Msg) Result) Handler { 429 return testHandler{ 430 process: proc, 431 } 432 } 433 434 type msgCounterHandler struct { 435 t *testing.T 436 capKey store.StoreKey 437 deliverKey []byte 438 } 439 440 func newMsgCounterHandler(t *testing.T, capKey store.StoreKey, deliverKey []byte) Handler { 441 t.Helper() 442 443 return msgCounterHandler{t, capKey, deliverKey} 444 } 445 446 func (mch msgCounterHandler) Process(ctx Context, msg Msg) (res Result) { 447 store := ctx.Store(mch.capKey) 448 var msgCount int64 449 switch m := msg.(type) { 450 case msgCounter: 451 if m.FailOnHandler { 452 res.Error = ABCIError(std.ErrInternal("message handler failure")) 453 return 454 } 455 msgCount = m.Counter 456 case msgCounter2: 457 msgCount = m.Counter 458 default: 459 panic(fmt.Sprint("unexpected msg type", reflect.TypeOf(msg))) 460 } 461 return incrementingCounter(mch.t, store, mch.deliverKey, msgCount) 462 } 463 464 func (mch msgCounterHandler) Query(ctx Context, req abci.RequestQuery) abci.ResponseQuery { 465 panic("should not happen") 466 } 467 468 func getIntFromStore(store store.Store, key []byte) int64 { 469 bz := store.Get(key) 470 if len(bz) == 0 { 471 return 0 472 } 473 i, err := binary.ReadVarint(bytes.NewBuffer(bz)) 474 if err != nil { 475 panic(err) 476 } 477 return i 478 } 479 480 func setIntOnStore(store store.Store, key []byte, i int64) { 481 bz := make([]byte, 8) 482 n := binary.PutVarint(bz, i) 483 store.Set(key, bz[:n]) 484 } 485 486 // check counter matches what's in store. 487 // increment and store 488 func incrementingCounter(t *testing.T, store store.Store, counterKey []byte, counter int64) (res Result) { 489 t.Helper() 490 491 storedCounter := getIntFromStore(store, counterKey) 492 require.Equal(t, storedCounter, counter) 493 setIntOnStore(store, counterKey, counter+1) 494 return 495 } 496 497 // --------------------------------------------------------------------- 498 // Tx processing - CheckTx, DeliverTx, SimulateTx. 499 // These tests use the serialized tx as input, while most others will use the 500 // Check(), Deliver(), Simulate() methods directly. 501 // Ensure that Check/Deliver/Simulate work as expected with the store. 502 503 // Test that successive CheckTx can see each others' effects 504 // on the store within a block, and that the CheckTx state 505 // gets reset to the latest committed state during Commit 506 func TestCheckTx(t *testing.T) { 507 t.Parallel() 508 509 // This ante handler reads the key and checks that the value matches the current counter. 510 // This ensures changes to the kvstore persist across successive CheckTx. 511 counterKey := []byte("counter-key") 512 513 anteOpt := func(bapp *BaseApp) { bapp.SetAnteHandler(anteHandlerTxTest(t, mainKey, counterKey)) } 514 routerOpt := func(bapp *BaseApp) { 515 // TODO: can remove this once CheckTx doesn't process msgs. 516 bapp.Router().AddRoute(routeMsgCounter, newTestHandler(func(ctx Context, msg Msg) Result { return Result{} })) 517 } 518 519 app := setupBaseApp(t, anteOpt, routerOpt) 520 521 nTxs := int64(5) 522 app.InitChain(abci.RequestInitChain{ChainID: "test-chain"}) 523 524 for i := int64(0); i < nTxs; i++ { 525 tx := newTxCounter(i, 0) 526 txBytes, err := amino.Marshal(tx) 527 require.NoError(t, err) 528 r := app.CheckTx(abci.RequestCheckTx{Tx: txBytes}) 529 assert.True(t, r.IsOK(), fmt.Sprintf("%v", r)) 530 } 531 532 checkStateStore := app.checkState.ctx.Store(mainKey) 533 storedCounter := getIntFromStore(checkStateStore, counterKey) 534 535 // Ensure AnteHandler ran 536 require.Equal(t, nTxs, storedCounter) 537 538 // If a block is committed, CheckTx state should be reset. 539 header := &bft.Header{ChainID: "test-chain", Height: 1} 540 app.BeginBlock(abci.RequestBeginBlock{Header: header}) 541 app.EndBlock(abci.RequestEndBlock{}) 542 app.Commit() 543 544 checkStateStore = app.checkState.ctx.Store(mainKey) 545 storedBytes := checkStateStore.Get(counterKey) 546 require.Nil(t, storedBytes) 547 } 548 549 // Test that successive DeliverTx can see each others' effects 550 // on the store, both within and across blocks. 551 func TestDeliverTx(t *testing.T) { 552 t.Parallel() 553 554 // test increments in the ante 555 anteKey := []byte("ante-key") 556 anteOpt := func(bapp *BaseApp) { bapp.SetAnteHandler(anteHandlerTxTest(t, mainKey, anteKey)) } 557 558 // test increments in the handler 559 deliverKey := []byte("deliver-key") 560 routerOpt := func(bapp *BaseApp) { 561 bapp.Router().AddRoute(routeMsgCounter, newMsgCounterHandler(t, mainKey, deliverKey)) 562 } 563 564 app := setupBaseApp(t, anteOpt, routerOpt) 565 app.InitChain(abci.RequestInitChain{ChainID: "test-chain"}) 566 567 nBlocks := 3 568 txPerHeight := 5 569 570 for blockN := 0; blockN < nBlocks; blockN++ { 571 header := &bft.Header{ChainID: "test-chain", Height: int64(blockN) + 1} 572 app.BeginBlock(abci.RequestBeginBlock{Header: header}) 573 574 for i := 0; i < txPerHeight; i++ { 575 counter := int64(blockN*txPerHeight + i) 576 tx := newTxCounter(counter, counter) 577 578 txBytes, err := amino.Marshal(tx) 579 require.NoError(t, err) 580 581 res := app.DeliverTx(abci.RequestDeliverTx{Tx: txBytes}) 582 require.True(t, res.IsOK(), fmt.Sprintf("%v", res)) 583 } 584 585 app.EndBlock(abci.RequestEndBlock{}) 586 app.Commit() 587 } 588 } 589 590 // One call to DeliverTx should process all the messages, in order. 591 func TestMultiMsgDeliverTx(t *testing.T) { 592 t.Parallel() 593 594 // increment the tx counter 595 anteKey := []byte("ante-key") 596 anteOpt := func(bapp *BaseApp) { bapp.SetAnteHandler(anteHandlerTxTest(t, mainKey, anteKey)) } 597 598 // increment the msg counter 599 deliverKey := []byte("deliver-key") 600 deliverKey2 := []byte("deliver-key2") 601 routerOpt := func(bapp *BaseApp) { 602 bapp.Router().AddRoute(routeMsgCounter, newMsgCounterHandler(t, mainKey, deliverKey)) 603 bapp.Router().AddRoute(routeMsgCounter2, newMsgCounterHandler(t, mainKey, deliverKey2)) 604 } 605 606 app := setupBaseApp(t, anteOpt, routerOpt) 607 608 // run a multi-msg tx 609 // with all msgs the same route 610 611 header := &bft.Header{ChainID: "test-chain", Height: 1} 612 app.BeginBlock(abci.RequestBeginBlock{Header: header}) 613 tx := newTxCounter(0, 0, 1, 2) 614 txBytes, err := amino.Marshal(tx) 615 require.NoError(t, err) 616 res := app.DeliverTx(abci.RequestDeliverTx{Tx: txBytes}) 617 require.True(t, res.IsOK(), fmt.Sprintf("%v", res)) 618 619 store := app.deliverState.ctx.Store(mainKey) 620 621 // tx counter only incremented once 622 txCounter := getIntFromStore(store, anteKey) 623 require.Equal(t, int64(1), txCounter) 624 625 // msg counter incremented three times 626 msgCounter := getIntFromStore(store, deliverKey) 627 require.Equal(t, int64(3), msgCounter) 628 629 // replace the second message with a msgCounter2 630 631 tx = newTxCounter(1, 3) 632 tx.Msgs = append(tx.Msgs, msgCounter2{0}) 633 tx.Msgs = append(tx.Msgs, msgCounter2{1}) 634 txBytes, err = amino.Marshal(tx) 635 require.NoError(t, err) 636 res = app.DeliverTx(abci.RequestDeliverTx{Tx: txBytes}) 637 require.True(t, res.IsOK(), fmt.Sprintf("%v", res)) 638 639 store = app.deliverState.ctx.Store(mainKey) 640 641 // tx counter only incremented once 642 txCounter = getIntFromStore(store, anteKey) 643 require.Equal(t, int64(2), txCounter) 644 645 // original counter increments by one 646 // new counter increments by two 647 msgCounter = getIntFromStore(store, deliverKey) 648 require.Equal(t, int64(4), msgCounter) 649 msgCounter2 := getIntFromStore(store, deliverKey2) 650 require.Equal(t, int64(2), msgCounter2) 651 } 652 653 // Simulate a transaction that uses gas to compute the gas. 654 // Simulate() and Query(".app/simulate", txBytes) should give 655 // the same results. 656 func TestSimulateTx(t *testing.T) { 657 t.Parallel() 658 659 gasConsumed := int64(5) 660 661 anteOpt := func(bapp *BaseApp) { 662 bapp.SetAnteHandler(func(ctx Context, tx Tx, simulate bool) (newCtx Context, res Result, abort bool) { 663 limit := gasConsumed 664 newCtx = ctx.WithGasMeter(store.NewGasMeter(limit)) 665 return 666 }) 667 } 668 669 routerOpt := func(bapp *BaseApp) { 670 bapp.Router().AddRoute(routeMsgCounter, newTestHandler(func(ctx Context, msg Msg) Result { 671 ctx.GasMeter().ConsumeGas(gasConsumed, "test") 672 return Result{GasUsed: ctx.GasMeter().GasConsumed()} 673 })) 674 } 675 676 app := setupBaseApp(t, anteOpt, routerOpt) 677 678 app.InitChain(abci.RequestInitChain{ChainID: "test-chain"}) 679 680 nBlocks := 3 681 for blockN := 0; blockN < nBlocks; blockN++ { 682 count := int64(blockN + 1) 683 header := &bft.Header{ChainID: "test-chain", Height: count} 684 app.BeginBlock(abci.RequestBeginBlock{Header: header}) 685 686 tx := newTxCounter(count, count) 687 txBytes, err := amino.Marshal(tx) 688 require.Nil(t, err) 689 690 // simulate a message, check gas reported 691 result := app.Simulate(txBytes, tx) 692 require.True(t, result.IsOK(), result.Log) 693 require.Equal(t, gasConsumed, result.GasUsed) 694 695 // simulate again, same result 696 result = app.Simulate(txBytes, tx) 697 require.True(t, result.IsOK(), result.Log) 698 require.Equal(t, gasConsumed, result.GasUsed) 699 700 // simulate by calling Query with encoded tx 701 query := abci.RequestQuery{ 702 Path: ".app/simulate", 703 Data: txBytes, 704 } 705 queryResult := app.Query(query) 706 require.True(t, queryResult.IsOK(), queryResult.Log) 707 708 var res Result 709 amino.MustUnmarshal(queryResult.Value, &res) 710 require.Nil(t, err, "Result unmarshalling failed") 711 require.True(t, res.IsOK(), res.Log) 712 require.Equal(t, gasConsumed, res.GasUsed, res.Log) 713 app.EndBlock(abci.RequestEndBlock{}) 714 app.Commit() 715 } 716 } 717 718 func TestRunInvalidTransaction(t *testing.T) { 719 t.Parallel() 720 721 anteOpt := func(bapp *BaseApp) { 722 bapp.SetAnteHandler(func(ctx Context, tx Tx, simulate bool) (newCtx Context, res Result, abort bool) { 723 newCtx = ctx 724 return 725 }) 726 } 727 routerOpt := func(bapp *BaseApp) { 728 bapp.Router().AddRoute(routeMsgCounter, newTestHandler(func(ctx Context, msg Msg) (res Result) { return })) 729 } 730 731 app := setupBaseApp(t, anteOpt, routerOpt) 732 733 header := &bft.Header{ChainID: "test-chain", Height: 1} 734 app.BeginBlock(abci.RequestBeginBlock{Header: header}) 735 736 // Transaction with no messages 737 { 738 emptyTx := std.Tx{} 739 err := app.Deliver(emptyTx) 740 _, ok := err.Error.(std.UnknownRequestError) 741 require.True(t, ok) 742 } 743 744 // Transaction where ValidateBasic fails 745 { 746 testCases := []struct { 747 tx std.Tx 748 fail bool 749 }{ 750 {newTxCounter(0, 0), false}, 751 {newTxCounter(-1, 0), false}, 752 {newTxCounter(100, 100), false}, 753 {newTxCounter(100, 5, 4, 3, 2, 1), false}, 754 755 {newTxCounter(0, -1), true}, 756 {newTxCounter(0, 1, -2), true}, 757 {newTxCounter(0, 1, 2, -10, 5), true}, 758 } 759 760 for _, testCase := range testCases { 761 tx := testCase.tx 762 res := app.Deliver(tx) 763 if testCase.fail { 764 _, ok := res.Error.(std.InvalidSequenceError) 765 require.True(t, ok) 766 } else { 767 require.True(t, res.IsOK(), fmt.Sprintf("%v", res)) 768 } 769 } 770 } 771 772 // Transaction with no known route 773 { 774 unknownRouteTx := std.Tx{Msgs: []Msg{msgNoRoute{}}} 775 err := app.Deliver(unknownRouteTx) 776 _, ok := err.Error.(std.UnknownRequestError) 777 require.True(t, ok) 778 779 unknownRouteTx = std.Tx{Msgs: []Msg{msgCounter{}, msgNoRoute{}}} 780 err = app.Deliver(unknownRouteTx) 781 _, ok = err.Error.(std.UnknownRequestError) 782 require.True(t, ok) 783 } 784 785 // Transaction with an unregistered message 786 { 787 txBytes := []byte{0xFF, 0xFF, 0xFF} 788 res := app.DeliverTx(abci.RequestDeliverTx{Tx: txBytes}) 789 _, ok := res.Error.(std.TxDecodeError) 790 require.True(t, ok) 791 } 792 } 793 794 // Test that transactions exceeding gas limits fail 795 func TestTxGasLimits(t *testing.T) { 796 t.Parallel() 797 798 gasGranted := int64(10) 799 anteOpt := func(bapp *BaseApp) { 800 bapp.SetAnteHandler(func(ctx Context, tx Tx, simulate bool) (newCtx Context, res Result, abort bool) { 801 gmeter := store.NewPassthroughGasMeter( 802 ctx.GasMeter(), 803 gasGranted, 804 ) 805 newCtx = ctx.WithGasMeter(gmeter) 806 807 count := getCounter(tx) 808 newCtx.GasMeter().ConsumeGas(count, "counter-ante") 809 res = Result{ 810 GasWanted: gasGranted, 811 } 812 return 813 }) 814 } 815 816 routerOpt := func(bapp *BaseApp) { 817 bapp.Router().AddRoute(routeMsgCounter, newTestHandler(func(ctx Context, msg Msg) Result { 818 count := msg.(msgCounter).Counter 819 ctx.GasMeter().ConsumeGas(count, "counter-handler") 820 return Result{} 821 })) 822 } 823 824 app := setupBaseApp(t, anteOpt, routerOpt) 825 826 header := &bft.Header{ChainID: "test-chain", Height: 1} 827 app.BeginBlock(abci.RequestBeginBlock{Header: header}) 828 829 testCases := []struct { 830 tx std.Tx 831 gasUsed int64 832 fail bool 833 }{ 834 {newTxCounter(0, 0), 0, false}, 835 {newTxCounter(1, 1), 2, false}, 836 {newTxCounter(9, 1), 10, false}, 837 {newTxCounter(1, 9), 10, false}, 838 {newTxCounter(10, 0), 10, false}, 839 {newTxCounter(0, 10), 10, false}, 840 {newTxCounter(0, 8, 2), 10, false}, 841 {newTxCounter(0, 5, 1, 1, 1, 1, 1), 10, false}, 842 {newTxCounter(0, 5, 1, 1, 1, 1), 9, false}, 843 844 {newTxCounter(9, 2), 11, true}, 845 {newTxCounter(2, 9), 11, true}, 846 {newTxCounter(9, 1, 1), 11, true}, 847 {newTxCounter(1, 8, 1, 1), 11, true}, 848 {newTxCounter(11, 0), 11, true}, 849 {newTxCounter(0, 11), 11, true}, 850 {newTxCounter(0, 5, 11), 16, true}, 851 } 852 853 for i, tc := range testCases { 854 tx := tc.tx 855 res := app.Deliver(tx) 856 857 // check gas used and wanted 858 require.Equal(t, tc.gasUsed, res.GasUsed, fmt.Sprintf("%d: %v, %v", i, tc, res)) 859 860 // check for out of gas 861 if !tc.fail { 862 require.True(t, res.IsOK(), fmt.Sprintf("%d: %v, %v", i, tc, res)) 863 } else { 864 _, ok := res.Error.(std.OutOfGasError) 865 require.True(t, ok, fmt.Sprintf("%d: %v, %v", i, tc, res)) 866 } 867 } 868 } 869 870 // Test that transactions exceeding gas limits fail 871 func TestMaxBlockGasLimits(t *testing.T) { 872 t.Parallel() 873 874 gasGranted := int64(10) 875 anteOpt := func(bapp *BaseApp) { 876 bapp.SetAnteHandler(func(ctx Context, tx Tx, simulate bool) (newCtx Context, res Result, abort bool) { 877 gmeter := store.NewPassthroughGasMeter( 878 ctx.GasMeter(), 879 gasGranted, 880 ) 881 newCtx = ctx.WithGasMeter(gmeter) 882 883 count := getCounter(tx) 884 newCtx.GasMeter().ConsumeGas(count, "counter-ante") 885 res = Result{ 886 GasWanted: gasGranted, 887 } 888 return 889 }) 890 } 891 892 routerOpt := func(bapp *BaseApp) { 893 bapp.Router().AddRoute(routeMsgCounter, newTestHandler(func(ctx Context, msg Msg) Result { 894 count := msg.(msgCounter).Counter 895 ctx.GasMeter().ConsumeGas(count, "counter-handler") 896 return Result{} 897 })) 898 } 899 900 app := setupBaseApp(t, anteOpt, routerOpt) 901 app.InitChain(abci.RequestInitChain{ 902 ChainID: "test-chain", 903 ConsensusParams: &abci.ConsensusParams{ 904 Block: &abci.BlockParams{ 905 MaxGas: 100, 906 }, 907 }, 908 }) 909 910 testCases := []struct { 911 tx std.Tx 912 numDelivers int 913 gasUsedPerDeliver int64 914 fail bool 915 failAfterDeliver int 916 }{ 917 {newTxCounter(0, 0), 0, 0, false, 0}, 918 {newTxCounter(9, 1), 2, 10, false, 0}, 919 {newTxCounter(10, 0), 3, 10, false, 0}, 920 {newTxCounter(10, 0), 10, 10, false, 0}, 921 {newTxCounter(2, 7), 11, 9, false, 0}, 922 {newTxCounter(10, 0), 10, 10, false, 0}, // hit the limit but pass 923 924 {newTxCounter(10, 0), 11, 10, true, 10}, 925 {newTxCounter(10, 0), 15, 10, true, 10}, 926 {newTxCounter(9, 0), 12, 9, true, 11}, // fly past the limit 927 } 928 929 for i, tc := range testCases { 930 fmt.Printf("debug i: %v\n", i) 931 tx := tc.tx 932 933 // reset the block gas 934 header := &bft.Header{ChainID: "test-chain", Height: app.LastBlockHeight() + 1} 935 app.BeginBlock(abci.RequestBeginBlock{Header: header}) 936 937 // execute the transaction multiple times 938 for j := 0; j < tc.numDelivers; j++ { 939 res := app.Deliver(tx) 940 941 ctx := app.getState(RunTxModeDeliver).ctx 942 blockGasUsed := ctx.BlockGasMeter().GasConsumed() 943 944 // check for failed transactions 945 if tc.fail && (j+1) > tc.failAfterDeliver { 946 _, ok := res.Error.(std.OutOfGasError) 947 require.True(t, ok, fmt.Sprintf("%d: %v, %v", i, tc, res)) 948 require.True(t, ctx.BlockGasMeter().IsOutOfGas()) 949 } else { 950 // check gas used and wanted 951 expBlockGasUsed := tc.gasUsedPerDeliver * int64(j+1) 952 require.Equal(t, expBlockGasUsed, blockGasUsed, 953 fmt.Sprintf("%d,%d: %v, %v, %v, %v", i, j, tc, expBlockGasUsed, blockGasUsed, res)) 954 955 require.True(t, res.IsOK(), fmt.Sprintf("%d,%d: %v, %v", i, j, tc, res)) 956 require.False(t, ctx.BlockGasMeter().IsPastLimit()) 957 } 958 } 959 } 960 } 961 962 func TestBaseAppAnteHandler(t *testing.T) { 963 t.Parallel() 964 965 anteKey := []byte("ante-key") 966 anteOpt := func(bapp *BaseApp) { 967 bapp.SetAnteHandler(anteHandlerTxTest(t, mainKey, anteKey)) 968 } 969 970 deliverKey := []byte("deliver-key") 971 routerOpt := func(bapp *BaseApp) { 972 bapp.Router().AddRoute(routeMsgCounter, newMsgCounterHandler(t, mainKey, deliverKey)) 973 } 974 975 app := setupBaseApp(t, anteOpt, routerOpt) 976 977 app.InitChain(abci.RequestInitChain{ChainID: "test-chain"}) 978 979 header := &bft.Header{ChainID: "test-chain", Height: app.LastBlockHeight() + 1} 980 app.BeginBlock(abci.RequestBeginBlock{Header: header}) 981 982 // execute a tx that will fail ante handler execution 983 // 984 // NOTE: State should not be mutated here. This will be implicitly checked by 985 // the next txs ante handler execution (anteHandlerTxTest). 986 tx := newTxCounter(0, 0) 987 setFailOnAnte(&tx, true) 988 txBytes, err := amino.Marshal(tx) 989 require.NoError(t, err) 990 res := app.DeliverTx(abci.RequestDeliverTx{Tx: txBytes}) 991 require.False(t, res.IsOK(), fmt.Sprintf("%v", res)) 992 993 ctx := app.getState(RunTxModeDeliver).ctx 994 store := ctx.Store(mainKey) 995 require.Equal(t, int64(0), getIntFromStore(store, anteKey)) 996 997 // execute at tx that will pass the ante handler (the checkTx state should 998 // mutate) but will fail the message handler 999 tx = newTxCounter(0, 0) 1000 setFailOnHandler(&tx, true) 1001 1002 txBytes, err = amino.Marshal(tx) 1003 require.NoError(t, err) 1004 1005 res = app.DeliverTx(abci.RequestDeliverTx{Tx: txBytes}) 1006 require.False(t, res.IsOK(), fmt.Sprintf("%v", res)) 1007 1008 ctx = app.getState(RunTxModeDeliver).ctx 1009 store = ctx.Store(mainKey) 1010 require.Equal(t, int64(1), getIntFromStore(store, anteKey)) 1011 require.Equal(t, int64(0), getIntFromStore(store, deliverKey)) 1012 1013 // execute a successful ante handler and message execution where state is 1014 // implicitly checked by previous tx executions 1015 tx = newTxCounter(1, 0) 1016 1017 txBytes, err = amino.Marshal(tx) 1018 require.NoError(t, err) 1019 1020 res = app.DeliverTx(abci.RequestDeliverTx{Tx: txBytes}) 1021 require.True(t, res.IsOK(), fmt.Sprintf("%v", res)) 1022 1023 ctx = app.getState(RunTxModeDeliver).ctx 1024 store = ctx.Store(mainKey) 1025 require.Equal(t, int64(2), getIntFromStore(store, anteKey)) 1026 require.Equal(t, int64(1), getIntFromStore(store, deliverKey)) 1027 1028 // commit 1029 app.EndBlock(abci.RequestEndBlock{}) 1030 app.Commit() 1031 } 1032 1033 func TestGasConsumptionBadTx(t *testing.T) { 1034 t.Parallel() 1035 1036 gasWanted := int64(5) 1037 anteOpt := func(bapp *BaseApp) { 1038 bapp.SetAnteHandler(func(ctx Context, tx Tx, simulate bool) (newCtx Context, res Result, abort bool) { 1039 gmeter := store.NewPassthroughGasMeter( 1040 ctx.GasMeter(), 1041 gasWanted, 1042 ) 1043 newCtx = ctx.WithGasMeter(gmeter) 1044 1045 newCtx.GasMeter().ConsumeGas(getCounter(tx), "counter-ante") 1046 if getFailOnAnte(tx) { 1047 res.Error = ABCIError(std.ErrInternal("ante handler failure")) 1048 return newCtx, res, true 1049 } 1050 1051 res = Result{ 1052 GasWanted: gasWanted, 1053 } 1054 return 1055 }) 1056 } 1057 1058 routerOpt := func(bapp *BaseApp) { 1059 bapp.Router().AddRoute(routeMsgCounter, newTestHandler(func(ctx Context, msg Msg) Result { 1060 count := msg.(msgCounter).Counter 1061 ctx.GasMeter().ConsumeGas(count, "counter-handler") 1062 return Result{} 1063 })) 1064 } 1065 1066 app := setupBaseApp(t, anteOpt, routerOpt) 1067 app.InitChain(abci.RequestInitChain{ 1068 ChainID: "test-chain", 1069 ConsensusParams: &abci.ConsensusParams{ 1070 Block: &abci.BlockParams{ 1071 MaxGas: 9, 1072 }, 1073 }, 1074 }) 1075 // app.InitChain(abci.RequestInitChain{ChainID: "test-chain"}) 1076 1077 header := &bft.Header{ChainID: "test-chain", Height: app.LastBlockHeight() + 1} 1078 app.BeginBlock(abci.RequestBeginBlock{Header: header}) 1079 1080 tx := newTxCounter(5, 0) 1081 setFailOnAnte(&tx, true) 1082 txBytes, err := amino.Marshal(tx) 1083 require.NoError(t, err) 1084 1085 res := app.DeliverTx(abci.RequestDeliverTx{Tx: txBytes}) 1086 require.False(t, res.IsOK(), fmt.Sprintf("%v", res)) 1087 1088 // require next tx to fail due to black gas limit 1089 tx = newTxCounter(5, 0) 1090 txBytes, err = amino.Marshal(tx) 1091 require.NoError(t, err) 1092 1093 res = app.DeliverTx(abci.RequestDeliverTx{Tx: txBytes}) 1094 require.False(t, res.IsOK(), fmt.Sprintf("%v", res)) 1095 } 1096 1097 // Test that we can only query from the latest committed state. 1098 func TestQuery(t *testing.T) { 1099 t.Parallel() 1100 1101 key, value := []byte("hello"), []byte("goodbye") 1102 anteOpt := func(bapp *BaseApp) { 1103 bapp.SetAnteHandler(func(ctx Context, tx Tx, simulate bool) (newCtx Context, res Result, abort bool) { 1104 newCtx = ctx 1105 store := ctx.Store(mainKey) 1106 store.Set(key, value) 1107 return 1108 }) 1109 } 1110 1111 routerOpt := func(bapp *BaseApp) { 1112 bapp.Router().AddRoute(routeMsgCounter, newTestHandler(func(ctx Context, msg Msg) Result { 1113 store := ctx.Store(mainKey) 1114 store.Set(key, value) 1115 return Result{} 1116 })) 1117 } 1118 1119 app := setupBaseApp(t, anteOpt, routerOpt) 1120 1121 app.InitChain(abci.RequestInitChain{ChainID: "test-chain"}) 1122 1123 // NOTE: "/store/main" tells us Store 1124 // and the final "/key" says to use the data as the 1125 // key in the given Store ... 1126 query := abci.RequestQuery{ 1127 Path: ".store/main/key", 1128 Data: key, 1129 } 1130 tx := newTxCounter(0, 0) 1131 1132 // query is empty before we do anything 1133 res := app.Query(query) 1134 require.Equal(t, 0, len(res.Value)) 1135 1136 // query is still empty after a CheckTx 1137 resTx := app.Check(tx) 1138 require.True(t, resTx.IsOK(), fmt.Sprintf("%v", resTx)) 1139 res = app.Query(query) 1140 require.Equal(t, 0, len(res.Value)) 1141 1142 // query is still empty after a DeliverTx before we commit 1143 header := &bft.Header{ChainID: "test-chain", Height: app.LastBlockHeight() + 1} 1144 app.BeginBlock(abci.RequestBeginBlock{Header: header}) 1145 1146 resTx = app.Deliver(tx) 1147 require.True(t, resTx.IsOK(), fmt.Sprintf("%v", resTx)) 1148 res = app.Query(query) 1149 require.Equal(t, 0, len(res.Value)) 1150 1151 // query returns correct value after Commit 1152 app.Commit() 1153 res = app.Query(query) 1154 require.Equal(t, value, res.Value) 1155 } 1156 1157 func TestGetMaximumBlockGas(t *testing.T) { 1158 app := setupBaseApp(t) 1159 1160 app.setConsensusParams(&abci.ConsensusParams{Block: &abci.BlockParams{MaxGas: 0}}) 1161 require.Equal(t, int64(0), app.getMaximumBlockGas()) 1162 1163 app.setConsensusParams(&abci.ConsensusParams{Block: &abci.BlockParams{MaxGas: -1}}) 1164 require.Equal(t, int64(0), app.getMaximumBlockGas()) 1165 1166 app.setConsensusParams(&abci.ConsensusParams{Block: &abci.BlockParams{MaxGas: 5000000}}) 1167 require.Equal(t, int64(5000000), app.getMaximumBlockGas()) 1168 1169 app.setConsensusParams(&abci.ConsensusParams{Block: &abci.BlockParams{MaxGas: -5000000}}) 1170 require.Panics(t, func() { app.getMaximumBlockGas() }) 1171 }