db_upgrade.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. // Copyright 2014 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. it.Seek(prefix)
  85. cnt := 0
  86. for bytes.HasPrefix(it.Key(), prefix) {
  87. keyPtr := it.Key()
  88. if len(keyPtr) < 20 {
  89. cnt++
  90. if cnt%100000 == 0 {
  91. glog.V(logger.Info).Infof("converting %d canonical numbers...", cnt)
  92. }
  93. number := big.NewInt(0).SetBytes(keyPtr[10:]).Uint64()
  94. newKey := []byte("h12345678n")
  95. binary.BigEndian.PutUint64(newKey[1:9], number)
  96. if err := db.Put(newKey, it.Value()); err != nil {
  97. return err, false
  98. }
  99. if err := db.Delete(keyPtr); err != nil {
  100. return err, false
  101. }
  102. }
  103. if stopFn() {
  104. return nil, true
  105. }
  106. it.Next()
  107. }
  108. if cnt > 0 {
  109. glog.V(logger.Info).Infof("converted %d canonical numbers...", cnt)
  110. }
  111. return nil, false
  112. }
  113. // upgradeSequentialBlocks reads all old format block headers, bodies, TDs and block
  114. // receipts from the database, writes them in new format and deletes the old ones
  115. // if successful.
  116. func upgradeSequentialBlocks(db ethdb.Database, stopFn func() bool) (error, bool) {
  117. prefix := []byte("block-")
  118. it := db.(*ethdb.LDBDatabase).NewIterator()
  119. it.Seek(prefix)
  120. cnt := 0
  121. for bytes.HasPrefix(it.Key(), prefix) {
  122. keyPtr := it.Key()
  123. if len(keyPtr) >= 38 {
  124. cnt++
  125. if cnt%10000 == 0 {
  126. glog.V(logger.Info).Infof("converting %d blocks...", cnt)
  127. }
  128. // convert header, body, td and block receipts
  129. var keyPrefix [38]byte
  130. copy(keyPrefix[:], keyPtr[0:38])
  131. hash := keyPrefix[6:38]
  132. if err := upgradeSequentialBlockData(db, hash); err != nil {
  133. return err, false
  134. }
  135. // delete old db entries belonging to this hash
  136. for bytes.HasPrefix(it.Key(), keyPrefix[:]) {
  137. if err := db.Delete(it.Key()); err != nil {
  138. return err, false
  139. }
  140. it.Next()
  141. }
  142. if err := db.Delete(append([]byte("receipts-block-"), hash...)); err != nil {
  143. return err, false
  144. }
  145. } else {
  146. it.Next()
  147. }
  148. if stopFn() {
  149. return nil, true
  150. }
  151. }
  152. if cnt > 0 {
  153. glog.V(logger.Info).Infof("converted %d blocks...", cnt)
  154. }
  155. return nil, false
  156. }
  157. // upgradeSequentialOrphanedReceipts removes any old format block receipts from the
  158. // database that did not have a corresponding block
  159. func upgradeSequentialOrphanedReceipts(db ethdb.Database, stopFn func() bool) (error, bool) {
  160. prefix := []byte("receipts-block-")
  161. it := db.(*ethdb.LDBDatabase).NewIterator()
  162. it.Seek(prefix)
  163. cnt := 0
  164. for bytes.HasPrefix(it.Key(), prefix) {
  165. // phase 2 already converted receipts belonging to existing
  166. // blocks, just remove if there's anything left
  167. cnt++
  168. if err := db.Delete(it.Key()); err != nil {
  169. return err, false
  170. }
  171. if stopFn() {
  172. return nil, true
  173. }
  174. it.Next()
  175. }
  176. if cnt > 0 {
  177. glog.V(logger.Info).Infof("removed %d orphaned block receipts...", cnt)
  178. }
  179. return nil, false
  180. }
  181. // upgradeSequentialBlockData upgrades the header, body, td and block receipts
  182. // database entries belonging to a single hash (doesn't delete old data).
  183. func upgradeSequentialBlockData(db ethdb.Database, hash []byte) error {
  184. // get old chain data and block number
  185. headerRLP, _ := db.Get(append(append([]byte("block-"), hash...), []byte("-header")...))
  186. if len(headerRLP) == 0 {
  187. return nil
  188. }
  189. header := new(types.Header)
  190. if err := rlp.Decode(bytes.NewReader(headerRLP), header); err != nil {
  191. return err
  192. }
  193. number := header.Number.Uint64()
  194. bodyRLP, _ := db.Get(append(append([]byte("block-"), hash...), []byte("-body")...))
  195. tdRLP, _ := db.Get(append(append([]byte("block-"), hash...), []byte("-td")...))
  196. receiptsRLP, _ := db.Get(append([]byte("receipts-block-"), hash...))
  197. // store new hash -> number association
  198. encNum := make([]byte, 8)
  199. binary.BigEndian.PutUint64(encNum, number)
  200. if err := db.Put(append([]byte("H"), hash...), encNum); err != nil {
  201. return err
  202. }
  203. // store new chain data
  204. if err := db.Put(append(append([]byte("h"), encNum...), hash...), headerRLP); err != nil {
  205. return err
  206. }
  207. if len(tdRLP) != 0 {
  208. if err := db.Put(append(append(append([]byte("h"), encNum...), hash...), []byte("t")...), tdRLP); err != nil {
  209. return err
  210. }
  211. }
  212. if len(bodyRLP) != 0 {
  213. if err := db.Put(append(append([]byte("b"), encNum...), hash...), bodyRLP); err != nil {
  214. return err
  215. }
  216. }
  217. if len(receiptsRLP) != 0 {
  218. if err := db.Put(append(append([]byte("r"), encNum...), hash...), receiptsRLP); err != nil {
  219. return err
  220. }
  221. }
  222. return nil
  223. }
  224. // upgradeChainDatabase ensures that the chain database stores block split into
  225. // separate header and body entries.
  226. func upgradeChainDatabase(db ethdb.Database) error {
  227. // Short circuit if the head block is stored already as separate header and body
  228. data, err := db.Get([]byte("LastBlock"))
  229. if err != nil {
  230. return nil
  231. }
  232. head := common.BytesToHash(data)
  233. if block := core.GetBlockByHashOld(db, head); block == nil {
  234. return nil
  235. }
  236. // At least some of the database is still the old format, upgrade (skip the head block!)
  237. glog.V(logger.Info).Info("Old database detected, upgrading...")
  238. if db, ok := db.(*ethdb.LDBDatabase); ok {
  239. blockPrefix := []byte("block-hash-")
  240. for it := db.NewIterator(); it.Next(); {
  241. // Skip anything other than a combined block
  242. if !bytes.HasPrefix(it.Key(), blockPrefix) {
  243. continue
  244. }
  245. // Skip the head block (merge last to signal upgrade completion)
  246. if bytes.HasSuffix(it.Key(), head.Bytes()) {
  247. continue
  248. }
  249. // Load the block, split and serialize (order!)
  250. block := core.GetBlockByHashOld(db, common.BytesToHash(bytes.TrimPrefix(it.Key(), blockPrefix)))
  251. if err := core.WriteTd(db, block.Hash(), block.NumberU64(), block.DeprecatedTd()); err != nil {
  252. return err
  253. }
  254. if err := core.WriteBody(db, block.Hash(), block.NumberU64(), block.Body()); err != nil {
  255. return err
  256. }
  257. if err := core.WriteHeader(db, block.Header()); err != nil {
  258. return err
  259. }
  260. if err := db.Delete(it.Key()); err != nil {
  261. return err
  262. }
  263. }
  264. // Lastly, upgrade the head block, disabling the upgrade mechanism
  265. current := core.GetBlockByHashOld(db, head)
  266. if err := core.WriteTd(db, current.Hash(), current.NumberU64(), current.DeprecatedTd()); err != nil {
  267. return err
  268. }
  269. if err := core.WriteBody(db, current.Hash(), current.NumberU64(), current.Body()); err != nil {
  270. return err
  271. }
  272. if err := core.WriteHeader(db, current.Header()); err != nil {
  273. return err
  274. }
  275. }
  276. return nil
  277. }
  278. func addMipmapBloomBins(db ethdb.Database) (err error) {
  279. const mipmapVersion uint = 2
  280. // check if the version is set. We ignore data for now since there's
  281. // only one version so we can easily ignore it for now
  282. var data []byte
  283. data, _ = db.Get([]byte("setting-mipmap-version"))
  284. if len(data) > 0 {
  285. var version uint
  286. if err := rlp.DecodeBytes(data, &version); err == nil && version == mipmapVersion {
  287. return nil
  288. }
  289. }
  290. defer func() {
  291. if err == nil {
  292. var val []byte
  293. val, err = rlp.EncodeToBytes(mipmapVersion)
  294. if err == nil {
  295. err = db.Put([]byte("setting-mipmap-version"), val)
  296. }
  297. return
  298. }
  299. }()
  300. latestHash := core.GetHeadBlockHash(db)
  301. latestBlock := core.GetBlock(db, latestHash, core.GetBlockNumber(db, latestHash))
  302. if latestBlock == nil { // clean database
  303. return
  304. }
  305. tstart := time.Now()
  306. glog.V(logger.Info).Infoln("upgrading db log bloom bins")
  307. for i := uint64(0); i <= latestBlock.NumberU64(); i++ {
  308. hash := core.GetCanonicalHash(db, i)
  309. if (hash == common.Hash{}) {
  310. return fmt.Errorf("chain db corrupted. Could not find block %d.", i)
  311. }
  312. core.WriteMipmapBloom(db, i, core.GetBlockReceipts(db, hash, i))
  313. }
  314. glog.V(logger.Info).Infoln("upgrade completed in", time.Since(tstart))
  315. return nil
  316. }