db_upgrade.go 8.5 KB

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