github.com/iotexproject/iotex-core@v1.14.1-rc1/state/factory/workingset_test.go (about)

     1  // Copyright (c) 2020 IoTeX Foundation
     2  // This source code is provided 'as is' and no warranties are given as to title or non-infringement, merchantability
     3  // or fitness for purpose and, to the extent permitted by law, all liability for your use of the code is disclaimed.
     4  // This source code is governed by Apache License 2.0 that can be found in the LICENSE file.
     5  
     6  package factory
     7  
     8  import (
     9  	"context"
    10  	"math/big"
    11  	"testing"
    12  	"time"
    13  
    14  	"github.com/iotexproject/go-pkgs/hash"
    15  	"github.com/pkg/errors"
    16  	"github.com/stretchr/testify/require"
    17  
    18  	"github.com/iotexproject/iotex-core/action"
    19  	"github.com/iotexproject/iotex-core/action/protocol"
    20  	"github.com/iotexproject/iotex-core/action/protocol/account"
    21  	"github.com/iotexproject/iotex-core/action/protocol/rewarding"
    22  	"github.com/iotexproject/iotex-core/blockchain"
    23  	"github.com/iotexproject/iotex-core/blockchain/block"
    24  	"github.com/iotexproject/iotex-core/blockchain/genesis"
    25  	"github.com/iotexproject/iotex-core/db"
    26  	"github.com/iotexproject/iotex-core/state"
    27  	"github.com/iotexproject/iotex-core/test/identityset"
    28  	"github.com/iotexproject/iotex-core/testutil"
    29  )
    30  
    31  type (
    32  	testString struct {
    33  		s string
    34  	}
    35  
    36  	workingSetCreator interface {
    37  		newWorkingSet(context.Context, uint64) (*workingSet, error)
    38  	}
    39  )
    40  
    41  func (s testString) Serialize() ([]byte, error) {
    42  	return []byte(s.s), nil
    43  }
    44  
    45  func (s *testString) Deserialize(v []byte) error {
    46  	s.s = string(v)
    47  	return nil
    48  }
    49  
    50  func newFactoryWorkingSet(t testing.TB) *workingSet {
    51  	r := require.New(t)
    52  	sf, err := NewFactory(DefaultConfig, db.NewMemKVStore())
    53  	r.NoError(err)
    54  
    55  	ctx := genesis.WithGenesisContext(
    56  		protocol.WithRegistry(context.Background(), protocol.NewRegistry()),
    57  		genesis.Default,
    58  	)
    59  	r.NoError(sf.Start(ctx))
    60  	// defer r.NoError(sf.Stop(ctx))
    61  
    62  	ws, err := sf.(workingSetCreator).newWorkingSet(ctx, 1)
    63  	r.NoError(err)
    64  	return ws
    65  }
    66  
    67  func newStateDBWorkingSet(t testing.TB) *workingSet {
    68  	r := require.New(t)
    69  	sf, err := NewStateDB(DefaultConfig, db.NewMemKVStore())
    70  	r.NoError(err)
    71  
    72  	ctx := genesis.WithGenesisContext(
    73  		protocol.WithRegistry(context.Background(), protocol.NewRegistry()),
    74  		genesis.Default,
    75  	)
    76  	r.NoError(sf.Start(ctx))
    77  	// defer r.NoError(sf.Stop(ctx))
    78  
    79  	ws, err := sf.(workingSetCreator).newWorkingSet(ctx, 1)
    80  	r.NoError(err)
    81  	return ws
    82  }
    83  
    84  func TestWorkingSet_ReadWriteView(t *testing.T) {
    85  	var (
    86  		r   = require.New(t)
    87  		set = []*workingSet{
    88  			newFactoryWorkingSet(t),
    89  			newStateDBWorkingSet(t),
    90  		}
    91  		tests = []struct{ key, val string }{
    92  			{"key1", "value1"},
    93  			{"key2", "value2"},
    94  			{"key3", "value3"},
    95  			{"key4", "value4"},
    96  		}
    97  	)
    98  	for _, ws := range set {
    99  		for _, test := range tests {
   100  			val, err := ws.ReadView(test.key)
   101  			r.Equal(protocol.ErrNoName, errors.Cause(err))
   102  			r.Equal(val, nil)
   103  			// write view into workingSet
   104  			r.NoError(ws.WriteView(test.key, test.val))
   105  		}
   106  
   107  		// read view and compare result
   108  		for _, test := range tests {
   109  			val, err := ws.ReadView(test.key)
   110  			r.NoError(err)
   111  			r.Equal(test.val, val)
   112  		}
   113  
   114  		// overwrite
   115  		newVal := "testvalue"
   116  		r.NoError(ws.WriteView(tests[0].key, newVal))
   117  		val, err := ws.ReadView(tests[0].key)
   118  		r.NoError(err)
   119  		r.Equal(newVal, val)
   120  	}
   121  }
   122  
   123  func TestWorkingSet_Dock(t *testing.T) {
   124  	var (
   125  		r   = require.New(t)
   126  		set = []*workingSet{
   127  			newFactoryWorkingSet(t),
   128  			newStateDBWorkingSet(t),
   129  		}
   130  		tests = []struct {
   131  			name, key string
   132  			val       state.Serializer
   133  		}{
   134  			{
   135  				"ns", "test1", &testString{"v1"},
   136  			},
   137  			{
   138  				"ns", "test2", &testString{"v2"},
   139  			},
   140  			{
   141  				"vs", "test3", &testString{"v3"},
   142  			},
   143  			{
   144  				"ts", "test4", &testString{"v4"},
   145  			},
   146  		}
   147  	)
   148  	for _, ws := range set {
   149  		// test empty dock
   150  		ts := &testString{}
   151  		for _, e := range tests {
   152  			r.False(ws.ProtocolDirty(e.name))
   153  			r.Equal(protocol.ErrNoName, ws.Unload(e.name, e.key, ts))
   154  		}
   155  
   156  		// populate the dock, and verify existence
   157  		for _, e := range tests {
   158  			r.NoError(ws.Load(e.name, e.key, e.val))
   159  			r.True(ws.ProtocolDirty(e.name))
   160  			r.NoError(ws.Unload(e.name, e.key, ts))
   161  			r.Equal(e.val, ts)
   162  			// test key that does not exist
   163  			r.Equal(protocol.ErrNoName, ws.Unload(e.name, "notexist", ts))
   164  		}
   165  
   166  		// overwrite
   167  		v5 := &testString{"v5"}
   168  		r.NoError(ws.Load(tests[1].name, tests[1].key, v5))
   169  		r.NoError(ws.Unload(tests[1].name, tests[1].key, ts))
   170  		r.Equal(v5, ts)
   171  
   172  		// add a new one
   173  		v6 := &testString{"v6"}
   174  		r.NoError(ws.Load("as", "test6", v6))
   175  		r.True(ws.ProtocolDirty("as"))
   176  		r.NoError(ws.Unload("as", "test6", ts))
   177  		r.Equal(v6, ts)
   178  
   179  		ws.Reset()
   180  		for _, e := range tests {
   181  			r.False(ws.ProtocolDirty(e.name))
   182  		}
   183  		r.False(ws.ProtocolDirty("as"))
   184  	}
   185  }
   186  
   187  func TestWorkingSet_ValidateBlock(t *testing.T) {
   188  	require := require.New(t)
   189  	registry := protocol.NewRegistry()
   190  	require.NoError(account.NewProtocol(rewarding.DepositGas).Register(registry))
   191  	cfg := Config{
   192  		Chain:   blockchain.DefaultConfig,
   193  		Genesis: genesis.TestDefault(),
   194  	}
   195  	cfg.Genesis.InitBalanceMap[identityset.Address(28).String()] = "100000000"
   196  	var (
   197  		f1, _          = NewFactory(cfg, db.NewMemKVStore(), RegistryOption(registry))
   198  		f2, _          = NewStateDB(cfg, db.NewMemKVStore(), RegistryStateDBOption(registry))
   199  		factories      = []Factory{f1, f2}
   200  		digestHash, _  = hash.HexStringToHash256("43f69c954ea0138917d69a01f7ba47da74c99cb2c6229f5969a7f0bf53efb775")
   201  		receiptRoot, _ = hash.HexStringToHash256("b8aaff4d845664a7a3f341f677365dafcdae0ae99a7fea821c7cc42c320acefe")
   202  		tests          = []struct {
   203  			block *block.Block
   204  			err   error
   205  		}{
   206  			{
   207  				makeBlock(t, hash.ZeroHash256, receiptRoot, digestHash, makeTransferAction(t, 1)),
   208  				nil,
   209  			},
   210  			{
   211  				makeBlock(t, hash.ZeroHash256, receiptRoot, digestHash, makeTransferAction(t, 3)),
   212  				action.ErrNonceTooHigh,
   213  			},
   214  			{
   215  				makeBlock(t, hash.ZeroHash256, hash.Hash256b([]byte("test")), digestHash, makeTransferAction(t, 1)),
   216  				block.ErrReceiptRootMismatch,
   217  			},
   218  			{
   219  				makeBlock(t, hash.ZeroHash256, receiptRoot, hash.Hash256b([]byte("test")), makeTransferAction(t, 1)),
   220  				block.ErrDeltaStateMismatch,
   221  			},
   222  		}
   223  	)
   224  
   225  	ctx := protocol.WithBlockCtx(
   226  		genesis.WithGenesisContext(context.Background(), cfg.Genesis),
   227  		protocol.BlockCtx{},
   228  	)
   229  	require.NoError(f1.Start(ctx))
   230  	require.NoError(f2.Start(ctx))
   231  	defer func() {
   232  		require.NoError(f1.Stop(ctx))
   233  		require.NoError(f2.Stop(ctx))
   234  	}()
   235  
   236  	zctx := protocol.WithBlockCtx(context.Background(),
   237  		protocol.BlockCtx{
   238  			BlockHeight: uint64(1),
   239  			Producer:    identityset.Address(27),
   240  			GasLimit:    testutil.TestGasLimit * 100000,
   241  		})
   242  	zctx = genesis.WithGenesisContext(zctx, cfg.Genesis)
   243  	zctx = protocol.WithFeatureCtx(protocol.WithBlockchainCtx(zctx, protocol.BlockchainCtx{
   244  		ChainID: 1,
   245  	}))
   246  	for _, f := range factories {
   247  		for _, test := range tests {
   248  			require.Equal(test.err, errors.Cause(f.Validate(zctx, test.block)))
   249  		}
   250  	}
   251  }
   252  
   253  func TestWorkingSet_ValidateBlock_SystemAction(t *testing.T) {
   254  	require := require.New(t)
   255  	cfg := Config{
   256  		Chain:   blockchain.DefaultConfig,
   257  		Genesis: genesis.TestDefault(),
   258  	}
   259  	cfg.Genesis.QuebecBlockHeight = 1 // enable validate system action
   260  	cfg.Genesis.InitBalanceMap[identityset.Address(28).String()] = "100000000"
   261  	registry := protocol.NewRegistry()
   262  	require.NoError(account.NewProtocol(rewarding.DepositGas).Register(registry))
   263  	require.NoError(rewarding.NewProtocol(cfg.Genesis.Rewarding).Register(registry))
   264  	var (
   265  		f1, _     = NewFactory(cfg, db.NewMemKVStore(), RegistryOption(registry))
   266  		f2, _     = NewStateDB(cfg, db.NewMemKVStore(), RegistryStateDBOption(registry))
   267  		factories = []Factory{f1, f2}
   268  	)
   269  
   270  	ctx := protocol.WithBlockCtx(
   271  		genesis.WithGenesisContext(context.Background(), cfg.Genesis),
   272  		protocol.BlockCtx{},
   273  	)
   274  	require.NoError(f1.Start(ctx))
   275  	require.NoError(f2.Start(ctx))
   276  	defer func() {
   277  		require.NoError(f1.Stop(ctx))
   278  		require.NoError(f2.Stop(ctx))
   279  	}()
   280  
   281  	zctx := protocol.WithBlockCtx(ctx, protocol.BlockCtx{
   282  		BlockHeight: uint64(1),
   283  		Producer:    identityset.Address(28),
   284  		GasLimit:    testutil.TestGasLimit * 100000,
   285  	})
   286  	zctx = protocol.WithFeatureCtx(protocol.WithBlockchainCtx(zctx, protocol.BlockchainCtx{
   287  		ChainID: 1,
   288  	}))
   289  
   290  	t.Run("missing system action", func(t *testing.T) {
   291  		digestHash, err := hash.HexStringToHash256("8f9b7694c325a4f4b0065cd382f8af0a4e913113a4ce7ef1ac899f96158c74f4")
   292  		require.NoError(err)
   293  		receiptRoot, err := hash.HexStringToHash256("f04673451e31386a8fddfcf7750665bfcf33f239f6c4919430bb11a144e1aa95")
   294  		require.NoError(err)
   295  		actions := []*action.SealedEnvelope{makeTransferAction(t, 1)}
   296  		for _, f := range factories {
   297  			block := makeBlock(t, hash.ZeroHash256, receiptRoot, digestHash, actions...)
   298  			require.ErrorIs(f.Validate(zctx, block), errInvalidSystemActionLayout)
   299  		}
   300  	})
   301  	t.Run("system action not on tail", func(t *testing.T) {
   302  		digestHash, err := hash.HexStringToHash256("8f9b7694c325a4f4b0065cd382f8af0a4e913113a4ce7ef1ac899f96158c74f4")
   303  		require.NoError(err)
   304  		receiptRoot, err := hash.HexStringToHash256("f04673451e31386a8fddfcf7750665bfcf33f239f6c4919430bb11a144e1aa95")
   305  		require.NoError(err)
   306  		actions := []*action.SealedEnvelope{makeRewardAction(t, 28), makeTransferAction(t, 1)}
   307  		for _, f := range factories {
   308  			block := makeBlock(t, hash.ZeroHash256, receiptRoot, digestHash, actions...)
   309  			require.ErrorIs(f.Validate(zctx, block), errInvalidSystemActionLayout)
   310  		}
   311  	})
   312  	t.Run("correct system action", func(t *testing.T) {
   313  		digestHash, err := hash.HexStringToHash256("ade24a5c647b5af34c4e74fe0d8f1fa410f6fb115f8fc2d39e45ca2f895de9ca")
   314  		require.NoError(err)
   315  		receiptRoot, err := hash.HexStringToHash256("a59bd06fe4d2bb537895f170dec1f9213045cb13480e4941f1abdc8d13b16fae")
   316  		require.NoError(err)
   317  		actions := []*action.SealedEnvelope{makeTransferAction(t, 1), makeRewardAction(t, 28)}
   318  		for _, f := range factories {
   319  			block := makeBlock(t, hash.ZeroHash256, receiptRoot, digestHash, actions...)
   320  			require.ErrorIs(f.Validate(zctx, block), nil)
   321  		}
   322  	})
   323  	t.Run("wrong reward action signer", func(t *testing.T) {
   324  		digestHash, err := hash.HexStringToHash256("ade24a5c647b5af34c4e74fe0d8f1fa410f6fb115f8fc2d39e45ca2f895de9ca")
   325  		require.NoError(err)
   326  		receiptRoot, err := hash.HexStringToHash256("a59bd06fe4d2bb537895f170dec1f9213045cb13480e4941f1abdc8d13b16fae")
   327  		require.NoError(err)
   328  		actions := []*action.SealedEnvelope{makeTransferAction(t, 1), makeRewardAction(t, 27)}
   329  		for _, f := range factories {
   330  			block := makeBlock(t, hash.ZeroHash256, receiptRoot, digestHash, actions...)
   331  			require.ErrorContains(f.Validate(zctx, block), "Only producer could create reward")
   332  		}
   333  	})
   334  	t.Run("postiche system action", func(t *testing.T) {
   335  		digestHash, err := hash.HexStringToHash256("ade24a5c647b5af34c4e74fe0d8f1fa410f6fb115f8fc2d39e45ca2f895de9ca")
   336  		require.NoError(err)
   337  		receiptRoot, err := hash.HexStringToHash256("a59bd06fe4d2bb537895f170dec1f9213045cb13480e4941f1abdc8d13b16fae")
   338  		require.NoError(err)
   339  		actions := []*action.SealedEnvelope{makeTransferAction(t, 1), makeRewardAction(t, 28), makeRewardAction(t, 28)}
   340  		for _, f := range factories {
   341  			block := makeBlock(t, hash.ZeroHash256, receiptRoot, digestHash, actions...)
   342  			require.ErrorIs(f.Validate(zctx, block), errInvalidSystemActionLayout)
   343  		}
   344  	})
   345  	t.Run("inconsistent system action", func(t *testing.T) {
   346  		digestHash, err := hash.HexStringToHash256("8f9b7694c325a4f4b0065cd382f8af0a4e913113a4ce7ef1ac899f96158c74f4")
   347  		require.NoError(err)
   348  		receiptRoot, err := hash.HexStringToHash256("f04673451e31386a8fddfcf7750665bfcf33f239f6c4919430bb11a144e1aa95")
   349  		require.NoError(err)
   350  		rewardAct := makeRewardAction(t, 28)
   351  		rewardAct.SetNonce(2)
   352  		actions := []*action.SealedEnvelope{makeTransferAction(t, 1), rewardAct}
   353  		for _, f := range factories {
   354  			block := makeBlock(t, hash.ZeroHash256, receiptRoot, digestHash, actions...)
   355  			require.ErrorIs(f.Validate(zctx, block), errInvalidSystemActionLayout)
   356  		}
   357  	})
   358  }
   359  
   360  func makeTransferAction(t *testing.T, nonce uint64) *action.SealedEnvelope {
   361  	tsf, err := action.NewTransfer(
   362  		uint64(nonce),
   363  		big.NewInt(1),
   364  		identityset.Address(29).String(),
   365  		nil,
   366  		testutil.TestGasLimit,
   367  		big.NewInt(0),
   368  	)
   369  	require.NoError(t, err)
   370  	eb := action.EnvelopeBuilder{}
   371  	evlp := eb.
   372  		SetAction(tsf).
   373  		SetGasLimit(tsf.GasLimit()).
   374  		SetGasPrice(tsf.GasPrice()).
   375  		SetNonce(nonce).
   376  		SetChainID(1).
   377  		SetVersion(1).
   378  		Build()
   379  	sevlp, err := action.Sign(evlp, identityset.PrivateKey(28))
   380  	require.NoError(t, err)
   381  	return sevlp
   382  }
   383  
   384  func makeRewardAction(t *testing.T, signer int) *action.SealedEnvelope {
   385  	gb := action.GrantRewardBuilder{}
   386  	grant := gb.SetRewardType(action.BlockReward).SetHeight(1).Build()
   387  	eb2 := action.EnvelopeBuilder{}
   388  	evlp := eb2.SetNonce(0).
   389  		SetGasPrice(big.NewInt(0)).
   390  		SetGasLimit(grant.GasLimit()).
   391  		SetAction(&grant).
   392  		Build()
   393  	sevlp, err := action.Sign(evlp, identityset.PrivateKey(signer))
   394  	require.NoError(t, err)
   395  	return sevlp
   396  }
   397  
   398  func makeBlock(t *testing.T, prevHash hash.Hash256, receiptRoot hash.Hash256, digest hash.Hash256, actions ...*action.SealedEnvelope) *block.Block {
   399  	rap := block.RunnableActionsBuilder{}
   400  	ra := rap.AddActions(actions...).Build()
   401  	blk, err := block.NewBuilder(ra).
   402  		SetHeight(1).
   403  		SetTimestamp(time.Now()).
   404  		SetVersion(1).
   405  		SetReceiptRoot(receiptRoot).
   406  		SetDeltaStateDigest(digest).
   407  		SetPrevBlockHash(prevHash).
   408  		SignAndBuild(identityset.PrivateKey(0))
   409  	require.NoError(t, err)
   410  	return &blk
   411  }