github.com/iotexproject/iotex-core@v1.14.1-rc1/blockindex/indexer_test.go (about) 1 // Copyright (c) 2019 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 blockindex 7 8 import ( 9 "context" 10 "hash/fnv" 11 "math/big" 12 "testing" 13 14 "github.com/pkg/errors" 15 "github.com/stretchr/testify/require" 16 17 "github.com/iotexproject/go-pkgs/hash" 18 19 "github.com/iotexproject/iotex-core/action" 20 "github.com/iotexproject/iotex-core/blockchain/block" 21 "github.com/iotexproject/iotex-core/blockchain/genesis" 22 "github.com/iotexproject/iotex-core/db" 23 "github.com/iotexproject/iotex-core/test/identityset" 24 "github.com/iotexproject/iotex-core/testutil" 25 ) 26 27 func getTestBlocks(t *testing.T) []*block.Block { 28 amount := uint64(50 << 22) 29 tsf1, err := action.SignedTransfer(identityset.Address(28).String(), identityset.PrivateKey(28), 1, big.NewInt(int64(amount)), nil, testutil.TestGasLimit, big.NewInt(0)) 30 require.NoError(t, err) 31 32 tsf2, err := action.SignedTransfer(identityset.Address(29).String(), identityset.PrivateKey(29), 2, big.NewInt(int64(amount)), nil, testutil.TestGasLimit, big.NewInt(0)) 33 require.NoError(t, err) 34 35 tsf3, err := action.SignedTransfer(identityset.Address(30).String(), identityset.PrivateKey(30), 3, big.NewInt(int64(amount)), nil, testutil.TestGasLimit, big.NewInt(0)) 36 require.NoError(t, err) 37 38 tsf4, err := action.SignedTransfer(identityset.Address(29).String(), identityset.PrivateKey(28), 2, big.NewInt(int64(amount)), nil, testutil.TestGasLimit, big.NewInt(0)) 39 require.NoError(t, err) 40 41 tsf5, err := action.SignedTransfer(identityset.Address(30).String(), identityset.PrivateKey(29), 3, big.NewInt(int64(amount)), nil, testutil.TestGasLimit, big.NewInt(0)) 42 require.NoError(t, err) 43 44 tsf6, err := action.SignedTransfer(identityset.Address(28).String(), identityset.PrivateKey(30), 4, big.NewInt(int64(amount)), nil, testutil.TestGasLimit, big.NewInt(0)) 45 require.NoError(t, err) 46 47 // create testing executions 48 execution1, err := action.SignedExecution(identityset.Address(31).String(), identityset.PrivateKey(28), 1, big.NewInt(1), 0, big.NewInt(0), nil) 49 require.NoError(t, err) 50 execution2, err := action.SignedExecution(identityset.Address(31).String(), identityset.PrivateKey(29), 2, big.NewInt(0), 0, big.NewInt(0), nil) 51 require.NoError(t, err) 52 execution3, err := action.SignedExecution(identityset.Address(31).String(), identityset.PrivateKey(30), 3, big.NewInt(2), 0, big.NewInt(0), nil) 53 require.NoError(t, err) 54 55 hash1 := hash.Hash256{} 56 fnv.New32().Sum(hash1[:]) 57 blk1, err := block.NewTestingBuilder(). 58 SetHeight(1). 59 SetPrevBlockHash(hash1). 60 SetTimeStamp(testutil.TimestampNow()). 61 AddActions(tsf1, tsf4, execution1). 62 SignAndBuild(identityset.PrivateKey(27)) 63 require.NoError(t, err) 64 65 hash2 := hash.Hash256{} 66 fnv.New32().Sum(hash2[:]) 67 blk2, err := block.NewTestingBuilder(). 68 SetHeight(2). 69 SetPrevBlockHash(hash2). 70 SetTimeStamp(testutil.TimestampNow()). 71 AddActions(tsf2, tsf5, execution2). 72 SignAndBuild(identityset.PrivateKey(27)) 73 require.NoError(t, err) 74 75 hash3 := hash.Hash256{} 76 fnv.New32().Sum(hash3[:]) 77 blk3, err := block.NewTestingBuilder(). 78 SetHeight(3). 79 SetPrevBlockHash(hash3). 80 SetTimeStamp(testutil.TimestampNow()). 81 AddActions(tsf3, tsf6, execution3). 82 SignAndBuild(identityset.PrivateKey(27)) 83 require.NoError(t, err) 84 return []*block.Block{&blk1, &blk2, &blk3} 85 } 86 87 func TestIndexer(t *testing.T) { 88 require := require.New(t) 89 90 blks := getTestBlocks(t) 91 t1Hash, _ := blks[0].Actions[0].Hash() 92 t4Hash, _ := blks[0].Actions[1].Hash() 93 e1Hash, _ := blks[0].Actions[2].Hash() 94 t2Hash, _ := blks[1].Actions[0].Hash() 95 t5Hash, _ := blks[1].Actions[1].Hash() 96 e2Hash, _ := blks[1].Actions[2].Hash() 97 t3Hash, _ := blks[2].Actions[0].Hash() 98 t6Hash, _ := blks[2].Actions[1].Hash() 99 e3Hash, _ := blks[2].Actions[2].Hash() 100 101 addr28 := hash.BytesToHash160(identityset.Address(28).Bytes()) 102 addr29 := hash.BytesToHash160(identityset.Address(29).Bytes()) 103 addr30 := hash.BytesToHash160(identityset.Address(30).Bytes()) 104 addr31 := hash.BytesToHash160(identityset.Address(31).Bytes()) 105 106 type index struct { 107 addr hash.Hash160 108 hashes [][]byte 109 } 110 111 indexTests := []struct { 112 total uint64 113 hashTotal [][]byte 114 actions [4]index 115 }{ 116 { 117 9, 118 [][]byte{t1Hash[:], t4Hash[:], e1Hash[:], t2Hash[:], t5Hash[:], e2Hash[:], t3Hash[:], t6Hash[:], e3Hash[:]}, 119 [4]index{ 120 {addr28, [][]byte{t1Hash[:], t4Hash[:], e1Hash[:], t6Hash[:]}}, 121 {addr29, [][]byte{t4Hash[:], t2Hash[:], t5Hash[:], e2Hash[:]}}, 122 {addr30, [][]byte{t5Hash[:], t3Hash[:], t6Hash[:], e3Hash[:]}}, 123 {addr31, [][]byte{e1Hash[:], e2Hash[:], e3Hash[:]}}, 124 }, 125 }, 126 { 127 6, 128 [][]byte{t1Hash[:], t4Hash[:], e1Hash[:], t2Hash[:], t5Hash[:], e2Hash[:]}, 129 [4]index{ 130 {addr28, [][]byte{t1Hash[:], t4Hash[:], e1Hash[:]}}, 131 {addr29, [][]byte{t4Hash[:], t2Hash[:], t5Hash[:], e2Hash[:]}}, 132 {addr30, [][]byte{t5Hash[:]}}, 133 {addr31, [][]byte{e1Hash[:], e2Hash[:]}}, 134 }, 135 }, 136 { 137 3, 138 [][]byte{t1Hash[:], t4Hash[:], e1Hash[:]}, 139 [4]index{ 140 {addr28, [][]byte{t1Hash[:], t4Hash[:], e1Hash[:]}}, 141 {addr29, [][]byte{t4Hash[:]}}, 142 {addr30, nil}, 143 {addr31, [][]byte{e1Hash[:]}}, 144 }, 145 }, 146 { 147 0, 148 nil, 149 [4]index{ 150 {addr28, nil}, 151 {addr29, nil}, 152 {addr30, nil}, 153 {addr31, nil}, 154 }, 155 }, 156 } 157 158 testIndexer := func(kvStore db.KVStore, t *testing.T) { 159 ctx := genesis.WithGenesisContext(context.Background(), genesis.Default) 160 indexer, err := NewIndexer(kvStore, hash.ZeroHash256) 161 require.NoError(err) 162 require.NoError(indexer.Start(ctx)) 163 defer func() { 164 require.NoError(indexer.Stop(ctx)) 165 }() 166 167 height, err := indexer.Height() 168 require.NoError(err) 169 require.EqualValues(0, height) 170 171 require.NoError(indexer.PutBlock(ctx, blks[0])) 172 // cannot skip block when indexing 173 err = indexer.PutBlock(context.Background(), blks[2]) 174 require.Equal(db.ErrInvalid, errors.Cause(err)) 175 require.NoError(indexer.PutBlock(ctx, blks[1])) 176 height, err = indexer.Height() 177 require.NoError(err) 178 require.EqualValues(2, height) 179 total, err := indexer.GetTotalActions() 180 require.NoError(err) 181 require.EqualValues(6, total) 182 183 require.NoError(indexer.PutBlock(ctx, blks[2])) 184 height, err = indexer.Height() 185 require.NoError(err) 186 require.EqualValues(3, height) 187 188 // test block index 189 for i := 0; i < 3; i++ { 190 h, err := indexer.GetBlockHash(blks[i].Height()) 191 require.NoError(err) 192 require.Equal(blks[i].HashBlock(), h) 193 height, err := indexer.GetBlockHeight(h) 194 require.NoError(err) 195 require.Equal(blks[i].Height(), height) 196 bd, err := indexer.GetBlockIndex(blks[i].Height()) 197 require.NoError(err) 198 require.Equal(h[:], bd.Hash()) 199 require.EqualValues(len(blks[i].Actions), bd.NumAction()) 200 201 // test amount 202 amount := big.NewInt(0) 203 tsfs, _ := classifyActions(blks[i].Actions) 204 for _, tsf := range tsfs { 205 amount.Add(amount, tsf.Amount()) 206 } 207 require.Equal(amount, bd.TsfAmount()) 208 209 // Test GetActionIndex 210 for j := 0; j < 3; j++ { 211 actIndex, err := indexer.GetActionIndex(indexTests[0].hashTotal[i*3+j]) 212 require.NoError(err) 213 require.Equal(blks[i].Height(), actIndex.blkHeight) 214 } 215 } 216 217 // non-existing address has 0 actions 218 actionCount, err := indexer.GetActionCountByAddress(hash.BytesToHash160(identityset.Address(13).Bytes())) 219 require.NoError(err) 220 require.EqualValues(0, actionCount) 221 222 // Test get actions 223 total, err = indexer.GetTotalActions() 224 require.NoError(err) 225 require.EqualValues(indexTests[0].total, total) 226 _, err = indexer.GetActionHashFromIndex(1, total) 227 require.Equal(db.ErrInvalid, errors.Cause(err)) 228 actions, err := indexer.GetActionHashFromIndex(0, total) 229 require.NoError(err) 230 require.Equal(actions, indexTests[0].hashTotal) 231 for i := range indexTests[0].actions { 232 actionCount, err := indexer.GetActionCountByAddress(indexTests[0].actions[i].addr) 233 require.NoError(err) 234 require.EqualValues(len(indexTests[0].actions[i].hashes), actionCount) 235 if actionCount > 0 { 236 actions, err := indexer.GetActionsByAddress(indexTests[0].actions[i].addr, 0, actionCount) 237 require.NoError(err) 238 require.Equal(actions, indexTests[0].actions[i].hashes) 239 } 240 } 241 } 242 243 testDelete := func(kvStore db.KVStore, t *testing.T) { 244 ctx := genesis.WithGenesisContext(context.Background(), genesis.Default) 245 indexer, err := NewIndexer(kvStore, hash.ZeroHash256) 246 require.NoError(err) 247 require.NoError(indexer.Start(ctx)) 248 defer func() { 249 require.NoError(indexer.Stop(ctx)) 250 }() 251 252 for i := 0; i < 3; i++ { 253 require.NoError(indexer.PutBlock(ctx, blks[i])) 254 } 255 256 for i := range indexTests[0].actions { 257 actionCount, err := indexer.GetActionCountByAddress(indexTests[0].actions[i].addr) 258 require.NoError(err) 259 require.EqualValues(len(indexTests[0].actions[i].hashes), actionCount) 260 } 261 262 // delete tip block one by one, verify address/action after each deletion 263 for i := range indexTests { 264 if i == 0 { 265 // tests[0] is the whole address/action data at block height 3 266 continue 267 } 268 269 require.NoError(indexer.DeleteTipBlock(ctx, blks[3-i])) 270 tipHeight, err := indexer.Height() 271 require.NoError(err) 272 require.EqualValues(uint64(3-i), tipHeight) 273 h, err := indexer.GetBlockHash(tipHeight) 274 require.NoError(err) 275 if i <= 2 { 276 require.Equal(blks[2-i].HashBlock(), h) 277 } else { 278 require.Equal(hash.ZeroHash256, h) 279 } 280 281 total, err := indexer.GetTotalActions() 282 require.NoError(err) 283 require.EqualValues(indexTests[i].total, total) 284 if total > 0 { 285 _, err = indexer.GetActionHashFromIndex(1, total) 286 require.Equal(db.ErrInvalid, errors.Cause(err)) 287 actions, err := indexer.GetActionHashFromIndex(0, total) 288 require.NoError(err) 289 require.Equal(actions, indexTests[i].hashTotal) 290 } 291 for j := range indexTests[i].actions { 292 actionCount, err := indexer.GetActionCountByAddress(indexTests[i].actions[j].addr) 293 require.NoError(err) 294 require.EqualValues(len(indexTests[i].actions[j].hashes), actionCount) 295 if actionCount > 0 { 296 actions, err := indexer.GetActionsByAddress(indexTests[i].actions[j].addr, 0, actionCount) 297 require.NoError(err) 298 require.Equal(actions, indexTests[i].actions[j].hashes) 299 } 300 } 301 } 302 303 tipHeight, err := indexer.Height() 304 require.NoError(err) 305 require.EqualValues(0, tipHeight) 306 total, err := indexer.GetTotalActions() 307 require.NoError(err) 308 require.EqualValues(0, total) 309 } 310 311 t.Run("In-memory KV indexer", func(t *testing.T) { 312 testIndexer(db.NewMemKVStore(), t) 313 }) 314 path := "test-indexer" 315 testPath, err := testutil.PathOfTempFile(path) 316 require.NoError(err) 317 defer testutil.CleanupPath(testPath) 318 cfg := db.DefaultConfig 319 cfg.DbPath = testPath 320 321 t.Run("Bolt DB indexer", func(t *testing.T) { 322 testutil.CleanupPath(testPath) 323 defer testutil.CleanupPath(testPath) 324 testIndexer(db.NewBoltDB(cfg), t) 325 }) 326 327 t.Run("In-memory KV delete", func(t *testing.T) { 328 testDelete(db.NewMemKVStore(), t) 329 }) 330 t.Run("Bolt DB delete", func(t *testing.T) { 331 testutil.CleanupPath(testPath) 332 defer testutil.CleanupPath(testPath) 333 testDelete(db.NewBoltDB(cfg), t) 334 }) 335 }