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 }