lightningdevkit / ldk-swift

48 stars 12 forks source link

Persisting ChannelManager and ChannelMonitors with Codable #90

Closed danielnordh closed 1 year ago

danielnordh commented 1 year ago

My first attempt at persisting the channel manager and monitors was to try to convert it to Data:

let data = try! JSONEncoder().encode(self.channelManager)swift

But since ChannelManager does not conform to Codable, that doesn't work.

Any chance we could make that class (and ChannelMonitor) Codable?

danielnordh commented 1 year ago

OK, I figured it out somewhat.

For monitors, in persistNewChannel and updatePersistedChannel the LDK input types have .write() functions that return bytes that can be encoded in a ChannelMonitorData struct I defined that confirms to Codable.

let channelMonitorData = ChannelMonitorData(idBytes: channelId.write(), dataBytes: data.write())
public struct ChannelMonitorData: Codable {
    public var idBytes: [UInt8]
    public var dataBytes: [UInt8]

    public init(idBytes: [UInt8], dataBytes: [UInt8]) {
        self.idBytes = idBytes
        self.dataBytes = dataBytes
    }
}

And similar for the channel manager in persistManager, getting encodable data from the .write() function

let channelManagerData = channelManager.write()
danielnordh commented 1 year ago

Actually, it is still not clear to me what the serialized monitor data should be like when saved, to work when fed back to the channelMonitorsSerialized parameter of ChannelManagerConstructor()

It expects [[UInt8]], but each channel serialization seems to need idBytes and data, which each produces [UInt8]. Many channel monitors together should then result in [[[UInt8]]]. I'm confused.

danielnordh commented 1 year ago

So after asking in discord I found out that channelMonitorSerialized expects and array of the data only.

If anyone else is trying this here is what I ended up with. I save a custom class I created called ChannelState

// Saving
// Save everything together as ChannelState. Have getters for the required serialized data.
public struct ChannelMonitorData: Codable {
    public var idBytes: [UInt8]
    public var dataBytes: [UInt8]

    public init(idBytes: [UInt8], dataBytes: [UInt8]) {
        self.idBytes = idBytes
        self.dataBytes = dataBytes
    }
}

public class ChannelState: Codable {
    public var channelMonitors: [ChannelMonitorData]
    public var channelManager: [UInt8]

    public init(monitorsData: [ChannelMonitorData], managerData: [UInt8]) {
        self.channelMonitors = monitorsData
        self.channelManager = managerData
    }

    public func getSerializedMonitors() -> [[UInt8]] {
        var serializedMonitors: [[UInt8]] = []
        for monitorData in self.channelMonitors {
            serializedMonitors.append(monitorData.dataBytes)
        }
        return serializedMonitors
    }

    public func addChannelMonitor(channelMonitor: ChannelMonitorData) {
        self.channelMonitors.append(channelMonitor)
        do {
            try saveChannelState(channelState: self)
        } catch let error {
            debugPrint(error.localizedDescription)
        }
    }

    public func updateChannelMonitor(channelMonitor: ChannelMonitorData) {
        if let index = channelMonitors.firstIndex(where: {$0.idBytes == channelMonitor.idBytes}) {
            channelMonitors[index] = channelMonitor
            do {
                try saveChannelState(channelState: self)
            } catch let error {
                debugPrint(error.localizedDescription)
            }
        }
    }

    public func updateChannelManager(channelManager: ChannelManager) {
        self.channelManager = channelManager.write()
        do {
            try saveChannelState(channelState: self)
        } catch let error {
            debugPrint(error.localizedDescription)
        }
    }
}