github.com/fff-chain/go-fff@v0.0.0-20220726032732-1c84420b8a99/cmd/geth/pruneblock_test.go (about) 1 // Copyright 2016 The go-ethereum Authors 2 // This file is part of go-ethereum. 3 // 4 // go-ethereum is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // go-ethereum is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. 16 17 package main 18 19 import ( 20 "bytes" 21 "encoding/hex" 22 "fmt" 23 "io/ioutil" 24 "math/big" 25 "os" 26 "path/filepath" 27 "testing" 28 "time" 29 30 "github.com/fff-chain/go-fff/cmd/utils" 31 "github.com/fff-chain/go-fff/common" 32 "github.com/fff-chain/go-fff/consensus" 33 "github.com/fff-chain/go-fff/consensus/ethash" 34 "github.com/fff-chain/go-fff/core" 35 "github.com/fff-chain/go-fff/core/rawdb" 36 "github.com/fff-chain/go-fff/core/state/pruner" 37 "github.com/fff-chain/go-fff/core/types" 38 "github.com/fff-chain/go-fff/core/vm" 39 "github.com/fff-chain/go-fff/crypto" 40 "github.com/fff-chain/go-fff/eth" 41 "github.com/fff-chain/go-fff/ethdb" 42 "github.com/fff-chain/go-fff/node" 43 "github.com/fff-chain/go-fff/params" 44 "github.com/fff-chain/go-fff/rlp" 45 ) 46 47 var ( 48 canonicalSeed = 1 49 blockPruneBackUpBlockNumber = 128 50 key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") 51 address = crypto.PubkeyToAddress(key.PublicKey) 52 balance = big.NewInt(10000000) 53 gspec = &core.Genesis{Config: params.TestChainConfig, Alloc: core.GenesisAlloc{address: {Balance: balance}}} 54 signer = types.LatestSigner(gspec.Config) 55 config = &core.CacheConfig{ 56 TrieCleanLimit: 256, 57 TrieDirtyLimit: 256, 58 TrieTimeLimit: 5 * time.Minute, 59 SnapshotLimit: 0, // Disable snapshot 60 TriesInMemory: 128, 61 } 62 engine = ethash.NewFullFaker() 63 ) 64 65 func TestOfflineBlockPrune(t *testing.T) { 66 //Corner case for 0 remain in ancinetStore. 67 testOfflineBlockPruneWithAmountReserved(t, 0) 68 //General case. 69 testOfflineBlockPruneWithAmountReserved(t, 100) 70 } 71 72 func testOfflineBlockPruneWithAmountReserved(t *testing.T, amountReserved uint64) { 73 datadir, err := ioutil.TempDir("", "") 74 if err != nil { 75 t.Fatalf("Failed to create temporary datadir: %v", err) 76 } 77 os.RemoveAll(datadir) 78 79 chaindbPath := filepath.Join(datadir, "chaindata") 80 oldAncientPath := filepath.Join(chaindbPath, "ancient") 81 newAncientPath := filepath.Join(chaindbPath, "ancient_back") 82 83 db, blocks, blockList, receiptsList, externTdList, startBlockNumber, _ := BlockchainCreator(t, chaindbPath, oldAncientPath, amountReserved) 84 node, _ := startEthService(t, gspec, blocks, chaindbPath) 85 defer node.Close() 86 87 //Initialize a block pruner for pruning, only remain amountReserved blocks backward. 88 testBlockPruner := pruner.NewBlockPruner(db, node, oldAncientPath, newAncientPath, amountReserved) 89 if err != nil { 90 t.Fatalf("failed to make new blockpruner: %v", err) 91 } 92 if err := testBlockPruner.BlockPruneBackUp(chaindbPath, 512, utils.MakeDatabaseHandles(), "", false, false); err != nil { 93 t.Fatalf("Failed to back up block: %v", err) 94 } 95 96 dbBack, err := rawdb.NewLevelDBDatabaseWithFreezer(chaindbPath, 0, 0, newAncientPath, "", false, true, false) 97 if err != nil { 98 t.Fatalf("failed to create database with ancient backend") 99 } 100 defer dbBack.Close() 101 102 //check against if the backup data matched original one 103 for blockNumber := startBlockNumber; blockNumber < startBlockNumber+amountReserved; blockNumber++ { 104 blockHash := rawdb.ReadCanonicalHash(dbBack, blockNumber) 105 block := rawdb.ReadBlock(dbBack, blockHash, blockNumber) 106 107 if block.Hash() != blockHash { 108 t.Fatalf("block data did not match between oldDb and backupDb") 109 } 110 if blockList[blockNumber-startBlockNumber].Hash() != blockHash { 111 t.Fatalf("block data did not match between oldDb and backupDb") 112 } 113 114 receipts := rawdb.ReadRawReceipts(dbBack, blockHash, blockNumber) 115 if err := checkReceiptsRLP(receipts, receiptsList[blockNumber-startBlockNumber]); err != nil { 116 t.Fatalf("receipts did not match between oldDb and backupDb") 117 } 118 // // Calculate the total difficulty of the block 119 td := rawdb.ReadTd(dbBack, blockHash, blockNumber) 120 if td == nil { 121 t.Fatalf("Failed to ReadTd: %v", consensus.ErrUnknownAncestor) 122 } 123 if td.Cmp(externTdList[blockNumber-startBlockNumber]) != 0 { 124 t.Fatalf("externTd did not match between oldDb and backupDb") 125 } 126 } 127 128 //check if ancientDb freezer replaced successfully 129 testBlockPruner.AncientDbReplacer() 130 if _, err := os.Stat(newAncientPath); err != nil { 131 if !os.IsNotExist(err) { 132 t.Fatalf("ancientDb replaced unsuccessfully") 133 } 134 } 135 if _, err := os.Stat(oldAncientPath); err != nil { 136 t.Fatalf("ancientDb replaced unsuccessfully") 137 } 138 } 139 140 func BlockchainCreator(t *testing.T, chaindbPath, AncientPath string, blockRemain uint64) (ethdb.Database, []*types.Block, []*types.Block, []types.Receipts, []*big.Int, uint64, *core.BlockChain) { 141 //create a database with ancient freezer 142 db, err := rawdb.NewLevelDBDatabaseWithFreezer(chaindbPath, 0, 0, AncientPath, "", false, false, false) 143 if err != nil { 144 t.Fatalf("failed to create database with ancient backend") 145 } 146 defer db.Close() 147 genesis := gspec.MustCommit(db) 148 // Initialize a fresh chain with only a genesis block 149 blockchain, err := core.NewBlockChain(db, config, gspec.Config, engine, vm.Config{}, nil, nil) 150 if err != nil { 151 t.Fatalf("Failed to create chain: %v", err) 152 } 153 154 // Make chain starting from genesis 155 blocks, _ := core.GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 500, func(i int, block *core.BlockGen) { 156 block.SetCoinbase(common.Address{0: byte(canonicalSeed), 19: byte(i)}) 157 tx, err := types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{0x00}, big.NewInt(1000), params.TxGas, nil, nil), signer, key) 158 if err != nil { 159 panic(err) 160 } 161 block.AddTx(tx) 162 block.SetDifficulty(big.NewInt(1000000)) 163 }) 164 if _, err := blockchain.InsertChain(blocks); err != nil { 165 t.Fatalf("Failed to import canonical chain start: %v", err) 166 } 167 168 // Force run a freeze cycle 169 type freezer interface { 170 Freeze(threshold uint64) error 171 Ancients() (uint64, error) 172 } 173 db.(freezer).Freeze(10) 174 175 frozen, err := db.Ancients() 176 //make sure there're frozen items 177 if err != nil || frozen == 0 { 178 t.Fatalf("Failed to import canonical chain start: %v", err) 179 } 180 if frozen < blockRemain { 181 t.Fatalf("block amount is not enough for pruning: %v", err) 182 } 183 184 oldOffSet := rawdb.ReadOffSetOfCurrentAncientFreezer(db) 185 // Get the actual start block number. 186 startBlockNumber := frozen - blockRemain + oldOffSet 187 // Initialize the slice to buffer the block data left. 188 blockList := make([]*types.Block, 0, blockPruneBackUpBlockNumber) 189 receiptsList := make([]types.Receipts, 0, blockPruneBackUpBlockNumber) 190 externTdList := make([]*big.Int, 0, blockPruneBackUpBlockNumber) 191 // All ancient data within the most recent 128 blocks write into memory buffer for future new ancient_back directory usage. 192 for blockNumber := startBlockNumber; blockNumber < frozen+oldOffSet; blockNumber++ { 193 blockHash := rawdb.ReadCanonicalHash(db, blockNumber) 194 block := rawdb.ReadBlock(db, blockHash, blockNumber) 195 blockList = append(blockList, block) 196 receipts := rawdb.ReadRawReceipts(db, blockHash, blockNumber) 197 receiptsList = append(receiptsList, receipts) 198 // Calculate the total difficulty of the block 199 td := rawdb.ReadTd(db, blockHash, blockNumber) 200 if td == nil { 201 t.Fatalf("Failed to ReadTd: %v", consensus.ErrUnknownAncestor) 202 } 203 externTdList = append(externTdList, td) 204 } 205 206 return db, blocks, blockList, receiptsList, externTdList, startBlockNumber, blockchain 207 } 208 209 func checkReceiptsRLP(have, want types.Receipts) error { 210 if len(have) != len(want) { 211 return fmt.Errorf("receipts sizes mismatch: have %d, want %d", len(have), len(want)) 212 } 213 for i := 0; i < len(want); i++ { 214 rlpHave, err := rlp.EncodeToBytes(have[i]) 215 if err != nil { 216 return err 217 } 218 rlpWant, err := rlp.EncodeToBytes(want[i]) 219 if err != nil { 220 return err 221 } 222 if !bytes.Equal(rlpHave, rlpWant) { 223 return fmt.Errorf("receipt #%d: receipt mismatch: have %s, want %s", i, hex.EncodeToString(rlpHave), hex.EncodeToString(rlpWant)) 224 } 225 } 226 return nil 227 } 228 229 // startEthService creates a full node instance for testing. 230 func startEthService(t *testing.T, genesis *core.Genesis, blocks []*types.Block, chaindbPath string) (*node.Node, *eth.Ethereum) { 231 t.Helper() 232 n, err := node.New(&node.Config{DataDir: chaindbPath}) 233 if err != nil { 234 t.Fatal("can't create node:", err) 235 } 236 237 if err := n.Start(); err != nil { 238 t.Fatal("can't start node:", err) 239 } 240 241 return n, nil 242 }