You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

246 lines
6.8 KiB

  1. // Copyright 2017-2018 DERO Project. All rights reserved.
  2. // Use of this source code in any form is governed by RESEARCH license.
  3. // license can be found in the LICENSE file.
  4. // GPG: 0F39 E425 8C65 3947 702A 8234 08B2 0360 A03A 9DE8
  5. //
  6. //
  7. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
  8. // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  9. // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
  10. // THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  11. // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  12. // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  13. // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  14. // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
  15. // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  16. // 64 bit arch will use this DB
  17. // +build amd64 boltdb
  18. package storage
  19. import "os"
  20. import "fmt"
  21. import "sync"
  22. import "path/filepath"
  23. import "encoding/binary"
  24. import "github.com/romana/rlog"
  25. import bolt "github.com/coreos/bbolt"
  26. import log "github.com/sirupsen/logrus"
  27. import "github.com/deroproject/derosuite/globals"
  28. type BoltStore struct {
  29. DB *bolt.DB
  30. tx *bolt.Tx
  31. sync.Mutex // lock this struct
  32. }
  33. var Bolt_backend *BoltStore = &BoltStore{} // global variable
  34. var logger *log.Entry
  35. func (b *BoltStore) Init(params map[string]interface{}) (err error) {
  36. logger = globals.Logger.WithFields(log.Fields{"com": "STORE"})
  37. current_path := filepath.Join(os.TempDir(), "derod_database.db")
  38. if params["--simulator"] == true {
  39. current_path = filepath.Join(os.TempDir(), "derod_simulation.db") // sp
  40. }
  41. logger.Infof("Initializing boltdb store at path %s", current_path)
  42. // Open the my.db data file in your current directory.
  43. // It will be created if it doesn't exist.
  44. b.DB, err = bolt.Open(current_path, 0600, nil)
  45. if err != nil {
  46. logger.Fatalf("Cannot open boltdb store err %s\n", err)
  47. }
  48. // if simulation, delete the file , so as it gets cleaned up automcatically
  49. if params["--simulator"] == true {
  50. os.Remove(current_path)
  51. }
  52. // place db in no sync mode
  53. //b.DB.NoSync = true
  54. return nil
  55. }
  56. func (b *BoltStore) Shutdown() (err error) {
  57. logger.Infof("Shutting boltdb store")
  58. if b.DB != nil {
  59. b.DB.Sync() // sync the DB before closing
  60. b.DB.Close()
  61. }
  62. return nil
  63. }
  64. // get a new writable tx,
  65. // we will manage the writable txs manually
  66. // since a block may cause changes to a number of fields which must be reflected atomically
  67. // this function is always triggered while the atomic lock is taken
  68. // this is done avoid a race condition in returning the tx and using it
  69. func (b *BoltStore) get_new_writable_tx() (tx *bolt.Tx) {
  70. if b.tx != nil {
  71. tx = b.tx // use existing pending tx
  72. } else { // create new pending tx
  73. tx, err := b.DB.Begin(true) // begin a new writable tx
  74. if err != nil {
  75. logger.Warnf("Error while creating new writable tx, err %s", err)
  76. } else {
  77. b.tx = tx
  78. rlog.Tracef(1, "Beginning new writable TX")
  79. }
  80. }
  81. return b.tx
  82. }
  83. // Commit the pending transaction to disk
  84. func (b *BoltStore) Commit() {
  85. b.Lock()
  86. if b.tx != nil {
  87. rlog.Tracef(1, "Committing writable TX")
  88. err := b.tx.Commit()
  89. if err != nil {
  90. logger.Warnf("Error while commit tx, err %s", err)
  91. }
  92. b.tx = nil
  93. } else {
  94. logger.Warnf("Trying to Commit a NULL transaction, NOT possible")
  95. }
  96. b.Unlock()
  97. }
  98. // Roll back existing changes to disk
  99. func (b *BoltStore) Rollback() {
  100. b.Lock()
  101. if b.tx != nil {
  102. rlog.Tracef(1, "Rollbacking writable TX")
  103. b.tx.Rollback()
  104. b.tx = nil
  105. } else {
  106. //logger.Warnf("Trying to Rollback a NULL transaction, NOT possible")
  107. }
  108. b.Unlock()
  109. }
  110. // sync the DB to disk
  111. func (b *BoltStore) Sync() {
  112. b.Lock()
  113. if b.DB != nil {
  114. b.DB.Sync() // sync the DB
  115. }
  116. b.Unlock()
  117. }
  118. func (b *BoltStore) StoreObject(universe_name []byte, galaxy_name []byte, solar_name []byte, key []byte, data []byte) (err error) {
  119. b.Lock()
  120. defer b.Unlock()
  121. rlog.Tracef(10, "Storing object %s %s %x data len %d\n", string(universe_name), string(galaxy_name), key, len(data))
  122. // open universe bucket
  123. tx := b.get_new_writable_tx()
  124. universe, err := tx.CreateBucketIfNotExists(universe_name)
  125. if err != nil {
  126. logger.Errorf("Error while creating universe bucket %s\n", err)
  127. return err
  128. }
  129. galaxy, err := universe.CreateBucketIfNotExists(galaxy_name)
  130. if err != nil {
  131. logger.Errorf("Error while creating galaxy bucket %s err %s\n", string(galaxy_name), err)
  132. return err
  133. }
  134. solar, err := galaxy.CreateBucketIfNotExists(solar_name)
  135. if err != nil {
  136. logger.Errorf("Error while creating solar bucket %s err %s\n", string(solar_name), err)
  137. return err
  138. }
  139. // now lets update the object attribute
  140. err = solar.Put(key, data)
  141. return err
  142. }
  143. func (b *BoltStore) LoadObject(universe_name []byte, bucket_name []byte, solar_bucket []byte, key []byte) (data []byte, err error) {
  144. rlog.Tracef(10, "Loading object %s %s %x\n", string(universe_name), string(bucket_name), key)
  145. b.Lock()
  146. defer b.Unlock()
  147. var tx *bolt.Tx
  148. if b.tx != nil {
  149. tx = b.tx
  150. } else {
  151. tx, err = b.DB.Begin(false) // create read only
  152. defer tx.Rollback() // read-only tx must be rolled back always, never commit
  153. }
  154. // open universe bucket
  155. {
  156. universe := tx.Bucket(universe_name)
  157. if universe == nil {
  158. return data, fmt.Errorf("No Such Universe %x\n", universe_name)
  159. }
  160. bucket := universe.Bucket(bucket_name)
  161. if bucket == nil {
  162. return data, fmt.Errorf("No Such Bucket %x\n", bucket_name)
  163. }
  164. solar := bucket.Bucket(solar_bucket)
  165. if solar == nil {
  166. return data, fmt.Errorf("No Such Bucket %x\n", solar_bucket)
  167. }
  168. // now lets find the object
  169. value := solar.Get(key)
  170. data = make([]byte, len(value), len(value))
  171. copy(data, value) // job done
  172. }
  173. return
  174. }
  175. // this function stores a uint64
  176. // this will automcatically use the lock
  177. func (b *BoltStore) StoreUint64(universe_bucket []byte, galaxy_bucket []byte, solar_bucket []byte, key []byte, data uint64) error {
  178. return b.StoreObject(universe_bucket, galaxy_bucket, solar_bucket, key, itob(data))
  179. }
  180. // this function loads the data as 64 byte integer
  181. func (b *BoltStore) LoadUint64(universe_bucket []byte, galaxy_bucket []byte, solar_bucket []byte, key []byte) (uint64, error) {
  182. object_data, err := b.LoadObject(universe_bucket, galaxy_bucket, solar_bucket, key)
  183. if err != nil {
  184. return 0, err
  185. }
  186. if len(object_data) == 0 {
  187. return 0, fmt.Errorf("No value stored here, we should look more")
  188. }
  189. if len(object_data) != 8 {
  190. panic("Database corruption, invalid data ")
  191. }
  192. value := binary.BigEndian.Uint64(object_data)
  193. return value, nil
  194. }
  195. // itob returns an 8-byte big endian representation of v.
  196. func itob(v uint64) []byte {
  197. b := make([]byte, 8)
  198. binary.BigEndian.PutUint64(b, uint64(v))
  199. return b
  200. }