github.com/iotexproject/iotex-core@v1.14.1-rc1/blockchain/filedao/filedao_v2_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 filedao 7 8 import ( 9 "context" 10 "encoding/hex" 11 "math/big" 12 "strings" 13 "testing" 14 15 "github.com/pkg/errors" 16 "github.com/stretchr/testify/require" 17 18 "github.com/iotexproject/go-pkgs/hash" 19 "github.com/iotexproject/iotex-proto/golang/iotextypes" 20 21 "github.com/iotexproject/iotex-core/blockchain/block" 22 "github.com/iotexproject/iotex-core/blockchain/genesis" 23 "github.com/iotexproject/iotex-core/db" 24 "github.com/iotexproject/iotex-core/pkg/compress" 25 "github.com/iotexproject/iotex-core/testutil" 26 ) 27 28 const ( 29 _blockStoreBatchSize = 16 30 ) 31 32 func TestNewFileDAOv2(t *testing.T) { 33 testNewFd := func(fd *fileDAOv2, t *testing.T) { 34 r := require.New(t) 35 36 ctx := context.Background() 37 r.NoError(fd.Start(ctx)) 38 defer fd.Stop(ctx) 39 tip := fd.loadTip().Height 40 r.NoError(testCommitBlocks(t, fd, tip+1, tip+3, hash.ZeroHash256)) 41 42 // new file does not use legacy's namespaces 43 for _, v := range []string{ 44 _blockNS, 45 _blockHeaderNS, 46 _blockBodyNS, 47 _blockFooterNS, 48 _receiptsNS, 49 } { 50 _, err := fd.kvStore.Get(v, []byte{}) 51 r.Error(err) 52 r.True(strings.Contains(err.Error(), " = "+hex.EncodeToString([]byte(v))+" doesn't exist")) 53 } 54 55 // test counting index add empty transaction log 56 ser := (&block.BlkTransactionLog{}).Serialize() 57 r.Equal([]byte{}, ser) 58 for _, test := range []struct { 59 compress string 60 height uint64 61 }{ 62 {"", 3}, 63 {compress.Gzip, 4}, 64 {compress.Snappy, 5}, 65 } { 66 data := ser 67 if test.compress != "" { 68 var err error 69 data, err = compress.Compress(ser, test.compress) 70 r.NoError(err) 71 } 72 r.NoError(addOneEntryToBatch(fd.hashStore, data, fd.batch)) 73 r.NoError(fd.kvStore.WriteBatch(fd.batch)) 74 v, err := fd.hashStore.Get(test.height) 75 r.NoError(err) 76 r.Equal(data, v) 77 if test.compress != "" { 78 v, err = compress.Decompress(v, test.compress) 79 } 80 r.NoError(err) 81 r.Equal(ser, v) 82 } 83 } 84 85 r := require.New(t) 86 testPath, err := testutil.PathOfTempFile("test-newfd") 87 r.NoError(err) 88 defer func() { 89 testutil.CleanupPath(testPath) 90 }() 91 92 cfg := db.DefaultConfig 93 r.Equal(compress.Snappy, cfg.Compressor) 94 r.Equal(16, cfg.BlockStoreBatchSize) 95 cfg.DbPath = testPath 96 deser := block.NewDeserializer(_defaultEVMNetworkID) 97 _, err = newFileDAOv2(0, cfg, deser) 98 r.Equal(ErrNotSupported, err) 99 100 inMemFd, err := newFileDAOv2InMem(1) 101 r.NoError(err) 102 fd, err := newFileDAOv2(2, cfg, deser) 103 r.NoError(err) 104 105 for _, v2Fd := range []*fileDAOv2{inMemFd, fd} { 106 t.Run("test newFileDAOv2", func(t *testing.T) { 107 testNewFd(v2Fd, t) 108 }) 109 } 110 } 111 112 func TestNewFdInterface(t *testing.T) { 113 testFdInterface := func(cfg db.Config, start uint64, t *testing.T) { 114 r := require.New(t) 115 116 testutil.CleanupPath(cfg.DbPath) 117 deser := block.NewDeserializer(_defaultEVMNetworkID) 118 fd, err := newFileDAOv2(start, cfg, deser) 119 r.NoError(err) 120 121 ctx := context.Background() 122 r.NoError(fd.Start(ctx)) 123 defer fd.Stop(ctx) 124 125 height, err := fd.Bottom() 126 r.NoError(err) 127 r.Equal(start, height) 128 height, err = fd.Height() 129 r.NoError(err) 130 r.Equal(start-1, height) 131 132 // cannot commit height != tip+1 133 builder := block.NewTestingBuilder() 134 h := hash.ZeroHash256 135 blk := createTestingBlock(builder, start-1, h) 136 r.Equal(ErrInvalidTipHeight, fd.PutBlock(ctx, blk)) 137 blk = createTestingBlock(builder, start+1, h) 138 r.Equal(ErrInvalidTipHeight, fd.PutBlock(ctx, blk)) 139 140 // verify API for genesis block 141 h, err = fd.GetBlockHash(0) 142 r.NoError(err) 143 r.Equal(block.GenesisHash(), h) 144 height, err = fd.GetBlockHeight(h) 145 r.NoError(err) 146 r.Zero(height) 147 blk, err = fd.GetBlock(h) 148 r.NoError(err) 149 r.Equal(block.GenesisBlock(), blk) 150 151 // commit _blockStoreBatchSize blocks 152 for i := uint64(0); i < fd.header.BlockStoreSize; i++ { 153 blk = createTestingBlock(builder, start+i, h) 154 r.NoError(fd.PutBlock(ctx, blk)) 155 h = blk.HashBlock() 156 height, err = fd.Height() 157 r.NoError(err) 158 r.Equal(start+i, height) 159 if i < fd.header.BlockStoreSize-1 { 160 r.EqualValues(0, fd.lowestBlockOfStoreTip()) 161 r.Equal(start-1, fd.highestBlockOfStoreTip()) 162 } else { 163 r.Equal(start, fd.lowestBlockOfStoreTip()) 164 r.Equal(start+fd.header.BlockStoreSize-1, fd.highestBlockOfStoreTip()) 165 r.Equal(start+fd.header.BlockStoreSize-1, height) 166 } 167 } 168 169 // commit 3 more blocks 170 for i := uint64(1); i <= 3; i++ { 171 blk = createTestingBlock(builder, height+i, h) 172 r.NoError(fd.PutBlock(ctx, blk)) 173 h = blk.HashBlock() 174 r.Equal(start, fd.lowestBlockOfStoreTip()) 175 r.Equal(start+fd.header.BlockStoreSize-1, fd.highestBlockOfStoreTip()) 176 } 177 height, err = fd.Height() 178 r.NoError(err) 179 r.Equal(start+fd.header.BlockStoreSize+2, height) 180 r.False(fd.ContainsHeight(start - 1)) 181 r.False(fd.ContainsHeight(height + 1)) 182 183 // verify API for all blocks 184 r.True(fd.ContainsTransactionLog()) 185 for i := height; i >= start; i-- { 186 height, err = fd.Bottom() 187 r.NoError(err) 188 r.Equal(start, height) 189 r.True(fd.ContainsHeight(i)) 190 height, err = fd.Height() 191 r.NoError(err) 192 r.Equal(i, height) 193 h, err = fd.GetBlockHash(i) 194 r.NoError(err) 195 height, err = fd.GetBlockHeight(h) 196 r.NoError(err) 197 r.Equal(height, i) 198 blk, err = fd.GetBlockByHeight(i) 199 r.NoError(err) 200 r.Equal(h, blk.HashBlock()) 201 receipt, err := fd.GetReceipts(i) 202 r.NoError(err) 203 r.EqualValues(1, receipt[0].Status) 204 r.Equal(height, receipt[0].BlockHeight) 205 r.Equal(blk.Header.PrevHash(), receipt[0].ActionHash) 206 log, err := fd.TransactionLogs(i) 207 r.NoError(err) 208 l := log.Logs[0] 209 r.Equal(receipt[0].ActionHash[:], l.ActionHash) 210 r.EqualValues(1, l.NumTransactions) 211 tx := l.Transactions[0] 212 r.Equal(big.NewInt(100).String(), tx.Amount) 213 r.Equal(hex.EncodeToString(l.ActionHash[:]), tx.Sender) 214 r.Equal(hex.EncodeToString(l.ActionHash[:]), tx.Recipient) 215 r.Equal(iotextypes.TransactionLogType_NATIVE_TRANSFER, tx.Type) 216 217 // test DeleteTipBlock() 218 r.NoError(fd.DeleteTipBlock()) 219 r.False(fd.ContainsHeight(i)) 220 _, err = fd.GetBlockHash(i) 221 r.Equal(db.ErrNotExist, err) 222 _, err = fd.GetBlockHeight(h) 223 r.Equal(db.ErrNotExist, errors.Cause(err)) 224 _, err = fd.GetBlock(h) 225 r.Equal(db.ErrNotExist, errors.Cause(err)) 226 _, err = fd.GetBlockByHeight(i) 227 r.Equal(db.ErrNotExist, errors.Cause(err)) 228 _, err = fd.GetReceipts(i) 229 r.Equal(db.ErrNotExist, errors.Cause(err)) 230 _, err = fd.TransactionLogs(i) 231 r.Equal(ErrNotSupported, err) 232 } 233 234 // after deleting all blocks 235 height, err = fd.Height() 236 r.NoError(err) 237 r.Equal(start-1, height) 238 h, err = fd.GetBlockHash(height) 239 if height == 0 { 240 r.NoError(err) 241 r.Equal(block.GenesisHash(), h) 242 } else { 243 r.Equal(db.ErrNotExist, err) 244 r.Equal(hash.ZeroHash256, h) 245 } 246 r.EqualValues(0, fd.lowestBlockOfStoreTip()) 247 r.Equal(start-1, fd.highestBlockOfStoreTip()) 248 } 249 250 r := require.New(t) 251 testPath, err := testutil.PathOfTempFile("test-interface") 252 r.NoError(err) 253 defer func() { 254 testutil.CleanupPath(testPath) 255 }() 256 257 cfg := db.DefaultConfig 258 cfg.DbPath = testPath 259 deser := block.NewDeserializer(_defaultEVMNetworkID) 260 _, err = newFileDAOv2(0, cfg, deser) 261 r.Equal(ErrNotSupported, err) 262 genesis.SetGenesisTimestamp(genesis.Default.Timestamp) 263 block.LoadGenesisHash(&genesis.Default) 264 265 for _, compress := range []string{"", compress.Snappy} { 266 for _, start := range []uint64{1, 5, _blockStoreBatchSize + 1, 4 * _blockStoreBatchSize} { 267 cfg.Compressor = compress 268 t.Run("test fileDAOv2 interface", func(t *testing.T) { 269 testFdInterface(cfg, start, t) 270 }) 271 } 272 } 273 } 274 275 func TestNewFdStart(t *testing.T) { 276 testFdStart := func(cfg db.Config, start uint64, t *testing.T) { 277 r := require.New(t) 278 deser := block.NewDeserializer(_defaultEVMNetworkID) 279 for _, num := range []uint64{3, _blockStoreBatchSize - 1, _blockStoreBatchSize, 2*_blockStoreBatchSize - 1} { 280 testutil.CleanupPath(cfg.DbPath) 281 fd, err := newFileDAOv2(start, cfg, deser) 282 r.NoError(err) 283 ctx := context.Background() 284 r.NoError(fd.Start(ctx)) 285 defer fd.Stop(ctx) 286 287 r.NoError(testCommitBlocks(t, fd, start, start+num-1, hash.ZeroHash256)) 288 height, err := fd.Height() 289 r.NoError(err) 290 r.Equal(start+num-1, height) 291 r.NoError(fd.Stop(ctx)) 292 293 // start from existing file 294 fd = openFileDAOv2(cfg, deser) 295 r.NoError(fd.Start(ctx)) 296 height, err = fd.Bottom() 297 r.NoError(err) 298 r.Equal(start, height) 299 height, err = fd.Height() 300 r.NoError(err) 301 r.Equal(start+num-1, height) 302 303 // verify API for all blocks 304 for i := start; i < start+num; i++ { 305 r.True(fd.ContainsHeight(i)) 306 h, err := fd.GetBlockHash(i) 307 r.NoError(err) 308 height, err = fd.GetBlockHeight(h) 309 r.NoError(err) 310 r.Equal(height, i) 311 blk, err := fd.GetBlockByHeight(i) 312 r.NoError(err) 313 r.Equal(h, blk.HashBlock()) 314 receipt, err := fd.GetReceipts(i) 315 r.NoError(err) 316 r.EqualValues(1, receipt[0].Status) 317 r.Equal(height, receipt[0].BlockHeight) 318 r.Equal(blk.Header.PrevHash(), receipt[0].ActionHash) 319 log, err := fd.TransactionLogs(i) 320 r.NoError(err) 321 r.NotNil(log) 322 l := log.Logs[0] 323 r.Equal(receipt[0].ActionHash[:], l.ActionHash) 324 r.EqualValues(1, l.NumTransactions) 325 tx := l.Transactions[0] 326 r.Equal(big.NewInt(100).String(), tx.Amount) 327 r.Equal(hex.EncodeToString(l.ActionHash[:]), tx.Sender) 328 r.Equal(hex.EncodeToString(l.ActionHash[:]), tx.Recipient) 329 r.Equal(iotextypes.TransactionLogType_NATIVE_TRANSFER, tx.Type) 330 } 331 } 332 } 333 334 r := require.New(t) 335 testPath, err := testutil.PathOfTempFile("test-start") 336 r.NoError(err) 337 defer func() { 338 testutil.CleanupPath(testPath) 339 }() 340 341 cfg := db.DefaultConfig 342 cfg.DbPath = testPath 343 for _, compress := range []string{"", compress.Gzip} { 344 for _, start := range []uint64{1, 5, _blockStoreBatchSize + 1, 4 * _blockStoreBatchSize} { 345 cfg.Compressor = compress 346 t.Run("test fileDAOv2 start", func(t *testing.T) { 347 testFdStart(cfg, start, t) 348 }) 349 } 350 } 351 }