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  }