github.com/bamzi/go-ethereum@v1.6.7-0.20170704111104-138f26c93af1/eth/db_upgrade.go (about) 1 // Copyright 2016 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser 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 // The go-ethereum library 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 Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 // Package eth implements the Ethereum protocol. 18 package eth 19 20 import ( 21 "bytes" 22 "encoding/binary" 23 "fmt" 24 "math/big" 25 "time" 26 27 "github.com/ethereum/go-ethereum/common" 28 "github.com/ethereum/go-ethereum/core" 29 "github.com/ethereum/go-ethereum/core/types" 30 "github.com/ethereum/go-ethereum/ethdb" 31 "github.com/ethereum/go-ethereum/log" 32 "github.com/ethereum/go-ethereum/rlp" 33 ) 34 35 var useSequentialKeys = []byte("dbUpgrade_20160530sequentialKeys") 36 37 // upgradeSequentialKeys checks the chain database version and 38 // starts a background process to make upgrades if necessary. 39 // Returns a stop function that blocks until the process has 40 // been safely stopped. 41 func upgradeSequentialKeys(db ethdb.Database) (stopFn func()) { 42 data, _ := db.Get(useSequentialKeys) 43 if len(data) > 0 && data[0] == 42 { 44 return nil // already converted 45 } 46 47 if data, _ := db.Get([]byte("LastHeader")); len(data) == 0 { 48 db.Put(useSequentialKeys, []byte{42}) 49 return nil // empty database, nothing to do 50 } 51 52 log.Warn("Upgrading chain database to use sequential keys") 53 54 stopChn := make(chan struct{}) 55 stoppedChn := make(chan struct{}) 56 57 go func() { 58 stopFn := func() bool { 59 select { 60 case <-time.After(time.Microsecond * 100): // make sure other processes don't get starved 61 case <-stopChn: 62 return true 63 } 64 return false 65 } 66 67 err, stopped := upgradeSequentialCanonicalNumbers(db, stopFn) 68 if err == nil && !stopped { 69 err, stopped = upgradeSequentialBlocks(db, stopFn) 70 } 71 if err == nil && !stopped { 72 err, stopped = upgradeSequentialOrphanedReceipts(db, stopFn) 73 } 74 if err == nil && !stopped { 75 log.Info("Database conversion successful") 76 db.Put(useSequentialKeys, []byte{42}) 77 } 78 if err != nil { 79 log.Error("Database conversion failed", "err", err) 80 } 81 close(stoppedChn) 82 }() 83 84 return func() { 85 close(stopChn) 86 <-stoppedChn 87 } 88 } 89 90 // upgradeSequentialCanonicalNumbers reads all old format canonical numbers from 91 // the database, writes them in new format and deletes the old ones if successful. 92 func upgradeSequentialCanonicalNumbers(db ethdb.Database, stopFn func() bool) (error, bool) { 93 prefix := []byte("block-num-") 94 it := db.(*ethdb.LDBDatabase).NewIterator() 95 defer func() { 96 it.Release() 97 }() 98 it.Seek(prefix) 99 cnt := 0 100 for bytes.HasPrefix(it.Key(), prefix) { 101 keyPtr := it.Key() 102 if len(keyPtr) < 20 { 103 cnt++ 104 if cnt%100000 == 0 { 105 it.Release() 106 it = db.(*ethdb.LDBDatabase).NewIterator() 107 it.Seek(keyPtr) 108 log.Info("Converting canonical numbers", "count", cnt) 109 } 110 number := big.NewInt(0).SetBytes(keyPtr[10:]).Uint64() 111 newKey := []byte("h12345678n") 112 binary.BigEndian.PutUint64(newKey[1:9], number) 113 if err := db.Put(newKey, it.Value()); err != nil { 114 return err, false 115 } 116 if err := db.Delete(keyPtr); err != nil { 117 return err, false 118 } 119 } 120 121 if stopFn() { 122 return nil, true 123 } 124 it.Next() 125 } 126 if cnt > 0 { 127 log.Info("converted canonical numbers", "count", cnt) 128 } 129 return nil, false 130 } 131 132 // upgradeSequentialBlocks reads all old format block headers, bodies, TDs and block 133 // receipts from the database, writes them in new format and deletes the old ones 134 // if successful. 135 func upgradeSequentialBlocks(db ethdb.Database, stopFn func() bool) (error, bool) { 136 prefix := []byte("block-") 137 it := db.(*ethdb.LDBDatabase).NewIterator() 138 defer func() { 139 it.Release() 140 }() 141 it.Seek(prefix) 142 cnt := 0 143 for bytes.HasPrefix(it.Key(), prefix) { 144 keyPtr := it.Key() 145 if len(keyPtr) >= 38 { 146 cnt++ 147 if cnt%10000 == 0 { 148 it.Release() 149 it = db.(*ethdb.LDBDatabase).NewIterator() 150 it.Seek(keyPtr) 151 log.Info("Converting blocks", "count", cnt) 152 } 153 // convert header, body, td and block receipts 154 var keyPrefix [38]byte 155 copy(keyPrefix[:], keyPtr[0:38]) 156 hash := keyPrefix[6:38] 157 if err := upgradeSequentialBlockData(db, hash); err != nil { 158 return err, false 159 } 160 // delete old db entries belonging to this hash 161 for bytes.HasPrefix(it.Key(), keyPrefix[:]) { 162 if err := db.Delete(it.Key()); err != nil { 163 return err, false 164 } 165 it.Next() 166 } 167 if err := db.Delete(append([]byte("receipts-block-"), hash...)); err != nil { 168 return err, false 169 } 170 } else { 171 it.Next() 172 } 173 174 if stopFn() { 175 return nil, true 176 } 177 } 178 if cnt > 0 { 179 log.Info("Converted blocks", "count", cnt) 180 } 181 return nil, false 182 } 183 184 // upgradeSequentialOrphanedReceipts removes any old format block receipts from the 185 // database that did not have a corresponding block 186 func upgradeSequentialOrphanedReceipts(db ethdb.Database, stopFn func() bool) (error, bool) { 187 prefix := []byte("receipts-block-") 188 it := db.(*ethdb.LDBDatabase).NewIterator() 189 defer it.Release() 190 it.Seek(prefix) 191 cnt := 0 192 for bytes.HasPrefix(it.Key(), prefix) { 193 // phase 2 already converted receipts belonging to existing 194 // blocks, just remove if there's anything left 195 cnt++ 196 if err := db.Delete(it.Key()); err != nil { 197 return err, false 198 } 199 200 if stopFn() { 201 return nil, true 202 } 203 it.Next() 204 } 205 if cnt > 0 { 206 log.Info("Removed orphaned block receipts", "count", cnt) 207 } 208 return nil, false 209 } 210 211 // upgradeSequentialBlockData upgrades the header, body, td and block receipts 212 // database entries belonging to a single hash (doesn't delete old data). 213 func upgradeSequentialBlockData(db ethdb.Database, hash []byte) error { 214 // get old chain data and block number 215 headerRLP, _ := db.Get(append(append([]byte("block-"), hash...), []byte("-header")...)) 216 if len(headerRLP) == 0 { 217 return nil 218 } 219 header := new(types.Header) 220 if err := rlp.Decode(bytes.NewReader(headerRLP), header); err != nil { 221 return err 222 } 223 number := header.Number.Uint64() 224 bodyRLP, _ := db.Get(append(append([]byte("block-"), hash...), []byte("-body")...)) 225 tdRLP, _ := db.Get(append(append([]byte("block-"), hash...), []byte("-td")...)) 226 receiptsRLP, _ := db.Get(append([]byte("receipts-block-"), hash...)) 227 // store new hash -> number association 228 encNum := make([]byte, 8) 229 binary.BigEndian.PutUint64(encNum, number) 230 if err := db.Put(append([]byte("H"), hash...), encNum); err != nil { 231 return err 232 } 233 // store new chain data 234 if err := db.Put(append(append([]byte("h"), encNum...), hash...), headerRLP); err != nil { 235 return err 236 } 237 if len(tdRLP) != 0 { 238 if err := db.Put(append(append(append([]byte("h"), encNum...), hash...), []byte("t")...), tdRLP); err != nil { 239 return err 240 } 241 } 242 if len(bodyRLP) != 0 { 243 if err := db.Put(append(append([]byte("b"), encNum...), hash...), bodyRLP); err != nil { 244 return err 245 } 246 } 247 if len(receiptsRLP) != 0 { 248 if err := db.Put(append(append([]byte("r"), encNum...), hash...), receiptsRLP); err != nil { 249 return err 250 } 251 } 252 return nil 253 } 254 255 func addMipmapBloomBins(db ethdb.Database) (err error) { 256 const mipmapVersion uint = 2 257 258 // check if the version is set. We ignore data for now since there's 259 // only one version so we can easily ignore it for now 260 var data []byte 261 data, _ = db.Get([]byte("setting-mipmap-version")) 262 if len(data) > 0 { 263 var version uint 264 if err := rlp.DecodeBytes(data, &version); err == nil && version == mipmapVersion { 265 return nil 266 } 267 } 268 269 defer func() { 270 if err == nil { 271 var val []byte 272 val, err = rlp.EncodeToBytes(mipmapVersion) 273 if err == nil { 274 err = db.Put([]byte("setting-mipmap-version"), val) 275 } 276 return 277 } 278 }() 279 latestHash := core.GetHeadBlockHash(db) 280 latestBlock := core.GetBlock(db, latestHash, core.GetBlockNumber(db, latestHash)) 281 if latestBlock == nil { // clean database 282 return 283 } 284 285 tstart := time.Now() 286 log.Warn("Upgrading db log bloom bins") 287 for i := uint64(0); i <= latestBlock.NumberU64(); i++ { 288 hash := core.GetCanonicalHash(db, i) 289 if (hash == common.Hash{}) { 290 return fmt.Errorf("chain db corrupted. Could not find block %d.", i) 291 } 292 core.WriteMipmapBloom(db, i, core.GetBlockReceipts(db, hash, i)) 293 } 294 log.Info("Bloom-bin upgrade completed", "elapsed", common.PrettyDuration(time.Since(tstart))) 295 return nil 296 }