github.com/iotexproject/iotex-core@v1.14.1-rc1/blockchain/filedao/filedao_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 "os" 12 "testing" 13 14 "github.com/iotexproject/go-pkgs/crypto" 15 "github.com/iotexproject/go-pkgs/hash" 16 "github.com/stretchr/testify/require" 17 18 "github.com/iotexproject/iotex-core/blockchain/block" 19 "github.com/iotexproject/iotex-core/db" 20 "github.com/iotexproject/iotex-core/pkg/compress" 21 ) 22 23 func TestChecksumNamespaceAndKeys(t *testing.T) { 24 r := require.New(t) 25 26 a := []hash.Hash256{ 27 // filedao 28 hash.BytesToHash256([]byte(_blockHashHeightMappingNS)), 29 hash.BytesToHash256([]byte(_systemLogNS)), 30 hash.BytesToHash256(_topHeightKey), 31 hash.BytesToHash256(_topHashKey), 32 hash.BytesToHash256(_hashPrefix), 33 // filedao_legacy 34 hash.BytesToHash256([]byte(_blockNS)), 35 hash.BytesToHash256([]byte(_blockHeaderNS)), 36 hash.BytesToHash256([]byte(_blockBodyNS)), 37 hash.BytesToHash256([]byte(_blockFooterNS)), 38 hash.BytesToHash256([]byte(_receiptsNS)), 39 hash.BytesToHash256(_heightPrefix), 40 hash.BytesToHash256(_heightToFileBucket), 41 // filedao_v2 42 hash.BytesToHash256([]byte(FileV2)), 43 hash.BytesToHash256([]byte{16}), 44 hash.BytesToHash256([]byte(compress.Gzip)), 45 hash.BytesToHash256([]byte(compress.Snappy)), 46 hash.BytesToHash256([]byte(_hashDataNS)), 47 hash.BytesToHash256([]byte(_blockDataNS)), 48 hash.BytesToHash256([]byte(_headerDataNs)), 49 hash.BytesToHash256(_fileHeaderKey), 50 } 51 52 checksum := crypto.NewMerkleTree(a) 53 r.NotNil(checksum) 54 h := checksum.HashTree() 55 r.Equal("18747e1ac5364ce3f398e03092f159121b55166449657f65ba1f9243e8830391", hex.EncodeToString(h[:])) 56 } 57 58 func TestReadFileHeader(t *testing.T) { 59 r := require.New(t) 60 61 cfg := db.DefaultConfig 62 cfg.DbPath = "./filedao_v2.db" 63 64 // test non-existing file 65 _, err := readFileHeader(cfg.DbPath, FileLegacyMaster) 66 r.Equal(ErrFileNotExist, err) 67 _, err = readFileHeader(cfg.DbPath, FileAll) 68 r.Equal(ErrFileNotExist, err) 69 70 // empty legacy file is invalid 71 deser := block.NewDeserializer(_defaultEVMNetworkID) 72 legacy, err := newFileDAOLegacy(cfg, deser) 73 r.NoError(err) 74 ctx := context.Background() 75 r.NoError(legacy.Start(ctx)) 76 r.NoError(legacy.Stop(ctx)) 77 _, err = readFileHeader(cfg.DbPath, FileLegacyMaster) 78 r.Equal(ErrFileInvalid, err) 79 _, err = readFileHeader(cfg.DbPath, FileAll) 80 r.Equal(ErrFileInvalid, err) 81 82 // commit 1 block to make it a valid legacy file 83 r.NoError(legacy.Start(ctx)) 84 builder := block.NewTestingBuilder() 85 blk := createTestingBlock(builder, 1, hash.ZeroHash256) 86 r.NoError(legacy.PutBlock(ctx, blk)) 87 height, err := legacy.Height() 88 r.NoError(err) 89 r.EqualValues(1, height) 90 r.NoError(legacy.Stop(ctx)) 91 92 type testCheckFile struct { 93 checkType, version string 94 err error 95 } 96 97 // test valid legacy master file 98 test1 := []testCheckFile{ 99 {FileLegacyMaster, FileLegacyMaster, nil}, 100 {FileLegacyAuxiliary, FileLegacyMaster, nil}, 101 {FileV2, "", ErrFileInvalid}, 102 {FileAll, FileLegacyMaster, nil}, 103 } 104 for _, v := range test1 { 105 h, err := readFileHeader(cfg.DbPath, v.checkType) 106 r.Equal(v.err, err) 107 if err == nil { 108 r.Equal(v.version, h.Version) 109 } 110 } 111 os.RemoveAll(cfg.DbPath) 112 // test valid v2 master file 113 r.NoError(createNewV2File(1, cfg, deser)) 114 defer os.RemoveAll(cfg.DbPath) 115 116 test2 := []testCheckFile{ 117 {FileLegacyMaster, "", ErrFileInvalid}, 118 {FileLegacyAuxiliary, "", ErrFileInvalid}, 119 {FileV2, FileV2, nil}, 120 {FileAll, FileV2, nil}, 121 } 122 for _, v := range test2 { 123 h, err := readFileHeader(cfg.DbPath, v.checkType) 124 r.Equal(v.err, err) 125 if err == nil { 126 r.Equal(v.version, h.Version) 127 } 128 } 129 130 r.Panics(func() { readFileHeader(cfg.DbPath, "") }) 131 } 132 133 func TestNewFileDAOSplitV2(t *testing.T) { 134 r := require.New(t) 135 136 cfg := db.DefaultConfig 137 cfg.V2BlocksToSplitDB = 10 138 cfg.DbPath = "./filedao_v2.db" 139 defer os.RemoveAll(cfg.DbPath) 140 141 // test non-existing file 142 _, err := readFileHeader(cfg.DbPath, FileAll) 143 r.Equal(ErrFileNotExist, err) 144 145 // test empty db file, this will create new v2 file 146 deser := block.NewDeserializer(_defaultEVMNetworkID) 147 fd, err := NewFileDAO(cfg, deser) 148 r.NoError(err) 149 r.NotNil(fd) 150 h, err := readFileHeader(cfg.DbPath, FileAll) 151 r.NoError(err) 152 r.Equal(FileV2, h.Version) 153 ctx := context.Background() 154 r.NoError(fd.Start(ctx)) 155 fm := fd.(*fileDAO) 156 r.EqualValues(0, fm.topIndex) 157 r.EqualValues(1, fm.splitHeight) 158 r.NoError(testCommitBlocks(t, fd, 1, 10, hash.ZeroHash256)) 159 r.EqualValues(0, fm.topIndex) 160 r.EqualValues(1, fm.splitHeight) 161 testVerifyChainDB(t, fd, 1, 10) 162 163 // block 11 will split a new file 164 // and test PutBlock() fail right after splitting file 165 testFailFd := newTestFailPutBlock(fm, 11) 166 r.Equal(ErrInvalidTipHeight, testCommitBlocks(t, testFailFd, 11, 11, hash.ZeroHash256)) 167 r.EqualValues(1, fm.topIndex) 168 r.EqualValues(11, fm.splitHeight) 169 // commit correct block 11 won't split file again 170 r.NoError(testCommitBlocks(t, fd, 11, 11, hash.ZeroHash256)) 171 r.EqualValues(1, fm.topIndex) 172 r.EqualValues(11, fm.splitHeight) 173 r.NoError(testCommitBlocks(t, fd, 12, 25, hash.ZeroHash256)) 174 r.EqualValues(2, fm.topIndex) 175 r.EqualValues(21, fm.splitHeight) 176 testVerifyChainDB(t, fd, 1, 25) 177 r.NoError(fd.Stop(ctx)) 178 top, files := checkAuxFiles(cfg.DbPath, FileV2) 179 r.EqualValues(2, top) 180 r.Equal(2, len(files)) 181 file1 := kthAuxFileName("./filedao_v2.db", 1) 182 file2 := kthAuxFileName("./filedao_v2.db", 2) 183 r.Equal(files[0], file1) 184 r.Equal(files[1], file2) 185 os.RemoveAll(file1) 186 os.RemoveAll(file2) 187 } 188 189 func TestNewFileDAOSplitLegacy(t *testing.T) { 190 r := require.New(t) 191 192 cfg := db.DefaultConfig 193 cfg.DbPath = "./filedao_v2.db" 194 defer os.RemoveAll(cfg.DbPath) 195 196 cfg.SplitDBHeight = 5 197 cfg.SplitDBSizeMB = 20 198 199 deser := block.NewDeserializer(_defaultEVMNetworkID) 200 fd, err := newFileDAOLegacy(cfg, deser) 201 r.NoError(err) 202 ctx := context.Background() 203 r.NoError(fd.Start(ctx)) 204 r.NoError(testCommitBlocks(t, fd, 1, 10, hash.ZeroHash256)) 205 testVerifyChainDB(t, fd, 1, 10) 206 r.NoError(fd.Stop(ctx)) 207 // block 1~5 in default file.db, block 6~10 in file-000000001.db 208 file1 := kthAuxFileName("./filedao_v2.db", 1) 209 defer os.RemoveAll(file1) 210 211 // set FileDAO to split at height 15, 30 and 40 212 cfg.V2BlocksToSplitDB = 15 213 214 fd, err = NewFileDAO(cfg, deser) 215 r.NoError(err) 216 r.NoError(fd.Start(ctx)) 217 fm := fd.(*fileDAO) 218 r.EqualValues(1, fm.topIndex) 219 r.EqualValues(1, fm.splitHeight) 220 r.NoError(testCommitBlocks(t, fd, 11, 28, hash.ZeroHash256)) 221 r.EqualValues(2, fm.topIndex) 222 r.EqualValues(16, fm.splitHeight) 223 // skip block 29, commit block 30 which is a split height 224 r.Equal(ErrInvalidTipHeight, testCommitBlocks(t, fd, 30, 55, hash.ZeroHash256)) 225 r.NoError(testCommitBlocks(t, fd, 29, 55, hash.ZeroHash256)) 226 r.EqualValues(4, fm.topIndex) 227 r.EqualValues(46, fm.splitHeight) 228 testVerifyChainDB(t, fd, 1, 55) 229 r.NoError(fd.Stop(ctx)) 230 231 // now we should have: 232 // block 1~5 in legacy file.db 233 // block 6~15 in legacy file-000000001.db 234 // block 16~30 in v2 file-000000002.db 235 // block 31~45 in v2 file-000000003.db 236 // block 46~55 in v2 file-000000004.db 237 file2 := kthAuxFileName("./filedao_v2.db", 2) 238 file3 := kthAuxFileName("./filedao_v2.db", 3) 239 file4 := kthAuxFileName("./filedao_v2.db", 4) 240 defer os.RemoveAll(file2) 241 defer os.RemoveAll(file3) 242 defer os.RemoveAll(file4) 243 h, err := readFileHeader(cfg.DbPath, FileAll) 244 r.NoError(err) 245 r.Equal(FileLegacyMaster, h.Version) 246 h, err = readFileHeader(file1, FileLegacyAuxiliary) 247 r.NoError(err) 248 r.Equal(FileLegacyAuxiliary, h.Version) 249 h, err = readFileHeader(file2, FileV2) 250 r.NoError(err) 251 r.Equal(FileV2, h.Version) 252 h, err = readFileHeader(file3, FileV2) 253 r.NoError(err) 254 r.Equal(FileV2, h.Version) 255 h, err = readFileHeader(file4, FileV2) 256 r.NoError(err) 257 r.Equal(FileV2, h.Version) 258 top, files := checkAuxFiles(cfg.DbPath, FileLegacyAuxiliary) 259 r.EqualValues(1, top) 260 r.Equal(1, len(files)) 261 r.Equal(files[0], file1) 262 top, files = checkAuxFiles(cfg.DbPath, FileV2) 263 r.EqualValues(4, top) 264 r.Equal(3, len(files)) 265 r.Equal(files[0], file2) 266 r.Equal(files[1], file3) 267 r.Equal(files[2], file4) 268 269 // open 4 db files and verify again 270 fd, err = NewFileDAO(cfg, deser) 271 fm = fd.(*fileDAO) 272 r.EqualValues(4, fm.topIndex) 273 r.EqualValues(1, fm.splitHeight) 274 r.NoError(err) 275 r.NoError(fd.Start(ctx)) 276 // Start() will update splitHeight from top v2 file 277 r.EqualValues(4, fm.topIndex) 278 r.EqualValues(46, fm.splitHeight) 279 // commit another 20 blocks 280 r.NoError(testCommitBlocks(t, fd, 56, 75, hash.ZeroHash256)) 281 r.EqualValues(5, fm.topIndex) 282 r.EqualValues(61, fm.splitHeight) 283 testVerifyChainDB(t, fd, 1, 75) 284 fd.Stop(ctx) 285 os.RemoveAll(kthAuxFileName("./filedao_v2.db", fm.topIndex)) 286 } 287 288 func TestCheckFiles(t *testing.T) { 289 r := require.New(t) 290 291 auxTests := []struct { 292 file, base string 293 index uint64 294 ok bool 295 }{ 296 {"/tmp/chain-00000003.db", "/tmp/chain", 0, false}, 297 {"/tmp/chain-00000003", "/tmp/chain.db", 0, false}, 298 {"/tmp/chain-00000003.dat", "/tmp/chain.db", 0, false}, 299 {"/tmp/chair-00000003.dat", "/tmp/chain.db", 0, false}, 300 {"/tmp/chain=00000003.db", "/tmp/chain.db", 0, false}, 301 {"/tmp/chain-0000003.db", "/tmp/chain.db", 0, false}, 302 {"/tmp/chain--0000003.db", "/tmp/chain.db", 0, false}, 303 {"/tmp/chain-00000003.db", "/tmp/chain.db", 3, true}, 304 } 305 306 for _, v := range auxTests { 307 index, ok := isAuxFile(v.file, v.base) 308 r.Equal(v.index, index) 309 r.Equal(v.ok, ok) 310 } 311 312 cfg := db.DefaultConfig 313 cfg.DbPath = "./filedao_v2.db" 314 _, files := checkAuxFiles(cfg.DbPath, FileLegacyAuxiliary) 315 r.Nil(files) 316 _, files = checkAuxFiles(cfg.DbPath, FileV2) 317 r.Nil(files) 318 319 deser := block.NewDeserializer(_defaultEVMNetworkID) 320 // create 3 v2 files 321 for i := 1; i <= 3; i++ { 322 cfg.DbPath = kthAuxFileName("./filedao_v2.db", uint64(i)) 323 r.NoError(createNewV2File(1, cfg, deser)) 324 } 325 defer func() { 326 for i := 1; i <= 3; i++ { 327 os.RemoveAll(kthAuxFileName("./filedao_v2.db", uint64(i))) 328 } 329 }() 330 top, files := checkAuxFiles("./filedao_v2.db", FileV2) 331 r.EqualValues(3, top) 332 r.Equal(3, len(files)) 333 for i := 1; i <= 3; i++ { 334 r.Equal(files[i-1], kthAuxFileName("./filedao_v2.db", uint64(i))) 335 } 336 }