github.com/decred/dcrlnd@v0.7.6/channeldb/migration21/legacy/legacy_decoding.go (about) 1 package legacy 2 3 import ( 4 "bytes" 5 "encoding/binary" 6 "errors" 7 "io" 8 9 lnwire "github.com/decred/dcrlnd/channeldb/migration/lnwire21" 10 "github.com/decred/dcrlnd/channeldb/migration21/common" 11 "github.com/decred/dcrlnd/kvdb" 12 ) 13 14 func deserializeHtlcs(r io.Reader) ([]common.HTLC, error) { 15 var numHtlcs uint16 16 if err := ReadElement(r, &numHtlcs); err != nil { 17 return nil, err 18 } 19 20 var htlcs []common.HTLC 21 if numHtlcs == 0 { 22 return htlcs, nil 23 } 24 25 htlcs = make([]common.HTLC, numHtlcs) 26 for i := uint16(0); i < numHtlcs; i++ { 27 if err := ReadElements(r, 28 &htlcs[i].Signature, &htlcs[i].RHash, &htlcs[i].Amt, 29 &htlcs[i].RefundTimeout, &htlcs[i].OutputIndex, 30 &htlcs[i].Incoming, &htlcs[i].OnionBlob, 31 &htlcs[i].HtlcIndex, &htlcs[i].LogIndex, 32 ); err != nil { 33 return htlcs, err 34 } 35 } 36 37 return htlcs, nil 38 } 39 40 func DeserializeLogUpdates(r io.Reader) ([]common.LogUpdate, error) { 41 var numUpdates uint16 42 if err := binary.Read(r, byteOrder, &numUpdates); err != nil { 43 return nil, err 44 } 45 46 logUpdates := make([]common.LogUpdate, numUpdates) 47 for i := 0; i < int(numUpdates); i++ { 48 err := ReadElements(r, 49 &logUpdates[i].LogIndex, &logUpdates[i].UpdateMsg, 50 ) 51 if err != nil { 52 return nil, err 53 } 54 } 55 56 return logUpdates, nil 57 } 58 59 func deserializeChanCommit(r io.Reader) (common.ChannelCommitment, error) { 60 var c common.ChannelCommitment 61 62 err := ReadElements(r, 63 &c.CommitHeight, &c.LocalLogIndex, &c.LocalHtlcIndex, &c.RemoteLogIndex, 64 &c.RemoteHtlcIndex, &c.LocalBalance, &c.RemoteBalance, 65 &c.CommitFee, &c.FeePerKB, &c.CommitTx, &c.CommitSig, 66 ) 67 if err != nil { 68 return c, err 69 } 70 71 c.Htlcs, err = deserializeHtlcs(r) 72 if err != nil { 73 return c, err 74 } 75 76 return c, nil 77 } 78 79 func DeserializeCommitDiff(r io.Reader) (*common.CommitDiff, error) { 80 var ( 81 d common.CommitDiff 82 err error 83 ) 84 85 d.Commitment, err = deserializeChanCommit(r) 86 if err != nil { 87 return nil, err 88 } 89 90 d.CommitSig = &lnwire.CommitSig{} 91 if err := d.CommitSig.Decode(r, 0); err != nil { 92 return nil, err 93 } 94 95 d.LogUpdates, err = DeserializeLogUpdates(r) 96 if err != nil { 97 return nil, err 98 } 99 100 var numOpenRefs uint16 101 if err := binary.Read(r, byteOrder, &numOpenRefs); err != nil { 102 return nil, err 103 } 104 105 d.OpenedCircuitKeys = make([]common.CircuitKey, numOpenRefs) 106 for i := 0; i < int(numOpenRefs); i++ { 107 err := ReadElements(r, 108 &d.OpenedCircuitKeys[i].ChanID, 109 &d.OpenedCircuitKeys[i].HtlcID) 110 if err != nil { 111 return nil, err 112 } 113 } 114 115 var numClosedRefs uint16 116 if err := binary.Read(r, byteOrder, &numClosedRefs); err != nil { 117 return nil, err 118 } 119 120 d.ClosedCircuitKeys = make([]common.CircuitKey, numClosedRefs) 121 for i := 0; i < int(numClosedRefs); i++ { 122 err := ReadElements(r, 123 &d.ClosedCircuitKeys[i].ChanID, 124 &d.ClosedCircuitKeys[i].HtlcID) 125 if err != nil { 126 return nil, err 127 } 128 } 129 130 return &d, nil 131 } 132 133 func serializeHtlcs(b io.Writer, htlcs ...common.HTLC) error { 134 numHtlcs := uint16(len(htlcs)) 135 if err := WriteElement(b, numHtlcs); err != nil { 136 return err 137 } 138 139 for _, htlc := range htlcs { 140 if err := WriteElements(b, 141 htlc.Signature, htlc.RHash, htlc.Amt, htlc.RefundTimeout, 142 htlc.OutputIndex, htlc.Incoming, htlc.OnionBlob, 143 htlc.HtlcIndex, htlc.LogIndex, 144 ); err != nil { 145 return err 146 } 147 } 148 149 return nil 150 } 151 152 func serializeChanCommit(w io.Writer, c *common.ChannelCommitment) error { 153 if err := WriteElements(w, 154 c.CommitHeight, c.LocalLogIndex, c.LocalHtlcIndex, 155 c.RemoteLogIndex, c.RemoteHtlcIndex, c.LocalBalance, 156 c.RemoteBalance, c.CommitFee, c.FeePerKB, c.CommitTx, 157 c.CommitSig, 158 ); err != nil { 159 return err 160 } 161 162 return serializeHtlcs(w, c.Htlcs...) 163 } 164 165 func SerializeLogUpdates(w io.Writer, logUpdates []common.LogUpdate) error { 166 numUpdates := uint16(len(logUpdates)) 167 if err := binary.Write(w, byteOrder, numUpdates); err != nil { 168 return err 169 } 170 171 for _, diff := range logUpdates { 172 err := WriteElements(w, diff.LogIndex, diff.UpdateMsg) 173 if err != nil { 174 return err 175 } 176 } 177 178 return nil 179 } 180 181 func SerializeCommitDiff(w io.Writer, diff *common.CommitDiff) error { // nolint: dupl 182 if err := serializeChanCommit(w, &diff.Commitment); err != nil { 183 return err 184 } 185 186 if err := diff.CommitSig.Encode(w, 0); err != nil { 187 return err 188 } 189 190 if err := SerializeLogUpdates(w, diff.LogUpdates); err != nil { 191 return err 192 } 193 194 numOpenRefs := uint16(len(diff.OpenedCircuitKeys)) 195 if err := binary.Write(w, byteOrder, numOpenRefs); err != nil { 196 return err 197 } 198 199 for _, openRef := range diff.OpenedCircuitKeys { 200 err := WriteElements(w, openRef.ChanID, openRef.HtlcID) 201 if err != nil { 202 return err 203 } 204 } 205 206 numClosedRefs := uint16(len(diff.ClosedCircuitKeys)) 207 if err := binary.Write(w, byteOrder, numClosedRefs); err != nil { 208 return err 209 } 210 211 for _, closedRef := range diff.ClosedCircuitKeys { 212 err := WriteElements(w, closedRef.ChanID, closedRef.HtlcID) 213 if err != nil { 214 return err 215 } 216 } 217 218 return nil 219 } 220 221 func DeserializeNetworkResult(r io.Reader) (*common.NetworkResult, error) { 222 var ( 223 err error 224 ) 225 226 n := &common.NetworkResult{} 227 228 n.Msg, err = lnwire.ReadMessage(r, 0) 229 if err != nil { 230 return nil, err 231 } 232 233 if err := ReadElements(r, 234 &n.Unencrypted, &n.IsResolution, 235 ); err != nil { 236 return nil, err 237 } 238 239 return n, nil 240 } 241 242 func SerializeNetworkResult(w io.Writer, n *common.NetworkResult) error { 243 if _, err := lnwire.WriteMessage(w, n.Msg, 0); err != nil { 244 return err 245 } 246 247 return WriteElements(w, n.Unencrypted, n.IsResolution) 248 } 249 250 func readChanConfig(b io.Reader, c *common.ChannelConfig) error { // nolint: dupl 251 return ReadElements(b, 252 &c.DustLimit, &c.MaxPendingAmount, &c.ChanReserve, 253 &c.MinHTLC, &c.MaxAcceptedHtlcs, &c.CsvDelay, 254 &c.MultiSigKey, &c.RevocationBasePoint, 255 &c.PaymentBasePoint, &c.DelayBasePoint, 256 &c.HtlcBasePoint, 257 ) 258 } 259 260 func DeserializeCloseChannelSummary(r io.Reader) (*common.ChannelCloseSummary, error) { // nolint: dupl 261 262 c := &common.ChannelCloseSummary{} 263 264 err := ReadElements(r, 265 &c.ChanPoint, &c.ShortChanID, &c.ChainHash, &c.ClosingTXID, 266 &c.CloseHeight, &c.RemotePub, &c.Capacity, &c.SettledBalance, 267 &c.TimeLockedBalance, &c.CloseType, &c.IsPending, 268 ) 269 if err != nil { 270 return nil, err 271 } 272 273 // We'll now check to see if the channel close summary was encoded with 274 // any of the additional optional fields. 275 var hasNewFields bool 276 err = ReadElements(r, &hasNewFields) 277 if err != nil { 278 return nil, err 279 } 280 281 // If fields are not present, we can return. 282 if !hasNewFields { 283 return c, nil 284 } 285 286 // Otherwise read the new fields. 287 if err := ReadElements(r, &c.RemoteCurrentRevocation); err != nil { 288 return nil, err 289 } 290 291 if err := readChanConfig(r, &c.LocalChanConfig); err != nil { 292 return nil, err 293 } 294 295 // Finally, we'll attempt to read the next unrevoked commitment point 296 // for the remote party. If we closed the channel before receiving a 297 // funding locked message then this might not be present. A boolean 298 // indicating whether the field is present will come first. 299 var hasRemoteNextRevocation bool 300 err = ReadElements(r, &hasRemoteNextRevocation) 301 if err != nil { 302 return nil, err 303 } 304 305 // If this field was written, read it. 306 if hasRemoteNextRevocation { 307 err = ReadElements(r, &c.RemoteNextRevocation) 308 if err != nil { 309 return nil, err 310 } 311 } 312 313 // Check if we have a channel sync message to read. 314 var hasChanSyncMsg bool 315 err = ReadElements(r, &hasChanSyncMsg) 316 if err == io.EOF { 317 return c, nil 318 } else if err != nil { 319 return nil, err 320 } 321 322 // If a chan sync message is present, read it. 323 if hasChanSyncMsg { 324 // We must pass in reference to a lnwire.Message for the codec 325 // to support it. 326 msg, err := lnwire.ReadMessage(r, 0) 327 if err != nil { 328 return nil, err 329 } 330 331 chanSync, ok := msg.(*lnwire.ChannelReestablish) 332 if !ok { 333 return nil, errors.New("unable cast db Message to " + 334 "ChannelReestablish") 335 } 336 c.LastChanSyncMsg = chanSync 337 } 338 339 return c, nil 340 } 341 342 func writeChanConfig(b io.Writer, c *common.ChannelConfig) error { // nolint: dupl 343 return WriteElements(b, 344 c.DustLimit, c.MaxPendingAmount, c.ChanReserve, c.MinHTLC, 345 c.MaxAcceptedHtlcs, c.CsvDelay, c.MultiSigKey, 346 c.RevocationBasePoint, c.PaymentBasePoint, c.DelayBasePoint, 347 c.HtlcBasePoint, 348 ) 349 } 350 351 func SerializeChannelCloseSummary(w io.Writer, cs *common.ChannelCloseSummary) error { 352 err := WriteElements(w, 353 cs.ChanPoint, cs.ShortChanID, cs.ChainHash, cs.ClosingTXID, 354 cs.CloseHeight, cs.RemotePub, cs.Capacity, cs.SettledBalance, 355 cs.TimeLockedBalance, cs.CloseType, cs.IsPending, 356 ) 357 if err != nil { 358 return err 359 } 360 361 // If this is a close channel summary created before the addition of 362 // the new fields, then we can exit here. 363 if cs.RemoteCurrentRevocation == nil { 364 return WriteElements(w, false) 365 } 366 367 // If fields are present, write boolean to indicate this, and continue. 368 if err := WriteElements(w, true); err != nil { 369 return err 370 } 371 372 if err := WriteElements(w, cs.RemoteCurrentRevocation); err != nil { 373 return err 374 } 375 376 if err := writeChanConfig(w, &cs.LocalChanConfig); err != nil { 377 return err 378 } 379 380 // The RemoteNextRevocation field is optional, as it's possible for a 381 // channel to be closed before we learn of the next unrevoked 382 // revocation point for the remote party. Write a boolen indicating 383 // whether this field is present or not. 384 if err := WriteElements(w, cs.RemoteNextRevocation != nil); err != nil { 385 return err 386 } 387 388 // Write the field, if present. 389 if cs.RemoteNextRevocation != nil { 390 if err = WriteElements(w, cs.RemoteNextRevocation); err != nil { 391 return err 392 } 393 } 394 395 // Write whether the channel sync message is present. 396 if err := WriteElements(w, cs.LastChanSyncMsg != nil); err != nil { 397 return err 398 } 399 400 // Write the channel sync message, if present. 401 if cs.LastChanSyncMsg != nil { 402 _, err = lnwire.WriteMessage(w, cs.LastChanSyncMsg, 0) 403 if err != nil { 404 return err 405 } 406 } 407 408 return nil 409 } 410 411 // ErrCorruptedFwdPkg signals that the on-disk structure of the forwarding 412 // package has potentially been mangled. 413 var ErrCorruptedFwdPkg = errors.New("fwding package db has been corrupted") 414 415 var ( 416 // fwdPackagesKey is the root-level bucket that all forwarding packages 417 // are written. This bucket is further subdivided based on the short 418 // channel ID of each channel. 419 fwdPackagesKey = []byte("fwd-packages") 420 421 // addBucketKey is the bucket to which all Add log updates are written. 422 addBucketKey = []byte("add-updates") 423 424 // failSettleBucketKey is the bucket to which all Settle/Fail log 425 // updates are written. 426 failSettleBucketKey = []byte("fail-settle-updates") 427 428 // fwdFilterKey is a key used to write the set of Adds that passed 429 // validation and are to be forwarded to the switch. 430 // NOTE: The presence of this key within a forwarding package indicates 431 // that the package has reached FwdStateProcessed. 432 fwdFilterKey = []byte("fwd-filter-key") 433 434 // ackFilterKey is a key used to access the PkgFilter indicating which 435 // Adds have received a Settle/Fail. This response may come from a 436 // number of sources, including: exitHop settle/fails, switch failures, 437 // chain arbiter interjections, as well as settle/fails from the 438 // next hop in the route. 439 ackFilterKey = []byte("ack-filter-key") 440 441 // settleFailFilterKey is a key used to access the PkgFilter indicating 442 // which Settles/Fails in have been received and processed by the link 443 // that originally received the Add. 444 settleFailFilterKey = []byte("settle-fail-filter-key") 445 ) 446 447 func makeLogKey(updateNum uint64) [8]byte { 448 var key [8]byte 449 byteOrder.PutUint64(key[:], updateNum) 450 return key 451 } 452 453 // uint16Key writes the provided 16-bit unsigned integer to a 2-byte slice. 454 func uint16Key(i uint16) []byte { 455 key := make([]byte, 2) 456 byteOrder.PutUint16(key, i) 457 return key 458 } 459 460 // ChannelPackager is used by a channel to manage the lifecycle of its forwarding 461 // packages. The packager is tied to a particular source channel ID, allowing it 462 // to create and edit its own packages. Each packager also has the ability to 463 // remove fail/settle htlcs that correspond to an add contained in one of 464 // source's packages. 465 type ChannelPackager struct { 466 source lnwire.ShortChannelID 467 } 468 469 // NewChannelPackager creates a new packager for a single channel. 470 func NewChannelPackager(source lnwire.ShortChannelID) *ChannelPackager { 471 return &ChannelPackager{ 472 source: source, 473 } 474 } 475 476 // AddFwdPkg writes a newly locked in forwarding package to disk. 477 func (*ChannelPackager) AddFwdPkg(tx kvdb.RwTx, fwdPkg *common.FwdPkg) error { // nolint: dupl 478 fwdPkgBkt, err := tx.CreateTopLevelBucket(fwdPackagesKey) 479 if err != nil { 480 return err 481 } 482 483 source := makeLogKey(fwdPkg.Source.ToUint64()) 484 sourceBkt, err := fwdPkgBkt.CreateBucketIfNotExists(source[:]) 485 if err != nil { 486 return err 487 } 488 489 heightKey := makeLogKey(fwdPkg.Height) 490 heightBkt, err := sourceBkt.CreateBucketIfNotExists(heightKey[:]) 491 if err != nil { 492 return err 493 } 494 495 // Write ADD updates we received at this commit height. 496 addBkt, err := heightBkt.CreateBucketIfNotExists(addBucketKey) 497 if err != nil { 498 return err 499 } 500 501 // Write SETTLE/FAIL updates we received at this commit height. 502 failSettleBkt, err := heightBkt.CreateBucketIfNotExists(failSettleBucketKey) 503 if err != nil { 504 return err 505 } 506 507 for i := range fwdPkg.Adds { 508 err = putLogUpdate(addBkt, uint16(i), &fwdPkg.Adds[i]) 509 if err != nil { 510 return err 511 } 512 } 513 514 // Persist the initialized pkg filter, which will be used to determine 515 // when we can remove this forwarding package from disk. 516 var ackFilterBuf bytes.Buffer 517 if err := fwdPkg.AckFilter.Encode(&ackFilterBuf); err != nil { 518 return err 519 } 520 521 if err := heightBkt.Put(ackFilterKey, ackFilterBuf.Bytes()); err != nil { 522 return err 523 } 524 525 for i := range fwdPkg.SettleFails { 526 err = putLogUpdate(failSettleBkt, uint16(i), &fwdPkg.SettleFails[i]) 527 if err != nil { 528 return err 529 } 530 } 531 532 var settleFailFilterBuf bytes.Buffer 533 err = fwdPkg.SettleFailFilter.Encode(&settleFailFilterBuf) 534 if err != nil { 535 return err 536 } 537 538 return heightBkt.Put(settleFailFilterKey, settleFailFilterBuf.Bytes()) 539 } 540 541 // putLogUpdate writes an htlc to the provided `bkt`, using `index` as the key. 542 func putLogUpdate(bkt kvdb.RwBucket, idx uint16, htlc *common.LogUpdate) error { 543 var b bytes.Buffer 544 if err := serializeLogUpdate(&b, htlc); err != nil { 545 return err 546 } 547 548 return bkt.Put(uint16Key(idx), b.Bytes()) 549 } 550 551 // LoadFwdPkgs scans the forwarding log for any packages that haven't been 552 // processed, and returns their deserialized log updates in a map indexed by the 553 // remote commitment height at which the updates were locked in. 554 func (p *ChannelPackager) LoadFwdPkgs(tx kvdb.RTx) ([]*common.FwdPkg, error) { 555 return loadChannelFwdPkgs(tx, p.source) 556 } 557 558 // loadChannelFwdPkgs loads all forwarding packages owned by `source`. 559 func loadChannelFwdPkgs(tx kvdb.RTx, source lnwire.ShortChannelID) ([]*common.FwdPkg, error) { 560 fwdPkgBkt := tx.ReadBucket(fwdPackagesKey) 561 if fwdPkgBkt == nil { 562 return nil, nil 563 } 564 565 sourceKey := makeLogKey(source.ToUint64()) 566 sourceBkt := fwdPkgBkt.NestedReadBucket(sourceKey[:]) 567 if sourceBkt == nil { 568 return nil, nil 569 } 570 571 var heights []uint64 572 if err := sourceBkt.ForEach(func(k, _ []byte) error { 573 if len(k) != 8 { 574 return ErrCorruptedFwdPkg 575 } 576 577 heights = append(heights, byteOrder.Uint64(k)) 578 579 return nil 580 }); err != nil { 581 return nil, err 582 } 583 584 // Load the forwarding package for each retrieved height. 585 fwdPkgs := make([]*common.FwdPkg, 0, len(heights)) 586 for _, height := range heights { 587 fwdPkg, err := loadFwdPkg(fwdPkgBkt, source, height) 588 if err != nil { 589 return nil, err 590 } 591 592 fwdPkgs = append(fwdPkgs, fwdPkg) 593 } 594 595 return fwdPkgs, nil 596 } 597 598 // loadFwdPkg reads the packager's fwd pkg at a given height, and determines the 599 // appropriate FwdState. 600 func loadFwdPkg(fwdPkgBkt kvdb.RBucket, source lnwire.ShortChannelID, 601 height uint64) (*common.FwdPkg, error) { 602 603 sourceKey := makeLogKey(source.ToUint64()) 604 sourceBkt := fwdPkgBkt.NestedReadBucket(sourceKey[:]) 605 if sourceBkt == nil { 606 return nil, ErrCorruptedFwdPkg 607 } 608 609 heightKey := makeLogKey(height) 610 heightBkt := sourceBkt.NestedReadBucket(heightKey[:]) 611 if heightBkt == nil { 612 return nil, ErrCorruptedFwdPkg 613 } 614 615 // Load ADDs from disk. 616 addBkt := heightBkt.NestedReadBucket(addBucketKey) 617 if addBkt == nil { 618 return nil, ErrCorruptedFwdPkg 619 } 620 621 adds, err := loadHtlcs(addBkt) 622 if err != nil { 623 return nil, err 624 } 625 626 // Load ack filter from disk. 627 ackFilterBytes := heightBkt.Get(ackFilterKey) 628 if ackFilterBytes == nil { 629 return nil, ErrCorruptedFwdPkg 630 } 631 ackFilterReader := bytes.NewReader(ackFilterBytes) 632 633 ackFilter := &common.PkgFilter{} 634 if err := ackFilter.Decode(ackFilterReader); err != nil { 635 return nil, err 636 } 637 638 // Load SETTLE/FAILs from disk. 639 failSettleBkt := heightBkt.NestedReadBucket(failSettleBucketKey) 640 if failSettleBkt == nil { 641 return nil, ErrCorruptedFwdPkg 642 } 643 644 failSettles, err := loadHtlcs(failSettleBkt) 645 if err != nil { 646 return nil, err 647 } 648 649 // Load settle fail filter from disk. 650 settleFailFilterBytes := heightBkt.Get(settleFailFilterKey) 651 if settleFailFilterBytes == nil { 652 return nil, ErrCorruptedFwdPkg 653 } 654 settleFailFilterReader := bytes.NewReader(settleFailFilterBytes) 655 656 settleFailFilter := &common.PkgFilter{} 657 if err := settleFailFilter.Decode(settleFailFilterReader); err != nil { 658 return nil, err 659 } 660 661 // Initialize the fwding package, which always starts in the 662 // FwdStateLockedIn. We can determine what state the package was left in 663 // by examining constraints on the information loaded from disk. 664 fwdPkg := &common.FwdPkg{ 665 Source: source, 666 State: common.FwdStateLockedIn, 667 Height: height, 668 Adds: adds, 669 AckFilter: ackFilter, 670 SettleFails: failSettles, 671 SettleFailFilter: settleFailFilter, 672 } 673 674 // Check to see if we have written the set exported filter adds to 675 // disk. If we haven't, processing of this package was never started, or 676 // failed during the last attempt. 677 fwdFilterBytes := heightBkt.Get(fwdFilterKey) 678 if fwdFilterBytes == nil { 679 nAdds := uint16(len(adds)) 680 fwdPkg.FwdFilter = common.NewPkgFilter(nAdds) 681 return fwdPkg, nil 682 } 683 684 fwdFilterReader := bytes.NewReader(fwdFilterBytes) 685 fwdPkg.FwdFilter = &common.PkgFilter{} 686 if err := fwdPkg.FwdFilter.Decode(fwdFilterReader); err != nil { 687 return nil, err 688 } 689 690 // Otherwise, a complete round of processing was completed, and we 691 // advance the package to FwdStateProcessed. 692 fwdPkg.State = common.FwdStateProcessed 693 694 // If every add, settle, and fail has been fully acknowledged, we can 695 // safely set the package's state to FwdStateCompleted, signalling that 696 // it can be garbage collected. 697 if fwdPkg.AckFilter.IsFull() && fwdPkg.SettleFailFilter.IsFull() { 698 fwdPkg.State = common.FwdStateCompleted 699 } 700 701 return fwdPkg, nil 702 } 703 704 // loadHtlcs retrieves all serialized htlcs in a bucket, returning 705 // them in order of the indexes they were written under. 706 func loadHtlcs(bkt kvdb.RBucket) ([]common.LogUpdate, error) { 707 var htlcs []common.LogUpdate 708 if err := bkt.ForEach(func(_, v []byte) error { 709 htlc, err := deserializeLogUpdate(bytes.NewReader(v)) 710 if err != nil { 711 return err 712 } 713 714 htlcs = append(htlcs, *htlc) 715 716 return nil 717 }); err != nil { 718 return nil, err 719 } 720 721 return htlcs, nil 722 } 723 724 // serializeLogUpdate writes a log update to the provided io.Writer. 725 func serializeLogUpdate(w io.Writer, l *common.LogUpdate) error { 726 return WriteElements(w, l.LogIndex, l.UpdateMsg) 727 } 728 729 // deserializeLogUpdate reads a log update from the provided io.Reader. 730 func deserializeLogUpdate(r io.Reader) (*common.LogUpdate, error) { 731 l := &common.LogUpdate{} 732 if err := ReadElements(r, &l.LogIndex, &l.UpdateMsg); err != nil { 733 return nil, err 734 } 735 736 return l, nil 737 }