github.com/decred/dcrlnd@v0.7.6/channeldb/migration_01_to_11/db.go (about) 1 package migration_01_to_11 2 3 import ( 4 "bytes" 5 "encoding/binary" 6 "fmt" 7 "os" 8 "path/filepath" 9 "time" 10 11 "github.com/decred/dcrlnd/kvdb" 12 ) 13 14 const ( 15 dbName = "channel.db" 16 dbFilePermission = 0600 17 ) 18 19 // migration is a function which takes a prior outdated version of the database 20 // instances and mutates the key/bucket structure to arrive at a more 21 // up-to-date version of the database. 22 type migration func(tx kvdb.RwTx) error 23 24 var ( 25 // Big endian is the preferred byte order, due to cursor scans over 26 // integer keys iterating in order. 27 byteOrder = binary.BigEndian 28 ) 29 30 // DB is the primary datastore for the lnd daemon. The database stores 31 // information related to nodes, routing data, open/closed channels, fee 32 // schedules, and reputation data. 33 type DB struct { 34 kvdb.Backend 35 dbPath string 36 graph *ChannelGraph 37 now func() time.Time 38 } 39 40 // Open opens an existing channeldb. Any necessary schemas migrations due to 41 // updates will take place as necessary. 42 func Open(dbPath string, modifiers ...OptionModifier) (*DB, error) { 43 path := filepath.Join(dbPath, dbName) 44 45 if !fileExists(path) { 46 if err := createChannelDB(dbPath); err != nil { 47 return nil, err 48 } 49 } 50 51 opts := DefaultOptions() 52 for _, modifier := range modifiers { 53 modifier(&opts) 54 } 55 56 // Specify bbolt freelist options to reduce heap pressure in case the 57 // freelist grows to be very large. 58 bdb, err := kvdb.Open( 59 kvdb.BoltBackendName, path, 60 opts.NoFreelistSync, opts.DBTimeout, 61 ) 62 if err != nil { 63 return nil, err 64 } 65 66 chanDB := &DB{ 67 Backend: bdb, 68 dbPath: dbPath, 69 now: time.Now, 70 } 71 chanDB.graph = newChannelGraph( 72 chanDB, opts.RejectCacheSize, opts.ChannelCacheSize, 73 ) 74 75 return chanDB, nil 76 } 77 78 // createChannelDB creates and initializes a fresh version of channeldb. In the 79 // case that the target path has not yet been created or doesn't yet exist, 80 // then the path is created. Additionally, all required top-level buckets used 81 // within the database are created. 82 func createChannelDB(dbPath string) error { 83 if !fileExists(dbPath) { 84 if err := os.MkdirAll(dbPath, 0700); err != nil { 85 return err 86 } 87 } 88 89 path := filepath.Join(dbPath, dbName) 90 bdb, err := kvdb.Create( 91 kvdb.BoltBackendName, path, false, kvdb.DefaultDBTimeout, 92 ) 93 if err != nil { 94 return err 95 } 96 97 err = kvdb.Update(bdb, func(tx kvdb.RwTx) error { 98 if _, err := tx.CreateTopLevelBucket(openChannelBucket); err != nil { 99 return err 100 } 101 if _, err := tx.CreateTopLevelBucket(closedChannelBucket); err != nil { 102 return err 103 } 104 105 if _, err := tx.CreateTopLevelBucket(invoiceBucket); err != nil { 106 return err 107 } 108 109 if _, err := tx.CreateTopLevelBucket(paymentBucket); err != nil { 110 return err 111 } 112 113 nodes, err := tx.CreateTopLevelBucket(nodeBucket) 114 if err != nil { 115 return err 116 } 117 _, err = nodes.CreateBucket(aliasIndexBucket) 118 if err != nil { 119 return err 120 } 121 _, err = nodes.CreateBucket(nodeUpdateIndexBucket) 122 if err != nil { 123 return err 124 } 125 126 edges, err := tx.CreateTopLevelBucket(edgeBucket) 127 if err != nil { 128 return err 129 } 130 if _, err := edges.CreateBucket(edgeIndexBucket); err != nil { 131 return err 132 } 133 if _, err := edges.CreateBucket(edgeUpdateIndexBucket); err != nil { 134 return err 135 } 136 if _, err := edges.CreateBucket(channelPointBucket); err != nil { 137 return err 138 } 139 if _, err := edges.CreateBucket(zombieBucket); err != nil { 140 return err 141 } 142 143 graphMeta, err := tx.CreateTopLevelBucket(graphMetaBucket) 144 if err != nil { 145 return err 146 } 147 _, err = graphMeta.CreateBucket(pruneLogBucket) 148 if err != nil { 149 return err 150 } 151 152 if _, err := tx.CreateTopLevelBucket(metaBucket); err != nil { 153 return err 154 } 155 156 meta := &Meta{ 157 DbVersionNumber: 0, 158 } 159 return putMeta(meta, tx) 160 }, func() {}) 161 if err != nil { 162 return fmt.Errorf("unable to create new channeldb") 163 } 164 165 return bdb.Close() 166 } 167 168 // fileExists returns true if the file exists, and false otherwise. 169 func fileExists(path string) bool { 170 if _, err := os.Stat(path); err != nil { 171 if os.IsNotExist(err) { 172 return false 173 } 174 } 175 176 return true 177 } 178 179 // FetchClosedChannels attempts to fetch all closed channels from the database. 180 // The pendingOnly bool toggles if channels that aren't yet fully closed should 181 // be returned in the response or not. When a channel was cooperatively closed, 182 // it becomes fully closed after a single confirmation. When a channel was 183 // forcibly closed, it will become fully closed after _all_ the pending funds 184 // (if any) have been swept. 185 func (d *DB) FetchClosedChannels(pendingOnly bool) ([]*ChannelCloseSummary, error) { 186 var chanSummaries []*ChannelCloseSummary 187 188 if err := kvdb.View(d, func(tx kvdb.RTx) error { 189 closeBucket := tx.ReadBucket(closedChannelBucket) 190 if closeBucket == nil { 191 return ErrNoClosedChannels 192 } 193 194 return closeBucket.ForEach(func(chanID []byte, summaryBytes []byte) error { 195 summaryReader := bytes.NewReader(summaryBytes) 196 chanSummary, err := deserializeCloseChannelSummary(summaryReader) 197 if err != nil { 198 return err 199 } 200 201 // If the query specified to only include pending 202 // channels, then we'll skip any channels which aren't 203 // currently pending. 204 if !chanSummary.IsPending && pendingOnly { 205 return nil 206 } 207 208 chanSummaries = append(chanSummaries, chanSummary) 209 return nil 210 }) 211 }, func() { 212 chanSummaries = nil 213 }); err != nil { 214 return nil, err 215 } 216 217 return chanSummaries, nil 218 } 219 220 // ChannelGraph returns a new instance of the directed channel graph. 221 func (d *DB) ChannelGraph() *ChannelGraph { 222 return d.graph 223 }