github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/bft/rpc/core/tx_test.go (about) 1 package core 2 3 import ( 4 "testing" 5 6 "github.com/gnolang/gno/tm2/pkg/amino" 7 abci "github.com/gnolang/gno/tm2/pkg/bft/abci/types" 8 "github.com/gnolang/gno/tm2/pkg/bft/state" 9 "github.com/gnolang/gno/tm2/pkg/bft/types" 10 "github.com/gnolang/gno/tm2/pkg/db/memdb" 11 "github.com/gnolang/gno/tm2/pkg/std" 12 "github.com/stretchr/testify/assert" 13 "github.com/stretchr/testify/require" 14 ) 15 16 func TestTxHandler(t *testing.T) { 17 // Tests are not run in parallel because the JSON-RPC 18 // handlers utilize global package-level variables, 19 // that are not friendly with concurrent test runs (or anything, really) 20 t.Run("tx result generated", func(t *testing.T) { 21 var ( 22 height = int64(10) 23 24 stdTx = &std.Tx{ 25 Memo: "example tx", 26 } 27 28 txResultIndex = state.TxResultIndex{ 29 BlockNum: height, 30 TxIndex: 0, 31 } 32 33 responses = &state.ABCIResponses{ 34 DeliverTxs: []abci.ResponseDeliverTx{ 35 { 36 GasWanted: 100, 37 }, 38 }, 39 } 40 ) 41 42 // Prepare the transaction 43 marshalledTx, err := amino.Marshal(stdTx) 44 require.NoError(t, err) 45 46 tx := types.Tx(marshalledTx) 47 48 // Prepare the DB 49 sdb := memdb.NewMemDB() 50 51 // Save the result index to the DB 52 sdb.Set(state.CalcTxResultKey(tx.Hash()), txResultIndex.Bytes()) 53 54 // Save the ABCI response to the DB 55 sdb.Set(state.CalcABCIResponsesKey(height), responses.Bytes()) 56 57 // Set the GLOBALLY referenced db 58 SetStateDB(sdb) 59 60 // Set the GLOBALLY referenced blockstore 61 blockStore := &mockBlockStore{ 62 heightFn: func() int64 { 63 return height 64 }, 65 loadBlockFn: func(h int64) *types.Block { 66 require.Equal(t, height, h) 67 68 return &types.Block{ 69 Data: types.Data{ 70 Txs: []types.Tx{ 71 tx, 72 }, 73 }, 74 } 75 }, 76 } 77 78 SetBlockStore(blockStore) 79 80 // Load the result 81 loadedTxResult, err := Tx(nil, tx.Hash()) 82 83 require.NoError(t, err) 84 require.NotNil(t, loadedTxResult) 85 86 // Compare the result 87 assert.Equal(t, txResultIndex.BlockNum, loadedTxResult.Height) 88 assert.Equal(t, txResultIndex.TxIndex, loadedTxResult.Index) 89 assert.Equal(t, responses.DeliverTxs[0], loadedTxResult.TxResult) 90 assert.Equal(t, tx, loadedTxResult.Tx) 91 assert.Equal(t, tx.Hash(), loadedTxResult.Tx.Hash()) 92 }) 93 94 t.Run("tx result index not found", func(t *testing.T) { 95 var ( 96 sdb = memdb.NewMemDB() 97 hash = []byte("hash") 98 expectedErr = state.NoTxResultForHashError{ 99 Hash: hash, 100 } 101 ) 102 103 // Set the GLOBALLY referenced db 104 SetStateDB(sdb) 105 106 // Load the result 107 loadedTxResult, err := Tx(nil, hash) 108 require.Nil(t, loadedTxResult) 109 110 assert.Equal(t, expectedErr, err) 111 }) 112 113 t.Run("invalid block transaction index", func(t *testing.T) { 114 var ( 115 height = int64(10) 116 117 stdTx = &std.Tx{ 118 Memo: "example tx", 119 } 120 121 txResultIndex = state.TxResultIndex{ 122 BlockNum: height, 123 TxIndex: 0, 124 } 125 ) 126 127 // Prepare the transaction 128 marshalledTx, err := amino.Marshal(stdTx) 129 require.NoError(t, err) 130 131 tx := types.Tx(marshalledTx) 132 133 // Prepare the DB 134 sdb := memdb.NewMemDB() 135 136 // Save the result index to the DB 137 sdb.Set(state.CalcTxResultKey(tx.Hash()), txResultIndex.Bytes()) 138 139 // Set the GLOBALLY referenced db 140 SetStateDB(sdb) 141 142 // Set the GLOBALLY referenced blockstore 143 blockStore := &mockBlockStore{ 144 heightFn: func() int64 { 145 return height 146 }, 147 loadBlockFn: func(h int64) *types.Block { 148 require.Equal(t, height, h) 149 150 return &types.Block{ 151 Data: types.Data{ 152 Txs: []types.Tx{}, // empty 153 }, 154 } 155 }, 156 } 157 158 SetBlockStore(blockStore) 159 160 // Load the result 161 loadedTxResult, err := Tx(nil, tx.Hash()) 162 require.Nil(t, loadedTxResult) 163 164 assert.ErrorContains(t, err, "unable to get block transaction") 165 }) 166 167 t.Run("invalid ABCI response index (corrupted state)", func(t *testing.T) { 168 var ( 169 height = int64(10) 170 171 stdTx = &std.Tx{ 172 Memo: "example tx", 173 } 174 175 txResultIndex = state.TxResultIndex{ 176 BlockNum: height, 177 TxIndex: 0, 178 } 179 ) 180 181 // Prepare the transaction 182 marshalledTx, err := amino.Marshal(stdTx) 183 require.NoError(t, err) 184 185 tx := types.Tx(marshalledTx) 186 187 // Prepare the DB 188 sdb := memdb.NewMemDB() 189 190 // Save the result index to the DB 191 sdb.Set(state.CalcTxResultKey(tx.Hash()), txResultIndex.Bytes()) 192 193 // Set the GLOBALLY referenced db 194 SetStateDB(sdb) 195 196 // Set the GLOBALLY referenced blockstore 197 blockStore := &mockBlockStore{ 198 heightFn: func() int64 { 199 return height 200 }, 201 loadBlockFn: func(h int64) *types.Block { 202 require.Equal(t, height, h) 203 204 return &types.Block{ 205 Data: types.Data{ 206 Txs: []types.Tx{ 207 tx, 208 }, 209 }, 210 } 211 }, 212 } 213 214 SetBlockStore(blockStore) 215 216 // Load the result 217 loadedTxResult, err := Tx(nil, tx.Hash()) 218 require.Nil(t, loadedTxResult) 219 220 assert.ErrorContains(t, err, "unable to load block results") 221 }) 222 }