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 }