github.com/decred/dcrlnd@v0.7.6/contractcourt/nursery_store.go (about) 1 package contractcourt 2 3 import ( 4 "bytes" 5 "errors" 6 "fmt" 7 8 "github.com/decred/dcrd/chaincfg/chainhash" 9 "github.com/decred/dcrd/wire" 10 "github.com/decred/dcrlnd/channeldb" 11 "github.com/decred/dcrlnd/kvdb" 12 ) 13 14 // Overview of Nursery Store Storage Hierarchy 15 // 16 // CHAIN SEGMENTATION 17 // 18 // The root directory of a nursery store is bucketed by the chain hash and 19 // the 'utxn' prefix. This allows multiple utxo nurseries for distinct chains 20 // to simultaneously use the same channel.DB instance. This is critical for 21 // providing replay protection and more to isolate chain-specific data in the 22 // multichain setting. 23 // 24 // utxn<chain-hash>/ 25 // | 26 // | CHANNEL INDEX 27 // | 28 // | The channel index contains a directory for each channel that has a 29 // | non-zero number of outputs being tracked by the nursery store. 30 // | Inside each channel directory are files containing serialized spendable 31 // | outputs that are awaiting some state transition. The name of each file 32 // | contains the outpoint of the spendable output in the file, and is 33 // | prefixed with 4-byte state prefix, indicating whether the spendable 34 // | output is a crib, preschool, or kindergarten, or graduated output. The 35 // | nursery store supports the ability to enumerate all outputs for a 36 // | particular channel, which is useful in constructing nursery reports. 37 // | 38 // ├── channel-index-key/ 39 // │ ├── <chan-point-1>/ <- CHANNEL BUCKET 40 // | | ├── <state-prefix><outpoint-1>: <spendable-output-1> 41 // | | └── <state-prefix><outpoint-2>: <spendable-output-2> 42 // │ ├── <chan-point-2>/ 43 // | | └── <state-prefix><outpoint-3>: <spendable-output-3> 44 // │ └── <chan-point-3>/ 45 // | ├── <state-prefix><outpoint-4>: <spendable-output-4> 46 // | └── <state-prefix><outpoint-5>: <spendable-output-5> 47 // | 48 // | HEIGHT INDEX 49 // | 50 // | The height index contains a directory for each height at which the 51 // | nursery still has scheduled actions. If an output is a crib or 52 // | kindergarten output, it will have an associated entry in the height 53 // | index. Inside a particular height directory, the structure is similar 54 // | to that of the channel index, containing multiple channel directories, 55 // | each of which contains subdirectories named with a prefixed outpoint 56 // | belonging to the channel. Enumerating these combinations yields a 57 // | relative file path: 58 // | e.g. <chan-point-3>/<prefix><outpoint-2>/ 59 // | that can be queried in the channel index to retrieve the serialized 60 // | output. 61 // | 62 // └── height-index-key/ 63 // ├── <height-1>/ <- HEIGHT BUCKET 64 // | ├── <chan-point-3>/ <- HEIGHT-CHANNEL BUCKET 65 // | | ├── <state-prefix><outpoint-4>: "" <- PREFIXED OUTPOINT 66 // | | └── <state-prefix><outpoint-5>: "" 67 // | ├── <chan-point-2>/ 68 // | | └── <state-prefix><outpoint-3>: "" 69 // └── <height-2>/ 70 // └── <chan-point-1>/ 71 // └── <state-prefix><outpoint-1>: "" 72 // └── <state-prefix><outpoint-2>: "" 73 74 // TODO(joostjager): Add database migration to clean up now unused last 75 // graduated height and finalized txes. This also prevents people downgrading 76 // and surprising the downgraded nursery with missing data. 77 78 // NurseryStorer abstracts the persistent storage layer for the utxo nursery. 79 // Concretely, it stores commitment and htlc outputs until any time-bounded 80 // constraints have fully matured. The store exposes methods for enumerating its 81 // contents, and persisting state transitions detected by the utxo nursery. 82 type NurseryStorer interface { 83 // Incubate registers a set of CSV delayed outputs (incoming HTLC's on 84 // our commitment transaction, or a commitment output), and a slice of 85 // outgoing htlc outputs to be swept back into the user's wallet. The 86 // event is persisted to disk, such that the nursery can resume the 87 // incubation process after a potential crash. 88 Incubate([]kidOutput, []babyOutput) error 89 90 // CribToKinder atomically moves a babyOutput in the crib bucket to the 91 // kindergarten bucket. Baby outputs are outgoing HTLC's which require 92 // us to go to the second-layer to claim. The now mature kidOutput 93 // contained in the babyOutput will be stored as it waits out the 94 // kidOutput's CSV delay. 95 CribToKinder(*babyOutput) error 96 97 // PreschoolToKinder atomically moves a kidOutput from the preschool 98 // bucket to the kindergarten bucket. This transition should be executed 99 // after receiving confirmation of the preschool output. Incoming HTLC's 100 // we need to go to the second-layer to claim, and also our commitment 101 // outputs fall into this class. 102 // 103 // An additional parameter specifies the last graduated height. This is 104 // used in case of late registration. It schedules the output for sweep 105 // at the next epoch even though it has already expired earlier. 106 PreschoolToKinder(kid *kidOutput, lastGradHeight uint32) error 107 108 // GraduateKinder atomically moves an output at the provided height into 109 // the graduated status. This involves removing the kindergarten entries 110 // from both the height and channel indexes. The height bucket will be 111 // opportunistically pruned from the height index as outputs are 112 // removed. 113 GraduateKinder(height uint32, output *kidOutput) error 114 115 // FetchPreschools returns a list of all outputs currently stored in 116 // the preschool bucket. 117 FetchPreschools() ([]kidOutput, error) 118 119 // FetchClass returns a list of kindergarten and crib outputs whose 120 // timelocks expire at the given height. 121 FetchClass(height uint32) ([]kidOutput, []babyOutput, error) 122 123 // HeightsBelowOrEqual returns the lowest non-empty heights in the 124 // height index, that exist at or below the provided upper bound. 125 HeightsBelowOrEqual(height uint32) ([]uint32, error) 126 127 // ForChanOutputs iterates over all outputs being incubated for a 128 // particular channel point. This method accepts a callback that allows 129 // the caller to process each key-value pair. The key will be a prefixed 130 // outpoint, and the value will be the serialized bytes for an output, 131 // whose type should be inferred from the key's prefix. 132 ForChanOutputs(*wire.OutPoint, func([]byte, []byte) error, func()) error 133 134 // ListChannels returns all channels the nursery is currently tracking. 135 ListChannels() ([]wire.OutPoint, error) 136 137 // IsMatureChannel determines the whether or not all of the outputs in a 138 // particular channel bucket have been marked as graduated. 139 IsMatureChannel(*wire.OutPoint) (bool, error) 140 141 // RemoveChannel channel erases all entries from the channel bucket for 142 // the provided channel point, this method should only be called if 143 // IsMatureChannel indicates the channel is ready for removal. 144 RemoveChannel(*wire.OutPoint) error 145 } 146 147 var ( 148 // utxnChainPrefix is used to prefix a particular chain hash and create 149 // the root-level, chain-segmented bucket for each nursery store. 150 utxnChainPrefix = []byte("utxn") 151 152 // channelIndexKey is a static key used to lookup the bucket containing 153 // all of the nursery's active channels. 154 channelIndexKey = []byte("channel-index") 155 156 // channelIndexKey is a static key used to retrieve a directory 157 // containing all heights for which the nursery will need to take 158 // action. 159 heightIndexKey = []byte("height-index") 160 ) 161 162 // Defines the state prefixes that will be used to persistently track an 163 // output's progress through the nursery. 164 // NOTE: Each state prefix MUST be exactly 4 bytes in length, the nursery logic 165 // depends on the ability to create keys for a different state by overwriting 166 // an existing state prefix. 167 var ( 168 // cribPrefix is the state prefix given to htlc outputs waiting for 169 // their first-stage, absolute locktime to elapse. 170 cribPrefix = []byte("crib") 171 172 // psclPrefix is the state prefix given to commitment outputs awaiting 173 // the confirmation of the commitment transaction, as this solidifies 174 // the absolute height at which they can be spent. 175 psclPrefix = []byte("pscl") 176 177 // kndrPrefix is the state prefix given to all CSV delayed outputs, 178 // either from the commitment transaction, or a stage-one htlc 179 // transaction, whose maturity height has solidified. Outputs marked in 180 // this state are in their final stage of incubation within the nursery, 181 // and will be swept into the wallet after waiting out the relative 182 // timelock. 183 kndrPrefix = []byte("kndr") 184 185 // gradPrefix is the state prefix given to all outputs that have been 186 // completely incubated. Once all outputs have been marked as graduated, 187 // this serves as a persistent marker that the nursery should mark the 188 // channel fully closed in the channeldb. 189 gradPrefix = []byte("grad") 190 ) 191 192 // prefixChainKey creates the root level keys for the nursery store. The keys 193 // are comprised of a nursery-specific prefix and the intended chain hash that 194 // this nursery store will be used for. This allows multiple nursery stores to 195 // isolate their state when operating on multiple chains or forks. 196 func prefixChainKey(sysPrefix []byte, hash *chainhash.Hash) ([]byte, error) { 197 // Create a buffer to which we will write the system prefix, e.g. 198 // "utxn", followed by the provided chain hash. 199 var pfxChainBuffer bytes.Buffer 200 if _, err := pfxChainBuffer.Write(sysPrefix); err != nil { 201 return nil, err 202 } 203 204 if _, err := pfxChainBuffer.Write(hash[:]); err != nil { 205 return nil, err 206 } 207 208 return pfxChainBuffer.Bytes(), nil 209 } 210 211 // prefixOutputKey creates a serialized key that prefixes the serialized 212 // outpoint with the provided state prefix. The returned bytes will be of the 213 // form <prefix><outpoint>. 214 func prefixOutputKey(statePrefix []byte, 215 outpoint *wire.OutPoint) ([]byte, error) { 216 217 // Create a buffer to which we will first write the state prefix, 218 // followed by the outpoint. 219 var pfxOutputBuffer bytes.Buffer 220 if _, err := pfxOutputBuffer.Write(statePrefix); err != nil { 221 return nil, err 222 } 223 224 err := writeOutpoint(&pfxOutputBuffer, outpoint) 225 if err != nil { 226 return nil, err 227 } 228 229 return pfxOutputBuffer.Bytes(), nil 230 } 231 232 // NurseryStore is a concrete instantiation of a NurseryStore that is backed by 233 // a channeldb.DB instance. 234 type NurseryStore struct { 235 chainHash chainhash.Hash 236 db *channeldb.DB 237 238 pfxChainKey []byte 239 } 240 241 // NewNurseryStore accepts a chain hash and a channeldb.DB instance, returning 242 // an instance of NurseryStore who's database is properly segmented for the 243 // given chain. 244 func NewNurseryStore(chainHash *chainhash.Hash, 245 db *channeldb.DB) (*NurseryStore, error) { 246 247 // Prefix the provided chain hash with "utxn" to create the key for the 248 // nursery store's root bucket, ensuring each one has proper chain 249 // segmentation. 250 pfxChainKey, err := prefixChainKey(utxnChainPrefix, chainHash) 251 if err != nil { 252 return nil, err 253 } 254 255 return &NurseryStore{ 256 chainHash: *chainHash, 257 db: db, 258 pfxChainKey: pfxChainKey, 259 }, nil 260 } 261 262 // Incubate persists the beginning of the incubation process for the 263 // CSV-delayed outputs (commitment and incoming HTLC's), commitment output and 264 // a list of outgoing two-stage htlc outputs. 265 func (ns *NurseryStore) Incubate(kids []kidOutput, babies []babyOutput) error { 266 return kvdb.Update(ns.db, func(tx kvdb.RwTx) error { 267 // If we have any kid outputs to incubate, then we'll attempt 268 // to add each of them to the nursery store. Any duplicate 269 // outputs will be ignored. 270 for _, kid := range kids { 271 if err := ns.enterPreschool(tx, &kid); err != nil { 272 return err 273 } 274 } 275 276 // Next, we'll Add all htlc outputs to the crib bucket. 277 // Similarly, we'll ignore any outputs that have already been 278 // inserted. 279 for _, baby := range babies { 280 if err := ns.enterCrib(tx, &baby); err != nil { 281 return err 282 } 283 } 284 285 return nil 286 }, func() {}) 287 } 288 289 // CribToKinder atomically moves a babyOutput in the crib bucket to the 290 // kindergarten bucket. The now mature kidOutput contained in the babyOutput 291 // will be stored as it waits out the kidOutput's CSV delay. 292 func (ns *NurseryStore) CribToKinder(bby *babyOutput) error { 293 return kvdb.Update(ns.db, func(tx kvdb.RwTx) error { 294 295 // First, retrieve or create the channel bucket corresponding to 296 // the baby output's origin channel point. 297 chanPoint := bby.OriginChanPoint() 298 chanBucket, err := ns.createChannelBucket(tx, chanPoint) 299 if err != nil { 300 return err 301 } 302 303 // The babyOutput should currently be stored in the crib bucket. 304 // So, we create a key that prefixes the babyOutput's outpoint 305 // with the crib prefix, allowing us to reference it in the 306 // store. 307 pfxOutputKey, err := prefixOutputKey(cribPrefix, bby.OutPoint()) 308 if err != nil { 309 return err 310 } 311 312 // Since the babyOutput is being moved to the kindergarten 313 // bucket, we remove the entry from the channel bucket under the 314 // crib-prefixed outpoint key. 315 if err := chanBucket.Delete(pfxOutputKey); err != nil { 316 return err 317 } 318 319 // Remove the crib output's entry in the height index. 320 err = ns.removeOutputFromHeight(tx, bby.expiry, chanPoint, 321 pfxOutputKey) 322 if err != nil { 323 return err 324 } 325 326 // Since we are moving this output from the crib bucket to the 327 // kindergarten bucket, we overwrite the existing prefix of this 328 // key with the kindergarten prefix. 329 copy(pfxOutputKey, kndrPrefix) 330 331 // Now, serialize babyOutput's encapsulated kidOutput such that 332 // it can be written to the channel bucket under the new 333 // kindergarten-prefixed key. 334 var kidBuffer bytes.Buffer 335 if err := bby.kidOutput.Encode(&kidBuffer); err != nil { 336 return err 337 } 338 kidBytes := kidBuffer.Bytes() 339 340 // Persist the serialized kidOutput under the 341 // kindergarten-prefixed outpoint key. 342 if err := chanBucket.Put(pfxOutputKey, kidBytes); err != nil { 343 return err 344 } 345 346 // Now, compute the height at which this kidOutput's CSV delay 347 // will expire. This is done by adding the required delay to 348 // the block height at which the output was confirmed. 349 maturityHeight := bby.ConfHeight() + bby.BlocksToMaturity() 350 351 // Retrieve or create a height-channel bucket corresponding to 352 // the kidOutput's maturity height. 353 hghtChanBucketCsv, err := ns.createHeightChanBucket(tx, 354 maturityHeight, chanPoint) 355 if err != nil { 356 return err 357 } 358 359 utxnLog.Tracef("Transitioning (crib -> baby) output for "+ 360 "chan_point=%v at height_index=%v", chanPoint, 361 maturityHeight) 362 363 // Register the kindergarten output's prefixed output key in the 364 // height-channel bucket corresponding to its maturity height. 365 // This informs the utxo nursery that it should attempt to spend 366 // this output when the blockchain reaches the maturity height. 367 return hghtChanBucketCsv.Put(pfxOutputKey, []byte{}) 368 }, func() {}) 369 } 370 371 // PreschoolToKinder atomically moves a kidOutput from the preschool bucket to 372 // the kindergarten bucket. This transition should be executed after receiving 373 // confirmation of the preschool output's commitment transaction. 374 func (ns *NurseryStore) PreschoolToKinder(kid *kidOutput, 375 lastGradHeight uint32) error { 376 377 return kvdb.Update(ns.db, func(tx kvdb.RwTx) error { 378 // Create or retrieve the channel bucket corresponding to the 379 // kid output's origin channel point. 380 chanPoint := kid.OriginChanPoint() 381 chanBucket, err := ns.createChannelBucket(tx, chanPoint) 382 if err != nil { 383 return err 384 } 385 386 // First, we will attempt to remove the existing serialized 387 // output from the channel bucket, where the kid's outpoint will 388 // be prefixed by a preschool prefix. 389 390 // Generate the key of existing serialized kid output by 391 // prefixing its outpoint with the preschool prefix... 392 pfxOutputKey, err := prefixOutputKey(psclPrefix, kid.OutPoint()) 393 if err != nil { 394 return err 395 } 396 397 // And remove the old serialized output from the database. 398 if err := chanBucket.Delete(pfxOutputKey); err != nil { 399 return err 400 } 401 402 // Next, we will write the provided kid outpoint to the channel 403 // bucket, using a key prefixed by the kindergarten prefix. 404 405 // Convert the preschool prefix key into a kindergarten key for 406 // the same outpoint. 407 copy(pfxOutputKey, kndrPrefix) 408 409 // Reserialize the kid here to capture any differences in the 410 // new and old kid output, such as the confirmation height. 411 var kidBuffer bytes.Buffer 412 if err := kid.Encode(&kidBuffer); err != nil { 413 return err 414 } 415 kidBytes := kidBuffer.Bytes() 416 417 // And store the kid output in its channel bucket using the 418 // kindergarten prefixed key. 419 if err := chanBucket.Put(pfxOutputKey, kidBytes); err != nil { 420 return err 421 } 422 423 // If this output has an absolute time lock, then we'll set the 424 // maturity height directly. 425 var maturityHeight uint32 426 if kid.BlocksToMaturity() == 0 { 427 maturityHeight = kid.absoluteMaturity 428 } else { 429 // Otherwise, since the CSV delay on the kid output has 430 // now begun ticking, we must insert a record of in the 431 // height index to remind us to revisit this output 432 // once it has fully matured. 433 // 434 // Compute the maturity height, by adding the output's 435 // CSV delay to its confirmation height. 436 maturityHeight = kid.ConfHeight() + kid.BlocksToMaturity() 437 } 438 439 if maturityHeight <= lastGradHeight { 440 utxnLog.Debugf("Late Registration for kid output=%v "+ 441 "detected: class_height=%v, "+ 442 "last_graduated_height=%v", kid.OutPoint(), 443 maturityHeight, lastGradHeight) 444 445 maturityHeight = lastGradHeight + 1 446 } 447 448 utxnLog.Infof("Transitioning (crib -> kid) output for "+ 449 "chan_point=%v at height_index=%v", chanPoint, 450 maturityHeight) 451 452 // Create or retrieve the height-channel bucket for this 453 // channel. This method will first create a height bucket for 454 // the given maturity height if none exists. 455 hghtChanBucket, err := ns.createHeightChanBucket(tx, 456 maturityHeight, chanPoint) 457 if err != nil { 458 return err 459 } 460 461 // Finally, we touch a key in the height-channel created above. 462 // The key is named using a kindergarten prefixed key, signaling 463 // that this CSV delayed output will be ready to broadcast at 464 // the maturity height, after a brief period of incubation. 465 return hghtChanBucket.Put(pfxOutputKey, []byte{}) 466 }, func() {}) 467 } 468 469 // GraduateKinder atomically moves an output at the provided height into the 470 // graduated status. This involves removing the kindergarten entries from both 471 // the height and channel indexes. The height bucket will be opportunistically 472 // pruned from the height index as outputs are removed. 473 func (ns *NurseryStore) GraduateKinder(height uint32, kid *kidOutput) error { 474 return kvdb.Update(ns.db, func(tx kvdb.RwTx) error { 475 476 hghtBucket := ns.getHeightBucket(tx, height) 477 if hghtBucket == nil { 478 // Nothing to delete, bucket has already been removed. 479 return nil 480 } 481 482 // For the kindergarten output, delete its entry from the 483 // height and channel index, and create a new grad output in the 484 // channel index. 485 outpoint := kid.OutPoint() 486 chanPoint := kid.OriginChanPoint() 487 488 // Construct the key under which the output is 489 // currently stored height and channel indexes. 490 pfxOutputKey, err := prefixOutputKey(kndrPrefix, 491 outpoint) 492 if err != nil { 493 return err 494 } 495 496 // Remove the grad output's entry in the height 497 // index. 498 err = ns.removeOutputFromHeight(tx, height, 499 chanPoint, pfxOutputKey) 500 if err != nil { 501 return err 502 } 503 504 chanBucket := ns.getChannelBucketWrite(tx, chanPoint) 505 if chanBucket == nil { 506 return ErrContractNotFound 507 } 508 509 // Remove previous output with kindergarten 510 // prefix. 511 err = chanBucket.Delete(pfxOutputKey) 512 if err != nil { 513 return err 514 } 515 516 // Convert kindergarten key to graduate key. 517 copy(pfxOutputKey, gradPrefix) 518 519 var gradBuffer bytes.Buffer 520 if err := kid.Encode(&gradBuffer); err != nil { 521 return err 522 } 523 524 // Insert serialized output into channel bucket 525 // using graduate-prefixed key. 526 return chanBucket.Put(pfxOutputKey, 527 gradBuffer.Bytes()) 528 }, func() {}) 529 } 530 531 // FetchClass returns a list of babyOutputs in the crib bucket whose CLTV 532 // delay expires at the provided block height. 533 // FetchClass returns a list of the kindergarten and crib outputs whose timeouts 534 // are expiring 535 func (ns *NurseryStore) FetchClass( 536 height uint32) ([]kidOutput, []babyOutput, error) { 537 538 // Construct list of all crib and kindergarten outputs that need to be 539 // processed at the provided block height. 540 var kids []kidOutput 541 var babies []babyOutput 542 if err := kvdb.View(ns.db, func(tx kvdb.RTx) error { 543 // Append each crib output to our list of babyOutputs. 544 if err := ns.forEachHeightPrefix(tx, cribPrefix, height, 545 func(buf []byte) error { 546 547 // We will attempt to deserialize all outputs 548 // stored with the crib prefix into babyOutputs, 549 // since this is the expected type that would 550 // have been serialized previously. 551 var baby babyOutput 552 babyReader := bytes.NewReader(buf) 553 if err := baby.Decode(babyReader); err != nil { 554 return err 555 } 556 557 babies = append(babies, baby) 558 559 return nil 560 561 }, 562 ); err != nil { 563 return err 564 } 565 566 // Append each kindergarten output to our list of kidOutputs. 567 return ns.forEachHeightPrefix(tx, kndrPrefix, height, 568 func(buf []byte) error { 569 // We will attempt to deserialize all outputs 570 // stored with the kindergarten prefix into 571 // kidOutputs, since this is the expected type 572 // that would have been serialized previously. 573 var kid kidOutput 574 kidReader := bytes.NewReader(buf) 575 if err := kid.Decode(kidReader); err != nil { 576 return err 577 } 578 579 kids = append(kids, kid) 580 581 return nil 582 583 }) 584 585 }, func() { 586 kids = nil 587 babies = nil 588 }); err != nil { 589 return nil, nil, err 590 } 591 592 return kids, babies, nil 593 } 594 595 // FetchPreschools returns a list of all outputs currently stored in the 596 // preschool bucket. 597 func (ns *NurseryStore) FetchPreschools() ([]kidOutput, error) { 598 var kids []kidOutput 599 if err := kvdb.View(ns.db, func(tx kvdb.RTx) error { 600 601 // Retrieve the existing chain bucket for this nursery store. 602 chainBucket := tx.ReadBucket(ns.pfxChainKey) 603 if chainBucket == nil { 604 return nil 605 } 606 607 // Load the existing channel index from the chain bucket. 608 chanIndex := chainBucket.NestedReadBucket(channelIndexKey) 609 if chanIndex == nil { 610 return nil 611 } 612 613 // Construct a list of all channels in the channel index that 614 // are currently being tracked by the nursery store. 615 var activeChannels [][]byte 616 if err := chanIndex.ForEach(func(chanBytes, _ []byte) error { 617 activeChannels = append(activeChannels, chanBytes) 618 return nil 619 }); err != nil { 620 return err 621 } 622 623 // Iterate over all of the accumulated channels, and do a prefix 624 // scan inside of each channel bucket. Each output found that 625 // has a preschool prefix will be deserialized into a kidOutput, 626 // and added to our list of preschool outputs to return to the 627 // caller. 628 for _, chanBytes := range activeChannels { 629 // Retrieve the channel bucket associated with this 630 // channel. 631 chanBucket := chanIndex.NestedReadBucket(chanBytes) 632 if chanBucket == nil { 633 continue 634 } 635 636 // All of the outputs of interest will start with the 637 // "pscl" prefix. So, we will perform a prefix scan of 638 // the channel bucket to efficiently enumerate all the 639 // desired outputs. 640 c := chanBucket.ReadCursor() 641 for k, v := c.Seek(psclPrefix); bytes.HasPrefix( 642 k, psclPrefix); k, v = c.Next() { 643 644 // Deserialize each output as a kidOutput, since 645 // this should have been the type that was 646 // serialized when it was written to disk. 647 var psclOutput kidOutput 648 psclReader := bytes.NewReader(v) 649 err := psclOutput.Decode(psclReader) 650 if err != nil { 651 return err 652 } 653 654 // Add the deserialized output to our list of 655 // preschool outputs. 656 kids = append(kids, psclOutput) 657 } 658 } 659 660 return nil 661 }, func() { 662 kids = nil 663 }); err != nil { 664 return nil, err 665 } 666 667 return kids, nil 668 } 669 670 // HeightsBelowOrEqual returns a slice of all non-empty heights in the height 671 // index at or below the provided upper bound. 672 func (ns *NurseryStore) HeightsBelowOrEqual(height uint32) ([]uint32, error) { 673 var activeHeights []uint32 674 err := kvdb.View(ns.db, func(tx kvdb.RTx) error { 675 // Ensure that the chain bucket for this nursery store exists. 676 chainBucket := tx.ReadBucket(ns.pfxChainKey) 677 if chainBucket == nil { 678 return nil 679 } 680 681 // Ensure that the height index has been properly initialized for this 682 // chain. 683 hghtIndex := chainBucket.NestedReadBucket(heightIndexKey) 684 if hghtIndex == nil { 685 return nil 686 } 687 688 // Serialize the provided height, as this will form the name of the 689 // bucket. 690 var lower, upper [4]byte 691 byteOrder.PutUint32(upper[:], height) 692 693 c := hghtIndex.ReadCursor() 694 for k, _ := c.Seek(lower[:]); bytes.Compare(k, upper[:]) <= 0 && 695 len(k) == 4; k, _ = c.Next() { 696 697 activeHeights = append(activeHeights, byteOrder.Uint32(k)) 698 } 699 700 return nil 701 }, func() { 702 activeHeights = nil 703 }) 704 if err != nil { 705 return nil, err 706 } 707 708 return activeHeights, nil 709 } 710 711 // ForChanOutputs iterates over all outputs being incubated for a particular 712 // channel point. This method accepts a callback that allows the caller to 713 // process each key-value pair. The key will be a prefixed outpoint, and the 714 // value will be the serialized bytes for an output, whose type should be 715 // inferred from the key's prefix. 716 // NOTE: The callback should not modify the provided byte slices and is 717 // preferably non-blocking. 718 func (ns *NurseryStore) ForChanOutputs(chanPoint *wire.OutPoint, 719 callback func([]byte, []byte) error, reset func()) error { 720 721 return kvdb.View(ns.db, func(tx kvdb.RTx) error { 722 return ns.forChanOutputs(tx, chanPoint, callback) 723 }, reset) 724 } 725 726 // ListChannels returns all channels the nursery is currently tracking. 727 func (ns *NurseryStore) ListChannels() ([]wire.OutPoint, error) { 728 var activeChannels []wire.OutPoint 729 if err := kvdb.View(ns.db, func(tx kvdb.RTx) error { 730 // Retrieve the existing chain bucket for this nursery store. 731 chainBucket := tx.ReadBucket(ns.pfxChainKey) 732 if chainBucket == nil { 733 return nil 734 } 735 736 // Retrieve the existing channel index. 737 chanIndex := chainBucket.NestedReadBucket(channelIndexKey) 738 if chanIndex == nil { 739 return nil 740 } 741 742 return chanIndex.ForEach(func(chanBytes, _ []byte) error { 743 var chanPoint wire.OutPoint 744 err := readOutpoint(bytes.NewReader(chanBytes), &chanPoint) 745 if err != nil { 746 return err 747 } 748 749 activeChannels = append(activeChannels, chanPoint) 750 751 return nil 752 }) 753 }, func() { 754 activeChannels = nil 755 }); err != nil { 756 return nil, err 757 } 758 759 return activeChannels, nil 760 } 761 762 // IsMatureChannel determines the whether or not all of the outputs in a 763 // particular channel bucket have been marked as graduated. 764 func (ns *NurseryStore) IsMatureChannel(chanPoint *wire.OutPoint) (bool, error) { 765 err := kvdb.View(ns.db, func(tx kvdb.RTx) error { 766 // Iterate over the contents of the channel bucket, computing 767 // both total number of outputs, and those that have the grad 768 // prefix. 769 return ns.forChanOutputs(tx, chanPoint, 770 func(pfxKey, _ []byte) error { 771 if !bytes.HasPrefix(pfxKey, gradPrefix) { 772 return ErrImmatureChannel 773 } 774 return nil 775 }) 776 777 }, func() {}) 778 if err != nil && err != ErrImmatureChannel { 779 return false, err 780 } 781 782 return err == nil, nil 783 } 784 785 // ErrImmatureChannel signals a channel cannot be removed because not all of its 786 // outputs have graduated. 787 var ErrImmatureChannel = errors.New("cannot remove immature channel, " + 788 "still has ungraduated outputs") 789 790 // RemoveChannel channel erases all entries from the channel bucket for the 791 // provided channel point. 792 // NOTE: The channel's entries in the height index are assumed to be removed. 793 func (ns *NurseryStore) RemoveChannel(chanPoint *wire.OutPoint) error { 794 return kvdb.Update(ns.db, func(tx kvdb.RwTx) error { 795 // Retrieve the existing chain bucket for this nursery store. 796 chainBucket := tx.ReadWriteBucket(ns.pfxChainKey) 797 if chainBucket == nil { 798 return nil 799 } 800 801 // Retrieve the channel index stored in the chain bucket. 802 chanIndex := chainBucket.NestedReadWriteBucket(channelIndexKey) 803 if chanIndex == nil { 804 return nil 805 } 806 807 // Serialize the provided channel point, such that we can delete 808 // the mature channel bucket. 809 var chanBuffer bytes.Buffer 810 if err := writeOutpoint(&chanBuffer, chanPoint); err != nil { 811 return err 812 } 813 chanBytes := chanBuffer.Bytes() 814 815 err := ns.forChanOutputs(tx, chanPoint, func(k, v []byte) error { 816 if !bytes.HasPrefix(k, gradPrefix) { 817 return ErrImmatureChannel 818 } 819 820 // Construct a kindergarten prefixed key, since this 821 // would have been the preceding state for a grad 822 // output. 823 kndrKey := make([]byte, len(k)) 824 copy(kndrKey, k) 825 copy(kndrKey[:4], kndrPrefix) 826 827 // Decode each to retrieve the output's maturity height. 828 var kid kidOutput 829 if err := kid.Decode(bytes.NewReader(v)); err != nil { 830 return err 831 } 832 833 maturityHeight := kid.ConfHeight() + kid.BlocksToMaturity() 834 835 hghtBucket := ns.getHeightBucketWrite(tx, maturityHeight) 836 if hghtBucket == nil { 837 return nil 838 } 839 840 return removeBucketIfExists(hghtBucket, chanBytes) 841 }) 842 if err != nil { 843 return err 844 } 845 846 return removeBucketIfExists(chanIndex, chanBytes) 847 }, func() {}) 848 } 849 850 // Helper Methods 851 852 // enterCrib accepts a new htlc output that the nursery will incubate through 853 // its two-stage process of sweeping funds back to the user's wallet. These 854 // outputs are persisted in the nursery store in the crib state, and will be 855 // revisited after the first-stage output's CLTV has expired. 856 func (ns *NurseryStore) enterCrib(tx kvdb.RwTx, baby *babyOutput) error { 857 // First, retrieve or create the channel bucket corresponding to the 858 // baby output's origin channel point. 859 chanPoint := baby.OriginChanPoint() 860 chanBucket, err := ns.createChannelBucket(tx, chanPoint) 861 if err != nil { 862 return err 863 } 864 865 // Since we are inserting this output into the crib bucket, we create a 866 // key that prefixes the baby output's outpoint with the crib prefix. 867 pfxOutputKey, err := prefixOutputKey(cribPrefix, baby.OutPoint()) 868 if err != nil { 869 return err 870 } 871 872 // We'll first check that we don't already have an entry for this 873 // output. If we do, then we can exit early. 874 if rawBytes := chanBucket.Get(pfxOutputKey); rawBytes != nil { 875 return nil 876 } 877 878 // Next, retrieve or create the height-channel bucket located in the 879 // height bucket corresponding to the baby output's CLTV expiry height. 880 881 // TODO: Handle late registration. 882 hghtChanBucket, err := ns.createHeightChanBucket(tx, 883 baby.expiry, chanPoint) 884 if err != nil { 885 return err 886 } 887 888 // Serialize the baby output so that it can be written to the 889 // underlying key-value store. 890 var babyBuffer bytes.Buffer 891 if err := baby.Encode(&babyBuffer); err != nil { 892 return err 893 } 894 babyBytes := babyBuffer.Bytes() 895 896 // Now, insert the serialized output into its channel bucket under the 897 // prefixed key created above. 898 if err := chanBucket.Put(pfxOutputKey, babyBytes); err != nil { 899 return err 900 } 901 902 // Finally, create a corresponding bucket in the height-channel bucket 903 // for this crib output. The existence of this bucket indicates that 904 // the serialized output can be retrieved from the channel bucket using 905 // the same prefix key. 906 return hghtChanBucket.Put(pfxOutputKey, []byte{}) 907 } 908 909 // enterPreschool accepts a new commitment output that the nursery will incubate 910 // through a single stage before sweeping. Outputs are stored in the preschool 911 // bucket until the commitment transaction has been confirmed, at which point 912 // they will be moved to the kindergarten bucket. 913 func (ns *NurseryStore) enterPreschool(tx kvdb.RwTx, kid *kidOutput) error { 914 // First, retrieve or create the channel bucket corresponding to the 915 // baby output's origin channel point. 916 chanPoint := kid.OriginChanPoint() 917 chanBucket, err := ns.createChannelBucket(tx, chanPoint) 918 if err != nil { 919 return err 920 } 921 922 // Since the kidOutput is being inserted into the preschool bucket, we 923 // create a key that prefixes its outpoint with the preschool prefix. 924 pfxOutputKey, err := prefixOutputKey(psclPrefix, kid.OutPoint()) 925 if err != nil { 926 return err 927 } 928 929 // We'll first check if an entry for this key is already stored. If so, 930 // then we'll ignore this request, and return a nil error. 931 if rawBytes := chanBucket.Get(pfxOutputKey); rawBytes != nil { 932 return nil 933 } 934 935 // Serialize the kidOutput and insert it into the channel bucket. 936 var kidBuffer bytes.Buffer 937 if err := kid.Encode(&kidBuffer); err != nil { 938 return err 939 } 940 941 return chanBucket.Put(pfxOutputKey, kidBuffer.Bytes()) 942 } 943 944 // createChannelBucket creates or retrieves a channel bucket for the provided 945 // channel point. 946 func (ns *NurseryStore) createChannelBucket(tx kvdb.RwTx, 947 chanPoint *wire.OutPoint) (kvdb.RwBucket, error) { 948 949 // Ensure that the chain bucket for this nursery store exists. 950 chainBucket, err := tx.CreateTopLevelBucket(ns.pfxChainKey) 951 if err != nil { 952 return nil, err 953 } 954 955 // Ensure that the channel index has been properly initialized for this 956 // chain. 957 chanIndex, err := chainBucket.CreateBucketIfNotExists(channelIndexKey) 958 if err != nil { 959 return nil, err 960 } 961 962 // Serialize the provided channel point, as this provides the name of 963 // the channel bucket of interest. 964 var chanBuffer bytes.Buffer 965 if err := writeOutpoint(&chanBuffer, chanPoint); err != nil { 966 return nil, err 967 } 968 969 // Finally, create or retrieve the channel bucket using the serialized 970 // key. 971 return chanIndex.CreateBucketIfNotExists(chanBuffer.Bytes()) 972 } 973 974 // getChannelBucket retrieves an existing channel bucket from the nursery store, 975 // using the given channel point. If the bucket does not exist, or any bucket 976 // along its path does not exist, a nil value is returned. 977 func (ns *NurseryStore) getChannelBucket(tx kvdb.RTx, 978 chanPoint *wire.OutPoint) kvdb.RBucket { 979 980 // Retrieve the existing chain bucket for this nursery store. 981 chainBucket := tx.ReadBucket(ns.pfxChainKey) 982 if chainBucket == nil { 983 return nil 984 } 985 986 // Retrieve the existing channel index. 987 chanIndex := chainBucket.NestedReadBucket(channelIndexKey) 988 if chanIndex == nil { 989 return nil 990 } 991 992 // Serialize the provided channel point and return the bucket matching 993 // the serialized key. 994 var chanBuffer bytes.Buffer 995 if err := writeOutpoint(&chanBuffer, chanPoint); err != nil { 996 return nil 997 } 998 999 return chanIndex.NestedReadBucket(chanBuffer.Bytes()) 1000 } 1001 1002 // getChannelBucketWrite retrieves an existing channel bucket from the nursery store, 1003 // using the given channel point. If the bucket does not exist, or any bucket 1004 // along its path does not exist, a nil value is returned. 1005 func (ns *NurseryStore) getChannelBucketWrite(tx kvdb.RwTx, 1006 chanPoint *wire.OutPoint) kvdb.RwBucket { 1007 1008 // Retrieve the existing chain bucket for this nursery store. 1009 chainBucket := tx.ReadWriteBucket(ns.pfxChainKey) 1010 if chainBucket == nil { 1011 return nil 1012 } 1013 1014 // Retrieve the existing channel index. 1015 chanIndex := chainBucket.NestedReadWriteBucket(channelIndexKey) 1016 if chanIndex == nil { 1017 return nil 1018 } 1019 1020 // Serialize the provided channel point and return the bucket matching 1021 // the serialized key. 1022 var chanBuffer bytes.Buffer 1023 if err := writeOutpoint(&chanBuffer, chanPoint); err != nil { 1024 return nil 1025 } 1026 1027 return chanIndex.NestedReadWriteBucket(chanBuffer.Bytes()) 1028 } 1029 1030 // createHeightBucket creates or retrieves an existing bucket from the height 1031 // index, corresponding to the provided height. 1032 func (ns *NurseryStore) createHeightBucket(tx kvdb.RwTx, 1033 height uint32) (kvdb.RwBucket, error) { 1034 1035 // Ensure that the chain bucket for this nursery store exists. 1036 chainBucket, err := tx.CreateTopLevelBucket(ns.pfxChainKey) 1037 if err != nil { 1038 return nil, err 1039 } 1040 1041 // Ensure that the height index has been properly initialized for this 1042 // chain. 1043 hghtIndex, err := chainBucket.CreateBucketIfNotExists(heightIndexKey) 1044 if err != nil { 1045 return nil, err 1046 } 1047 1048 // Serialize the provided height, as this will form the name of the 1049 // bucket. 1050 var heightBytes [4]byte 1051 byteOrder.PutUint32(heightBytes[:], height) 1052 1053 // Finally, create or retrieve the bucket in question. 1054 return hghtIndex.CreateBucketIfNotExists(heightBytes[:]) 1055 } 1056 1057 // getHeightBucketPath retrieves an existing height bucket from the nursery 1058 // store, using the provided block height. If the bucket does not exist, or any 1059 // bucket along its path does not exist, a nil value is returned. 1060 func (ns *NurseryStore) getHeightBucketPath(tx kvdb.RTx, 1061 height uint32) (kvdb.RBucket, kvdb.RBucket, kvdb.RBucket) { 1062 1063 // Retrieve the existing chain bucket for this nursery store. 1064 chainBucket := tx.ReadBucket(ns.pfxChainKey) 1065 if chainBucket == nil { 1066 return nil, nil, nil 1067 } 1068 1069 // Retrieve the existing channel index. 1070 hghtIndex := chainBucket.NestedReadBucket(heightIndexKey) 1071 if hghtIndex == nil { 1072 return nil, nil, nil 1073 } 1074 1075 // Serialize the provided block height and return the bucket matching 1076 // the serialized key. 1077 var heightBytes [4]byte 1078 byteOrder.PutUint32(heightBytes[:], height) 1079 1080 return chainBucket, hghtIndex, hghtIndex.NestedReadBucket(heightBytes[:]) 1081 } 1082 1083 // getHeightBucketPathWrite retrieves an existing height bucket from the nursery 1084 // store, using the provided block height. If the bucket does not exist, or any 1085 // bucket along its path does not exist, a nil value is returned. 1086 func (ns *NurseryStore) getHeightBucketPathWrite(tx kvdb.RwTx, 1087 height uint32) (kvdb.RwBucket, kvdb.RwBucket, kvdb.RwBucket) { 1088 1089 // Retrieve the existing chain bucket for this nursery store. 1090 chainBucket := tx.ReadWriteBucket(ns.pfxChainKey) 1091 if chainBucket == nil { 1092 return nil, nil, nil 1093 } 1094 1095 // Retrieve the existing channel index. 1096 hghtIndex := chainBucket.NestedReadWriteBucket(heightIndexKey) 1097 if hghtIndex == nil { 1098 return nil, nil, nil 1099 } 1100 1101 // Serialize the provided block height and return the bucket matching 1102 // the serialized key. 1103 var heightBytes [4]byte 1104 byteOrder.PutUint32(heightBytes[:], height) 1105 1106 return chainBucket, hghtIndex, hghtIndex.NestedReadWriteBucket( 1107 heightBytes[:], 1108 ) 1109 } 1110 1111 // getHeightBucket retrieves an existing height bucket from the nursery store, 1112 // using the provided block height. If the bucket does not exist, or any bucket 1113 // along its path does not exist, a nil value is returned. 1114 func (ns *NurseryStore) getHeightBucket(tx kvdb.RTx, 1115 height uint32) kvdb.RBucket { 1116 _, _, hghtBucket := ns.getHeightBucketPath(tx, height) 1117 1118 return hghtBucket 1119 } 1120 1121 // getHeightBucketWrite retrieves an existing height bucket from the nursery store, 1122 // using the provided block height. If the bucket does not exist, or any bucket 1123 // along its path does not exist, a nil value is returned. 1124 func (ns *NurseryStore) getHeightBucketWrite(tx kvdb.RwTx, 1125 height uint32) kvdb.RwBucket { 1126 1127 _, _, hghtBucket := ns.getHeightBucketPathWrite(tx, height) 1128 1129 return hghtBucket 1130 } 1131 1132 // createHeightChanBucket creates or retrieves an existing height-channel bucket 1133 // for the provided block height and channel point. This method will attempt to 1134 // instantiate all buckets along the path if required. 1135 func (ns *NurseryStore) createHeightChanBucket(tx kvdb.RwTx, 1136 height uint32, chanPoint *wire.OutPoint) (kvdb.RwBucket, error) { 1137 1138 // Ensure that the height bucket for this nursery store exists. 1139 hghtBucket, err := ns.createHeightBucket(tx, height) 1140 if err != nil { 1141 return nil, err 1142 } 1143 1144 // Serialize the provided channel point, as this generates the name of 1145 // the subdirectory corresponding to the channel of interest. 1146 var chanBuffer bytes.Buffer 1147 if err := writeOutpoint(&chanBuffer, chanPoint); err != nil { 1148 return nil, err 1149 } 1150 chanBytes := chanBuffer.Bytes() 1151 1152 // Finally, create or retrieve an existing height-channel bucket for 1153 // this channel point. 1154 return hghtBucket.CreateBucketIfNotExists(chanBytes) 1155 } 1156 1157 // getHeightChanBucketWrite retrieves an existing height-channel bucket from the 1158 // nursery store, using the provided block height and channel point. if the 1159 // bucket does not exist, or any bucket along its path does not exist, a nil 1160 // value is returned. 1161 func (ns *NurseryStore) getHeightChanBucketWrite(tx kvdb.RwTx, 1162 height uint32, chanPoint *wire.OutPoint) kvdb.RwBucket { 1163 1164 // Retrieve the existing height bucket from this nursery store. 1165 hghtBucket := ns.getHeightBucketWrite(tx, height) 1166 if hghtBucket == nil { 1167 return nil 1168 } 1169 1170 // Serialize the provided channel point, which generates the key for 1171 // looking up the proper height-channel bucket inside the height bucket. 1172 var chanBuffer bytes.Buffer 1173 if err := writeOutpoint(&chanBuffer, chanPoint); err != nil { 1174 return nil 1175 } 1176 chanBytes := chanBuffer.Bytes() 1177 1178 // Finally, return the height bucket specified by the serialized channel 1179 // point. 1180 return hghtBucket.NestedReadWriteBucket(chanBytes) 1181 } 1182 1183 // forEachHeightPrefix enumerates all outputs at the given height whose state 1184 // prefix matches that which is provided. This is used as a subroutine to help 1185 // enumerate crib and kindergarten outputs at a particular height. The callback 1186 // is invoked with serialized bytes retrieved for each output of interest, 1187 // allowing the caller to deserialize them into the appropriate type. 1188 func (ns *NurseryStore) forEachHeightPrefix(tx kvdb.RTx, prefix []byte, 1189 height uint32, callback func([]byte) error) error { 1190 1191 // Start by retrieving the height bucket corresponding to the provided 1192 // block height. 1193 chainBucket, _, hghtBucket := ns.getHeightBucketPath(tx, height) 1194 if hghtBucket == nil { 1195 return nil 1196 } 1197 1198 // Using the height bucket as a starting point, we will traverse its 1199 // entire two-tier directory structure, and filter for outputs that have 1200 // the provided prefix. The first layer of the height bucket contains 1201 // buckets identified by a channel point, thus we first create list of 1202 // channels contained in this height bucket. 1203 var channelsAtHeight [][]byte 1204 if err := hghtBucket.ForEach(func(chanBytes, v []byte) error { 1205 if v == nil { 1206 channelsAtHeight = append(channelsAtHeight, chanBytes) 1207 } 1208 return nil 1209 }); err != nil { 1210 return err 1211 } 1212 1213 // Additionally, grab the chain index, which we will facilitate queries 1214 // for each of the channel buckets of each of the channels in the list 1215 // we assembled above. 1216 chanIndex := chainBucket.NestedReadBucket(channelIndexKey) 1217 if chanIndex == nil { 1218 return errors.New("unable to retrieve channel index") 1219 } 1220 1221 // Now, we are ready to enumerate all outputs with the desired prefix at 1222 // this block height. We do so by iterating over our list of channels at 1223 // this height, filtering for outputs in each height-channel bucket that 1224 // begin with the given prefix, and then retrieving the serialized 1225 // outputs from the appropriate channel bucket. 1226 for _, chanBytes := range channelsAtHeight { 1227 // Retrieve the height-channel bucket for this channel, which 1228 // holds a sub-bucket for all outputs maturing at this height. 1229 hghtChanBucket := hghtBucket.NestedReadBucket(chanBytes) 1230 if hghtChanBucket == nil { 1231 return fmt.Errorf("unable to retrieve height-channel "+ 1232 "bucket at height %d for %x", height, chanBytes) 1233 } 1234 1235 // Load the appropriate channel bucket from the channel index, 1236 // this will allow us to retrieve the individual serialized 1237 // outputs. 1238 chanBucket := chanIndex.NestedReadBucket(chanBytes) 1239 if chanBucket == nil { 1240 return fmt.Errorf("unable to retrieve channel "+ 1241 "bucket: '%x'", chanBytes) 1242 } 1243 1244 // Since all of the outputs of interest will start with the same 1245 // prefix, we will perform a prefix scan of the buckets 1246 // contained in the height-channel bucket, efficiently 1247 // enumerating the desired outputs. 1248 c := hghtChanBucket.ReadCursor() 1249 for k, _ := c.Seek(prefix); bytes.HasPrefix( 1250 k, prefix); k, _ = c.Next() { 1251 1252 // Use the prefix output key emitted from our scan to 1253 // load the serialized babyOutput from the appropriate 1254 // channel bucket. 1255 outputBytes := chanBucket.Get(k) 1256 if outputBytes == nil { 1257 return errors.New("unable to retrieve output") 1258 } 1259 1260 // Present the serialized bytes to our call back 1261 // function, which is responsible for deserializing the 1262 // bytes into the appropriate type. 1263 if err := callback(outputBytes); err != nil { 1264 return err 1265 } 1266 } 1267 } 1268 1269 return nil 1270 } 1271 1272 // forChanOutputs enumerates the outputs contained in a channel bucket to the 1273 // provided callback. The callback accepts a key-value pair of byte slices 1274 // corresponding to the prefixed-output key and the serialized output, 1275 // respectively. 1276 func (ns *NurseryStore) forChanOutputs(tx kvdb.RTx, chanPoint *wire.OutPoint, 1277 callback func([]byte, []byte) error) error { 1278 1279 chanBucket := ns.getChannelBucket(tx, chanPoint) 1280 if chanBucket == nil { 1281 return ErrContractNotFound 1282 } 1283 1284 return chanBucket.ForEach(callback) 1285 } 1286 1287 // errBucketNotEmpty signals that an attempt to prune a particular 1288 // bucket failed because it still has active outputs. 1289 var errBucketNotEmpty = errors.New("bucket is not empty, cannot be pruned") 1290 1291 // removeOutputFromHeight will delete the given output from the specified 1292 // height-channel bucket, and attempt to prune the upstream directories if they 1293 // are empty. 1294 func (ns *NurseryStore) removeOutputFromHeight(tx kvdb.RwTx, height uint32, 1295 chanPoint *wire.OutPoint, pfxKey []byte) error { 1296 1297 // Retrieve the height-channel bucket and delete the prefixed output. 1298 hghtChanBucket := ns.getHeightChanBucketWrite(tx, height, chanPoint) 1299 if hghtChanBucket == nil { 1300 // Height-channel bucket already removed. 1301 return nil 1302 } 1303 1304 // Try to delete the prefixed output from the target height-channel 1305 // bucket. 1306 if err := hghtChanBucket.Delete(pfxKey); err != nil { 1307 return err 1308 } 1309 1310 // Retrieve the height bucket that contains the height-channel bucket. 1311 hghtBucket := ns.getHeightBucketWrite(tx, height) 1312 if hghtBucket == nil { 1313 return errors.New("height bucket not found") 1314 } 1315 1316 var chanBuffer bytes.Buffer 1317 if err := writeOutpoint(&chanBuffer, chanPoint); err != nil { 1318 return err 1319 } 1320 1321 // Try to remove the channel-height bucket if it this was the last 1322 // output in the bucket. 1323 err := removeBucketIfEmpty(hghtBucket, chanBuffer.Bytes()) 1324 if err != nil && err != errBucketNotEmpty { 1325 return err 1326 } else if err == errBucketNotEmpty { 1327 return nil 1328 } 1329 1330 // Attempt to prune the height bucket matching the kid output's 1331 // confirmation height in case that was the last height-chan bucket. 1332 pruned, err := ns.pruneHeight(tx, height) 1333 if err != nil && err != errBucketNotEmpty { 1334 return err 1335 } else if err == nil && pruned { 1336 utxnLog.Infof("Height bucket %d pruned", height) 1337 } 1338 1339 return nil 1340 } 1341 1342 // pruneHeight removes the height bucket at the provided height if and only if 1343 // all active outputs at this height have been removed from their respective 1344 // height-channel buckets. The returned boolean value indicated whether or not 1345 // this invocation successfully pruned the height bucket. 1346 func (ns *NurseryStore) pruneHeight(tx kvdb.RwTx, height uint32) (bool, error) { 1347 // Fetch the existing height index and height bucket. 1348 _, hghtIndex, hghtBucket := ns.getHeightBucketPathWrite(tx, height) 1349 if hghtBucket == nil { 1350 return false, nil 1351 } 1352 1353 // Iterate over all channels stored at this block height. We will 1354 // attempt to remove each one if they are empty, keeping track of the 1355 // number of height-channel buckets that still have active outputs. 1356 if err := hghtBucket.ForEach(func(chanBytes, v []byte) error { 1357 // Skip the finalized txn key if it still exists from a previous 1358 // db version. 1359 if v != nil { 1360 return nil 1361 } 1362 1363 // Attempt to each height-channel bucket from the height bucket 1364 // located above. 1365 hghtChanBucket := hghtBucket.NestedReadWriteBucket(chanBytes) 1366 if hghtChanBucket == nil { 1367 return errors.New("unable to find height-channel bucket") 1368 } 1369 1370 return isBucketEmpty(hghtChanBucket) 1371 1372 }); err != nil { 1373 return false, err 1374 } 1375 1376 // Serialize the provided block height, such that it can be used as the 1377 // key to delete desired height bucket. 1378 var heightBytes [4]byte 1379 byteOrder.PutUint32(heightBytes[:], height) 1380 1381 // All of the height-channel buckets are empty or have been previously 1382 // removed, proceed by removing the height bucket 1383 // altogether. 1384 if err := removeBucketIfExists(hghtIndex, heightBytes[:]); err != nil { 1385 return false, err 1386 } 1387 1388 return true, nil 1389 } 1390 1391 // removeBucketIfEmpty attempts to delete a bucket specified by name from the 1392 // provided parent bucket. 1393 func removeBucketIfEmpty(parent kvdb.RwBucket, bktName []byte) error { 1394 // Attempt to fetch the named bucket from its parent. 1395 bkt := parent.NestedReadWriteBucket(bktName) 1396 if bkt == nil { 1397 // No bucket was found, already removed? 1398 return nil 1399 } 1400 1401 // The bucket exists, fail if it still has children. 1402 if err := isBucketEmpty(bkt); err != nil { 1403 return err 1404 } 1405 1406 return parent.DeleteNestedBucket(bktName) 1407 } 1408 1409 // removeBucketIfExists safely deletes the named bucket by first checking 1410 // that it exists in the parent bucket. 1411 func removeBucketIfExists(parent kvdb.RwBucket, bktName []byte) error { 1412 // Attempt to fetch the named bucket from its parent. 1413 bkt := parent.NestedReadWriteBucket(bktName) 1414 if bkt == nil { 1415 // No bucket was found, already removed? 1416 return nil 1417 } 1418 1419 return parent.DeleteNestedBucket(bktName) 1420 } 1421 1422 // isBucketEmpty returns errBucketNotEmpty if the bucket has a non-zero number 1423 // of children. 1424 func isBucketEmpty(parent kvdb.RBucket) error { 1425 return parent.ForEach(func(_, _ []byte) error { 1426 return errBucketNotEmpty 1427 }) 1428 } 1429 1430 // Compile-time constraint to ensure NurseryStore implements NurseryStorer. 1431 var _ NurseryStorer = (*NurseryStore)(nil)