github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/libs/cosmos-sdk/types/context_test.go (about)

     1  package types_test
     2  
     3  import (
     4  	"context"
     5  	"testing"
     6  	"time"
     7  
     8  	"github.com/stretchr/testify/require"
     9  
    10  	"github.com/fibonacci-chain/fbc/libs/tendermint/libs/log"
    11  	dbm "github.com/fibonacci-chain/fbc/libs/tm-db"
    12  
    13  	abci "github.com/fibonacci-chain/fbc/libs/tendermint/abci/types"
    14  
    15  	"github.com/fibonacci-chain/fbc/libs/tendermint/crypto/secp256k1"
    16  
    17  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/store"
    18  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types"
    19  )
    20  
    21  type MockLogger struct {
    22  	logs *[]string
    23  }
    24  
    25  func NewMockLogger() MockLogger {
    26  	logs := make([]string, 0)
    27  	return MockLogger{
    28  		&logs,
    29  	}
    30  }
    31  
    32  func (l MockLogger) Debug(msg string, kvs ...interface{}) {
    33  	*l.logs = append(*l.logs, msg)
    34  }
    35  
    36  func (l MockLogger) Info(msg string, kvs ...interface{}) {
    37  	*l.logs = append(*l.logs, msg)
    38  }
    39  
    40  func (l MockLogger) Error(msg string, kvs ...interface{}) {
    41  	*l.logs = append(*l.logs, msg)
    42  }
    43  
    44  func (l MockLogger) With(kvs ...interface{}) log.Logger {
    45  	panic("not implemented")
    46  }
    47  
    48  func defaultContext(key types.StoreKey) types.Context {
    49  	db := dbm.NewMemDB()
    50  	cms := store.NewCommitMultiStore(db)
    51  	cms.MountStoreWithDB(key, types.StoreTypeIAVL, db)
    52  	cms.LoadLatestVersion()
    53  	ctx := types.NewContext(cms, abci.Header{}, false, log.NewNopLogger())
    54  	return ctx
    55  }
    56  
    57  func TestCacheContext(t *testing.T) {
    58  	key := types.NewKVStoreKey(t.Name())
    59  	k1 := []byte("hello")
    60  	v1 := []byte("world")
    61  	k2 := []byte("key")
    62  	v2 := []byte("value")
    63  
    64  	ctx := defaultContext(key)
    65  	store := ctx.KVStore(key)
    66  	store.Set(k1, v1)
    67  	require.Equal(t, v1, store.Get(k1))
    68  	require.Nil(t, store.Get(k2))
    69  
    70  	cctx, write := ctx.CacheContext()
    71  	cstore := cctx.KVStore(key)
    72  	require.Equal(t, v1, cstore.Get(k1))
    73  	require.Nil(t, cstore.Get(k2))
    74  
    75  	cstore.Set(k2, v2)
    76  	require.Equal(t, v2, cstore.Get(k2))
    77  	require.Nil(t, store.Get(k2))
    78  
    79  	write()
    80  
    81  	require.Equal(t, v2, store.Get(k2))
    82  }
    83  
    84  func TestLogContext(t *testing.T) {
    85  	key := types.NewKVStoreKey(t.Name())
    86  	ctx := defaultContext(key)
    87  	logger := NewMockLogger()
    88  	ctx.SetLogger(logger)
    89  	ctx.Logger().Debug("debug")
    90  	ctx.Logger().Info("info")
    91  	ctx.Logger().Error("error")
    92  	require.Equal(t, *logger.logs, []string{"debug", "info", "error"})
    93  }
    94  
    95  type dummy int64 //nolint:unused
    96  
    97  func (d dummy) Clone() interface{} {
    98  	return d
    99  }
   100  
   101  // Testing saving/loading sdk type values to/from the context
   102  func TestContextWithCustom(t *testing.T) {
   103  	var ctx types.Context
   104  	require.True(t, ctx.IsZero())
   105  
   106  	header := abci.Header{}
   107  	height := int64(1)
   108  	chainid := "chainid"
   109  	ischeck := true
   110  	txbytes := []byte("txbytes")
   111  	logger := NewMockLogger()
   112  	voteinfos := []abci.VoteInfo{{}}
   113  	meter := types.NewGasMeter(10000)
   114  	minGasPrices := types.DecCoins{types.NewInt64DecCoin("feetoken", 1)}
   115  
   116  	ctx = types.NewContext(nil, header, ischeck, logger)
   117  	require.Equal(t, header, ctx.BlockHeader())
   118  
   119  	ctx.
   120  		SetBlockHeight(height).
   121  		SetChainID(chainid).
   122  		SetTxBytes(txbytes).
   123  		SetVoteInfos(voteinfos).
   124  		SetGasMeter(meter).
   125  		SetMinGasPrices(minGasPrices)
   126  
   127  	require.Equal(t, height, ctx.BlockHeight())
   128  	require.Equal(t, chainid, ctx.ChainID())
   129  	require.Equal(t, ischeck, ctx.IsCheckTx())
   130  	require.Equal(t, txbytes, ctx.TxBytes())
   131  	require.Equal(t, logger, ctx.Logger())
   132  	require.Equal(t, voteinfos, ctx.VoteInfos())
   133  	require.Equal(t, meter, ctx.GasMeter())
   134  	require.Equal(t, minGasPrices, ctx.MinGasPrices())
   135  }
   136  
   137  // Testing saving/loading of header fields to/from the context
   138  func TestContextHeader(t *testing.T) {
   139  	var ctx types.Context
   140  
   141  	height := int64(5)
   142  	time := time.Now()
   143  	addr := secp256k1.GenPrivKey().PubKey().Address()
   144  	proposer := types.ConsAddress(addr)
   145  
   146  	ctx = types.NewContext(nil, abci.Header{}, false, nil)
   147  
   148  	ctx.
   149  		SetBlockHeight(height).
   150  		SetBlockTime(time).
   151  		SetProposer(proposer)
   152  
   153  	require.Equal(t, height, ctx.BlockHeight())
   154  	require.Equal(t, height, ctx.BlockHeader().Height)
   155  	require.Equal(t, time.UTC(), ctx.BlockHeader().Time)
   156  	require.Equal(t, proposer.Bytes(), ctx.BlockHeader().ProposerAddress)
   157  }
   158  
   159  func TestContextHeaderClone(t *testing.T) {
   160  	cases := map[string]struct {
   161  		h abci.Header
   162  	}{
   163  		"empty": {
   164  			h: abci.Header{},
   165  		},
   166  		"height": {
   167  			h: abci.Header{
   168  				Height: 77,
   169  			},
   170  		},
   171  		"time": {
   172  			h: abci.Header{
   173  				Time: time.Unix(12345677, 12345),
   174  			},
   175  		},
   176  		"zero time": {
   177  			h: abci.Header{
   178  				Time: time.Unix(0, 0),
   179  			},
   180  		},
   181  		"many items": {
   182  			h: abci.Header{
   183  				Height:  823,
   184  				Time:    time.Unix(9999999999, 0),
   185  				ChainID: "silly-demo",
   186  			},
   187  		},
   188  		"many items with hash": {
   189  			h: abci.Header{
   190  				Height:        823,
   191  				Time:          time.Unix(9999999999, 0),
   192  				ChainID:       "silly-demo",
   193  				AppHash:       []byte{5, 34, 11, 3, 23},
   194  				ConsensusHash: []byte{11, 3, 23, 87, 3, 1},
   195  			},
   196  		},
   197  	}
   198  
   199  	for name, tc := range cases {
   200  		tc := tc
   201  		t.Run(name, func(t *testing.T) {
   202  			ctx := types.NewContext(nil, tc.h, false, nil)
   203  			require.Equal(t, tc.h.Height, ctx.BlockHeight())
   204  			require.Equal(t, tc.h.Time.UTC(), ctx.BlockTime())
   205  
   206  			// update only changes one field
   207  			var newHeight int64 = 17
   208  			ctx.SetBlockHeight(newHeight)
   209  			require.Equal(t, newHeight, ctx.BlockHeight())
   210  			require.Equal(t, tc.h.Time.UTC(), ctx.BlockTime())
   211  		})
   212  	}
   213  }
   214  
   215  //go:noinline
   216  func testFoo(ctx types.Context) int {
   217  	return len(ctx.From())
   218  }
   219  
   220  func BenchmarkContextDuffCopy(b *testing.B) {
   221  	ctx := types.NewContext(nil, abci.Header{}, false, nil)
   222  	b.Run("1", func(b *testing.B) {
   223  		b.Run("with", func(b *testing.B) {
   224  			for i := 0; i < b.N; i++ {
   225  				ctx = ctx.WithIsCheckTx(true)
   226  				testFoo(ctx)
   227  			}
   228  		})
   229  		b.Run("set", func(b *testing.B) {
   230  			for i := 0; i < b.N; i++ {
   231  				ctx.SetIsCheckTx(true)
   232  				testFoo(ctx)
   233  			}
   234  		})
   235  	})
   236  
   237  	b.Run("2", func(b *testing.B) {
   238  		b.Run("with", func(b *testing.B) {
   239  			for i := 0; i < b.N; i++ {
   240  				newCtx := ctx.WithIsCheckTx(true)
   241  				testFoo(newCtx)
   242  			}
   243  		})
   244  		b.Run("set", func(b *testing.B) {
   245  			for i := 0; i < b.N; i++ {
   246  				newCtx := ctx
   247  				newCtx.SetIsCheckTx(true)
   248  				testFoo(newCtx)
   249  			}
   250  		})
   251  	})
   252  
   253  	b.Run("3", func(b *testing.B) {
   254  		b.Run("with", func(b *testing.B) {
   255  			for i := 0; i < b.N; i++ {
   256  				testFoo(ctx.WithIsCheckTx(true))
   257  			}
   258  		})
   259  		b.Run("set", func(b *testing.B) {
   260  			for i := 0; i < b.N; i++ {
   261  				newCtx := ctx
   262  				newCtx.SetIsCheckTx(true)
   263  				testFoo(newCtx)
   264  			}
   265  		})
   266  	})
   267  
   268  	b.Run("4", func(b *testing.B) {
   269  		b.Run("with", func(b *testing.B) {
   270  			for i := 0; i < b.N; i++ {
   271  				testFoo(ctx.WithIsCheckTx(true).WithIsReCheckTx(false))
   272  			}
   273  		})
   274  		b.Run("set", func(b *testing.B) {
   275  			for i := 0; i < b.N; i++ {
   276  				newCtx := ctx
   277  				newCtx.SetIsCheckTx(true).SetIsReCheckTx(false)
   278  				testFoo(newCtx)
   279  			}
   280  		})
   281  	})
   282  }
   283  
   284  func BenchmarkContextWrapAndUnwrap(b *testing.B) {
   285  	key := types.NewKVStoreKey(b.Name())
   286  
   287  	ctx := defaultContext(key)
   288  	logger := NewMockLogger()
   289  	ctx.SetLogger(logger)
   290  
   291  	height := int64(1)
   292  	chainid := "chainid"
   293  	txbytes := []byte("txbytes")
   294  	voteinfos := []abci.VoteInfo{{}}
   295  	meter := types.NewGasMeter(10000)
   296  	minGasPrices := types.DecCoins{types.NewInt64DecCoin("feetoken", 1)}
   297  	ctx.
   298  		SetBlockHeight(height).
   299  		SetChainID(chainid).
   300  		SetTxBytes(txbytes).
   301  		SetVoteInfos(voteinfos).
   302  		SetGasMeter(meter).
   303  		SetMinGasPrices(minGasPrices).SetContext(context.Background())
   304  	ctxC := types.WrapSDKContext(ctx)
   305  	b.Run("UnwrapSDKContext", func(b *testing.B) {
   306  		b.ResetTimer()
   307  		b.ReportAllocs()
   308  		for i := 0; i < b.N; i++ {
   309  			types.UnwrapSDKContext(ctxC)
   310  		}
   311  	})
   312  
   313  	b.Run("WrapSDKContext", func(b *testing.B) {
   314  		b.ResetTimer()
   315  		b.ReportAllocs()
   316  		for i := 0; i < b.N; i++ {
   317  			types.WrapSDKContext(ctx)
   318  		}
   319  	})
   320  }