github.com/decred/dcrlnd@v0.7.6/channeldb/migration20/migration.go (about)

     1  package migration20
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  
     7  	"github.com/decred/dcrd/wire"
     8  	"github.com/decred/dcrlnd/kvdb"
     9  	"github.com/decred/dcrlnd/tlv"
    10  )
    11  
    12  var (
    13  	// openChanBucket stores all the open channel information.
    14  	openChanBucket = []byte("open-chan-bucket")
    15  
    16  	// closedChannelBucket stores all the closed channel information.
    17  	closedChannelBucket = []byte("closed-chan-bucket")
    18  
    19  	// outpointBucket is an index mapping outpoints to a tlv
    20  	// stream of channel data.
    21  	outpointBucket = []byte("outpoint-bucket")
    22  )
    23  
    24  const (
    25  	// A tlv type definition used to serialize an outpoint's indexStatus for
    26  	// use in the outpoint index.
    27  	indexStatusType tlv.Type = 0
    28  )
    29  
    30  // indexStatus is an enum-like type that describes what state the
    31  // outpoint is in. Currently only two possible values.
    32  type indexStatus uint8
    33  
    34  const (
    35  	// outpointOpen represents an outpoint that is open in the outpoint index.
    36  	outpointOpen indexStatus = 0
    37  
    38  	// outpointClosed represents an outpoint that is closed in the outpoint
    39  	// index.
    40  	outpointClosed indexStatus = 1
    41  )
    42  
    43  // MigrateOutpointIndex populates the outpoint index with outpoints that
    44  // the node already has. This takes every outpoint in the open channel
    45  // bucket and every outpoint in the closed channel bucket and stores them
    46  // in this index.
    47  func MigrateOutpointIndex(tx kvdb.RwTx) error {
    48  	log.Info("Migrating to the outpoint index")
    49  
    50  	// First get the set of open outpoints.
    51  	openList, err := getOpenOutpoints(tx)
    52  	if err != nil {
    53  		return err
    54  	}
    55  
    56  	// Then get the set of closed outpoints.
    57  	closedList, err := getClosedOutpoints(tx)
    58  	if err != nil {
    59  		return err
    60  	}
    61  
    62  	log.Infof("Found %d open %d closed", len(openList), len(closedList))
    63  
    64  	// Get the outpoint bucket which was created in migration 19.
    65  	bucket := tx.ReadWriteBucket(outpointBucket)
    66  
    67  	// Store the set of open outpoints in the outpoint bucket.
    68  	if err := putOutpoints(bucket, openList, false); err != nil {
    69  		return err
    70  	}
    71  
    72  	// Store the set of closed outpoints in the outpoint bucket.
    73  	return putOutpoints(bucket, closedList, true)
    74  }
    75  
    76  // getOpenOutpoints traverses through the openChanBucket and returns the
    77  // list of these channels' outpoints.
    78  func getOpenOutpoints(tx kvdb.RwTx) ([]*wire.OutPoint, error) {
    79  	var ops []*wire.OutPoint
    80  
    81  	openBucket := tx.ReadBucket(openChanBucket)
    82  	if openBucket == nil {
    83  		return ops, nil
    84  	}
    85  
    86  	// Iterate through every node and chain bucket to get every open
    87  	// outpoint.
    88  	//
    89  	// The bucket tree:
    90  	// openChanBucket -> nodePub -> chainHash -> chanPoint
    91  	err := openBucket.ForEach(func(k, v []byte) error {
    92  		// Ensure that the key is the same size as a pubkey and the
    93  		// value is nil.
    94  		if len(k) != 33 || v != nil {
    95  			log.Warnf("key not 33 bytes (%x)", k)
    96  			return nil
    97  		}
    98  
    99  		nodeBucket := openBucket.NestedReadBucket(k)
   100  		if nodeBucket == nil {
   101  			return nil
   102  		}
   103  
   104  		return nodeBucket.ForEach(func(k, v []byte) error {
   105  			// If there's a value it's not a bucket.
   106  			if v != nil {
   107  				log.Warnf("key not a bucket: %x", k)
   108  				return nil
   109  			}
   110  
   111  			chainBucket := nodeBucket.NestedReadBucket(k)
   112  			if chainBucket == nil {
   113  				return fmt.Errorf("unable to read "+
   114  					"bucket for chain: %x", k)
   115  			}
   116  
   117  			return chainBucket.ForEach(func(k, v []byte) error {
   118  				// If there's a value it's not a bucket.
   119  				if v != nil {
   120  					log.Warnf("entry in chainBucket not a bucket: %x", k)
   121  					return nil
   122  				}
   123  
   124  				var op wire.OutPoint
   125  				r := bytes.NewReader(k)
   126  				if err := readOutpoint(r, &op); err != nil {
   127  					return err
   128  				}
   129  
   130  				ops = append(ops, &op)
   131  
   132  				log.Infof("Appending as open channel %s", op)
   133  
   134  				return nil
   135  			})
   136  		})
   137  	})
   138  	if err != nil {
   139  		return nil, err
   140  	}
   141  	return ops, nil
   142  }
   143  
   144  // getClosedOutpoints traverses through the closedChanBucket and returns
   145  // a list of closed outpoints.
   146  func getClosedOutpoints(tx kvdb.RwTx) ([]*wire.OutPoint, error) {
   147  	var ops []*wire.OutPoint
   148  	closedBucket := tx.ReadBucket(closedChannelBucket)
   149  	if closedBucket == nil {
   150  		return ops, nil
   151  	}
   152  
   153  	// Iterate through every key-value pair to gather all outpoints.
   154  	err := closedBucket.ForEach(func(k, v []byte) error {
   155  		var op wire.OutPoint
   156  		r := bytes.NewReader(k)
   157  		if err := readOutpoint(r, &op); err != nil {
   158  			return err
   159  		}
   160  
   161  		ops = append(ops, &op)
   162  
   163  		return nil
   164  	})
   165  	if err != nil {
   166  		return nil, err
   167  	}
   168  
   169  	return ops, nil
   170  }
   171  
   172  // putOutpoints puts the set of outpoints into the outpoint bucket.
   173  func putOutpoints(bucket kvdb.RwBucket, ops []*wire.OutPoint, isClosed bool) error {
   174  	status := uint8(outpointOpen)
   175  	if isClosed {
   176  		status = uint8(outpointClosed)
   177  	}
   178  
   179  	record := tlv.MakePrimitiveRecord(indexStatusType, &status)
   180  	stream, err := tlv.NewStream(record)
   181  	if err != nil {
   182  		return err
   183  	}
   184  
   185  	var b bytes.Buffer
   186  	if err := stream.Encode(&b); err != nil {
   187  		return err
   188  	}
   189  
   190  	// Store the set of outpoints with the encoded tlv stream.
   191  	for _, op := range ops {
   192  		var opBuf bytes.Buffer
   193  		if err := writeOutpoint(&opBuf, op); err != nil {
   194  			return err
   195  		}
   196  
   197  		if err := bucket.Put(opBuf.Bytes(), b.Bytes()); err != nil {
   198  			return err
   199  		}
   200  	}
   201  
   202  	return nil
   203  }