You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
737 lines
19 KiB
737 lines
19 KiB
package legacy |
|
|
|
import ( |
|
"bytes" |
|
"encoding/binary" |
|
"errors" |
|
"io" |
|
|
|
lnwire "github.com/lightningnetwork/lnd/channeldb/migration/lnwire21" |
|
"github.com/lightningnetwork/lnd/channeldb/migration21/common" |
|
"github.com/lightningnetwork/lnd/kvdb" |
|
) |
|
|
|
func deserializeHtlcs(r io.Reader) ([]common.HTLC, error) { |
|
var numHtlcs uint16 |
|
if err := ReadElement(r, &numHtlcs); err != nil { |
|
return nil, err |
|
} |
|
|
|
var htlcs []common.HTLC |
|
if numHtlcs == 0 { |
|
return htlcs, nil |
|
} |
|
|
|
htlcs = make([]common.HTLC, numHtlcs) |
|
for i := uint16(0); i < numHtlcs; i++ { |
|
if err := ReadElements(r, |
|
&htlcs[i].Signature, &htlcs[i].RHash, &htlcs[i].Amt, |
|
&htlcs[i].RefundTimeout, &htlcs[i].OutputIndex, |
|
&htlcs[i].Incoming, &htlcs[i].OnionBlob, |
|
&htlcs[i].HtlcIndex, &htlcs[i].LogIndex, |
|
); err != nil { |
|
return htlcs, err |
|
} |
|
} |
|
|
|
return htlcs, nil |
|
} |
|
|
|
func DeserializeLogUpdates(r io.Reader) ([]common.LogUpdate, error) { |
|
var numUpdates uint16 |
|
if err := binary.Read(r, byteOrder, &numUpdates); err != nil { |
|
return nil, err |
|
} |
|
|
|
logUpdates := make([]common.LogUpdate, numUpdates) |
|
for i := 0; i < int(numUpdates); i++ { |
|
err := ReadElements(r, |
|
&logUpdates[i].LogIndex, &logUpdates[i].UpdateMsg, |
|
) |
|
if err != nil { |
|
return nil, err |
|
} |
|
} |
|
|
|
return logUpdates, nil |
|
} |
|
|
|
func deserializeChanCommit(r io.Reader) (common.ChannelCommitment, error) { |
|
var c common.ChannelCommitment |
|
|
|
err := ReadElements(r, |
|
&c.CommitHeight, &c.LocalLogIndex, &c.LocalHtlcIndex, &c.RemoteLogIndex, |
|
&c.RemoteHtlcIndex, &c.LocalBalance, &c.RemoteBalance, |
|
&c.CommitFee, &c.FeePerKw, &c.CommitTx, &c.CommitSig, |
|
) |
|
if err != nil { |
|
return c, err |
|
} |
|
|
|
c.Htlcs, err = deserializeHtlcs(r) |
|
if err != nil { |
|
return c, err |
|
} |
|
|
|
return c, nil |
|
} |
|
|
|
func DeserializeCommitDiff(r io.Reader) (*common.CommitDiff, error) { |
|
var ( |
|
d common.CommitDiff |
|
err error |
|
) |
|
|
|
d.Commitment, err = deserializeChanCommit(r) |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
d.CommitSig = &lnwire.CommitSig{} |
|
if err := d.CommitSig.Decode(r, 0); err != nil { |
|
return nil, err |
|
} |
|
|
|
d.LogUpdates, err = DeserializeLogUpdates(r) |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
var numOpenRefs uint16 |
|
if err := binary.Read(r, byteOrder, &numOpenRefs); err != nil { |
|
return nil, err |
|
} |
|
|
|
d.OpenedCircuitKeys = make([]common.CircuitKey, numOpenRefs) |
|
for i := 0; i < int(numOpenRefs); i++ { |
|
err := ReadElements(r, |
|
&d.OpenedCircuitKeys[i].ChanID, |
|
&d.OpenedCircuitKeys[i].HtlcID) |
|
if err != nil { |
|
return nil, err |
|
} |
|
} |
|
|
|
var numClosedRefs uint16 |
|
if err := binary.Read(r, byteOrder, &numClosedRefs); err != nil { |
|
return nil, err |
|
} |
|
|
|
d.ClosedCircuitKeys = make([]common.CircuitKey, numClosedRefs) |
|
for i := 0; i < int(numClosedRefs); i++ { |
|
err := ReadElements(r, |
|
&d.ClosedCircuitKeys[i].ChanID, |
|
&d.ClosedCircuitKeys[i].HtlcID) |
|
if err != nil { |
|
return nil, err |
|
} |
|
} |
|
|
|
return &d, nil |
|
} |
|
|
|
func serializeHtlcs(b io.Writer, htlcs ...common.HTLC) error { |
|
numHtlcs := uint16(len(htlcs)) |
|
if err := WriteElement(b, numHtlcs); err != nil { |
|
return err |
|
} |
|
|
|
for _, htlc := range htlcs { |
|
if err := WriteElements(b, |
|
htlc.Signature, htlc.RHash, htlc.Amt, htlc.RefundTimeout, |
|
htlc.OutputIndex, htlc.Incoming, htlc.OnionBlob, |
|
htlc.HtlcIndex, htlc.LogIndex, |
|
); err != nil { |
|
return err |
|
} |
|
} |
|
|
|
return nil |
|
} |
|
|
|
func serializeChanCommit(w io.Writer, c *common.ChannelCommitment) error { |
|
if err := WriteElements(w, |
|
c.CommitHeight, c.LocalLogIndex, c.LocalHtlcIndex, |
|
c.RemoteLogIndex, c.RemoteHtlcIndex, c.LocalBalance, |
|
c.RemoteBalance, c.CommitFee, c.FeePerKw, c.CommitTx, |
|
c.CommitSig, |
|
); err != nil { |
|
return err |
|
} |
|
|
|
return serializeHtlcs(w, c.Htlcs...) |
|
} |
|
|
|
func SerializeLogUpdates(w io.Writer, logUpdates []common.LogUpdate) error { |
|
numUpdates := uint16(len(logUpdates)) |
|
if err := binary.Write(w, byteOrder, numUpdates); err != nil { |
|
return err |
|
} |
|
|
|
for _, diff := range logUpdates { |
|
err := WriteElements(w, diff.LogIndex, diff.UpdateMsg) |
|
if err != nil { |
|
return err |
|
} |
|
} |
|
|
|
return nil |
|
} |
|
|
|
func SerializeCommitDiff(w io.Writer, diff *common.CommitDiff) error { // nolint: dupl |
|
if err := serializeChanCommit(w, &diff.Commitment); err != nil { |
|
return err |
|
} |
|
|
|
if err := diff.CommitSig.Encode(w, 0); err != nil { |
|
return err |
|
} |
|
|
|
if err := SerializeLogUpdates(w, diff.LogUpdates); err != nil { |
|
return err |
|
} |
|
|
|
numOpenRefs := uint16(len(diff.OpenedCircuitKeys)) |
|
if err := binary.Write(w, byteOrder, numOpenRefs); err != nil { |
|
return err |
|
} |
|
|
|
for _, openRef := range diff.OpenedCircuitKeys { |
|
err := WriteElements(w, openRef.ChanID, openRef.HtlcID) |
|
if err != nil { |
|
return err |
|
} |
|
} |
|
|
|
numClosedRefs := uint16(len(diff.ClosedCircuitKeys)) |
|
if err := binary.Write(w, byteOrder, numClosedRefs); err != nil { |
|
return err |
|
} |
|
|
|
for _, closedRef := range diff.ClosedCircuitKeys { |
|
err := WriteElements(w, closedRef.ChanID, closedRef.HtlcID) |
|
if err != nil { |
|
return err |
|
} |
|
} |
|
|
|
return nil |
|
} |
|
|
|
func DeserializeNetworkResult(r io.Reader) (*common.NetworkResult, error) { |
|
var ( |
|
err error |
|
) |
|
|
|
n := &common.NetworkResult{} |
|
|
|
n.Msg, err = lnwire.ReadMessage(r, 0) |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
if err := ReadElements(r, |
|
&n.Unencrypted, &n.IsResolution, |
|
); err != nil { |
|
return nil, err |
|
} |
|
|
|
return n, nil |
|
} |
|
|
|
func SerializeNetworkResult(w io.Writer, n *common.NetworkResult) error { |
|
if _, err := lnwire.WriteMessage(w, n.Msg, 0); err != nil { |
|
return err |
|
} |
|
|
|
return WriteElements(w, n.Unencrypted, n.IsResolution) |
|
} |
|
|
|
func readChanConfig(b io.Reader, c *common.ChannelConfig) error { // nolint: dupl |
|
return ReadElements(b, |
|
&c.DustLimit, &c.MaxPendingAmount, &c.ChanReserve, |
|
&c.MinHTLC, &c.MaxAcceptedHtlcs, &c.CsvDelay, |
|
&c.MultiSigKey, &c.RevocationBasePoint, |
|
&c.PaymentBasePoint, &c.DelayBasePoint, |
|
&c.HtlcBasePoint, |
|
) |
|
} |
|
|
|
func DeserializeCloseChannelSummary(r io.Reader) (*common.ChannelCloseSummary, error) { // nolint: dupl |
|
|
|
c := &common.ChannelCloseSummary{} |
|
|
|
err := ReadElements(r, |
|
&c.ChanPoint, &c.ShortChanID, &c.ChainHash, &c.ClosingTXID, |
|
&c.CloseHeight, &c.RemotePub, &c.Capacity, &c.SettledBalance, |
|
&c.TimeLockedBalance, &c.CloseType, &c.IsPending, |
|
) |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
// We'll now check to see if the channel close summary was encoded with |
|
// any of the additional optional fields. |
|
var hasNewFields bool |
|
err = ReadElements(r, &hasNewFields) |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
// If fields are not present, we can return. |
|
if !hasNewFields { |
|
return c, nil |
|
} |
|
|
|
// Otherwise read the new fields. |
|
if err := ReadElements(r, &c.RemoteCurrentRevocation); err != nil { |
|
return nil, err |
|
} |
|
|
|
if err := readChanConfig(r, &c.LocalChanConfig); err != nil { |
|
return nil, err |
|
} |
|
|
|
// Finally, we'll attempt to read the next unrevoked commitment point |
|
// for the remote party. If we closed the channel before receiving a |
|
// funding locked message then this might not be present. A boolean |
|
// indicating whether the field is present will come first. |
|
var hasRemoteNextRevocation bool |
|
err = ReadElements(r, &hasRemoteNextRevocation) |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
// If this field was written, read it. |
|
if hasRemoteNextRevocation { |
|
err = ReadElements(r, &c.RemoteNextRevocation) |
|
if err != nil { |
|
return nil, err |
|
} |
|
} |
|
|
|
// Check if we have a channel sync message to read. |
|
var hasChanSyncMsg bool |
|
err = ReadElements(r, &hasChanSyncMsg) |
|
if err == io.EOF { |
|
return c, nil |
|
} else if err != nil { |
|
return nil, err |
|
} |
|
|
|
// If a chan sync message is present, read it. |
|
if hasChanSyncMsg { |
|
// We must pass in reference to a lnwire.Message for the codec |
|
// to support it. |
|
msg, err := lnwire.ReadMessage(r, 0) |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
chanSync, ok := msg.(*lnwire.ChannelReestablish) |
|
if !ok { |
|
return nil, errors.New("unable cast db Message to " + |
|
"ChannelReestablish") |
|
} |
|
c.LastChanSyncMsg = chanSync |
|
} |
|
|
|
return c, nil |
|
} |
|
|
|
func writeChanConfig(b io.Writer, c *common.ChannelConfig) error { // nolint: dupl |
|
return WriteElements(b, |
|
c.DustLimit, c.MaxPendingAmount, c.ChanReserve, c.MinHTLC, |
|
c.MaxAcceptedHtlcs, c.CsvDelay, c.MultiSigKey, |
|
c.RevocationBasePoint, c.PaymentBasePoint, c.DelayBasePoint, |
|
c.HtlcBasePoint, |
|
) |
|
} |
|
|
|
func SerializeChannelCloseSummary(w io.Writer, cs *common.ChannelCloseSummary) error { |
|
err := WriteElements(w, |
|
cs.ChanPoint, cs.ShortChanID, cs.ChainHash, cs.ClosingTXID, |
|
cs.CloseHeight, cs.RemotePub, cs.Capacity, cs.SettledBalance, |
|
cs.TimeLockedBalance, cs.CloseType, cs.IsPending, |
|
) |
|
if err != nil { |
|
return err |
|
} |
|
|
|
// If this is a close channel summary created before the addition of |
|
// the new fields, then we can exit here. |
|
if cs.RemoteCurrentRevocation == nil { |
|
return WriteElements(w, false) |
|
} |
|
|
|
// If fields are present, write boolean to indicate this, and continue. |
|
if err := WriteElements(w, true); err != nil { |
|
return err |
|
} |
|
|
|
if err := WriteElements(w, cs.RemoteCurrentRevocation); err != nil { |
|
return err |
|
} |
|
|
|
if err := writeChanConfig(w, &cs.LocalChanConfig); err != nil { |
|
return err |
|
} |
|
|
|
// The RemoteNextRevocation field is optional, as it's possible for a |
|
// channel to be closed before we learn of the next unrevoked |
|
// revocation point for the remote party. Write a boolen indicating |
|
// whether this field is present or not. |
|
if err := WriteElements(w, cs.RemoteNextRevocation != nil); err != nil { |
|
return err |
|
} |
|
|
|
// Write the field, if present. |
|
if cs.RemoteNextRevocation != nil { |
|
if err = WriteElements(w, cs.RemoteNextRevocation); err != nil { |
|
return err |
|
} |
|
} |
|
|
|
// Write whether the channel sync message is present. |
|
if err := WriteElements(w, cs.LastChanSyncMsg != nil); err != nil { |
|
return err |
|
} |
|
|
|
// Write the channel sync message, if present. |
|
if cs.LastChanSyncMsg != nil { |
|
_, err = lnwire.WriteMessage(w, cs.LastChanSyncMsg, 0) |
|
if err != nil { |
|
return err |
|
} |
|
} |
|
|
|
return nil |
|
} |
|
|
|
// ErrCorruptedFwdPkg signals that the on-disk structure of the forwarding |
|
// package has potentially been mangled. |
|
var ErrCorruptedFwdPkg = errors.New("fwding package db has been corrupted") |
|
|
|
var ( |
|
// fwdPackagesKey is the root-level bucket that all forwarding packages |
|
// are written. This bucket is further subdivided based on the short |
|
// channel ID of each channel. |
|
fwdPackagesKey = []byte("fwd-packages") |
|
|
|
// addBucketKey is the bucket to which all Add log updates are written. |
|
addBucketKey = []byte("add-updates") |
|
|
|
// failSettleBucketKey is the bucket to which all Settle/Fail log |
|
// updates are written. |
|
failSettleBucketKey = []byte("fail-settle-updates") |
|
|
|
// fwdFilterKey is a key used to write the set of Adds that passed |
|
// validation and are to be forwarded to the switch. |
|
// NOTE: The presence of this key within a forwarding package indicates |
|
// that the package has reached FwdStateProcessed. |
|
fwdFilterKey = []byte("fwd-filter-key") |
|
|
|
// ackFilterKey is a key used to access the PkgFilter indicating which |
|
// Adds have received a Settle/Fail. This response may come from a |
|
// number of sources, including: exitHop settle/fails, switch failures, |
|
// chain arbiter interjections, as well as settle/fails from the |
|
// next hop in the route. |
|
ackFilterKey = []byte("ack-filter-key") |
|
|
|
// settleFailFilterKey is a key used to access the PkgFilter indicating |
|
// which Settles/Fails in have been received and processed by the link |
|
// that originally received the Add. |
|
settleFailFilterKey = []byte("settle-fail-filter-key") |
|
) |
|
|
|
func makeLogKey(updateNum uint64) [8]byte { |
|
var key [8]byte |
|
byteOrder.PutUint64(key[:], updateNum) |
|
return key |
|
} |
|
|
|
// uint16Key writes the provided 16-bit unsigned integer to a 2-byte slice. |
|
func uint16Key(i uint16) []byte { |
|
key := make([]byte, 2) |
|
byteOrder.PutUint16(key, i) |
|
return key |
|
} |
|
|
|
// ChannelPackager is used by a channel to manage the lifecycle of its forwarding |
|
// packages. The packager is tied to a particular source channel ID, allowing it |
|
// to create and edit its own packages. Each packager also has the ability to |
|
// remove fail/settle htlcs that correspond to an add contained in one of |
|
// source's packages. |
|
type ChannelPackager struct { |
|
source lnwire.ShortChannelID |
|
} |
|
|
|
// NewChannelPackager creates a new packager for a single channel. |
|
func NewChannelPackager(source lnwire.ShortChannelID) *ChannelPackager { |
|
return &ChannelPackager{ |
|
source: source, |
|
} |
|
} |
|
|
|
// AddFwdPkg writes a newly locked in forwarding package to disk. |
|
func (*ChannelPackager) AddFwdPkg(tx kvdb.RwTx, fwdPkg *common.FwdPkg) error { // nolint: dupl |
|
fwdPkgBkt, err := tx.CreateTopLevelBucket(fwdPackagesKey) |
|
if err != nil { |
|
return err |
|
} |
|
|
|
source := makeLogKey(fwdPkg.Source.ToUint64()) |
|
sourceBkt, err := fwdPkgBkt.CreateBucketIfNotExists(source[:]) |
|
if err != nil { |
|
return err |
|
} |
|
|
|
heightKey := makeLogKey(fwdPkg.Height) |
|
heightBkt, err := sourceBkt.CreateBucketIfNotExists(heightKey[:]) |
|
if err != nil { |
|
return err |
|
} |
|
|
|
// Write ADD updates we received at this commit height. |
|
addBkt, err := heightBkt.CreateBucketIfNotExists(addBucketKey) |
|
if err != nil { |
|
return err |
|
} |
|
|
|
// Write SETTLE/FAIL updates we received at this commit height. |
|
failSettleBkt, err := heightBkt.CreateBucketIfNotExists(failSettleBucketKey) |
|
if err != nil { |
|
return err |
|
} |
|
|
|
for i := range fwdPkg.Adds { |
|
err = putLogUpdate(addBkt, uint16(i), &fwdPkg.Adds[i]) |
|
if err != nil { |
|
return err |
|
} |
|
} |
|
|
|
// Persist the initialized pkg filter, which will be used to determine |
|
// when we can remove this forwarding package from disk. |
|
var ackFilterBuf bytes.Buffer |
|
if err := fwdPkg.AckFilter.Encode(&ackFilterBuf); err != nil { |
|
return err |
|
} |
|
|
|
if err := heightBkt.Put(ackFilterKey, ackFilterBuf.Bytes()); err != nil { |
|
return err |
|
} |
|
|
|
for i := range fwdPkg.SettleFails { |
|
err = putLogUpdate(failSettleBkt, uint16(i), &fwdPkg.SettleFails[i]) |
|
if err != nil { |
|
return err |
|
} |
|
} |
|
|
|
var settleFailFilterBuf bytes.Buffer |
|
err = fwdPkg.SettleFailFilter.Encode(&settleFailFilterBuf) |
|
if err != nil { |
|
return err |
|
} |
|
|
|
return heightBkt.Put(settleFailFilterKey, settleFailFilterBuf.Bytes()) |
|
} |
|
|
|
// putLogUpdate writes an htlc to the provided `bkt`, using `index` as the key. |
|
func putLogUpdate(bkt kvdb.RwBucket, idx uint16, htlc *common.LogUpdate) error { |
|
var b bytes.Buffer |
|
if err := serializeLogUpdate(&b, htlc); err != nil { |
|
return err |
|
} |
|
|
|
return bkt.Put(uint16Key(idx), b.Bytes()) |
|
} |
|
|
|
// LoadFwdPkgs scans the forwarding log for any packages that haven't been |
|
// processed, and returns their deserialized log updates in a map indexed by the |
|
// remote commitment height at which the updates were locked in. |
|
func (p *ChannelPackager) LoadFwdPkgs(tx kvdb.RTx) ([]*common.FwdPkg, error) { |
|
return loadChannelFwdPkgs(tx, p.source) |
|
} |
|
|
|
// loadChannelFwdPkgs loads all forwarding packages owned by `source`. |
|
func loadChannelFwdPkgs(tx kvdb.RTx, source lnwire.ShortChannelID) ([]*common.FwdPkg, error) { // nolint: dupl |
|
fwdPkgBkt := tx.ReadBucket(fwdPackagesKey) |
|
if fwdPkgBkt == nil { |
|
return nil, nil |
|
} |
|
|
|
sourceKey := makeLogKey(source.ToUint64()) |
|
sourceBkt := fwdPkgBkt.NestedReadBucket(sourceKey[:]) |
|
if sourceBkt == nil { |
|
return nil, nil |
|
} |
|
|
|
var heights []uint64 |
|
if err := sourceBkt.ForEach(func(k, _ []byte) error { |
|
if len(k) != 8 { |
|
return ErrCorruptedFwdPkg |
|
} |
|
|
|
heights = append(heights, byteOrder.Uint64(k)) |
|
|
|
return nil |
|
}); err != nil { |
|
return nil, err |
|
} |
|
|
|
// Load the forwarding package for each retrieved height. |
|
fwdPkgs := make([]*common.FwdPkg, 0, len(heights)) |
|
for _, height := range heights { |
|
fwdPkg, err := loadFwdPkg(fwdPkgBkt, source, height) |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
fwdPkgs = append(fwdPkgs, fwdPkg) |
|
} |
|
|
|
return fwdPkgs, nil |
|
} |
|
|
|
// loadFwdPkg reads the packager's fwd pkg at a given height, and determines the |
|
// appropriate FwdState. |
|
func loadFwdPkg(fwdPkgBkt kvdb.RBucket, source lnwire.ShortChannelID, |
|
height uint64) (*common.FwdPkg, error) { |
|
|
|
sourceKey := makeLogKey(source.ToUint64()) |
|
sourceBkt := fwdPkgBkt.NestedReadBucket(sourceKey[:]) |
|
if sourceBkt == nil { |
|
return nil, ErrCorruptedFwdPkg |
|
} |
|
|
|
heightKey := makeLogKey(height) |
|
heightBkt := sourceBkt.NestedReadBucket(heightKey[:]) |
|
if heightBkt == nil { |
|
return nil, ErrCorruptedFwdPkg |
|
} |
|
|
|
// Load ADDs from disk. |
|
addBkt := heightBkt.NestedReadBucket(addBucketKey) |
|
if addBkt == nil { |
|
return nil, ErrCorruptedFwdPkg |
|
} |
|
|
|
adds, err := loadHtlcs(addBkt) |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
// Load ack filter from disk. |
|
ackFilterBytes := heightBkt.Get(ackFilterKey) |
|
if ackFilterBytes == nil { |
|
return nil, ErrCorruptedFwdPkg |
|
} |
|
ackFilterReader := bytes.NewReader(ackFilterBytes) |
|
|
|
ackFilter := &common.PkgFilter{} |
|
if err := ackFilter.Decode(ackFilterReader); err != nil { |
|
return nil, err |
|
} |
|
|
|
// Load SETTLE/FAILs from disk. |
|
failSettleBkt := heightBkt.NestedReadBucket(failSettleBucketKey) |
|
if failSettleBkt == nil { |
|
return nil, ErrCorruptedFwdPkg |
|
} |
|
|
|
failSettles, err := loadHtlcs(failSettleBkt) |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
// Load settle fail filter from disk. |
|
settleFailFilterBytes := heightBkt.Get(settleFailFilterKey) |
|
if settleFailFilterBytes == nil { |
|
return nil, ErrCorruptedFwdPkg |
|
} |
|
settleFailFilterReader := bytes.NewReader(settleFailFilterBytes) |
|
|
|
settleFailFilter := &common.PkgFilter{} |
|
if err := settleFailFilter.Decode(settleFailFilterReader); err != nil { |
|
return nil, err |
|
} |
|
|
|
// Initialize the fwding package, which always starts in the |
|
// FwdStateLockedIn. We can determine what state the package was left in |
|
// by examining constraints on the information loaded from disk. |
|
fwdPkg := &common.FwdPkg{ |
|
Source: source, |
|
State: common.FwdStateLockedIn, |
|
Height: height, |
|
Adds: adds, |
|
AckFilter: ackFilter, |
|
SettleFails: failSettles, |
|
SettleFailFilter: settleFailFilter, |
|
} |
|
|
|
// Check to see if we have written the set exported filter adds to |
|
// disk. If we haven't, processing of this package was never started, or |
|
// failed during the last attempt. |
|
fwdFilterBytes := heightBkt.Get(fwdFilterKey) |
|
if fwdFilterBytes == nil { |
|
nAdds := uint16(len(adds)) |
|
fwdPkg.FwdFilter = common.NewPkgFilter(nAdds) |
|
return fwdPkg, nil |
|
} |
|
|
|
fwdFilterReader := bytes.NewReader(fwdFilterBytes) |
|
fwdPkg.FwdFilter = &common.PkgFilter{} |
|
if err := fwdPkg.FwdFilter.Decode(fwdFilterReader); err != nil { |
|
return nil, err |
|
} |
|
|
|
// Otherwise, a complete round of processing was completed, and we |
|
// advance the package to FwdStateProcessed. |
|
fwdPkg.State = common.FwdStateProcessed |
|
|
|
// If every add, settle, and fail has been fully acknowledged, we can |
|
// safely set the package's state to FwdStateCompleted, signalling that |
|
// it can be garbage collected. |
|
if fwdPkg.AckFilter.IsFull() && fwdPkg.SettleFailFilter.IsFull() { |
|
fwdPkg.State = common.FwdStateCompleted |
|
} |
|
|
|
return fwdPkg, nil |
|
} |
|
|
|
// loadHtlcs retrieves all serialized htlcs in a bucket, returning |
|
// them in order of the indexes they were written under. |
|
func loadHtlcs(bkt kvdb.RBucket) ([]common.LogUpdate, error) { |
|
var htlcs []common.LogUpdate |
|
if err := bkt.ForEach(func(_, v []byte) error { |
|
htlc, err := deserializeLogUpdate(bytes.NewReader(v)) |
|
if err != nil { |
|
return err |
|
} |
|
|
|
htlcs = append(htlcs, *htlc) |
|
|
|
return nil |
|
}); err != nil { |
|
return nil, err |
|
} |
|
|
|
return htlcs, nil |
|
} |
|
|
|
// serializeLogUpdate writes a log update to the provided io.Writer. |
|
func serializeLogUpdate(w io.Writer, l *common.LogUpdate) error { |
|
return WriteElements(w, l.LogIndex, l.UpdateMsg) |
|
} |
|
|
|
// deserializeLogUpdate reads a log update from the provided io.Reader. |
|
func deserializeLogUpdate(r io.Reader) (*common.LogUpdate, error) { |
|
l := &common.LogUpdate{} |
|
if err := ReadElements(r, &l.LogIndex, &l.UpdateMsg); err != nil { |
|
return nil, err |
|
} |
|
|
|
return l, nil |
|
}
|
|
|