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  }