db_upgrade.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  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. // Package eth implements the Ethereum protocol.
  17. package eth
  18. import (
  19. "bytes"
  20. "encoding/binary"
  21. "fmt"
  22. "math/big"
  23. "time"
  24. "github.com/ethereum/go-ethereum/common"
  25. "github.com/ethereum/go-ethereum/core"
  26. "github.com/ethereum/go-ethereum/core/types"
  27. "github.com/ethereum/go-ethereum/ethdb"
  28. "github.com/ethereum/go-ethereum/logger"
  29. "github.com/ethereum/go-ethereum/logger/glog"
  30. "github.com/ethereum/go-ethereum/rlp"
  31. )
  32. var useSequentialKeys = []byte("dbUpgrade_20160530sequentialKeys")
  33. // upgradeSequentialKeys checks the chain database version and
  34. // starts a background process to make upgrades if necessary.
  35. // Returns a stop function that blocks until the process has
  36. // been safely stopped.
  37. func upgradeSequentialKeys(db ethdb.Database) (stopFn func()) {
  38. data, _ := db.Get(useSequentialKeys)
  39. if len(data) > 0 && data[0] == 42 {
  40. return nil // already converted
  41. }
  42. if data, _ := db.Get([]byte("LastHeader")); len(data) == 0 {
  43. db.Put(useSequentialKeys, []byte{42})
  44. return nil // empty database, nothing to do
  45. }
  46. glog.V(logger.Info).Infof("Upgrading chain database to use sequential keys")
  47. stopChn := make(chan struct{})
  48. stoppedChn := make(chan struct{})
  49. go func() {
  50. stopFn := func() bool {
  51. select {
  52. case <-time.After(time.Microsecond * 100): // make sure other processes don't get starved
  53. case <-stopChn:
  54. return true
  55. }
  56. return false
  57. }
  58. err, stopped := upgradeSequentialCanonicalNumbers(db, stopFn)
  59. if err == nil && !stopped {
  60. err, stopped = upgradeSequentialBlocks(db, stopFn)
  61. }
  62. if err == nil && !stopped {
  63. err, stopped = upgradeSequentialOrphanedReceipts(db, stopFn)
  64. }
  65. if err == nil && !stopped {
  66. glog.V(logger.Info).Infof("Database conversion successful")
  67. db.Put(useSequentialKeys, []byte{42})
  68. }
  69. if err != nil {
  70. glog.V(logger.Error).Infof("Database conversion failed: %v", err)
  71. }
  72. close(stoppedChn)
  73. }()
  74. return func() {
  75. close(stopChn)
  76. <-stoppedChn
  77. }
  78. }
  79. // upgradeSequentialCanonicalNumbers reads all old format canonical numbers from
  80. // the database, writes them in new format and deletes the old ones if successful.
  81. func upgradeSequentialCanonicalNumbers(db ethdb.Database, stopFn func() bool) (error, bool) {
  82. prefix := []byte("block-num-")
  83. it := db.(*ethdb.LDBDatabase).NewIterator()
  84. defer func() {
  85. it.Release()
  86. }()
  87. it.Seek(prefix)
  88. cnt := 0
  89. for bytes.HasPrefix(it.Key(), prefix) {
  90. keyPtr := it.Key()
  91. if len(keyPtr) < 20 {
  92. cnt++
  93. if cnt%100000 == 0 {
  94. it.Release()
  95. it = db.(*ethdb.LDBDatabase).NewIterator()
  96. it.Seek(keyPtr)
  97. glog.V(logger.Info).Infof("converting %d canonical numbers...", cnt)
  98. }
  99. number := big.NewInt(0).SetBytes(keyPtr[10:]).Uint64()
  100. newKey := []byte("h12345678n")
  101. binary.BigEndian.PutUint64(newKey[1:9], number)
  102. if err := db.Put(newKey, it.Value()); err != nil {
  103. return err, false
  104. }
  105. if err := db.Delete(keyPtr); err != nil {
  106. return err, false
  107. }
  108. }
  109. if stopFn() {
  110. return nil, true
  111. }
  112. it.Next()
  113. }
  114. if cnt > 0 {
  115. glog.V(logger.Info).Infof("converted %d canonical numbers...", cnt)
  116. }
  117. return nil, false
  118. }
  119. // upgradeSequentialBlocks reads all old format block headers, bodies, TDs and block
  120. // receipts from the database, writes them in new format and deletes the old ones
  121. // if successful.
  122. func upgradeSequentialBlocks(db ethdb.Database, stopFn func() bool) (error, bool) {
  123. prefix := []byte("block-")
  124. it := db.(*ethdb.LDBDatabase).NewIterator()
  125. defer func() {
  126. it.Release()
  127. }()
  128. it.Seek(prefix)
  129. cnt := 0
  130. for bytes.HasPrefix(it.Key(), prefix) {
  131. keyPtr := it.Key()
  132. if len(keyPtr) >= 38 {
  133. cnt++
  134. if cnt%10000 == 0 {
  135. it.Release()
  136. it = db.(*ethdb.LDBDatabase).NewIterator()
  137. it.Seek(keyPtr)
  138. glog.V(logger.Info).Infof("converting %d blocks...", cnt)
  139. }
  140. // convert header, body, td and block receipts
  141. var keyPrefix [38]byte
  142. copy(keyPrefix[:], keyPtr[0:38])
  143. hash := keyPrefix[6:38]
  144. if err := upgradeSequentialBlockData(db, hash); err != nil {
  145. return err, false
  146. }
  147. // delete old db entries belonging to this hash
  148. for bytes.HasPrefix(it.Key(), keyPrefix[:]) {
  149. if err := db.Delete(it.Key()); err != nil {
  150. return err, false
  151. }
  152. it.Next()
  153. }
  154. if err := db.Delete(append([]byte("receipts-block-"), hash...)); err != nil {
  155. return err, false
  156. }
  157. } else {
  158. it.Next()
  159. }
  160. if stopFn() {
  161. return nil, true
  162. }
  163. }
  164. if cnt > 0 {
  165. glog.V(logger.Info).Infof("converted %d blocks...", cnt)
  166. }
  167. return nil, false
  168. }
  169. // upgradeSequentialOrphanedReceipts removes any old format block receipts from the
  170. // database that did not have a corresponding block
  171. func upgradeSequentialOrphanedReceipts(db ethdb.Database, stopFn func() bool) (error, bool) {
  172. prefix := []byte("receipts-block-")
  173. it := db.(*ethdb.LDBDatabase).NewIterator()
  174. defer it.Release()
  175. it.Seek(prefix)
  176. cnt := 0
  177. for bytes.HasPrefix(it.Key(), prefix) {
  178. // phase 2 already converted receipts belonging to existing
  179. // blocks, just remove if there's anything left
  180. cnt++
  181. if err := db.Delete(it.Key()); err != nil {
  182. return err, false
  183. }
  184. if stopFn() {
  185. return nil, true
  186. }
  187. it.Next()
  188. }
  189. if cnt > 0 {
  190. glog.V(logger.Info).Infof("removed %d orphaned block receipts...", cnt)
  191. }
  192. return nil, false
  193. }
  194. // upgradeSequentialBlockData upgrades the header, body, td and block receipts
  195. // database entries belonging to a single hash (doesn't delete old data).
  196. func upgradeSequentialBlockData(db ethdb.Database, hash []byte) error {
  197. // get old chain data and block number
  198. headerRLP, _ := db.Get(append(append([]byte("block-"), hash...), []byte("-header")...))
  199. if len(headerRLP) == 0 {
  200. return nil
  201. }
  202. header := new(types.Header)
  203. if err := rlp.Decode(bytes.NewReader(headerRLP), header); err != nil {
  204. return err
  205. }
  206. number := header.Number.Uint64()
  207. bodyRLP, _ := db.Get(append(append([]byte("block-"), hash...), []byte("-body")...))
  208. tdRLP, _ := db.Get(append(append([]byte("block-"), hash...), []byte("-td")...))
  209. receiptsRLP, _ := db.Get(append([]byte("receipts-block-"), hash...))
  210. // store new hash -> number association
  211. encNum := make([]byte, 8)
  212. binary.BigEndian.PutUint64(encNum, number)
  213. if err := db.Put(append([]byte("H"), hash...), encNum); err != nil {
  214. return err
  215. }
  216. // store new chain data
  217. if err := db.Put(append(append([]byte("h"), encNum...), hash...), headerRLP); err != nil {
  218. return err
  219. }
  220. if len(tdRLP) != 0 {
  221. if err := db.Put(append(append(append([]byte("h"), encNum...), hash...), []byte("t")...), tdRLP); err != nil {
  222. return err
  223. }
  224. }
  225. if len(bodyRLP) != 0 {
  226. if err := db.Put(append(append([]byte("b"), encNum...), hash...), bodyRLP); err != nil {
  227. return err
  228. }
  229. }
  230. if len(receiptsRLP) != 0 {
  231. if err := db.Put(append(append([]byte("r"), encNum...), hash...), receiptsRLP); err != nil {
  232. return err
  233. }
  234. }
  235. return nil
  236. }
  237. // upgradeChainDatabase ensures that the chain database stores block split into
  238. // separate header and body entries.
  239. func upgradeChainDatabase(db ethdb.Database) error {
  240. // Short circuit if the head block is stored already as separate header and body
  241. data, err := db.Get([]byte("LastBlock"))
  242. if err != nil {
  243. return nil
  244. }
  245. head := common.BytesToHash(data)
  246. if block := core.GetBlockByHashOld(db, head); block == nil {
  247. return nil
  248. }
  249. // At least some of the database is still the old format, upgrade (skip the head block!)
  250. glog.V(logger.Info).Info("Old database detected, upgrading...")
  251. if db, ok := db.(*ethdb.LDBDatabase); ok {
  252. blockPrefix := []byte("block-hash-")
  253. for it := db.NewIterator(); it.Next(); {
  254. // Skip anything other than a combined block
  255. if !bytes.HasPrefix(it.Key(), blockPrefix) {
  256. continue
  257. }
  258. // Skip the head block (merge last to signal upgrade completion)
  259. if bytes.HasSuffix(it.Key(), head.Bytes()) {
  260. continue
  261. }
  262. // Load the block, split and serialize (order!)
  263. block := core.GetBlockByHashOld(db, common.BytesToHash(bytes.TrimPrefix(it.Key(), blockPrefix)))
  264. if err := core.WriteTd(db, block.Hash(), block.NumberU64(), block.DeprecatedTd()); err != nil {
  265. return err
  266. }
  267. if err := core.WriteBody(db, block.Hash(), block.NumberU64(), block.Body()); err != nil {
  268. return err
  269. }
  270. if err := core.WriteHeader(db, block.Header()); err != nil {
  271. return err
  272. }
  273. if err := db.Delete(it.Key()); err != nil {
  274. return err
  275. }
  276. }
  277. // Lastly, upgrade the head block, disabling the upgrade mechanism
  278. current := core.GetBlockByHashOld(db, head)
  279. if err := core.WriteTd(db, current.Hash(), current.NumberU64(), current.DeprecatedTd()); err != nil {
  280. return err
  281. }
  282. if err := core.WriteBody(db, current.Hash(), current.NumberU64(), current.Body()); err != nil {
  283. return err
  284. }
  285. if err := core.WriteHeader(db, current.Header()); err != nil {
  286. return err
  287. }
  288. }
  289. return nil
  290. }
  291. func addMipmapBloomBins(db ethdb.Database) (err error) {
  292. const mipmapVersion uint = 2
  293. // check if the version is set. We ignore data for now since there's
  294. // only one version so we can easily ignore it for now
  295. var data []byte
  296. data, _ = db.Get([]byte("setting-mipmap-version"))
  297. if len(data) > 0 {
  298. var version uint
  299. if err := rlp.DecodeBytes(data, &version); err == nil && version == mipmapVersion {
  300. return nil
  301. }
  302. }
  303. defer func() {
  304. if err == nil {
  305. var val []byte
  306. val, err = rlp.EncodeToBytes(mipmapVersion)
  307. if err == nil {
  308. err = db.Put([]byte("setting-mipmap-version"), val)
  309. }
  310. return
  311. }
  312. }()
  313. latestHash := core.GetHeadBlockHash(db)
  314. latestBlock := core.GetBlock(db, latestHash, core.GetBlockNumber(db, latestHash))
  315. if latestBlock == nil { // clean database
  316. return
  317. }
  318. tstart := time.Now()
  319. glog.V(logger.Info).Infoln("upgrading db log bloom bins")
  320. for i := uint64(0); i <= latestBlock.NumberU64(); i++ {
  321. hash := core.GetCanonicalHash(db, i)
  322. if (hash == common.Hash{}) {
  323. return fmt.Errorf("chain db corrupted. Could not find block %d.", i)
  324. }
  325. core.WriteMipmapBloom(db, i, core.GetBlockReceipts(db, hash, i))
  326. }
  327. glog.V(logger.Info).Infoln("upgrade completed in", time.Since(tstart))
  328. return nil
  329. }