From 591c58608bdf1a3e1c22902c42b4f00530b1dd9d Mon Sep 17 00:00:00 2001 From: Arik Sosman Date: Tue, 10 Sep 2024 23:40:59 -0700 Subject: [PATCH 1/2] Allow auto-formating battery files. --- src/config.mts | 12 ++++++++++++ src/generation/index.mts | 18 +++++++++++++----- src/index.mts | 2 +- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/src/config.mts b/src/config.mts index c641e8d4..c0c60eb4 100644 --- a/src/config.mts +++ b/src/config.mts @@ -30,6 +30,18 @@ export default class Config { return outputDirectoryPath; } + getBatteryDirectoryPaths(): string[] { + const __dirname = url.fileURLToPath(new URL('.', import.meta.url)); + const ciProjectPath = `${__dirname}/../ci/LDKSwift`; + const xcodeProjectPath = `${__dirname}/../xcode/LDKFramework`; + return [ + `${ciProjectPath}/Sources/LDKSwift/batteries/`, + `${ciProjectPath}/Tests/LDKSwiftTests/`, + `${xcodeProjectPath}/DirectlyLinkedBindingsApp/`, + `${xcodeProjectPath}/DirectlyLinkedBindingsAppTests/`, + ]; + } + getSwiftFormatterBinaryPath(): string | null { let swiftformatPath = process.env['SWIFT_FORMAT_PATH']; if (!swiftformatPath) { diff --git a/src/generation/index.mts b/src/generation/index.mts index 0153b128..c7ee11fc 100644 --- a/src/generation/index.mts +++ b/src/generation/index.mts @@ -154,7 +154,7 @@ export default class Generator { } } - async runFormatter() { + async runFormatter(formatBatteryFiles = false) { const configFilePath = url.fileURLToPath(new URL('.', import.meta.url)) + '../../.swift-format'; const outputDirectory = this.parser.config.getOutputBaseDirectoryPath(); const swiftFormatterBinary = this.parser.config.getSwiftFormatterBinaryPath(); @@ -162,16 +162,24 @@ export default class Generator { return; } + const paths = [outputDirectory]; + if (formatBatteryFiles) { + paths.push(...this.parser.config.getBatteryDirectoryPaths()); + } + try { const command = `${swiftFormatterBinary} --configuration ${configFilePath} --recursive --in-place ./ `; console.log(command); - child_process.execSync(command, { - cwd: outputDirectory - }).toString('utf-8'); + for (const currentDirectory of paths) { + console.log(currentDirectory); + child_process.execSync(command, { + cwd: currentDirectory + }).toString('utf-8'); + } } catch (e) { const errorOutput = e.stderr.toString('utf-8').trim(); console.error('Failed to format Swift output:', errorOutput); - throw new Error(errorOutput) + throw new Error(errorOutput); } // after all is generated and formatted, we generate the version file diff --git a/src/index.mts b/src/index.mts index 001ab9e2..a8122e41 100644 --- a/src/index.mts +++ b/src/index.mts @@ -18,7 +18,7 @@ import Generator from './generation/index.mjs'; await generator.generateTypes(); await generator.generateFunctions(); - await generator.runFormatter(); + await generator.runFormatter(true); })(); interface NullTest { From 5389f06232e7d047c7af5233ddfe5b269ecad667 Mon Sep 17 00:00:00 2001 From: Arik Sosman Date: Tue, 10 Sep 2024 23:45:04 -0700 Subject: [PATCH 2/2] Run formatter. --- .../batteries/ChannelManagerConstructor.swift | 873 ++++---- .../HumanObjectPeerTestInstance.swift | 1991 +++++++++-------- .../Tests/LDKSwiftTests/LDKSwiftTests.swift | 923 ++++---- .../Tests/LDKSwiftTests/LDKTestFixtures.swift | 637 +++++- .../TestBroadcasterInterface.swift | 14 +- .../TestChannelManagerPersister.swift | 40 +- .../LDKSwiftTests/TestFeeEstimator.swift | 10 +- .../Tests/LDKSwiftTests/TestFilter.swift | 36 +- .../Tests/LDKSwiftTests/TestLogger.swift | 11 +- .../Tests/LDKSwiftTests/TestPersister.swift | 25 +- .../WrappedSignerProviderTests.swift | 313 +-- .../LDKSwiftTests/bitcoin/BTCBlock.swift | 215 +- .../LDKSwiftTests/bitcoin/BTCHashing.swift | 30 +- .../bitcoin/BTCTransaction.swift | 348 +-- .../LDKSwiftTests/bitcoin/BTCVarInt.swift | 70 +- .../LDKSwiftTests/bitcoin/BitcoinTests.swift | 412 ++-- out/VersionDescriptor.swift | 6 +- .../ContentView.swift | 106 +- .../DirectlyLinkedBindingsAppApp.swift | 76 +- .../PolarConnectionExperiment.swift | 313 +-- .../app-batteries/BlockchainObserver.swift | 88 +- .../app-batteries/CombineUtils.swift | 2 +- .../app-batteries/LNSyncHandler.swift | 565 ++--- .../PolarIntegrationSample.swift | 1275 ++++++----- .../RegtestBlockchainManager.swift | 28 +- .../RegtestBlockchainObserverOld.swift | 814 +++---- .../RegtestBroadcasterInterface.swift | 58 +- .../RegtestChannelManagerPersister.swift | 168 +- .../SwiftSocketEchoHandler.swift | 275 +-- .../SwiftSocketPeerHandler.swift | 419 ++-- .../DirectlyLinkedBindingsAppTests.swift | 45 +- .../polar/BlockchainObserver.swift | 88 +- .../polar/CombineUtils.swift | 40 +- .../polar/PolarIntegrationSample.swift | 613 ++--- .../polar/RegtestBlockchainManager.swift | 28 +- 35 files changed, 6024 insertions(+), 4931 deletions(-) diff --git a/ci/LDKSwift/Sources/LDKSwift/batteries/ChannelManagerConstructor.swift b/ci/LDKSwift/Sources/LDKSwift/batteries/ChannelManagerConstructor.swift index 4ef81044..dda56642 100644 --- a/ci/LDKSwift/Sources/LDKSwift/batteries/ChannelManagerConstructor.swift +++ b/ci/LDKSwift/Sources/LDKSwift/batteries/ChannelManagerConstructor.swift @@ -5,324 +5,393 @@ // Created by Arik Sosman on 5/19/21. // +import Foundation + #if SWIFT_PACKAGE -import LDKHeaders + import LDKHeaders #endif -import Foundation enum InvalidSerializedDataError: Error { - case invalidSerializedChannelMonitor - case invalidSerializedChannelManager - case invalidSerializedNetworkGraph - case duplicateSerializedChannelMonitor - case badNodeSecret + case invalidSerializedChannelMonitor + case invalidSerializedChannelManager + case invalidSerializedNetworkGraph + case duplicateSerializedChannelMonitor + case badNodeSecret } enum RouterUnavailableError: Error { - case channelManagerInitializationMissingScorerOrNetworkGraph + case channelManagerInitializationMissingScorerOrNetworkGraph } /// Argument used when deserializing ChannelManager instances. If the user has previously already taken it upon themselves to deserialize the NetworkGraph, the `instance` /// case may be used. Otherwise, `serialized` may be passed to have the deserializer handle the NetworkGraph, too. public enum NetworkGraphArgument { - /// Use if you have a serialized NetworkGraph that should be deserialized. - case serialized([UInt8]) - /// Use if you have already deserialized a NetworkGraph and wish to pass that instance here. - case instance(NetworkGraph) + /// Use if you have a serialized NetworkGraph that should be deserialized. + case serialized([UInt8]) + /// Use if you have already deserialized a NetworkGraph and wish to pass that instance here. + case instance(NetworkGraph) } public struct ChannelManagerConstructionParameters { - public var config: UserConfig - public var entropySource: EntropySource - public var nodeSigner: NodeSigner - public var signerProvider: SignerProvider - public var feeEstimator: FeeEstimator - public var chainMonitor: ChainMonitor - public var txBroadcaster: BroadcasterInterface - public var enableP2PGossip: Bool = false - public var scorer: MultiThreadedLockableScore? - public var scoreParams: ProbabilisticScoringFeeParameters? - public var payerRetries: Retry = Retry.initWithAttempts(a: UInt32(3)) - public var logger: Logger - - public init (config: UserConfig, entropySource: EntropySource, nodeSigner: NodeSigner, signerProvider: SignerProvider, feeEstimator: FeeEstimator, chainMonitor: ChainMonitor, txBroadcaster: BroadcasterInterface, logger: Logger, enableP2PGossip: Bool = false, scorer: MultiThreadedLockableScore? = nil, scoreParams: ProbabilisticScoringFeeParameters? = nil, payerRetries: Retry = Retry.initWithAttempts(a: UInt32(3))) { - self.config = config - self.entropySource = entropySource - self.nodeSigner = nodeSigner - self.signerProvider = signerProvider - self.feeEstimator = feeEstimator - self.chainMonitor = chainMonitor - self.txBroadcaster = txBroadcaster - self.logger = logger - - self.enableP2PGossip = enableP2PGossip - self.scorer = scorer - if scorer != nil && scoreParams == nil { - self.scoreParams = ProbabilisticScoringFeeParameters.initWithDefault() - } else { - self.scoreParams = scoreParams - } - self.payerRetries = payerRetries - } - - private class CMCRouter: Router { - override func findRoute(payer: [UInt8], routeParams: Bindings.RouteParameters, firstHops: [Bindings.ChannelDetails]?, inflightHtlcs: Bindings.InFlightHtlcs) -> Bindings.Result_RouteLightningErrorZ { - let errorString = "Error: Router unavailable: ChannelManagerConstructor initialized without Scorer or NetworkGraph." - Bindings.print(errorString, severity: .ERROR) - return .initWithErr(e: LightningError(errArg: errorString, actionArg: ErrorAction.initWithIgnoreAndLog(a: .Error))) - } - override func findRouteWithId(payer: [UInt8], routeParams: Bindings.RouteParameters, firstHops: [Bindings.ChannelDetails]?, inflightHtlcs: Bindings.InFlightHtlcs, paymentHash PaymentHash: [UInt8], paymentId PaymentId: [UInt8]) -> Bindings.Result_RouteLightningErrorZ { - let errorString = "Error: Router unavailable: ChannelManagerConstructor initialized without Scorer or NetworkGraph." - Bindings.print(errorString, severity: .ERROR) - return .initWithErr(e: LightningError(errArg: errorString, actionArg: ErrorAction.initWithIgnoreAndLog(a: .Error))) - } - } - - fileprivate func router(networkGraph: NetworkGraph?) -> Router { - if let netGraph = networkGraph, let scorer = self.scorer, let scoreParams = self.scoreParams { - return DefaultRouter(networkGraph: netGraph, logger: self.logger, entropySource: self.entropySource, scorer: scorer.asLockableScore(), scoreParams: scoreParams).asRouter() - } - return CMCRouter(messageRouter: MessageRouter()) - } + public var config: UserConfig + public var entropySource: EntropySource + public var nodeSigner: NodeSigner + public var signerProvider: SignerProvider + public var feeEstimator: FeeEstimator + public var chainMonitor: ChainMonitor + public var txBroadcaster: BroadcasterInterface + public var enableP2PGossip: Bool = false + public var scorer: MultiThreadedLockableScore? + public var scoreParams: ProbabilisticScoringFeeParameters? + public var payerRetries: Retry = Retry.initWithAttempts(a: UInt32(3)) + public var logger: Logger + + public init( + config: UserConfig, entropySource: EntropySource, nodeSigner: NodeSigner, signerProvider: SignerProvider, + feeEstimator: FeeEstimator, chainMonitor: ChainMonitor, txBroadcaster: BroadcasterInterface, logger: Logger, + enableP2PGossip: Bool = false, scorer: MultiThreadedLockableScore? = nil, + scoreParams: ProbabilisticScoringFeeParameters? = nil, + payerRetries: Retry = Retry.initWithAttempts(a: UInt32(3)) + ) { + self.config = config + self.entropySource = entropySource + self.nodeSigner = nodeSigner + self.signerProvider = signerProvider + self.feeEstimator = feeEstimator + self.chainMonitor = chainMonitor + self.txBroadcaster = txBroadcaster + self.logger = logger + + self.enableP2PGossip = enableP2PGossip + self.scorer = scorer + if scorer != nil && scoreParams == nil { + self.scoreParams = ProbabilisticScoringFeeParameters.initWithDefault() + } else { + self.scoreParams = scoreParams + } + self.payerRetries = payerRetries + } + + private class CMCRouter: Router { + override func findRoute( + payer: [UInt8], routeParams: Bindings.RouteParameters, firstHops: [Bindings.ChannelDetails]?, + inflightHtlcs: Bindings.InFlightHtlcs + ) -> Bindings.Result_RouteLightningErrorZ { + let errorString = + "Error: Router unavailable: ChannelManagerConstructor initialized without Scorer or NetworkGraph." + Bindings.print(errorString, severity: .ERROR) + return .initWithErr( + e: LightningError(errArg: errorString, actionArg: ErrorAction.initWithIgnoreAndLog(a: .Error))) + } + override func findRouteWithId( + payer: [UInt8], routeParams: Bindings.RouteParameters, firstHops: [Bindings.ChannelDetails]?, + inflightHtlcs: Bindings.InFlightHtlcs, paymentHash PaymentHash: [UInt8], paymentId PaymentId: [UInt8] + ) -> Bindings.Result_RouteLightningErrorZ { + let errorString = + "Error: Router unavailable: ChannelManagerConstructor initialized without Scorer or NetworkGraph." + Bindings.print(errorString, severity: .ERROR) + return .initWithErr( + e: LightningError(errArg: errorString, actionArg: ErrorAction.initWithIgnoreAndLog(a: .Error))) + } + } + + fileprivate func router(networkGraph: NetworkGraph?) -> Router { + if let netGraph = networkGraph, let scorer = self.scorer, let scoreParams = self.scoreParams { + return DefaultRouter( + networkGraph: netGraph, logger: self.logger, entropySource: self.entropySource, + scorer: scorer.asLockableScore(), scoreParams: scoreParams + ) + .asRouter() + } + return CMCRouter(messageRouter: MessageRouter()) + } } public class ChannelManagerConstructor: NativeTypeWrapper { - public let channelManager: ChannelManager + public let channelManager: ChannelManager - /** + /** * The latest block has the channel manager saw. If this is non-null it is a 32-byte block hash. * You should sync the blockchain starting with the block that builds on this block. */ - public let channel_manager_latest_block_hash: [UInt8]? - - private let constructionParameters: ChannelManagerConstructionParameters + public let channel_manager_latest_block_hash: [UInt8]? - fileprivate var customPersister: CustomChannelManagerPersister? - fileprivate var customEventHandler: EventHandler? - public private(set) var netGraph: NetworkGraph? - fileprivate var graphMessageHandler: GossipSync? + private let constructionParameters: ChannelManagerConstructionParameters + fileprivate var customPersister: CustomChannelManagerPersister? + fileprivate var customEventHandler: EventHandler? + public private(set) var netGraph: NetworkGraph? + fileprivate var graphMessageHandler: GossipSync? + // public private(set) var payer: InvoicePayer? - // public private(set) var payer: InvoicePayer? + public let peerManager: PeerManager + private var tcpPeerHandler: TCPPeerHandler? - public let peerManager: PeerManager - private var tcpPeerHandler: TCPPeerHandler? - - /** + /** * A list of ChannelMonitors and the last block they each saw. You should sync the blockchain on each individually * starting with the block that builds on the hash given. * After doing so (and syncing the blockchain on the channel manager as well), you should call chainSyncCompleted() * and then continue to normal application operation. */ - public private(set) var channel_monitors: [(ChannelMonitor, [UInt8])] - - - public init(channelManagerSerialized: [UInt8], channelMonitorsSerialized: [[UInt8]], networkGraph: NetworkGraphArgument, filter: Filter?, params: ChannelManagerConstructionParameters, logger: Logger) throws { - - self.constructionParameters = params - - var monitors: [ChannelMonitor] = [] - self.channel_monitors = [] - - var monitorFundingSet = Set() - - for currentSerializedChannelMonitor in channelMonitorsSerialized { - let channelMonitorResult = Bindings.readThirtyTwoBytesChannelMonitor(ser: currentSerializedChannelMonitor, argA: params.entropySource, argB: params.signerProvider) - - guard let (blockHash, channelMonitor) = channelMonitorResult.getValue() else { - throw InvalidSerializedDataError.invalidSerializedChannelMonitor - } - - let (channelFundingOutpoint, _) = channelMonitor.getFundingTxo() - - // TODO: figure out what to do with txid nullability - let fundingOutpointHash = "\(channelFundingOutpoint.getTxid()):\(channelFundingOutpoint.getIndex())" - if monitorFundingSet.contains(fundingOutpointHash) { - Bindings.print("Duplicate channel monitor funding txo: \(fundingOutpointHash)", severity: .ERROR) - throw InvalidSerializedDataError.duplicateSerializedChannelMonitor - } - monitorFundingSet.insert(fundingOutpointHash) - - monitors.append(channelMonitor) - self.channel_monitors.append((channelMonitor, blockHash)) - } - - print("Collected channel monitors, reading channel manager") - - switch networkGraph { - case .instance(let graph): - self.netGraph = graph - case .serialized(let serializedNetworkGraph): - let netGraphResult = NetworkGraph.read(ser: serializedNetworkGraph, arg: params.logger) - if !netGraphResult.isOk(){ - throw InvalidSerializedDataError.invalidSerializedNetworkGraph - } - self.netGraph = netGraphResult.getValue() - } - - // TODO: figure out better way to obtain a router - let router = params.router(networkGraph: self.netGraph) - let channelManagerReadArgs = ChannelManagerReadArgs(entropySource: params.entropySource, nodeSigner: params.nodeSigner, signerProvider: params.signerProvider, feeEstimator: params.feeEstimator, chainMonitor: params.chainMonitor.asWatch(), txBroadcaster: params.txBroadcaster, router: router, logger: params.logger, defaultConfig: params.config, channelMonitors: monitors) - - guard let (latestBlockHash, channelManager) = Bindings.readThirtyTwoBytesChannelManager(ser: channelManagerSerialized, arg: channelManagerReadArgs).getValue() else { - throw InvalidSerializedDataError.invalidSerializedChannelManager - } - - /* for clonedChannelMonitor in self.channel_monitors { + public private(set) var channel_monitors: [(ChannelMonitor, [UInt8])] + + + public init( + channelManagerSerialized: [UInt8], channelMonitorsSerialized: [[UInt8]], networkGraph: NetworkGraphArgument, + filter: Filter?, params: ChannelManagerConstructionParameters, logger: Logger + ) throws { + + self.constructionParameters = params + + var monitors: [ChannelMonitor] = [] + self.channel_monitors = [] + + var monitorFundingSet = Set() + + for currentSerializedChannelMonitor in channelMonitorsSerialized { + let channelMonitorResult = Bindings.readThirtyTwoBytesChannelMonitor( + ser: currentSerializedChannelMonitor, argA: params.entropySource, argB: params.signerProvider) + + guard let (blockHash, channelMonitor) = channelMonitorResult.getValue() else { + throw InvalidSerializedDataError.invalidSerializedChannelMonitor + } + + let (channelFundingOutpoint, _) = channelMonitor.getFundingTxo() + + // TODO: figure out what to do with txid nullability + let fundingOutpointHash = "\(channelFundingOutpoint.getTxid()):\(channelFundingOutpoint.getIndex())" + if monitorFundingSet.contains(fundingOutpointHash) { + Bindings.print("Duplicate channel monitor funding txo: \(fundingOutpointHash)", severity: .ERROR) + throw InvalidSerializedDataError.duplicateSerializedChannelMonitor + } + monitorFundingSet.insert(fundingOutpointHash) + + monitors.append(channelMonitor) + self.channel_monitors.append((channelMonitor, blockHash)) + } + + print("Collected channel monitors, reading channel manager") + + switch networkGraph { + case .instance(let graph): + self.netGraph = graph + case .serialized(let serializedNetworkGraph): + let netGraphResult = NetworkGraph.read(ser: serializedNetworkGraph, arg: params.logger) + if !netGraphResult.isOk() { + throw InvalidSerializedDataError.invalidSerializedNetworkGraph + } + self.netGraph = netGraphResult.getValue() + } + + // TODO: figure out better way to obtain a router + let router = params.router(networkGraph: self.netGraph) + let channelManagerReadArgs = ChannelManagerReadArgs( + entropySource: params.entropySource, nodeSigner: params.nodeSigner, signerProvider: params.signerProvider, + feeEstimator: params.feeEstimator, chainMonitor: params.chainMonitor.asWatch(), + txBroadcaster: params.txBroadcaster, router: router, logger: params.logger, defaultConfig: params.config, + channelMonitors: monitors) + + guard + let (latestBlockHash, channelManager) = + Bindings.readThirtyTwoBytesChannelManager(ser: channelManagerSerialized, arg: channelManagerReadArgs) + .getValue() + else { + throw InvalidSerializedDataError.invalidSerializedChannelManager + } + + /* for clonedChannelMonitor in self.channel_monitors { clonedChannelMonitor.0.cType!.is_owned = true } */ - self.channelManager = channelManager - self.channel_manager_latest_block_hash = latestBlockHash + self.channelManager = channelManager + self.channel_manager_latest_block_hash = latestBlockHash - let random_data = params.entropySource.getSecureRandomBytes(); + let random_data = params.entropySource.getSecureRandomBytes() - let channelMessageHandler = channelManager.asChannelMessageHandler() - let noCustomMessages = IgnoringMessageHandler().asCustomMessageHandler() - let noOnionMessages = IgnoringMessageHandler().asOnionMessageHandler() - let noRoutingMessages = IgnoringMessageHandler().asRoutingMessageHandler() + let channelMessageHandler = channelManager.asChannelMessageHandler() + let noCustomMessages = IgnoringMessageHandler().asCustomMessageHandler() + let noOnionMessages = IgnoringMessageHandler().asOnionMessageHandler() + let noRoutingMessages = IgnoringMessageHandler().asRoutingMessageHandler() - var messageHandler: MessageHandler! - if let netGraph = self.netGraph, params.enableP2PGossip { - let p2pGossipSync = P2PGossipSync(networkGraph: netGraph, utxoLookup: nil, logger: params.logger) - self.graphMessageHandler = GossipSync.initWithP2P(a: p2pGossipSync) - messageHandler = MessageHandler(chanHandlerArg: channelMessageHandler, routeHandlerArg: p2pGossipSync.asRoutingMessageHandler(), onionMessageHandlerArg: noOnionMessages, customMessageHandlerArg: noCustomMessages) - } else { - messageHandler = MessageHandler(chanHandlerArg: channelMessageHandler, routeHandlerArg: noRoutingMessages, onionMessageHandlerArg: noOnionMessages, customMessageHandlerArg: noCustomMessages) - } - let timestampSeconds = UInt32(NSDate().timeIntervalSince1970) - self.peerManager = PeerManager(messageHandler: messageHandler, currentTime: timestampSeconds, ephemeralRandomData: random_data, logger: params.logger, nodeSigner: params.nodeSigner) + var messageHandler: MessageHandler! + if let netGraph = self.netGraph, params.enableP2PGossip { + let p2pGossipSync = P2PGossipSync(networkGraph: netGraph, utxoLookup: nil, logger: params.logger) + self.graphMessageHandler = GossipSync.initWithP2P(a: p2pGossipSync) + messageHandler = MessageHandler( + chanHandlerArg: channelMessageHandler, routeHandlerArg: p2pGossipSync.asRoutingMessageHandler(), + onionMessageHandlerArg: noOnionMessages, customMessageHandlerArg: noCustomMessages) + } else { + messageHandler = MessageHandler( + chanHandlerArg: channelMessageHandler, routeHandlerArg: noRoutingMessages, + onionMessageHandlerArg: noOnionMessages, customMessageHandlerArg: noCustomMessages) + } + let timestampSeconds = UInt32(NSDate().timeIntervalSince1970) + self.peerManager = PeerManager( + messageHandler: messageHandler, currentTime: timestampSeconds, ephemeralRandomData: random_data, + logger: params.logger, nodeSigner: params.nodeSigner) - if let filter = filter { - for (currentMonitor, _) in self.channel_monitors { - currentMonitor.loadOutputsToWatch(filter: filter, logger: logger) - } - } - - super.init(conflictAvoidingVariableName: 0, instantiationContext: "ChannelManagerConstructor.swift::\(#function):\(#line)") - // try! self.peerManager.addAnchor(anchor: self) - // try! self.channelManager.addAnchor(anchor: self) + if let filter = filter { + for (currentMonitor, _) in self.channel_monitors { + currentMonitor.loadOutputsToWatch(filter: filter, logger: logger) + } + } - } + super + .init( + conflictAvoidingVariableName: 0, + instantiationContext: "ChannelManagerConstructor.swift::\(#function):\(#line)") + // try! self.peerManager.addAnchor(anchor: self) + // try! self.channelManager.addAnchor(anchor: self) + } - /** + /** * Constructs a channel manager from the given interface implementations */ - public init(network: Network, currentBlockchainTipHash: [UInt8], currentBlockchainTipHeight: UInt32, netGraph: NetworkGraph?, params: ChannelManagerConstructionParameters) { - - self.constructionParameters = params - - self.channel_monitors = [] - self.channel_manager_latest_block_hash = nil - let block = BestBlock(blockHashArg: currentBlockchainTipHash, heightArg: currentBlockchainTipHeight) - let chainParameters = ChainParameters(networkArg: network, bestBlockArg: block) - - self.netGraph = netGraph - let router = params.router(networkGraph: self.netGraph) - let timestampSeconds = UInt32(NSDate().timeIntervalSince1970) - self.channelManager = ChannelManager(feeEst: params.feeEstimator, chainMonitor: params.chainMonitor.asWatch(), txBroadcaster: params.txBroadcaster, router: router, logger: params.logger, entropySource: params.entropySource, nodeSigner: params.nodeSigner, signerProvider: params.signerProvider, config: params.config, params: chainParameters, currentTimestamp: timestampSeconds) - - let channelMessageHandler = channelManager.asChannelMessageHandler() - let noCustomMessages = IgnoringMessageHandler().asCustomMessageHandler() - let noOnionMessages = IgnoringMessageHandler().asOnionMessageHandler() - let noRoutingMessages = IgnoringMessageHandler().asRoutingMessageHandler() - - var messageHandler: MessageHandler! - if let netGraph = netGraph, params.enableP2PGossip { - let p2pGossipSync = P2PGossipSync(networkGraph: netGraph, utxoLookup: nil, logger: params.logger) - self.graphMessageHandler = GossipSync.initWithP2P(a: p2pGossipSync) - messageHandler = MessageHandler(chanHandlerArg: channelMessageHandler, routeHandlerArg: p2pGossipSync.asRoutingMessageHandler(), onionMessageHandlerArg: noOnionMessages, customMessageHandlerArg: noCustomMessages) - } else { - messageHandler = MessageHandler(chanHandlerArg: channelMessageHandler, routeHandlerArg: noRoutingMessages, onionMessageHandlerArg: noOnionMessages, customMessageHandlerArg: noCustomMessages) - } - - let random_data = params.entropySource.getSecureRandomBytes(); - - self.peerManager = PeerManager(messageHandler: messageHandler, currentTime: timestampSeconds, ephemeralRandomData: random_data, logger: params.logger, nodeSigner: params.nodeSigner) - - super.init(conflictAvoidingVariableName: 0, instantiationContext: "ChannelManagerConstructor.swift::\(#function):\(#line)") - // try! self.peerManager.addAnchor(anchor: self) - // try! self.channelManager.addAnchor(anchor: self) - } - - - var persisterWorkItem: DispatchWorkItem? - var backgroundProcessor: BackgroundProcessor? - var shutdown = false - - /** + public init( + network: Network, currentBlockchainTipHash: [UInt8], currentBlockchainTipHeight: UInt32, + netGraph: NetworkGraph?, params: ChannelManagerConstructionParameters + ) { + + self.constructionParameters = params + + self.channel_monitors = [] + self.channel_manager_latest_block_hash = nil + let block = BestBlock(blockHashArg: currentBlockchainTipHash, heightArg: currentBlockchainTipHeight) + let chainParameters = ChainParameters(networkArg: network, bestBlockArg: block) + + self.netGraph = netGraph + let router = params.router(networkGraph: self.netGraph) + let timestampSeconds = UInt32(NSDate().timeIntervalSince1970) + self.channelManager = ChannelManager( + feeEst: params.feeEstimator, chainMonitor: params.chainMonitor.asWatch(), + txBroadcaster: params.txBroadcaster, router: router, logger: params.logger, + entropySource: params.entropySource, nodeSigner: params.nodeSigner, signerProvider: params.signerProvider, + config: params.config, params: chainParameters, currentTimestamp: timestampSeconds) + + let channelMessageHandler = channelManager.asChannelMessageHandler() + let noCustomMessages = IgnoringMessageHandler().asCustomMessageHandler() + let noOnionMessages = IgnoringMessageHandler().asOnionMessageHandler() + let noRoutingMessages = IgnoringMessageHandler().asRoutingMessageHandler() + + var messageHandler: MessageHandler! + if let netGraph = netGraph, params.enableP2PGossip { + let p2pGossipSync = P2PGossipSync(networkGraph: netGraph, utxoLookup: nil, logger: params.logger) + self.graphMessageHandler = GossipSync.initWithP2P(a: p2pGossipSync) + messageHandler = MessageHandler( + chanHandlerArg: channelMessageHandler, routeHandlerArg: p2pGossipSync.asRoutingMessageHandler(), + onionMessageHandlerArg: noOnionMessages, customMessageHandlerArg: noCustomMessages) + } else { + messageHandler = MessageHandler( + chanHandlerArg: channelMessageHandler, routeHandlerArg: noRoutingMessages, + onionMessageHandlerArg: noOnionMessages, customMessageHandlerArg: noCustomMessages) + } + + let random_data = params.entropySource.getSecureRandomBytes() + + self.peerManager = PeerManager( + messageHandler: messageHandler, currentTime: timestampSeconds, ephemeralRandomData: random_data, + logger: params.logger, nodeSigner: params.nodeSigner) + + super + .init( + conflictAvoidingVariableName: 0, + instantiationContext: "ChannelManagerConstructor.swift::\(#function):\(#line)") + // try! self.peerManager.addAnchor(anchor: self) + // try! self.channelManager.addAnchor(anchor: self) + } + + + var persisterWorkItem: DispatchWorkItem? + var backgroundProcessor: BackgroundProcessor? + var shutdown = false + + /** * Utility which adds all of the deserialized ChannelMonitors to the chain watch so that further updates from the * ChannelManager are processed as normal. * * This also spawns a background thread which will call the appropriate methods on the provided * ChannelManagerPersister as required. */ - public func chainSyncCompleted(persister: ExtendedChannelManagerPersister) { - - if self.backgroundProcessor != nil { - return - } - - for (currentChannelMonitor, _) in self.channel_monitors { - let chainMonitorWatch = self.constructionParameters.chainMonitor.asWatch() - // let monitorClone = currentChannelMonitor.dynamicallyDangledClone() - // monitorClone.cType?.is_owned = false - let (outPoint, _) = currentChannelMonitor.getFundingTxo() - print("watching channel") - let monitorWatchResult = chainMonitorWatch.watchChannel(fundingTxo: outPoint, monitor: currentChannelMonitor) - if !monitorWatchResult.isOk() || monitorWatchResult.getValue() != .Completed { - Bindings.print("Some issue occurred with a chainMonitorWatch.watch_channel call: \(monitorWatchResult)", severity: .WARNING) - } - // monitorClone.cType?.is_owned = true - } - - self.customPersister = CustomChannelManagerPersister(handler: persister) - self.customEventHandler = CustomEventHandler(handler: persister) - - let ignoringMessageHandler = IgnoringMessageHandler() - let onionMessenger = OnionMessenger.initWith(entropySource: self.constructionParameters.entropySource, nodeSigner: self.constructionParameters.nodeSigner, logger: self.constructionParameters.logger, nodeIdLookup: NodeIdLookUp(), messageRouter: MessageRouter(), offersHandler: self.channelManager.asOffersMessageHandler(), asyncPaymentsHandler: ignoringMessageHandler.asAsyncPaymentsMessageHandler(), customHandler: ignoringMessageHandler.asCustomOnionMessageHandler()) - - // if there is a graph msg handler, set its is_owned to false - // self.graph_msg_handler?.cOpaqueStruct?.is_owned = false - self.backgroundProcessor = BackgroundProcessor.start(persister: self.customPersister!, eventHandler: self.customEventHandler!, chainMonitor: self.constructionParameters.chainMonitor, channelManager: self.channelManager, onionMessenger: onionMessenger, gossipSync: self.graphMessageHandler ?? GossipSync.none(), peerManager: self.peerManager, logger: self.constructionParameters.logger, scorer: self.constructionParameters.scorer?.asWriteableScore()) - - // restore it back to true - // self.graph_msg_handler?.cOpaqueStruct?.is_owned = true - - try? self.backgroundProcessor!.addAnchor(anchor: self.peerManager) - try? self.backgroundProcessor!.addAnchor(anchor: persister) - try? self.backgroundProcessor!.addAnchor(anchor: self.customEventHandler!) - try? self.backgroundProcessor!.addAnchor(anchor: self.customPersister!) - - } - - public func interrupt() { - if self.shutdown { - Bindings.print("ChannelManagerConstructor previously interrupted, nothing to do.") - return - } - - self.shutdown = true - if let tcpHandler = self.tcpPeerHandler { - print("stopping TCP peer handler") - tcpHandler.interrupt() - print("stopped TCP peer handler") - } - print("stopping background processor") - self.backgroundProcessor?.stop() - print("stopped background processor") - /* + public func chainSyncCompleted(persister: ExtendedChannelManagerPersister) { + + if self.backgroundProcessor != nil { + return + } + + for (currentChannelMonitor, _) in self.channel_monitors { + let chainMonitorWatch = self.constructionParameters.chainMonitor.asWatch() + // let monitorClone = currentChannelMonitor.dynamicallyDangledClone() + // monitorClone.cType?.is_owned = false + let (outPoint, _) = currentChannelMonitor.getFundingTxo() + print("watching channel") + let monitorWatchResult = chainMonitorWatch.watchChannel( + fundingTxo: outPoint, monitor: currentChannelMonitor) + if !monitorWatchResult.isOk() || monitorWatchResult.getValue() != .Completed { + Bindings.print( + "Some issue occurred with a chainMonitorWatch.watch_channel call: \(monitorWatchResult)", + severity: .WARNING) + } + // monitorClone.cType?.is_owned = true + } + + self.customPersister = CustomChannelManagerPersister(handler: persister) + self.customEventHandler = CustomEventHandler(handler: persister) + + let ignoringMessageHandler = IgnoringMessageHandler() + let onionMessenger = OnionMessenger.initWith( + entropySource: self.constructionParameters.entropySource, + nodeSigner: self.constructionParameters.nodeSigner, logger: self.constructionParameters.logger, + nodeIdLookup: NodeIdLookUp(), messageRouter: MessageRouter(), + offersHandler: self.channelManager.asOffersMessageHandler(), + asyncPaymentsHandler: ignoringMessageHandler.asAsyncPaymentsMessageHandler(), + customHandler: ignoringMessageHandler.asCustomOnionMessageHandler()) + + // if there is a graph msg handler, set its is_owned to false + // self.graph_msg_handler?.cOpaqueStruct?.is_owned = false + self.backgroundProcessor = BackgroundProcessor.start( + persister: self.customPersister!, eventHandler: self.customEventHandler!, + chainMonitor: self.constructionParameters.chainMonitor, channelManager: self.channelManager, + onionMessenger: onionMessenger, gossipSync: self.graphMessageHandler ?? GossipSync.none(), + peerManager: self.peerManager, logger: self.constructionParameters.logger, + scorer: self.constructionParameters.scorer?.asWriteableScore()) + + // restore it back to true + // self.graph_msg_handler?.cOpaqueStruct?.is_owned = true + + try? self.backgroundProcessor!.addAnchor(anchor: self.peerManager) + try? self.backgroundProcessor!.addAnchor(anchor: persister) + try? self.backgroundProcessor!.addAnchor(anchor: self.customEventHandler!) + try? self.backgroundProcessor!.addAnchor(anchor: self.customPersister!) + + } + + public func interrupt() { + if self.shutdown { + Bindings.print("ChannelManagerConstructor previously interrupted, nothing to do.") + return + } + + self.shutdown = true + if let tcpHandler = self.tcpPeerHandler { + print("stopping TCP peer handler") + tcpHandler.interrupt() + print("stopped TCP peer handler") + } + print("stopping background processor") + self.backgroundProcessor?.stop() + print("stopped background processor") + /* if let processor = self.backgroundProcessor { for currentAnchor in processor.anchors { if currentAnchor is NativeTraitWrapper { @@ -333,80 +402,80 @@ public class ChannelManagerConstructor: NativeTypeWrapper { print("removed background processor anchors") } */ - self.backgroundProcessor = nil - print("unset background processor") - } + self.backgroundProcessor = nil + print("unset background processor") + } - public func getTCPPeerHandler() -> TCPPeerHandler { - if let handler = self.tcpPeerHandler { - return handler - } - let handler = TCPPeerHandler(peerManager: self.peerManager) - self.tcpPeerHandler = handler - return handler - } + public func getTCPPeerHandler() -> TCPPeerHandler { + if let handler = self.tcpPeerHandler { + return handler + } + let handler = TCPPeerHandler(peerManager: self.peerManager) + self.tcpPeerHandler = handler + return handler + } - deinit { - Bindings.print("Freeing and interrupting ChannelManagerConstructor") - self.interrupt() - } + deinit { + Bindings.print("Freeing and interrupting ChannelManagerConstructor") + self.interrupt() + } } -fileprivate class CustomChannelManagerPersister: Persister { +private class CustomChannelManagerPersister: Persister { - let handler: ExtendedChannelManagerPersister + let handler: ExtendedChannelManagerPersister - init(handler: ExtendedChannelManagerPersister) { - self.handler = handler - super.init() - } + init(handler: ExtendedChannelManagerPersister) { + self.handler = handler + super.init() + } - override func persistManager(channelManager: Bindings.ChannelManager) -> Bindings.Result_NoneIOErrorZ { - return self.handler.persistManager(channelManager: channelManager) - } + override func persistManager(channelManager: Bindings.ChannelManager) -> Bindings.Result_NoneIOErrorZ { + return self.handler.persistManager(channelManager: channelManager) + } - override func persistGraph(networkGraph: Bindings.NetworkGraph) -> Bindings.Result_NoneIOErrorZ { - return self.handler.persistGraph(networkGraph: networkGraph) - } + override func persistGraph(networkGraph: Bindings.NetworkGraph) -> Bindings.Result_NoneIOErrorZ { + return self.handler.persistGraph(networkGraph: networkGraph) + } - override func persistScorer(scorer: Bindings.WriteableScore) -> Bindings.Result_NoneIOErrorZ { - return self.handler.persistScorer(scorer: scorer) - } + override func persistScorer(scorer: Bindings.WriteableScore) -> Bindings.Result_NoneIOErrorZ { + return self.handler.persistScorer(scorer: scorer) + } } -fileprivate class CustomEventHandler: EventHandler { +private class CustomEventHandler: EventHandler { - let handler: ExtendedChannelManagerPersister + let handler: ExtendedChannelManagerPersister - init(handler: ExtendedChannelManagerPersister) { - self.handler = handler - super.init() - } + init(handler: ExtendedChannelManagerPersister) { + self.handler = handler + super.init() + } - override func handleEvent(event: Bindings.Event) -> Bindings.Result_NoneReplayEventZ { - // self.handler.handle_event(event: event.clone()) - self.handler.handleEvent(event: event) - } + override func handleEvent(event: Bindings.Event) -> Bindings.Result_NoneReplayEventZ { + // self.handler.handle_event(event: event.clone()) + self.handler.handleEvent(event: event) + } } public protocol ExtendedChannelManagerPersister: Persister { - func handleEvent(event: Event) -> Result_NoneReplayEventZ; + func handleEvent(event: Event) -> Result_NoneReplayEventZ } public class TCPPeerHandler { - private let peerManager: PeerManager - private let socketHandler: UnsafeMutableRawPointer? - private var isBound = false - private var isInterrupted = false + private let peerManager: PeerManager + private let socketHandler: UnsafeMutableRawPointer? + private var isBound = false + private var isInterrupted = false - fileprivate init(peerManager: PeerManager) { - self.peerManager = peerManager + fileprivate init(peerManager: PeerManager) { + self.peerManager = peerManager - /* + /* let socketHandler = withUnsafePointer(to: self.peerManager.cOpaqueStruct!) { (pointer: UnsafePointer) -> UnsafeMutableRawPointer? in let socketHandler = init_socket_handling(pointer) return socketHandler @@ -415,96 +484,104 @@ public class TCPPeerHandler { */ - // self.socketHandler = UnsafeMutableRawPointer(bitPattern: 0) - - - - let peerManagerPointer: UnsafeRawPointer = UnsafeRawPointer(&self.peerManager.cType!) - Bindings.print("TCPPeerHandler peer manager pointer address: \(peerManagerPointer)") - let memoryReboundPeerManagerPointer: UnsafePointer = peerManagerPointer.assumingMemoryBound(to: LDKPeerManager.self) - Bindings.print("TCPPeerHandler peer manager rebound memory address: \(memoryReboundPeerManagerPointer)") - self.socketHandler = init_socket_handling(memoryReboundPeerManagerPointer) - print("TCPPeerHandler self address: \(Unmanaged.passUnretained(self).toOpaque())") - } - - public func bind(address: String, port: UInt16) -> Bool { - if(self.isBound){ - // already bound - return false - } - self.isBound = true - var addressObject = sockaddr_in() - addressObject.sin_family = sa_family_t(AF_INET) - addressObject.sin_port = port.bigEndian - - addressObject.sin_addr.s_addr = inet_addr(address) - - let sin_length = UInt8(MemoryLayout.size(ofValue: addressObject)) - - let result = withUnsafePointer(to: &addressObject, { addressPointer in - - addressPointer.withMemoryRebound(to: sockaddr.self, capacity: 1) { addressSecondPointer -> Int32 in - let addressMutablePointer = UnsafeMutablePointer(mutating: addressSecondPointer) - let result = socket_bind(self.socketHandler, addressMutablePointer, socklen_t(sin_length)) - return result - } - }) - if result != 0 { - // something failed - print("TCP socket_bind error code: \(result)") - let errorDetails = strerror(result) - if let errorDetails = errorDetails { - let errorString = String(cString: errorDetails) - print("TCP socket_bind error: \(errorString)") - } - self.isBound = false - return false - } - return true - - } - - public func connect(address: String, port: UInt16, theirNodeId: [UInt8]) -> Bool { - - var addressObject = sockaddr_in() - addressObject.sin_family = sa_family_t(AF_INET) - addressObject.sin_port = port.bigEndian - - addressObject.sin_addr.s_addr = inet_addr(address) - - let sin_length = UInt8(MemoryLayout.size(ofValue: addressObject)) - let publicKey = PublicKey(value: theirNodeId, instantiationContext: "ChannelManagerConstructor.swift::\(#function):\(#line)").cType! - - let result = withUnsafePointer(to: &addressObject, { addressPointer in - - addressPointer.withMemoryRebound(to: sockaddr.self, capacity: 1) { addressSecondPointer -> Int32 in - let addressMutablePointer = UnsafeMutablePointer(mutating: addressSecondPointer) - let result = socket_connect(self.socketHandler, publicKey, addressMutablePointer, Int(socklen_t(sin_length))) - return result - } - }) - - if result != 0 { - // something failed - return false - } - return true - - } - - fileprivate func interrupt() { - if self.isInterrupted { - Bindings.print("TCPPeerHandler previously interrupted, nothing to do.") - return - } - self.isInterrupted = true - interrupt_socket_handling(self.socketHandler) - Bindings.print("TCPPeerHandler successfully interrupted.") - } - - deinit { - Bindings.print("Freeing and interrupting TCPPeerHandler.") - self.interrupt() - } + // self.socketHandler = UnsafeMutableRawPointer(bitPattern: 0) + + + let peerManagerPointer: UnsafeRawPointer = UnsafeRawPointer(&self.peerManager.cType!) + Bindings.print("TCPPeerHandler peer manager pointer address: \(peerManagerPointer)") + let memoryReboundPeerManagerPointer: UnsafePointer = peerManagerPointer.assumingMemoryBound( + to: LDKPeerManager.self) + Bindings.print("TCPPeerHandler peer manager rebound memory address: \(memoryReboundPeerManagerPointer)") + self.socketHandler = init_socket_handling(memoryReboundPeerManagerPointer) + print("TCPPeerHandler self address: \(Unmanaged.passUnretained(self).toOpaque())") + } + + public func bind(address: String, port: UInt16) -> Bool { + if self.isBound { + // already bound + return false + } + self.isBound = true + var addressObject = sockaddr_in() + addressObject.sin_family = sa_family_t(AF_INET) + addressObject.sin_port = port.bigEndian + + addressObject.sin_addr.s_addr = inet_addr(address) + + let sin_length = UInt8(MemoryLayout.size(ofValue: addressObject)) + + let result = withUnsafePointer( + to: &addressObject, + { addressPointer in + + addressPointer.withMemoryRebound(to: sockaddr.self, capacity: 1) { addressSecondPointer -> Int32 in + let addressMutablePointer = UnsafeMutablePointer(mutating: addressSecondPointer) + let result = socket_bind(self.socketHandler, addressMutablePointer, socklen_t(sin_length)) + return result + } + }) + if result != 0 { + // something failed + print("TCP socket_bind error code: \(result)") + let errorDetails = strerror(result) + if let errorDetails = errorDetails { + let errorString = String(cString: errorDetails) + print("TCP socket_bind error: \(errorString)") + } + self.isBound = false + return false + } + return true + + } + + public func connect(address: String, port: UInt16, theirNodeId: [UInt8]) -> Bool { + + var addressObject = sockaddr_in() + addressObject.sin_family = sa_family_t(AF_INET) + addressObject.sin_port = port.bigEndian + + addressObject.sin_addr.s_addr = inet_addr(address) + + let sin_length = UInt8(MemoryLayout.size(ofValue: addressObject)) + let publicKey = PublicKey( + value: theirNodeId, instantiationContext: "ChannelManagerConstructor.swift::\(#function):\(#line)" + ) + .cType! + + let result = withUnsafePointer( + to: &addressObject, + { addressPointer in + + addressPointer.withMemoryRebound(to: sockaddr.self, capacity: 1) { addressSecondPointer -> Int32 in + let addressMutablePointer = UnsafeMutablePointer(mutating: addressSecondPointer) + let result = socket_connect( + self.socketHandler, publicKey, addressMutablePointer, Int(socklen_t(sin_length))) + return result + } + }) + + if result != 0 { + // something failed + return false + } + return true + + } + + fileprivate func interrupt() { + if self.isInterrupted { + Bindings.print("TCPPeerHandler previously interrupted, nothing to do.") + return + } + self.isInterrupted = true + interrupt_socket_handling(self.socketHandler) + Bindings.print("TCPPeerHandler successfully interrupted.") + } + + deinit { + Bindings.print("Freeing and interrupting TCPPeerHandler.") + self.interrupt() + } } diff --git a/ci/LDKSwift/Tests/LDKSwiftTests/HumanObjectPeerTestInstance.swift b/ci/LDKSwift/Tests/LDKSwiftTests/HumanObjectPeerTestInstance.swift index f9c00c89..7da17655 100644 --- a/ci/LDKSwift/Tests/LDKSwiftTests/HumanObjectPeerTestInstance.swift +++ b/ci/LDKSwift/Tests/LDKSwiftTests/HumanObjectPeerTestInstance.swift @@ -5,979 +5,1040 @@ // Created by Arik Sosman on 7/22/21. // +import XCTest + #if SWIFT_PACKAGE -import LDKSwift -import LDKHeaders + import LDKSwift + import LDKHeaders #endif -import XCTest public class HumanObjectPeerTestInstance { - private let configuration: Configuration - - public class Configuration { -// - public var useFilter: Bool = false - public var useRouter: Bool = false - public var useWrappedSignerProvider: Bool = false - public var shouldRecipientRejectPayment: Bool = false - public var ephemeralNetworkGraphForScorer: Bool = false - public var reserializedProbabilisticScorer: Bool = false - - // public var nice_close: Bool = false; - // public var use_km_wrapper: Bool = false; - // public var use_manual_watch: Bool = false; - // public var reload_peers: Bool = false; - // public var break_cross_peer_refs: Bool = false; - // public var use_nio_peer_handler: Bool = false; - - private class func listCustomizeableProperties() -> [String] { - return ["useFilter", "useRouter", "useWrappedSignerProvider", "shouldRecipientRejectPayment", "ephemeralNetworkGraphForScorer", "reserializedProbabilisticScorer"] - } - - public class func combinationCount() -> UInt { - return 1 << self.listCustomizeableProperties().count - } - - } - - - - public init(configuration: Configuration) { - self.configuration = configuration - } - - fileprivate class Peer { - - private(set) var txBroadcaster: BroadcasterInterface! - weak var master: HumanObjectPeerTestInstance! - let logger: Logger - let feeEstimator: FeeEstimator - let seed: UInt8 - var filterAdditions: Set - let monitors: [String: ChannelMonitor] - private(set) var filter: Filter? - private(set) var explicitKeysManager: KeysManager! - private(set) var wrappedKeysManager: WrappedSignerProviderTests.MyKeysManager? - private(set) var router: GossipSync! - private(set) var channelManager: ChannelManager! - private(set) var peerManager: PeerManager! - - - private(set) var constructor: ChannelManagerConstructor? - private(set) var tcpSocketHandler: TCPPeerHandler? - private(set) var tcpPort: UInt16? - - fileprivate enum SocketBindError: Error { - case failedToBindSocket - } - - fileprivate enum ChannelManagerEventError: Error { - case notUsingChannelManagerConstructor - } - - fileprivate actor PendingEventTracker { - - private(set) var pendingManagerEvents: [Event] = [] - private(set) var continuations: [CheckedContinuation] = [] - - private func triggerContinuations(){ - let continuations = self.continuations - self.continuations.removeAll() - for currentContinuation in continuations { - currentContinuation.resume() - } - } - - func addEvent(event: Event) { - self.pendingManagerEvents.append(event) - self.triggerContinuations() - } - - func addEvents(events: [Event]) { - self.pendingManagerEvents.append(contentsOf: events) - self.triggerContinuations() - } - - func getCount() -> Int { - return self.pendingManagerEvents.count - } - - func getEvents() -> [Event] { - return self.pendingManagerEvents - } - - func getAndClearEvents() -> [Event]{ - let events = self.pendingManagerEvents - self.pendingManagerEvents.removeAll() - return events - } - - func awaitAddition() async { - await withCheckedContinuation({ continuation in - self.continuations.append(continuation) - }) - } - } - private(set) var pendingEventTracker = PendingEventTracker() - - fileprivate actor PendingBroadcastTracker { - private(set) var pendingBroadcastSet: Set<[UInt8]> = Set() - private(set) var continuations: [CheckedContinuation] = [] - - func addTransaction(tx: [UInt8]) { - self.pendingBroadcastSet.insert(tx) - self.triggerContinuations() - } - - func getCount() -> Int { - return self.pendingBroadcastSet.count - } - - func getTransactions() -> Set<[UInt8]> { - return self.pendingBroadcastSet - } - - func getAndClearTransactions() -> Set<[UInt8]> { - let txs = self.pendingBroadcastSet - self.pendingBroadcastSet.removeAll() - return txs - } - - private func triggerContinuations(){ - let continuations = self.continuations - self.continuations.removeAll() - for currentContinuation in continuations { - currentContinuation.resume() - } - } - - func awaitAddition() async { - await withCheckedContinuation({ continuation in - self.continuations.append(continuation) - }) - } - } - private(set) var pendingBroadcastTracker = PendingBroadcastTracker() - - private(set) var nodeId: [UInt8]? - - var chainWatch: Watch? - var chainMonitor: ChainMonitor? - - fileprivate class TestBroadcaster: BroadcasterInterface { - weak var master: Peer! - - fileprivate init(master: Peer) { - self.master = master - super.init() - } - - override func broadcastTransactions(txs: [[UInt8]]) { - for tx in txs { - Task { - await self.master.pendingBroadcastTracker.addTransaction(tx: tx) - } - } - } - } - - fileprivate class TestFilter: Filter { - - weak var master: Peer! - - fileprivate init(master: Peer) { - self.master = master - super.init() - } - - override func registerOutput(output: WatchedOutput) { - let outpoint = output.getOutpoint() - self.master.filterAdditions.insert("\(outpoint.getTxid()):\(outpoint.getIndex())") - } - - override func registerTx(txid: [UInt8]?, scriptPubkey: [UInt8]) { - self.master.filterAdditions.insert("\(txid)") - } - } - - fileprivate class TestKeysInterface: SignerProvider { - - weak var master: Peer! - let interface: SignerProvider - - fileprivate init(master: Peer, underlyingInterface: SignerProvider) { - self.master = master - self.interface = underlyingInterface - super.init() - } - - override func deriveChannelSigner(channelValueSatoshis: UInt64, channelKeysId: [UInt8]) -> Bindings.EcdsaChannelSigner { - let ck = self.interface.deriveChannelSigner(channelValueSatoshis: channelValueSatoshis, channelKeysId: channelKeysId) - return ck - } - } - - fileprivate class TestChannelManagerPersister: Persister, ExtendedChannelManagerPersister { - - weak var master: Peer! - - fileprivate init(master: Peer) { - self.master = master - super.init() - } - - func handleEvent(event: Event) -> Result_NoneReplayEventZ { - print("peer \(self.master.seed) received event: \(event.getValueType())") - Task { - // clone to avoid deallocation-related issues - await master.pendingEventTracker.addEvent(event: event) - } - return .initWithOk() - } - - override func persistScorer(scorer: Bindings.WriteableScore) -> Bindings.Result_NoneIOErrorZ { - .initWithOk() - } - - override func persistGraph(networkGraph: Bindings.NetworkGraph) -> Bindings.Result_NoneIOErrorZ { - let writtenGraph = networkGraph.write(); - return .initWithOk() - } - - override func persistManager(channelManager: Bindings.ChannelManager) -> Bindings.Result_NoneIOErrorZ { - .initWithOk() - } - - } - - fileprivate class TestPersister: Persist { - - override func persistNewChannel(channelFundingOutpoint: Bindings.OutPoint, monitor: Bindings.ChannelMonitor) -> Bindings.ChannelMonitorUpdateStatus { - .Completed - } - - override func updatePersistedChannel(channelFundingOutpoint: Bindings.OutPoint, monitorUpdate: Bindings.ChannelMonitorUpdate, monitor: Bindings.ChannelMonitor) -> Bindings.ChannelMonitorUpdateStatus { - .Completed - } - } - - fileprivate init(master: HumanObjectPeerTestInstance, _dummy: Any, seed: UInt8) { - - self.master = master - self.logger = TestLogger() - self.feeEstimator = TestFeeEstimator() - self.monitors = [String: ChannelMonitor]() - self.seed = seed - let persister = TestPersister() - self.filterAdditions = Set() - - self.txBroadcaster = TestBroadcaster(master: self) - - if master.configuration.useFilter { - self.filter = TestFilter(master: self) - } - - // never use manual watch - do { - self.chainMonitor = ChainMonitor(chainSource: self.filter, broadcaster: self.txBroadcaster, logger: self.logger, feeest: self.feeEstimator, persister: persister) - self.chainWatch = self.chainMonitor!.asWatch() - } - - var keySeed = [UInt8](repeating: 0, count: 32) - for i in 0..<32 { - keySeed[i] = UInt8(i) ^ seed - } - - let timestamp_seconds = UInt64(NSDate().timeIntervalSince1970) - let timestamp_nanos = UInt32(truncating: NSNumber(value: timestamp_seconds * 1000 * 1000)) - let keysManager = KeysManager(seed: keySeed, startingTimeSecs: timestamp_seconds, startingTimeNanos: timestamp_nanos) - self.explicitKeysManager = keysManager - - if master.configuration.useWrappedSignerProvider { - self.wrappedKeysManager = WrappedSignerProviderTests.MyKeysManager(seed: keySeed, startingTimeSecs: timestamp_seconds, startingTimeNanos: timestamp_nanos) - } - - if master.configuration.useRouter { - let networkGraph = NetworkGraph(network: .Regtest, logger: self.logger) - self.router = GossipSync.initWithP2P(a: P2PGossipSync(networkGraph: networkGraph, utxoLookup: nil, logger: self.logger)) - } else { - self.router = GossipSync.none() - } - } - - fileprivate convenience init(master: HumanObjectPeerTestInstance, seed: UInt8) { - self.init(master: master, _dummy: (), seed: seed) - - do { - // channel manager constructor is mandatory - - let graph = NetworkGraph(network: .Regtest, logger: self.logger) - var scorerGraph = graph - if master.configuration.ephemeralNetworkGraphForScorer { - scorerGraph = NetworkGraph(network: .Regtest, logger: self.logger) - } - - let decayParams = ProbabilisticScoringDecayParameters.initWithDefault() - var probabalisticScorer = ProbabilisticScorer(decayParams: decayParams, networkGraph: scorerGraph, logger: self.logger) - if master.configuration.reserializedProbabilisticScorer { - let serializedScorer = probabalisticScorer.write() - let probabalisticScorerResult = ProbabilisticScorer.read(ser: serializedScorer, argA: decayParams, argB: scorerGraph, argC: self.logger) - probabalisticScorer = probabalisticScorerResult.getValue()! - } - let score = probabalisticScorer.asScore() - let multiThreadedScorer = MultiThreadedLockableScore(score: score) - - let signerProvider = master.configuration.useWrappedSignerProvider ? self.wrappedKeysManager!.signerProvider : self.explicitKeysManager.asSignerProvider() - let constructionParameters = ChannelManagerConstructionParameters( - config: UserConfig.initWithDefault(), - entropySource: self.explicitKeysManager.asEntropySource(), - nodeSigner: self.explicitKeysManager.asNodeSigner(), - signerProvider: signerProvider, - feeEstimator: self.feeEstimator, - chainMonitor: self.chainMonitor!, - txBroadcaster: self.txBroadcaster, - logger: self.logger, - scorer: multiThreadedScorer - ) - - self.constructor = ChannelManagerConstructor(network: .Bitcoin, currentBlockchainTipHash: [UInt8](repeating: 0, count: 32), currentBlockchainTipHeight: 0, netGraph: graph, params: constructionParameters) - - self.constructor?.chainSyncCompleted(persister: TestChannelManagerPersister(master: self)) - self.channelManager = self.constructor!.channelManager - self.peerManager = self.constructor!.peerManager - } - self.nodeId = self.channelManager.getOurNodeId() - try! self.bindSocketHandler() - } - - fileprivate convenience init(original: Peer) { - self.init(master: original.master, _dummy: (), seed: original.seed) - - do { - // channel manager constructor is mandatory - let graph = NetworkGraph(network: .Bitcoin, logger: self.logger) - - let constructionParameters = ChannelManagerConstructionParameters( - config: UserConfig.initWithDefault(), - entropySource: self.explicitKeysManager.asEntropySource(), - nodeSigner: self.explicitKeysManager.asNodeSigner(), - signerProvider: self.explicitKeysManager.asSignerProvider(), - feeEstimator: self.feeEstimator, - chainMonitor: self.chainMonitor!, - txBroadcaster: self.txBroadcaster, - logger: self.logger - ) - self.constructor = ChannelManagerConstructor(network: .Bitcoin, currentBlockchainTipHash: [UInt8](repeating: 0, count: 32), currentBlockchainTipHeight: 0, netGraph: graph, params: constructionParameters) - self.constructor?.chainSyncCompleted(persister: TestChannelManagerPersister(master: self)) - self.channelManager = self.constructor!.channelManager - Task { - let events = await original.pendingEventTracker.getEvents() - await self.pendingEventTracker.addEvents(events: events) - } - } - self.nodeId = self.channelManager.getOurNodeId() - try! self.bindSocketHandler() - } - - private func bindSocketHandler() throws { - self.tcpSocketHandler = self.constructor!.getTCPPeerHandler() - print("Attempting to bind socket…") - for i in 10000...65535 { - let port = UInt16(i) - let bound = self.tcpSocketHandler!.bind(address: "127.0.0.1", port: port) - if bound { - print("Bound socket to port \(port) on attempt \(i-9999)") - self.tcpPort = port - return - } - } - print("Failed to bind on any port") - throw SocketBindError.failedToBindSocket - } - - fileprivate func payInvoice() { - - } - - fileprivate func getManualWatch() { - - } - - fileprivate func getManagerEvents(expectedCount: UInt) async throws -> [Event] { - print("Awaiting \(expectedCount) manager events…") - while true { - if await self.pendingEventTracker.getCount() >= expectedCount { - print("Found enough manager events for expected count of \(expectedCount)") - let events = await self.pendingEventTracker.getAndClearEvents() - print("Found manager event count: \(events.count)") - return events - } - // sleep for 0.1 seconds - // try await Task.sleep(nanoseconds: 0_100_000_000) - await self.pendingEventTracker.awaitAddition() - } - } - - fileprivate func getPendingBroadcasts(expectedCount: UInt) async -> Set<[UInt8]> { - while true { - if await self.pendingBroadcastTracker.getCount() >= expectedCount { - return await self.pendingBroadcastTracker.getAndClearTransactions() - } - await self.pendingBroadcastTracker.awaitAddition() - } - } - - fileprivate func connectBlock(block: BTCBlock, height: UInt32, expectedMonitorUpdateLength: Int) { - let blockSerialization = block.serialize() - var transactionTuples: [(UInt, [UInt8])] = [] - - if block.transactions.count > 0 { - XCTAssertEqual(block.transactions.count, 1) - transactionTuples.append((0, block.transactions[0].serialize())) - } - - if let chainMonitor = self.chainMonitor { - self.channelManager.asListen().blockConnected(block: blockSerialization, height: height) - chainMonitor.asListen().blockConnected(block: blockSerialization, height: height) - } else { - let blockHeader = block.calculateHeader() - self.channelManager.asConfirm().transactionsConfirmed(header: blockHeader, txdata: transactionTuples, height: height) - self.channelManager.asConfirm().bestBlockUpdated(header: blockHeader, height: height) - - for (_, monitor) in self.monitors { - let connectionResult = monitor.blockConnected(header: blockHeader, txdata: transactionTuples, height: height, broadcaster: self.txBroadcaster, feeEstimator: self.feeEstimator, logger: self.logger) - XCTAssertEqual(connectionResult.count, expectedMonitorUpdateLength) - } - } - - } - - deinit { - print("deiniting Peer") - } - - } - - func do_read_event(pm: PeerManager, descriptor: SocketDescriptor, data: [UInt8]) { - let res = pm.readEvent(peerDescriptor: descriptor, data: data) - assert(res.isOk()) - } - - - fileprivate func connectPeers(peerA: Peer, peerB: Peer) { - - let connectionResult = peerA.tcpSocketHandler?.connect(address: "127.0.0.1", port: peerB.tcpPort!, theirNodeId: peerB.nodeId!) - print("connection result: \(connectionResult)") - - } - - func test_multiple_peer_connections() async { - let peerA = Peer(master: self, seed: 1) - let peerB = Peer(master: self, seed: 2) - let peerC = Peer(master: self, seed: 3) - - let originalPeersA = peerA.peerManager.listPeers() - let originalPeersB = peerB.peerManager.listPeers() - let originalPeersC = peerC.peerManager.listPeers() - XCTAssertEqual(originalPeersA.count, 0) - XCTAssertEqual(originalPeersB.count, 0) - XCTAssertEqual(originalPeersC.count, 0) - - do { - connectPeers(peerA: peerA, peerB: peerB) - - // sleep for one second - try! await Task.sleep(nanoseconds: 1_000_000_000) - let connectedPeersA = peerA.peerManager.listPeers() - let connectedPeersB = peerB.peerManager.listPeers() - XCTAssertEqual(connectedPeersA.count, 1) - XCTAssertEqual(connectedPeersB.count, 1) - } - - do { - connectPeers(peerA: peerA, peerB: peerC) - - // sleep for one second - try! await Task.sleep(nanoseconds: 1_000_000_000) - let connectedPeersA = peerA.peerManager.listPeers() - let connectedPeersC = peerC.peerManager.listPeers() - XCTAssertEqual(connectedPeersA.count, 2) - XCTAssertEqual(connectedPeersC.count, 1) - } - - do { - // sleep for a total of 10 seconds - for i in 0..<100 { - // sleep for 100ms - try! await Task.sleep(nanoseconds: 0_100_000_000) - } - - peerA.constructor?.interrupt() - peerB.constructor?.interrupt() - peerC.constructor?.interrupt() - } - } - - fileprivate class func openChannel(peerA: Peer, peerB: Peer, fundingAmount: UInt64, latestBlock: BTCBlock? = nil, otherPeers: [Peer] = []) async -> BTCBlock? { - do { - // initiate channel opening - let config = UserConfig.initWithDefault() - let theirNodeId = peerB.channelManager.getOurNodeId() - let userChannelId: [UInt8] = [UInt8](repeating: 42, count: 16); - let channelOpenResult = peerA.channelManager.createChannel(theirNetworkKey: theirNodeId, channelValueSatoshis: fundingAmount, pushMsat: 1000, userChannelId: userChannelId, temporaryChannelId: ChannelId.initWithZero(), overrideConfig: config) - - XCTAssertTrue(channelOpenResult.isOk()) - let channels = peerA.channelManager.listChannels() - let firstChannel = channels[0] - let fundingTxo = firstChannel.getFundingTxo() - XCTAssertNil(fundingTxo) - } - - let channelsA = peerA.channelManager.listChannels() - XCTAssertEqual(channelsA.count, 1) - - let managerEvents = try! await peerA.getManagerEvents(expectedCount: 1) - XCTAssertEqual(managerEvents.count, 1) - - let managerEvent = managerEvents[0] - guard case .FundingGenerationReady = managerEvent.getValueType() else { - XCTAssert(false, "Expected .FundingGenerationReady, got \(managerEvent.getValueType())") - return nil - } - - let fundingReadyEvent = managerEvent.getValueAsFundingGenerationReady()! - XCTAssertEqual(fundingReadyEvent.getChannelValueSatoshis(), fundingAmount) - let expectedUserChannelId: [UInt8] = [UInt8](repeating: 42, count: 16); - XCTAssertEqual(fundingReadyEvent.getUserChannelId(), expectedUserChannelId) - - let fundingOutputScript = fundingReadyEvent.getOutputScript(); - let temporaryChannelId = fundingReadyEvent.getTemporaryChannelId(); - - XCTAssertEqual(fundingOutputScript.count, 34) - XCTAssertEqual(fundingOutputScript[0], 0) - XCTAssertEqual(fundingOutputScript[1], 32) - - let fundingTransaction = BTCTransaction() - fundingTransaction.version = 1 - let input = BTCTransaction.Input( - previousTransactionHash: BTCHashing.SHA_ZERO_HASH, - previousOutputIndex: 0xffffffff, // coinbase transaction has a u32::MAX prev output index - script: [] - ) - fundingTransaction.inputs = [input] - fundingTransaction.setWitnessForInput(inputIndex: 0, witness: BTCTransaction.Witness(stackElements: [[1]])) - - let output = BTCTransaction.Output(value: fundingAmount, script: fundingOutputScript) - fundingTransaction.outputs = [output] - let serializedFundingTx = fundingTransaction.serialize() - let fundingResult = peerA.channelManager.fundingTransactionGenerated(temporaryChannelId: temporaryChannelId, counterpartyNodeId: peerB.channelManager.getOurNodeId(), fundingTransaction: serializedFundingTx) - XCTAssertTrue(fundingResult.isOk()) - - let pendingBroadcasts = await peerA.getPendingBroadcasts(expectedCount: 1) - - XCTAssertEqual(pendingBroadcasts.count, 1) - XCTAssertEqual(pendingBroadcasts.first!, serializedFundingTx) - - let fundingBlock = BTCBlock() - fundingBlock.version = 2 - fundingBlock.previousBlockHash = latestBlock?.calculateHash() ?? BTCHashing.SHA_ZERO_HASH - fundingBlock.merkleRoot = BTCHashing.SHA_ZERO_HASH - fundingBlock.timestamp = 42 - fundingBlock.difficultyTarget = 0 - fundingBlock.nonce = 0 - fundingBlock.height = (latestBlock?.height ?? 0) + 1; - fundingBlock.transactions = [fundingTransaction] - - print("Connecting funding block…") - peerA.connectBlock(block: fundingBlock, height: fundingBlock.height, expectedMonitorUpdateLength: 0) - peerB.connectBlock(block: fundingBlock, height: fundingBlock.height, expectedMonitorUpdateLength: 0) - for currentPeer in otherPeers { - currentPeer.connectBlock(block: fundingBlock, height: fundingBlock.height, expectedMonitorUpdateLength: 0) - } - - print("Connecting confirmation blocks…") - var previousBlock = fundingBlock - for height in (fundingBlock.height+1)..<(fundingBlock.height+100) { - let currentBlock = BTCBlock() - currentBlock.version = 2 - currentBlock.previousBlockHash = previousBlock.calculateHash() - currentBlock.merkleRoot = BTCHashing.SHA_ZERO_HASH - currentBlock.timestamp = UInt32(42 + height) - currentBlock.difficultyTarget = 0 - currentBlock.nonce = 0 - currentBlock.height = height - - peerA.connectBlock(block: currentBlock, height: UInt32(height), expectedMonitorUpdateLength: 0) - peerB.connectBlock(block: currentBlock, height: UInt32(height), expectedMonitorUpdateLength: 0) - for currentPeer in otherPeers { - currentPeer.connectBlock(block: currentBlock, height: UInt32(height), expectedMonitorUpdateLength: 0) - } - previousBlock = currentBlock - } - - let peer1Events = try! await peerA.getManagerEvents(expectedCount: 2) - let peer1ReadyEvent = peer1Events[1] - guard case .ChannelReady = peer1ReadyEvent.getValueType() else { - XCTAssert(false, "Expected .ChannelReady, got \(peer1ReadyEvent.getValueType())") - return nil - } - - let peer2Events = try! await peerB.getManagerEvents(expectedCount: 2) - let peer2ReadyEvent = peer2Events[1] - guard case .ChannelReady = peer2ReadyEvent.getValueType() else { - XCTAssert(false, "Expected .ChannelReady, got \(peer2ReadyEvent.getValueType())") - return nil - } - - var usableChannelsA = [ChannelDetails]() - var usableChannelsB = [ChannelDetails]() - print("Awaiting usable channels to populate…") - while (usableChannelsA.isEmpty || usableChannelsB.isEmpty) { - usableChannelsA = peerA.channelManager.listUsableChannels() - usableChannelsB = peerB.channelManager.listUsableChannels() - // sleep for 100ms - try! await Task.sleep(nanoseconds: 0_100_000_000) - } - print("Usable channels have been populated") - - XCTAssertEqual(usableChannelsA.count, 1) - XCTAssertEqual(usableChannelsB.count, 1) - - let channelAToB = usableChannelsA[0] - let channelBToA = usableChannelsB[0] - XCTAssertEqual(channelAToB.getChannelValueSatoshis(), fundingAmount) - let shortChannelId = channelAToB.getShortChannelId()! - - let fundingChannelId = ChannelId.initWithV1FromFundingTxid(txid: fundingTransaction.calculateId(), outputIndex: 0) - XCTAssertEqual(usableChannelsA[0].getChannelId().write(), fundingChannelId.write()) - XCTAssertEqual(usableChannelsB[0].getChannelId().write(), fundingChannelId.write()) - - let originalChannelBalanceAToB = channelAToB.getBalanceMsat() - let originalChannelBalanceBToA = channelBToA.getBalanceMsat() - print("original balance A->B mSats: \(originalChannelBalanceAToB)") - print("original balance B->A mSats: \(originalChannelBalanceBToA)") - - // return the latest block after activating the channel - return previousBlock - } - - func testMessageHandling() async { - - let FUNDING_SATOSHI_AMOUNT: UInt64 = 100_000 // 100k satoshis - let SEND_MSAT_AMOUNT_A_TO_B: UInt64 = 10_000_000 // 10k satoshis - let SEND_MSAT_AMOUNT_B_TO_A: UInt64 = 3_000_000 // 3k satoshis - let SEND_MSAT_AMOUNT_A_TO_C: UInt64 = 5_000_000 // 5k satoshis, with intermediate hop - - let peer1 = Peer(master: self, seed: 1) - let peer2 = Peer(master: self, seed: 2) - let peer3 = Peer(master: self, seed: 3) - - do { - // connect the nodes - let originalPeersA = peer1.peerManager.listPeers() - let originalPeersB = peer2.peerManager.listPeers() - XCTAssertEqual(originalPeersA.count, 0) - XCTAssertEqual(originalPeersB.count, 0) - - connectPeers(peerA: peer1, peerB: peer2) - connectPeers(peerA: peer2, peerB: peer3) - - // sleep for one second - try! await Task.sleep(nanoseconds: 1_000_000_000) - - let connectedPeersA = peer1.peerManager.listPeers() - let connectedPeersB = peer2.peerManager.listPeers() - let connectedPeersC = peer3.peerManager.listPeers() - XCTAssertEqual(connectedPeersA.count, 1) - XCTAssertEqual(connectedPeersB.count, 2) - XCTAssertEqual(connectedPeersC.count, 1) - } - - var confirmedChannelBlock = await HumanObjectPeerTestInstance.openChannel(peerA: peer1, peerB: peer2, fundingAmount: FUNDING_SATOSHI_AMOUNT, otherPeers: [peer3]) - - let usableChannelsA = peer1.channelManager.listUsableChannels() - let usableChannelsB = peer2.channelManager.listUsableChannels() - let channelAToB = usableChannelsA[0] - let channelBToA = usableChannelsB[0] - - // confirmedChannelBlock = await HumanObjectPeerTestInstance.openChannel(peerA: peer2, peerB: peer3, fundingAmount: FUNDING_SATOSHI_AMOUNT, latestBlock: confirmedChannelBlock, otherPeers: [peer1]) - - let originalChannelBalanceAToB = channelAToB.getBalanceMsat() - let originalChannelBalanceBToA = channelBToA.getBalanceMsat() - - let secondChannelBalanceAToB = channelAToB.getBalanceMsat() - let secondChannelBalanceBToA = channelBToA.getBalanceMsat() - - let logger = TestLogger() - - do { - // create invoice for 10k satoshis to pay to peer2 - - let invoiceResult = Bindings.createInvoiceFromChannelmanager(channelmanager: peer2.channelManager, nodeSigner: peer2.explicitKeysManager.asNodeSigner(), logger: logger, network: .Bitcoin, amtMsat: SEND_MSAT_AMOUNT_A_TO_B, description: "Invoice description", invoiceExpiryDeltaSecs: 60, minFinalCltvExpiryDelta: 24) - if let invoiceError = invoiceResult.getError(){ - let creationError = invoiceError.getValueAsCreationError() - print("creation error: \(creationError)") - } - let invoice = invoiceResult.getValue()! - print("Invoice: \(invoice.toStr())") - - let recreatedInvoice = Bolt11Invoice.fromStr(s: invoice.toStr()) - XCTAssertTrue(recreatedInvoice.isOk()) - - // find route - - do { - let payerPubkey = peer1.channelManager.getOurNodeId() - let payeePubkey = peer2.channelManager.getOurNodeId() - let paymentParameters = PaymentParameters.initForKeysend(payeePubkey: payeePubkey, finalCltvExpiryDelta: 3, allowMpp: false) - - let amount = invoice.amountMilliSatoshis()! - let routeParameters = RouteParameters(paymentParamsArg: paymentParameters, finalValueMsatArg: amount, maxTotalRoutingFeeMsatArg: nil) - let randomSeedBytes: [UInt8] = [UInt8](repeating: 0, count: 32) - let scoringParams = ProbabilisticScoringDecayParameters.initWithDefault() - let networkGraph = peer1.constructor!.netGraph! - let scorer = ProbabilisticScorer(decayParams: scoringParams, networkGraph: networkGraph, logger: logger) - let score = scorer.asScoreLookUp() - - let scoreParams = ProbabilisticScoringFeeParameters.initWithDefault() - - let foundRoute = Bindings.findRoute( - ourNodePubkey: payerPubkey, - routeParams: routeParameters, - networkGraph: networkGraph, - firstHops: usableChannelsA, - logger: logger, - scorer: score, - scoreParams: scoreParams, - randomSeedBytes: randomSeedBytes - ) - - let route = foundRoute.getValue()! - let fees = route.getTotalFees() - print("found route fees: \(fees)") - } - - let channelManagerConstructor = peer1.constructor! - - let (paymentHash, recipientOnion, routeParameters) = Bindings.paymentParametersFromInvoice(invoice: invoice).getValue()! - let paymentId = invoice.paymentHash()! - - - let invoicePaymentResult = channelManagerConstructor.channelManager.sendPayment(paymentHash: paymentHash, recipientOnion: recipientOnion, paymentId: paymentId, routeParams: routeParameters, retryStrategy: Bindings.Retry.initWithAttempts(a: 3)) - // let invoicePaymentResult = Bindings.payInvoice(invoice: invoice, retryStrategy: Bindings.Retry.initWithAttempts(a: 3), channelmanager: channelManagerConstructor.channelManager) - XCTAssertTrue(invoicePaymentResult.isOk()) - - do { - // process HTLCs - let peer2Event = try! await peer2.getManagerEvents(expectedCount: 1)[0] - guard case .PendingHTLCsForwardable = peer2Event.getValueType() else { - return XCTAssert(false, "Expected .PendingHTLCsForwardable, got \(peer2Event.getValueType())") - } - let pendingHTLCsForwardable = peer2Event.getValueAsPendingHtlcsForwardable()! - print("forwardable time: \(pendingHTLCsForwardable.getTimeForwardable())") - peer2.channelManager.processPendingHtlcForwards() - print("processed pending HTLC forwards") - } - - do { - // process payment - let peer2Event = try! await peer2.getManagerEvents(expectedCount: 1)[0] - guard case .PaymentClaimable = peer2Event.getValueType() else { - return XCTAssert(false, "Expected .PaymentReceived, got \(peer2Event.getValueType())") - } - let paymentClaimable = peer2Event.getValueAsPaymentClaimable()! - let paymentHash = paymentClaimable.getPaymentHash() - print("received payment of \(paymentClaimable.getAmountMsat()) with hash \(paymentHash)") - let paymentPurpose = paymentClaimable.getPurpose() - guard case .Bolt11InvoicePayment = paymentPurpose.getValueType() else { - return XCTAssert(false, "Expected .InvoicePayment, got \(paymentPurpose.getValueType())") - } - let invoicePayment = paymentPurpose.getValueAsBolt11InvoicePayment()! - let preimage = invoicePayment.getPaymentPreimage()! - let secret = invoicePayment.getPaymentSecret() - if self.configuration.shouldRecipientRejectPayment { - print("about to fail payment because shouldRecipientRejectPayment flag is set") - peer2.channelManager.failHtlcBackwards(paymentHash: paymentHash) - print("deliberately failed payment with hash \(paymentHash)") - for _ in 0..<10{ - // this may have a random delay, run it repeatedly - peer2.channelManager.processPendingHtlcForwards() - try! await Task.sleep(nanoseconds: 0_100_000_000) - } - } else { - peer2.channelManager.claimFunds(paymentPreimage: preimage) - print("claimed payment with secret \(secret) using preimage \(preimage)") - } - } - - if self.configuration.shouldRecipientRejectPayment { - let peer1Events = try! await peer1.getManagerEvents(expectedCount: 1) - let paymentPathFailedEvent = peer1Events[0] - guard case .PaymentPathFailed = paymentPathFailedEvent.getValueType() else { - return XCTAssert(false, "Expected .PaymentPathFailed, got \(paymentPathFailedEvent.getValueType())") - } - let paymentPathFailed = paymentPathFailedEvent.getValueAsPaymentPathFailed()! - let failureDescriptor: [String: Any] = [ - "payment_id": paymentPathFailed.getPaymentId(), - "payment_hash": paymentPathFailed.getPaymentHash(), - "payment_failed_permanently": paymentPathFailed.getPaymentFailedPermanently(), - "short_channel_id": paymentPathFailed.getShortChannelId(), - "path": paymentPathFailed.getPath().getHops().map { $0.getShortChannelId() }, - "failure": paymentPathFailed.getFailure() - ] - - print("payent path failure: \(failureDescriptor)") - print("here") - return - - } else { - // process payment - let peer1Events = try! await peer1.getManagerEvents(expectedCount: 2) - let paymentSentEvent = peer1Events[0] - let paymentPathSuccessfulEvent = peer1Events[1] - guard case .PaymentSent = paymentSentEvent.getValueType() else { - return XCTAssert(false, "Expected .PaymentSent, got \(paymentSentEvent.getValueType())") - } - guard case .PaymentPathSuccessful = paymentPathSuccessfulEvent.getValueType() else { - return XCTAssert(false, "Expected .PaymentPathSuccessful, got \(paymentPathSuccessfulEvent.getValueType())") - } - let paymentSent = paymentSentEvent.getValueAsPaymentSent()! - let paymentPathSuccessful = paymentPathSuccessfulEvent.getValueAsPaymentPathSuccessful()! - print("sent payment \(paymentSent.getPaymentId()) with fee \(paymentSent.getFeePaidMsat()) via \(paymentPathSuccessful.getPath().getHops().map { h in h.getShortChannelId() })") - } - - var currentChannelABalance = secondChannelBalanceAToB - var currentChannelBBalance = secondChannelBalanceBToA - while true { - let channelA = peer1.channelManager.listUsableChannels()[0] - let channelB = peer2.channelManager.listUsableChannels()[0] - currentChannelABalance = channelA.getBalanceMsat() - currentChannelBBalance = channelB.getBalanceMsat() - if currentChannelABalance != secondChannelBalanceAToB && currentChannelBBalance != secondChannelBalanceBToA { - break - } - // sleep for 100ms - try! await Task.sleep(nanoseconds: 0_100_000_000) - } - - - // let invoicePayment = invoicePaymentResult.getValue()! - XCTAssert(invoicePaymentResult.isOk()) - XCTAssertEqual(currentChannelABalance, secondChannelBalanceAToB - SEND_MSAT_AMOUNT_A_TO_B) - XCTAssertEqual(currentChannelBBalance, secondChannelBalanceBToA + SEND_MSAT_AMOUNT_A_TO_B) - } - - do { - // send some money back - // create invoice for 10k satoshis to pay to peer2 - let prePaymentBalanceAToB = peer1.channelManager.listUsableChannels()[0].getBalanceMsat() - let prePaymentBalanceBToA = peer2.channelManager.listUsableChannels()[0].getBalanceMsat() - print("pre-payment balance A->B mSats: \(prePaymentBalanceAToB)") - print("pre-payment balance B->A mSats: \(prePaymentBalanceBToA)") - - let invoiceResult = Bindings.createInvoiceFromChannelmanager(channelmanager: peer1.channelManager, nodeSigner: peer1.explicitKeysManager.asNodeSigner(), logger: logger, network: .Bitcoin, amtMsat: nil, description: "Second invoice description", invoiceExpiryDeltaSecs: 60, minFinalCltvExpiryDelta: 24) - let invoice = invoiceResult.getValue()! - print("Implicit amount invoice: \(invoice.toStr())") + private let configuration: Configuration + + public class Configuration { + // + public var useFilter: Bool = false + public var useRouter: Bool = false + public var useWrappedSignerProvider: Bool = false + public var shouldRecipientRejectPayment: Bool = false + public var ephemeralNetworkGraphForScorer: Bool = false + public var reserializedProbabilisticScorer: Bool = false + + // public var nice_close: Bool = false; + // public var use_km_wrapper: Bool = false; + // public var use_manual_watch: Bool = false; + // public var reload_peers: Bool = false; + // public var break_cross_peer_refs: Bool = false; + // public var use_nio_peer_handler: Bool = false; + + private class func listCustomizeableProperties() -> [String] { + return [ + "useFilter", "useRouter", "useWrappedSignerProvider", "shouldRecipientRejectPayment", + "ephemeralNetworkGraphForScorer", "reserializedProbabilisticScorer", + ] + } + + public class func combinationCount() -> UInt { + return 1 << self.listCustomizeableProperties().count + } + + } + + + public init(configuration: Configuration) { + self.configuration = configuration + } + + fileprivate class Peer { + + private(set) var txBroadcaster: BroadcasterInterface! + weak var master: HumanObjectPeerTestInstance! + let logger: Logger + let feeEstimator: FeeEstimator + let seed: UInt8 + var filterAdditions: Set + let monitors: [String: ChannelMonitor] + private(set) var filter: Filter? + private(set) var explicitKeysManager: KeysManager! + private(set) var wrappedKeysManager: WrappedSignerProviderTests.MyKeysManager? + private(set) var router: GossipSync! + private(set) var channelManager: ChannelManager! + private(set) var peerManager: PeerManager! + + + private(set) var constructor: ChannelManagerConstructor? + private(set) var tcpSocketHandler: TCPPeerHandler? + private(set) var tcpPort: UInt16? + + fileprivate enum SocketBindError: Error { + case failedToBindSocket + } + + fileprivate enum ChannelManagerEventError: Error { + case notUsingChannelManagerConstructor + } + + fileprivate actor PendingEventTracker { + + private(set) var pendingManagerEvents: [Event] = [] + private(set) var continuations: [CheckedContinuation] = [] + + private func triggerContinuations() { + let continuations = self.continuations + self.continuations.removeAll() + for currentContinuation in continuations { + currentContinuation.resume() + } + } + + func addEvent(event: Event) { + self.pendingManagerEvents.append(event) + self.triggerContinuations() + } + + func addEvents(events: [Event]) { + self.pendingManagerEvents.append(contentsOf: events) + self.triggerContinuations() + } + + func getCount() -> Int { + return self.pendingManagerEvents.count + } + + func getEvents() -> [Event] { + return self.pendingManagerEvents + } + + func getAndClearEvents() -> [Event] { + let events = self.pendingManagerEvents + self.pendingManagerEvents.removeAll() + return events + } + + func awaitAddition() async { + await withCheckedContinuation({ continuation in + self.continuations.append(continuation) + }) + } + } + private(set) var pendingEventTracker = PendingEventTracker() + + fileprivate actor PendingBroadcastTracker { + private(set) var pendingBroadcastSet: Set<[UInt8]> = Set() + private(set) var continuations: [CheckedContinuation] = [] + + func addTransaction(tx: [UInt8]) { + self.pendingBroadcastSet.insert(tx) + self.triggerContinuations() + } + + func getCount() -> Int { + return self.pendingBroadcastSet.count + } + + func getTransactions() -> Set<[UInt8]> { + return self.pendingBroadcastSet + } + + func getAndClearTransactions() -> Set<[UInt8]> { + let txs = self.pendingBroadcastSet + self.pendingBroadcastSet.removeAll() + return txs + } + + private func triggerContinuations() { + let continuations = self.continuations + self.continuations.removeAll() + for currentContinuation in continuations { + currentContinuation.resume() + } + } + + func awaitAddition() async { + await withCheckedContinuation({ continuation in + self.continuations.append(continuation) + }) + } + } + private(set) var pendingBroadcastTracker = PendingBroadcastTracker() + + private(set) var nodeId: [UInt8]? + + var chainWatch: Watch? + var chainMonitor: ChainMonitor? + + fileprivate class TestBroadcaster: BroadcasterInterface { + weak var master: Peer! + + fileprivate init(master: Peer) { + self.master = master + super.init() + } + + override func broadcastTransactions(txs: [[UInt8]]) { + for tx in txs { + Task { + await self.master.pendingBroadcastTracker.addTransaction(tx: tx) + } + } + } + } + + fileprivate class TestFilter: Filter { + + weak var master: Peer! + + fileprivate init(master: Peer) { + self.master = master + super.init() + } + + override func registerOutput(output: WatchedOutput) { + let outpoint = output.getOutpoint() + self.master.filterAdditions.insert("\(outpoint.getTxid()):\(outpoint.getIndex())") + } + + override func registerTx(txid: [UInt8]?, scriptPubkey: [UInt8]) { + self.master.filterAdditions.insert("\(txid)") + } + } + + fileprivate class TestKeysInterface: SignerProvider { + + weak var master: Peer! + let interface: SignerProvider + + fileprivate init(master: Peer, underlyingInterface: SignerProvider) { + self.master = master + self.interface = underlyingInterface + super.init() + } + + override func deriveChannelSigner(channelValueSatoshis: UInt64, channelKeysId: [UInt8]) + -> Bindings.EcdsaChannelSigner + { + let ck = self.interface.deriveChannelSigner( + channelValueSatoshis: channelValueSatoshis, channelKeysId: channelKeysId) + return ck + } + } + + fileprivate class TestChannelManagerPersister: Persister, ExtendedChannelManagerPersister { + + weak var master: Peer! + + fileprivate init(master: Peer) { + self.master = master + super.init() + } + + func handleEvent(event: Event) -> Result_NoneReplayEventZ { + print("peer \(self.master.seed) received event: \(event.getValueType())") + Task { + // clone to avoid deallocation-related issues + await master.pendingEventTracker.addEvent(event: event) + } + return .initWithOk() + } + + override func persistScorer(scorer: Bindings.WriteableScore) -> Bindings.Result_NoneIOErrorZ { + .initWithOk() + } + + override func persistGraph(networkGraph: Bindings.NetworkGraph) -> Bindings.Result_NoneIOErrorZ { + let writtenGraph = networkGraph.write() + return .initWithOk() + } + + override func persistManager(channelManager: Bindings.ChannelManager) -> Bindings.Result_NoneIOErrorZ { + .initWithOk() + } + + } + + fileprivate class TestPersister: Persist { + + override func persistNewChannel(channelFundingOutpoint: Bindings.OutPoint, monitor: Bindings.ChannelMonitor) + -> Bindings.ChannelMonitorUpdateStatus + { + .Completed + } + + override func updatePersistedChannel( + channelFundingOutpoint: Bindings.OutPoint, monitorUpdate: Bindings.ChannelMonitorUpdate, + monitor: Bindings.ChannelMonitor + ) -> Bindings.ChannelMonitorUpdateStatus { + .Completed + } + } + + fileprivate init(master: HumanObjectPeerTestInstance, _dummy: Any, seed: UInt8) { + + self.master = master + self.logger = TestLogger() + self.feeEstimator = TestFeeEstimator() + self.monitors = [String: ChannelMonitor]() + self.seed = seed + let persister = TestPersister() + self.filterAdditions = Set() + + self.txBroadcaster = TestBroadcaster(master: self) + + if master.configuration.useFilter { + self.filter = TestFilter(master: self) + } + + // never use manual watch + do { + self.chainMonitor = ChainMonitor( + chainSource: self.filter, broadcaster: self.txBroadcaster, logger: self.logger, + feeest: self.feeEstimator, persister: persister) + self.chainWatch = self.chainMonitor!.asWatch() + } + + var keySeed = [UInt8](repeating: 0, count: 32) + for i in 0..<32 { + keySeed[i] = UInt8(i) ^ seed + } + + let timestamp_seconds = UInt64(NSDate().timeIntervalSince1970) + let timestamp_nanos = UInt32(truncating: NSNumber(value: timestamp_seconds * 1000 * 1000)) + let keysManager = KeysManager( + seed: keySeed, startingTimeSecs: timestamp_seconds, startingTimeNanos: timestamp_nanos) + self.explicitKeysManager = keysManager + + if master.configuration.useWrappedSignerProvider { + self.wrappedKeysManager = WrappedSignerProviderTests.MyKeysManager( + seed: keySeed, startingTimeSecs: timestamp_seconds, startingTimeNanos: timestamp_nanos) + } + + if master.configuration.useRouter { + let networkGraph = NetworkGraph(network: .Regtest, logger: self.logger) + self.router = GossipSync.initWithP2P( + a: P2PGossipSync(networkGraph: networkGraph, utxoLookup: nil, logger: self.logger)) + } else { + self.router = GossipSync.none() + } + } + + fileprivate convenience init(master: HumanObjectPeerTestInstance, seed: UInt8) { + self.init(master: master, _dummy: (), seed: seed) + + do { + // channel manager constructor is mandatory + + let graph = NetworkGraph(network: .Regtest, logger: self.logger) + var scorerGraph = graph + if master.configuration.ephemeralNetworkGraphForScorer { + scorerGraph = NetworkGraph(network: .Regtest, logger: self.logger) + } + + let decayParams = ProbabilisticScoringDecayParameters.initWithDefault() + var probabalisticScorer = ProbabilisticScorer( + decayParams: decayParams, networkGraph: scorerGraph, logger: self.logger) + if master.configuration.reserializedProbabilisticScorer { + let serializedScorer = probabalisticScorer.write() + let probabalisticScorerResult = ProbabilisticScorer.read( + ser: serializedScorer, argA: decayParams, argB: scorerGraph, argC: self.logger) + probabalisticScorer = probabalisticScorerResult.getValue()! + } + let score = probabalisticScorer.asScore() + let multiThreadedScorer = MultiThreadedLockableScore(score: score) + + let signerProvider = + master.configuration.useWrappedSignerProvider + ? self.wrappedKeysManager!.signerProvider : self.explicitKeysManager.asSignerProvider() + let constructionParameters = ChannelManagerConstructionParameters( + config: UserConfig.initWithDefault(), + entropySource: self.explicitKeysManager.asEntropySource(), + nodeSigner: self.explicitKeysManager.asNodeSigner(), + signerProvider: signerProvider, + feeEstimator: self.feeEstimator, + chainMonitor: self.chainMonitor!, + txBroadcaster: self.txBroadcaster, + logger: self.logger, + scorer: multiThreadedScorer + ) + + self.constructor = ChannelManagerConstructor( + network: .Bitcoin, currentBlockchainTipHash: [UInt8](repeating: 0, count: 32), + currentBlockchainTipHeight: 0, netGraph: graph, params: constructionParameters) + + self.constructor?.chainSyncCompleted(persister: TestChannelManagerPersister(master: self)) + self.channelManager = self.constructor!.channelManager + self.peerManager = self.constructor!.peerManager + } + self.nodeId = self.channelManager.getOurNodeId() + try! self.bindSocketHandler() + } + + fileprivate convenience init(original: Peer) { + self.init(master: original.master, _dummy: (), seed: original.seed) + + do { + // channel manager constructor is mandatory + let graph = NetworkGraph(network: .Bitcoin, logger: self.logger) + + let constructionParameters = ChannelManagerConstructionParameters( + config: UserConfig.initWithDefault(), + entropySource: self.explicitKeysManager.asEntropySource(), + nodeSigner: self.explicitKeysManager.asNodeSigner(), + signerProvider: self.explicitKeysManager.asSignerProvider(), + feeEstimator: self.feeEstimator, + chainMonitor: self.chainMonitor!, + txBroadcaster: self.txBroadcaster, + logger: self.logger + ) + self.constructor = ChannelManagerConstructor( + network: .Bitcoin, currentBlockchainTipHash: [UInt8](repeating: 0, count: 32), + currentBlockchainTipHeight: 0, netGraph: graph, params: constructionParameters) + self.constructor?.chainSyncCompleted(persister: TestChannelManagerPersister(master: self)) + self.channelManager = self.constructor!.channelManager + Task { + let events = await original.pendingEventTracker.getEvents() + await self.pendingEventTracker.addEvents(events: events) + } + } + self.nodeId = self.channelManager.getOurNodeId() + try! self.bindSocketHandler() + } + + private func bindSocketHandler() throws { + self.tcpSocketHandler = self.constructor!.getTCPPeerHandler() + print("Attempting to bind socket…") + for i in 10000...65535 { + let port = UInt16(i) + let bound = self.tcpSocketHandler!.bind(address: "127.0.0.1", port: port) + if bound { + print("Bound socket to port \(port) on attempt \(i-9999)") + self.tcpPort = port + return + } + } + print("Failed to bind on any port") + throw SocketBindError.failedToBindSocket + } + + fileprivate func payInvoice() { + + } + + fileprivate func getManualWatch() { + + } + + fileprivate func getManagerEvents(expectedCount: UInt) async throws -> [Event] { + print("Awaiting \(expectedCount) manager events…") + while true { + if await self.pendingEventTracker.getCount() >= expectedCount { + print("Found enough manager events for expected count of \(expectedCount)") + let events = await self.pendingEventTracker.getAndClearEvents() + print("Found manager event count: \(events.count)") + return events + } + // sleep for 0.1 seconds + // try await Task.sleep(nanoseconds: 0_100_000_000) + await self.pendingEventTracker.awaitAddition() + } + } + + fileprivate func getPendingBroadcasts(expectedCount: UInt) async -> Set<[UInt8]> { + while true { + if await self.pendingBroadcastTracker.getCount() >= expectedCount { + return await self.pendingBroadcastTracker.getAndClearTransactions() + } + await self.pendingBroadcastTracker.awaitAddition() + } + } + + fileprivate func connectBlock(block: BTCBlock, height: UInt32, expectedMonitorUpdateLength: Int) { + let blockSerialization = block.serialize() + var transactionTuples: [(UInt, [UInt8])] = [] + + if block.transactions.count > 0 { + XCTAssertEqual(block.transactions.count, 1) + transactionTuples.append((0, block.transactions[0].serialize())) + } + + if let chainMonitor = self.chainMonitor { + self.channelManager.asListen().blockConnected(block: blockSerialization, height: height) + chainMonitor.asListen().blockConnected(block: blockSerialization, height: height) + } else { + let blockHeader = block.calculateHeader() + self.channelManager.asConfirm() + .transactionsConfirmed(header: blockHeader, txdata: transactionTuples, height: height) + self.channelManager.asConfirm().bestBlockUpdated(header: blockHeader, height: height) + + for (_, monitor) in self.monitors { + let connectionResult = monitor.blockConnected( + header: blockHeader, txdata: transactionTuples, height: height, broadcaster: self.txBroadcaster, + feeEstimator: self.feeEstimator, logger: self.logger) + XCTAssertEqual(connectionResult.count, expectedMonitorUpdateLength) + } + } + + } + + deinit { + print("deiniting Peer") + } + + } + + func do_read_event(pm: PeerManager, descriptor: SocketDescriptor, data: [UInt8]) { + let res = pm.readEvent(peerDescriptor: descriptor, data: data) + assert(res.isOk()) + } + + + fileprivate func connectPeers(peerA: Peer, peerB: Peer) { + + let connectionResult = peerA.tcpSocketHandler? + .connect(address: "127.0.0.1", port: peerB.tcpPort!, theirNodeId: peerB.nodeId!) + print("connection result: \(connectionResult)") + + } + + func test_multiple_peer_connections() async { + let peerA = Peer(master: self, seed: 1) + let peerB = Peer(master: self, seed: 2) + let peerC = Peer(master: self, seed: 3) + + let originalPeersA = peerA.peerManager.listPeers() + let originalPeersB = peerB.peerManager.listPeers() + let originalPeersC = peerC.peerManager.listPeers() + XCTAssertEqual(originalPeersA.count, 0) + XCTAssertEqual(originalPeersB.count, 0) + XCTAssertEqual(originalPeersC.count, 0) + + do { + connectPeers(peerA: peerA, peerB: peerB) + + // sleep for one second + try! await Task.sleep(nanoseconds: 1_000_000_000) + let connectedPeersA = peerA.peerManager.listPeers() + let connectedPeersB = peerB.peerManager.listPeers() + XCTAssertEqual(connectedPeersA.count, 1) + XCTAssertEqual(connectedPeersB.count, 1) + } + + do { + connectPeers(peerA: peerA, peerB: peerC) + + // sleep for one second + try! await Task.sleep(nanoseconds: 1_000_000_000) + let connectedPeersA = peerA.peerManager.listPeers() + let connectedPeersC = peerC.peerManager.listPeers() + XCTAssertEqual(connectedPeersA.count, 2) + XCTAssertEqual(connectedPeersC.count, 1) + } + + do { + // sleep for a total of 10 seconds + for i in 0..<100 { + // sleep for 100ms + try! await Task.sleep(nanoseconds: 0_100_000_000) + } + + peerA.constructor?.interrupt() + peerB.constructor?.interrupt() + peerC.constructor?.interrupt() + } + } + + fileprivate class func openChannel( + peerA: Peer, peerB: Peer, fundingAmount: UInt64, latestBlock: BTCBlock? = nil, otherPeers: [Peer] = [] + ) async -> BTCBlock? { + do { + // initiate channel opening + let config = UserConfig.initWithDefault() + let theirNodeId = peerB.channelManager.getOurNodeId() + let userChannelId: [UInt8] = [UInt8](repeating: 42, count: 16) + let channelOpenResult = peerA.channelManager.createChannel( + theirNetworkKey: theirNodeId, channelValueSatoshis: fundingAmount, pushMsat: 1000, + userChannelId: userChannelId, temporaryChannelId: ChannelId.initWithZero(), overrideConfig: config) + + XCTAssertTrue(channelOpenResult.isOk()) + let channels = peerA.channelManager.listChannels() + let firstChannel = channels[0] + let fundingTxo = firstChannel.getFundingTxo() + XCTAssertNil(fundingTxo) + } + + let channelsA = peerA.channelManager.listChannels() + XCTAssertEqual(channelsA.count, 1) + + let managerEvents = try! await peerA.getManagerEvents(expectedCount: 1) + XCTAssertEqual(managerEvents.count, 1) + + let managerEvent = managerEvents[0] + guard case .FundingGenerationReady = managerEvent.getValueType() else { + XCTAssert(false, "Expected .FundingGenerationReady, got \(managerEvent.getValueType())") + return nil + } + + let fundingReadyEvent = managerEvent.getValueAsFundingGenerationReady()! + XCTAssertEqual(fundingReadyEvent.getChannelValueSatoshis(), fundingAmount) + let expectedUserChannelId: [UInt8] = [UInt8](repeating: 42, count: 16) + XCTAssertEqual(fundingReadyEvent.getUserChannelId(), expectedUserChannelId) + + let fundingOutputScript = fundingReadyEvent.getOutputScript() + let temporaryChannelId = fundingReadyEvent.getTemporaryChannelId() + + XCTAssertEqual(fundingOutputScript.count, 34) + XCTAssertEqual(fundingOutputScript[0], 0) + XCTAssertEqual(fundingOutputScript[1], 32) + + let fundingTransaction = BTCTransaction() + fundingTransaction.version = 1 + let input = BTCTransaction.Input( + previousTransactionHash: BTCHashing.SHA_ZERO_HASH, + previousOutputIndex: 0xffff_ffff, // coinbase transaction has a u32::MAX prev output index + script: [] + ) + fundingTransaction.inputs = [input] + fundingTransaction.setWitnessForInput(inputIndex: 0, witness: BTCTransaction.Witness(stackElements: [[1]])) + + let output = BTCTransaction.Output(value: fundingAmount, script: fundingOutputScript) + fundingTransaction.outputs = [output] + let serializedFundingTx = fundingTransaction.serialize() + let fundingResult = peerA.channelManager.fundingTransactionGenerated( + temporaryChannelId: temporaryChannelId, counterpartyNodeId: peerB.channelManager.getOurNodeId(), + fundingTransaction: serializedFundingTx) + XCTAssertTrue(fundingResult.isOk()) + + let pendingBroadcasts = await peerA.getPendingBroadcasts(expectedCount: 1) + + XCTAssertEqual(pendingBroadcasts.count, 1) + XCTAssertEqual(pendingBroadcasts.first!, serializedFundingTx) + + let fundingBlock = BTCBlock() + fundingBlock.version = 2 + fundingBlock.previousBlockHash = latestBlock?.calculateHash() ?? BTCHashing.SHA_ZERO_HASH + fundingBlock.merkleRoot = BTCHashing.SHA_ZERO_HASH + fundingBlock.timestamp = 42 + fundingBlock.difficultyTarget = 0 + fundingBlock.nonce = 0 + fundingBlock.height = (latestBlock?.height ?? 0) + 1 + fundingBlock.transactions = [fundingTransaction] + + print("Connecting funding block…") + peerA.connectBlock(block: fundingBlock, height: fundingBlock.height, expectedMonitorUpdateLength: 0) + peerB.connectBlock(block: fundingBlock, height: fundingBlock.height, expectedMonitorUpdateLength: 0) + for currentPeer in otherPeers { + currentPeer.connectBlock(block: fundingBlock, height: fundingBlock.height, expectedMonitorUpdateLength: 0) + } + + print("Connecting confirmation blocks…") + var previousBlock = fundingBlock + for height in (fundingBlock.height + 1)..<(fundingBlock.height + 100) { + let currentBlock = BTCBlock() + currentBlock.version = 2 + currentBlock.previousBlockHash = previousBlock.calculateHash() + currentBlock.merkleRoot = BTCHashing.SHA_ZERO_HASH + currentBlock.timestamp = UInt32(42 + height) + currentBlock.difficultyTarget = 0 + currentBlock.nonce = 0 + currentBlock.height = height + + peerA.connectBlock(block: currentBlock, height: UInt32(height), expectedMonitorUpdateLength: 0) + peerB.connectBlock(block: currentBlock, height: UInt32(height), expectedMonitorUpdateLength: 0) + for currentPeer in otherPeers { + currentPeer.connectBlock(block: currentBlock, height: UInt32(height), expectedMonitorUpdateLength: 0) + } + previousBlock = currentBlock + } + + let peer1Events = try! await peerA.getManagerEvents(expectedCount: 2) + let peer1ReadyEvent = peer1Events[1] + guard case .ChannelReady = peer1ReadyEvent.getValueType() else { + XCTAssert(false, "Expected .ChannelReady, got \(peer1ReadyEvent.getValueType())") + return nil + } + + let peer2Events = try! await peerB.getManagerEvents(expectedCount: 2) + let peer2ReadyEvent = peer2Events[1] + guard case .ChannelReady = peer2ReadyEvent.getValueType() else { + XCTAssert(false, "Expected .ChannelReady, got \(peer2ReadyEvent.getValueType())") + return nil + } + + var usableChannelsA = [ChannelDetails]() + var usableChannelsB = [ChannelDetails]() + print("Awaiting usable channels to populate…") + while usableChannelsA.isEmpty || usableChannelsB.isEmpty { + usableChannelsA = peerA.channelManager.listUsableChannels() + usableChannelsB = peerB.channelManager.listUsableChannels() + // sleep for 100ms + try! await Task.sleep(nanoseconds: 0_100_000_000) + } + print("Usable channels have been populated") + + XCTAssertEqual(usableChannelsA.count, 1) + XCTAssertEqual(usableChannelsB.count, 1) + + let channelAToB = usableChannelsA[0] + let channelBToA = usableChannelsB[0] + XCTAssertEqual(channelAToB.getChannelValueSatoshis(), fundingAmount) + let shortChannelId = channelAToB.getShortChannelId()! + + let fundingChannelId = ChannelId.initWithV1FromFundingTxid( + txid: fundingTransaction.calculateId(), outputIndex: 0) + XCTAssertEqual(usableChannelsA[0].getChannelId().write(), fundingChannelId.write()) + XCTAssertEqual(usableChannelsB[0].getChannelId().write(), fundingChannelId.write()) + + let originalChannelBalanceAToB = channelAToB.getBalanceMsat() + let originalChannelBalanceBToA = channelBToA.getBalanceMsat() + print("original balance A->B mSats: \(originalChannelBalanceAToB)") + print("original balance B->A mSats: \(originalChannelBalanceBToA)") + + // return the latest block after activating the channel + return previousBlock + } + + func testMessageHandling() async { + + let FUNDING_SATOSHI_AMOUNT: UInt64 = 100_000 // 100k satoshis + let SEND_MSAT_AMOUNT_A_TO_B: UInt64 = 10_000_000 // 10k satoshis + let SEND_MSAT_AMOUNT_B_TO_A: UInt64 = 3_000_000 // 3k satoshis + let SEND_MSAT_AMOUNT_A_TO_C: UInt64 = 5_000_000 // 5k satoshis, with intermediate hop + + let peer1 = Peer(master: self, seed: 1) + let peer2 = Peer(master: self, seed: 2) + let peer3 = Peer(master: self, seed: 3) + + do { + // connect the nodes + let originalPeersA = peer1.peerManager.listPeers() + let originalPeersB = peer2.peerManager.listPeers() + XCTAssertEqual(originalPeersA.count, 0) + XCTAssertEqual(originalPeersB.count, 0) + + connectPeers(peerA: peer1, peerB: peer2) + connectPeers(peerA: peer2, peerB: peer3) + + // sleep for one second + try! await Task.sleep(nanoseconds: 1_000_000_000) + + let connectedPeersA = peer1.peerManager.listPeers() + let connectedPeersB = peer2.peerManager.listPeers() + let connectedPeersC = peer3.peerManager.listPeers() + XCTAssertEqual(connectedPeersA.count, 1) + XCTAssertEqual(connectedPeersB.count, 2) + XCTAssertEqual(connectedPeersC.count, 1) + } + + var confirmedChannelBlock = await HumanObjectPeerTestInstance.openChannel( + peerA: peer1, peerB: peer2, fundingAmount: FUNDING_SATOSHI_AMOUNT, otherPeers: [peer3]) + + let usableChannelsA = peer1.channelManager.listUsableChannels() + let usableChannelsB = peer2.channelManager.listUsableChannels() + let channelAToB = usableChannelsA[0] + let channelBToA = usableChannelsB[0] + + // confirmedChannelBlock = await HumanObjectPeerTestInstance.openChannel(peerA: peer2, peerB: peer3, fundingAmount: FUNDING_SATOSHI_AMOUNT, latestBlock: confirmedChannelBlock, otherPeers: [peer1]) + + let originalChannelBalanceAToB = channelAToB.getBalanceMsat() + let originalChannelBalanceBToA = channelBToA.getBalanceMsat() + + let secondChannelBalanceAToB = channelAToB.getBalanceMsat() + let secondChannelBalanceBToA = channelBToA.getBalanceMsat() + + let logger = TestLogger() + + do { + // create invoice for 10k satoshis to pay to peer2 + + let invoiceResult = Bindings.createInvoiceFromChannelmanager( + channelmanager: peer2.channelManager, nodeSigner: peer2.explicitKeysManager.asNodeSigner(), + logger: logger, network: .Bitcoin, amtMsat: SEND_MSAT_AMOUNT_A_TO_B, description: "Invoice description", + invoiceExpiryDeltaSecs: 60, minFinalCltvExpiryDelta: 24) + if let invoiceError = invoiceResult.getError() { + let creationError = invoiceError.getValueAsCreationError() + print("creation error: \(creationError)") + } + let invoice = invoiceResult.getValue()! + print("Invoice: \(invoice.toStr())") + + let recreatedInvoice = Bolt11Invoice.fromStr(s: invoice.toStr()) + XCTAssertTrue(recreatedInvoice.isOk()) + + // find route + + do { + let payerPubkey = peer1.channelManager.getOurNodeId() + let payeePubkey = peer2.channelManager.getOurNodeId() + let paymentParameters = PaymentParameters.initForKeysend( + payeePubkey: payeePubkey, finalCltvExpiryDelta: 3, allowMpp: false) + + let amount = invoice.amountMilliSatoshis()! + let routeParameters = RouteParameters( + paymentParamsArg: paymentParameters, finalValueMsatArg: amount, maxTotalRoutingFeeMsatArg: nil) + let randomSeedBytes: [UInt8] = [UInt8](repeating: 0, count: 32) + let scoringParams = ProbabilisticScoringDecayParameters.initWithDefault() + let networkGraph = peer1.constructor!.netGraph! + let scorer = ProbabilisticScorer(decayParams: scoringParams, networkGraph: networkGraph, logger: logger) + let score = scorer.asScoreLookUp() + + let scoreParams = ProbabilisticScoringFeeParameters.initWithDefault() + + let foundRoute = Bindings.findRoute( + ourNodePubkey: payerPubkey, + routeParams: routeParameters, + networkGraph: networkGraph, + firstHops: usableChannelsA, + logger: logger, + scorer: score, + scoreParams: scoreParams, + randomSeedBytes: randomSeedBytes + ) + + let route = foundRoute.getValue()! + let fees = route.getTotalFees() + print("found route fees: \(fees)") + } + + let channelManagerConstructor = peer1.constructor! + + let (paymentHash, recipientOnion, routeParameters) = Bindings.paymentParametersFromInvoice(invoice: invoice) + .getValue()! + let paymentId = invoice.paymentHash()! + + + let invoicePaymentResult = channelManagerConstructor.channelManager.sendPayment( + paymentHash: paymentHash, recipientOnion: recipientOnion, paymentId: paymentId, + routeParams: routeParameters, retryStrategy: Bindings.Retry.initWithAttempts(a: 3)) + // let invoicePaymentResult = Bindings.payInvoice(invoice: invoice, retryStrategy: Bindings.Retry.initWithAttempts(a: 3), channelmanager: channelManagerConstructor.channelManager) + XCTAssertTrue(invoicePaymentResult.isOk()) + + do { + // process HTLCs + let peer2Event = try! await peer2.getManagerEvents(expectedCount: 1)[0] + guard case .PendingHTLCsForwardable = peer2Event.getValueType() else { + return XCTAssert(false, "Expected .PendingHTLCsForwardable, got \(peer2Event.getValueType())") + } + let pendingHTLCsForwardable = peer2Event.getValueAsPendingHtlcsForwardable()! + print("forwardable time: \(pendingHTLCsForwardable.getTimeForwardable())") + peer2.channelManager.processPendingHtlcForwards() + print("processed pending HTLC forwards") + } + + do { + // process payment + let peer2Event = try! await peer2.getManagerEvents(expectedCount: 1)[0] + guard case .PaymentClaimable = peer2Event.getValueType() else { + return XCTAssert(false, "Expected .PaymentReceived, got \(peer2Event.getValueType())") + } + let paymentClaimable = peer2Event.getValueAsPaymentClaimable()! + let paymentHash = paymentClaimable.getPaymentHash() + print("received payment of \(paymentClaimable.getAmountMsat()) with hash \(paymentHash)") + let paymentPurpose = paymentClaimable.getPurpose() + guard case .Bolt11InvoicePayment = paymentPurpose.getValueType() else { + return XCTAssert(false, "Expected .InvoicePayment, got \(paymentPurpose.getValueType())") + } + let invoicePayment = paymentPurpose.getValueAsBolt11InvoicePayment()! + let preimage = invoicePayment.getPaymentPreimage()! + let secret = invoicePayment.getPaymentSecret() + if self.configuration.shouldRecipientRejectPayment { + print("about to fail payment because shouldRecipientRejectPayment flag is set") + peer2.channelManager.failHtlcBackwards(paymentHash: paymentHash) + print("deliberately failed payment with hash \(paymentHash)") + for _ in 0..<10 { + // this may have a random delay, run it repeatedly + peer2.channelManager.processPendingHtlcForwards() + try! await Task.sleep(nanoseconds: 0_100_000_000) + } + } else { + peer2.channelManager.claimFunds(paymentPreimage: preimage) + print("claimed payment with secret \(secret) using preimage \(preimage)") + } + } + + if self.configuration.shouldRecipientRejectPayment { + let peer1Events = try! await peer1.getManagerEvents(expectedCount: 1) + let paymentPathFailedEvent = peer1Events[0] + guard case .PaymentPathFailed = paymentPathFailedEvent.getValueType() else { + return XCTAssert(false, "Expected .PaymentPathFailed, got \(paymentPathFailedEvent.getValueType())") + } + let paymentPathFailed = paymentPathFailedEvent.getValueAsPaymentPathFailed()! + let failureDescriptor: [String: Any] = [ + "payment_id": paymentPathFailed.getPaymentId(), + "payment_hash": paymentPathFailed.getPaymentHash(), + "payment_failed_permanently": paymentPathFailed.getPaymentFailedPermanently(), + "short_channel_id": paymentPathFailed.getShortChannelId(), + "path": paymentPathFailed.getPath().getHops().map { $0.getShortChannelId() }, + "failure": paymentPathFailed.getFailure(), + ] + + print("payent path failure: \(failureDescriptor)") + print("here") + return + + } else { + // process payment + let peer1Events = try! await peer1.getManagerEvents(expectedCount: 2) + let paymentSentEvent = peer1Events[0] + let paymentPathSuccessfulEvent = peer1Events[1] + guard case .PaymentSent = paymentSentEvent.getValueType() else { + return XCTAssert(false, "Expected .PaymentSent, got \(paymentSentEvent.getValueType())") + } + guard case .PaymentPathSuccessful = paymentPathSuccessfulEvent.getValueType() else { + return XCTAssert( + false, "Expected .PaymentPathSuccessful, got \(paymentPathSuccessfulEvent.getValueType())") + } + let paymentSent = paymentSentEvent.getValueAsPaymentSent()! + let paymentPathSuccessful = paymentPathSuccessfulEvent.getValueAsPaymentPathSuccessful()! + print( + "sent payment \(paymentSent.getPaymentId()) with fee \(paymentSent.getFeePaidMsat()) via \(paymentPathSuccessful.getPath().getHops().map { h in h.getShortChannelId() })" + ) + } + + var currentChannelABalance = secondChannelBalanceAToB + var currentChannelBBalance = secondChannelBalanceBToA + while true { + let channelA = peer1.channelManager.listUsableChannels()[0] + let channelB = peer2.channelManager.listUsableChannels()[0] + currentChannelABalance = channelA.getBalanceMsat() + currentChannelBBalance = channelB.getBalanceMsat() + if currentChannelABalance != secondChannelBalanceAToB + && currentChannelBBalance != secondChannelBalanceBToA + { + break + } + // sleep for 100ms + try! await Task.sleep(nanoseconds: 0_100_000_000) + } + + + // let invoicePayment = invoicePaymentResult.getValue()! + XCTAssert(invoicePaymentResult.isOk()) + XCTAssertEqual(currentChannelABalance, secondChannelBalanceAToB - SEND_MSAT_AMOUNT_A_TO_B) + XCTAssertEqual(currentChannelBBalance, secondChannelBalanceBToA + SEND_MSAT_AMOUNT_A_TO_B) + } + + do { + // send some money back + // create invoice for 10k satoshis to pay to peer2 + let prePaymentBalanceAToB = peer1.channelManager.listUsableChannels()[0].getBalanceMsat() + let prePaymentBalanceBToA = peer2.channelManager.listUsableChannels()[0].getBalanceMsat() + print("pre-payment balance A->B mSats: \(prePaymentBalanceAToB)") + print("pre-payment balance B->A mSats: \(prePaymentBalanceBToA)") + + let invoiceResult = Bindings.createInvoiceFromChannelmanager( + channelmanager: peer1.channelManager, nodeSigner: peer1.explicitKeysManager.asNodeSigner(), + logger: logger, network: .Bitcoin, amtMsat: nil, description: "Second invoice description", + invoiceExpiryDeltaSecs: 60, minFinalCltvExpiryDelta: 24) + let invoice = invoiceResult.getValue()! + print("Implicit amount invoice: \(invoice.toStr())") let invoiceString = invoice.toStr() - let recreatedInvoice = Bolt11Invoice.fromStr(s: invoiceString) - XCTAssertTrue(recreatedInvoice.isOk()) - - - let (paymentHash, recipientOnion, routeParameters) = Bindings.paymentParametersFromZeroAmountInvoice(invoice: invoice, amountMsat: SEND_MSAT_AMOUNT_B_TO_A).getValue()! - let paymentId = invoice.paymentHash()! - let invoicePaymentResult = peer2.channelManager.sendPayment(paymentHash: paymentHash, recipientOnion: recipientOnion, paymentId: paymentId, routeParams: routeParameters, retryStrategy: Retry.initWithAttempts(a: 3)) - if let error = invoicePaymentResult.getError() { - print("sending error: \(error)") - } - XCTAssertTrue(invoicePaymentResult.isOk()) - - do { - // process HTLCs - let peer1Event = try! await peer1.getManagerEvents(expectedCount: 1)[0] - guard case .PendingHTLCsForwardable = peer1Event.getValueType() else { - return XCTAssert(false, "Expected .PendingHTLCsForwardable, got \(peer1Event.getValueType())") - } - let pendingHTLCsForwardable = peer1Event.getValueAsPendingHtlcsForwardable()! - print("forwardable time: \(pendingHTLCsForwardable.getTimeForwardable())") - peer1.channelManager.processPendingHtlcForwards() - print("processed pending HTLC forwards") - } - - do { - // process payment - let peer1Event = try! await peer1.getManagerEvents(expectedCount: 1)[0] - guard case .PaymentClaimable = peer1Event.getValueType() else { - return XCTAssert(false, "Expected .PaymentReceived, got \(peer1Event.getValueType())") - } - let paymentClaimable = peer1Event.getValueAsPaymentClaimable()! - let paymentHash = paymentClaimable.getPaymentHash() - print("received payment of \(paymentClaimable.getAmountMsat()) with hash \(paymentHash)") - let paymentPurpose = paymentClaimable.getPurpose() - guard case .Bolt11InvoicePayment = paymentPurpose.getValueType() else { - return XCTAssert(false, "Expected .InvoicePayment, got \(paymentPurpose.getValueType())") - } - let invoicePayment = paymentPurpose.getValueAsBolt11InvoicePayment()! - let preimage = invoicePayment.getPaymentPreimage()! - let secret = invoicePayment.getPaymentSecret() - peer1.channelManager.claimFunds(paymentPreimage: preimage) - print("claimed payment with secret \(secret) using preimage \(preimage)") - } - - do { - // process payment - let peer2Events = try! await peer2.getManagerEvents(expectedCount: 3) - print("received event count: \(peer2Events.count)") - let paymentClaimedEvent = peer2Events[0] - let paymentSentEvent = peer2Events[1] - let paymentPathSuccessfulEvent = peer2Events[2] - guard case .PaymentClaimed = paymentClaimedEvent.getValueType() else { - return XCTAssert(false, "Expected .PaymentClaimed, got \(paymentClaimedEvent.getValueType())") - } - guard case .PaymentSent = paymentSentEvent.getValueType() else { - return XCTAssert(false, "Expected .PaymentSent, got \(paymentSentEvent.getValueType())") - } - guard case .PaymentPathSuccessful = paymentPathSuccessfulEvent.getValueType() else { - return XCTAssert(false, "Expected .PaymentPathSuccessful, got \(paymentPathSuccessfulEvent.getValueType())") - } - let paymentClaimed = paymentClaimedEvent.getValueAsPaymentClaimed()! - let paymentSent = paymentSentEvent.getValueAsPaymentSent()! - let paymentPathSuccessful = paymentPathSuccessfulEvent.getValueAsPaymentPathSuccessful()! - print("sent payment \(paymentSent.getPaymentId()) worth \(paymentClaimed.getAmountMsat()) with fee \(paymentSent.getFeePaidMsat()) via \(paymentPathSuccessful.getPath().getHops().map { h in h.getShortChannelId() })") - } - - var currentChannelABalance = prePaymentBalanceAToB - var currentChannelBBalance = prePaymentBalanceBToA - while true { - let channelA = peer1.channelManager.listUsableChannels()[0] - let channelB = peer2.channelManager.listUsableChannels()[0] - currentChannelABalance = channelA.getBalanceMsat() - currentChannelBBalance = channelB.getBalanceMsat() - if currentChannelABalance != prePaymentBalanceAToB && currentChannelBBalance != prePaymentBalanceBToA { - break - } - // sleep for 100ms - try! await Task.sleep(nanoseconds: 0_100_000_000) - } - - // let invoicePayment = invoicePaymentResult.getValue()! - XCTAssert(invoicePaymentResult.isOk()) - XCTAssertEqual(currentChannelABalance, prePaymentBalanceAToB + SEND_MSAT_AMOUNT_B_TO_A) - XCTAssertEqual(currentChannelBBalance, prePaymentBalanceBToA - SEND_MSAT_AMOUNT_B_TO_A) - XCTAssertEqual(currentChannelABalance, secondChannelBalanceAToB - SEND_MSAT_AMOUNT_A_TO_B + SEND_MSAT_AMOUNT_B_TO_A) - XCTAssertEqual(currentChannelBBalance, secondChannelBalanceBToA + SEND_MSAT_AMOUNT_A_TO_B - SEND_MSAT_AMOUNT_B_TO_A) - } - - - do { - // sleep for 5 seconds to ensure sanity - try! await Task.sleep(nanoseconds: 5_000_000_000) - - peer1.constructor?.interrupt() + let recreatedInvoice = Bolt11Invoice.fromStr(s: invoiceString) + XCTAssertTrue(recreatedInvoice.isOk()) + + + let (paymentHash, recipientOnion, routeParameters) = + Bindings.paymentParametersFromZeroAmountInvoice(invoice: invoice, amountMsat: SEND_MSAT_AMOUNT_B_TO_A) + .getValue()! + let paymentId = invoice.paymentHash()! + let invoicePaymentResult = peer2.channelManager.sendPayment( + paymentHash: paymentHash, recipientOnion: recipientOnion, paymentId: paymentId, + routeParams: routeParameters, retryStrategy: Retry.initWithAttempts(a: 3)) + if let error = invoicePaymentResult.getError() { + print("sending error: \(error)") + } + XCTAssertTrue(invoicePaymentResult.isOk()) + + do { + // process HTLCs + let peer1Event = try! await peer1.getManagerEvents(expectedCount: 1)[0] + guard case .PendingHTLCsForwardable = peer1Event.getValueType() else { + return XCTAssert(false, "Expected .PendingHTLCsForwardable, got \(peer1Event.getValueType())") + } + let pendingHTLCsForwardable = peer1Event.getValueAsPendingHtlcsForwardable()! + print("forwardable time: \(pendingHTLCsForwardable.getTimeForwardable())") + peer1.channelManager.processPendingHtlcForwards() + print("processed pending HTLC forwards") + } + + do { + // process payment + let peer1Event = try! await peer1.getManagerEvents(expectedCount: 1)[0] + guard case .PaymentClaimable = peer1Event.getValueType() else { + return XCTAssert(false, "Expected .PaymentReceived, got \(peer1Event.getValueType())") + } + let paymentClaimable = peer1Event.getValueAsPaymentClaimable()! + let paymentHash = paymentClaimable.getPaymentHash() + print("received payment of \(paymentClaimable.getAmountMsat()) with hash \(paymentHash)") + let paymentPurpose = paymentClaimable.getPurpose() + guard case .Bolt11InvoicePayment = paymentPurpose.getValueType() else { + return XCTAssert(false, "Expected .InvoicePayment, got \(paymentPurpose.getValueType())") + } + let invoicePayment = paymentPurpose.getValueAsBolt11InvoicePayment()! + let preimage = invoicePayment.getPaymentPreimage()! + let secret = invoicePayment.getPaymentSecret() + peer1.channelManager.claimFunds(paymentPreimage: preimage) + print("claimed payment with secret \(secret) using preimage \(preimage)") + } + + do { + // process payment + let peer2Events = try! await peer2.getManagerEvents(expectedCount: 3) + print("received event count: \(peer2Events.count)") + let paymentClaimedEvent = peer2Events[0] + let paymentSentEvent = peer2Events[1] + let paymentPathSuccessfulEvent = peer2Events[2] + guard case .PaymentClaimed = paymentClaimedEvent.getValueType() else { + return XCTAssert(false, "Expected .PaymentClaimed, got \(paymentClaimedEvent.getValueType())") + } + guard case .PaymentSent = paymentSentEvent.getValueType() else { + return XCTAssert(false, "Expected .PaymentSent, got \(paymentSentEvent.getValueType())") + } + guard case .PaymentPathSuccessful = paymentPathSuccessfulEvent.getValueType() else { + return XCTAssert( + false, "Expected .PaymentPathSuccessful, got \(paymentPathSuccessfulEvent.getValueType())") + } + let paymentClaimed = paymentClaimedEvent.getValueAsPaymentClaimed()! + let paymentSent = paymentSentEvent.getValueAsPaymentSent()! + let paymentPathSuccessful = paymentPathSuccessfulEvent.getValueAsPaymentPathSuccessful()! + print( + "sent payment \(paymentSent.getPaymentId()) worth \(paymentClaimed.getAmountMsat()) with fee \(paymentSent.getFeePaidMsat()) via \(paymentPathSuccessful.getPath().getHops().map { h in h.getShortChannelId() })" + ) + } + + var currentChannelABalance = prePaymentBalanceAToB + var currentChannelBBalance = prePaymentBalanceBToA + while true { + let channelA = peer1.channelManager.listUsableChannels()[0] + let channelB = peer2.channelManager.listUsableChannels()[0] + currentChannelABalance = channelA.getBalanceMsat() + currentChannelBBalance = channelB.getBalanceMsat() + if currentChannelABalance != prePaymentBalanceAToB && currentChannelBBalance != prePaymentBalanceBToA { + break + } + // sleep for 100ms + try! await Task.sleep(nanoseconds: 0_100_000_000) + } + + // let invoicePayment = invoicePaymentResult.getValue()! + XCTAssert(invoicePaymentResult.isOk()) + XCTAssertEqual(currentChannelABalance, prePaymentBalanceAToB + SEND_MSAT_AMOUNT_B_TO_A) + XCTAssertEqual(currentChannelBBalance, prePaymentBalanceBToA - SEND_MSAT_AMOUNT_B_TO_A) + XCTAssertEqual( + currentChannelABalance, secondChannelBalanceAToB - SEND_MSAT_AMOUNT_A_TO_B + SEND_MSAT_AMOUNT_B_TO_A) + XCTAssertEqual( + currentChannelBBalance, secondChannelBalanceBToA + SEND_MSAT_AMOUNT_A_TO_B - SEND_MSAT_AMOUNT_B_TO_A) + } + + + do { + // sleep for 5 seconds to ensure sanity + try! await Task.sleep(nanoseconds: 5_000_000_000) + + peer1.constructor?.interrupt() peer2.constructor?.interrupt() - } + } - } + } } diff --git a/ci/LDKSwift/Tests/LDKSwiftTests/LDKSwiftTests.swift b/ci/LDKSwift/Tests/LDKSwiftTests/LDKSwiftTests.swift index 4dc5ad1d..36777262 100644 --- a/ci/LDKSwift/Tests/LDKSwiftTests/LDKSwiftTests.swift +++ b/ci/LDKSwift/Tests/LDKSwiftTests/LDKSwiftTests.swift @@ -5,82 +5,102 @@ // Created by Arik Sosman on 7/27/21. // +import XCTest + #if SWIFT_PACKAGE -import LDKSwift -import LDKHeaders + import LDKSwift + import LDKHeaders #endif -import XCTest class LDKSwiftTests: XCTestCase { - override func setUpWithError() throws { - // Put setup code here. This method is called before the invocation of each test method in the class. - Bindings.setLogThreshold(severity: .DEBUG) - } - - override func tearDownWithError() throws { - // Put teardown code here. This method is called after the invocation of each test method in the class. - } - - func testExample() throws { - // This is an example of a functional test case. - // Use XCTAssert and related functions to verify your tests produce the correct results. - } - - func testVersionSanity() throws { - check_get_ldk_version() - check_get_ldk_bindings_version() - Bindings.getLDKSwiftBindingsSerializationHash() - Bindings.getLDKSwiftBindingsVersion() - Bindings.getLDKSwiftBindingsCommitHash() - Bindings.ldkGetCompiledVersion() - Bindings.ldkCBindingsGetCompiledVersion() - // check_platform() - } - - private func incrementalMemoryLeakTest() throws { - let filter = TestFilter() - let broadcaster = TestBroadcasterInterface() - let logger = TestLogger() - let feeEstimator = TestFeeEstimator() - let persister = TestPersister() - - let chainMonitor = ChainMonitor(chainSource: filter, broadcaster: broadcaster, logger: logger, feeest: feeEstimator, persister: persister) - - let seed: [UInt8] = [UInt8](Data(base64Encoded: "//////////////////////////////////////////8=")!) - let timestamp_seconds = UInt64(NSDate().timeIntervalSince1970) - let timestamp_nanos = UInt32(truncating: NSNumber(value: timestamp_seconds * 1000 * 1000)) - - let keysManager = KeysManager(seed: seed, startingTimeSecs: timestamp_seconds, startingTimeNanos: timestamp_nanos) - let config = UserConfig.initWithDefault() - - let serialized_channel_manager = LDKTestFixtures.serializedChannelManager - - let serializedChannelMonitors: [[UInt8]] = LDKTestFixtures.serializedChannelMonitors - - var monitors: [LDKChannelMonitor] = [] - - let constructionParameters = ChannelManagerConstructionParameters( - config: config, - entropySource: keysManager.asEntropySource(), - nodeSigner: keysManager.asNodeSigner(), - signerProvider: keysManager.asSignerProvider(), - feeEstimator: feeEstimator, - chainMonitor: chainMonitor, - txBroadcaster: broadcaster, - logger: logger - ) - - let networkGraph = NetworkGraph(network: .Regtest, logger: logger) - let channelManagerConstructor = try ChannelManagerConstructor(channelManagerSerialized: serialized_channel_manager, channelMonitorsSerialized: serializedChannelMonitors, networkGraph: NetworkGraphArgument.instance(networkGraph), filter: filter, params: constructionParameters, logger: logger) - - let channelManager = channelManagerConstructor.channelManager; - let cmPersister = TestChannelManagerPersister(channelManager: channelManager) - - let header = Self.hexStringToBytes(hexString: "f5591ea0b69ae3edc0de11497ffb0fdd91f769ede96c5d662c805364e9bf8b2243e8e5b9d1833eff7cb19abd9fc9da3cd26fe84d718bbf8a336966ae4f7dea6a81372961ffff7f200400000001020000") - channelManager.asConfirm().transactionsConfirmed(header: header, txdata: [(2, Self.hexStringToBytes(hexString: "020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03530101ffffffff0200f2052a0100000017a9149e6d815a46cd349527961f58cc20d41d15fcb99e870000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000")!)], height: 525) - - /* + override func setUpWithError() throws { + // Put setup code here. This method is called before the invocation of each test method in the class. + Bindings.setLogThreshold(severity: .DEBUG) + } + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testExample() throws { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct results. + } + + func testVersionSanity() throws { + check_get_ldk_version() + check_get_ldk_bindings_version() + Bindings.getLDKSwiftBindingsSerializationHash() + Bindings.getLDKSwiftBindingsVersion() + Bindings.getLDKSwiftBindingsCommitHash() + Bindings.ldkGetCompiledVersion() + Bindings.ldkCBindingsGetCompiledVersion() + // check_platform() + } + + private func incrementalMemoryLeakTest() throws { + let filter = TestFilter() + let broadcaster = TestBroadcasterInterface() + let logger = TestLogger() + let feeEstimator = TestFeeEstimator() + let persister = TestPersister() + + let chainMonitor = ChainMonitor( + chainSource: filter, broadcaster: broadcaster, logger: logger, feeest: feeEstimator, persister: persister) + + let seed: [UInt8] = [UInt8](Data(base64Encoded: "//////////////////////////////////////////8=")!) + let timestamp_seconds = UInt64(NSDate().timeIntervalSince1970) + let timestamp_nanos = UInt32(truncating: NSNumber(value: timestamp_seconds * 1000 * 1000)) + + let keysManager = KeysManager( + seed: seed, startingTimeSecs: timestamp_seconds, startingTimeNanos: timestamp_nanos) + let config = UserConfig.initWithDefault() + + let serialized_channel_manager = LDKTestFixtures.serializedChannelManager + + let serializedChannelMonitors: [[UInt8]] = LDKTestFixtures.serializedChannelMonitors + + var monitors: [LDKChannelMonitor] = [] + + let constructionParameters = ChannelManagerConstructionParameters( + config: config, + entropySource: keysManager.asEntropySource(), + nodeSigner: keysManager.asNodeSigner(), + signerProvider: keysManager.asSignerProvider(), + feeEstimator: feeEstimator, + chainMonitor: chainMonitor, + txBroadcaster: broadcaster, + logger: logger + ) + + let networkGraph = NetworkGraph(network: .Regtest, logger: logger) + let channelManagerConstructor = try ChannelManagerConstructor( + channelManagerSerialized: serialized_channel_manager, channelMonitorsSerialized: serializedChannelMonitors, + networkGraph: NetworkGraphArgument.instance(networkGraph), filter: filter, params: constructionParameters, + logger: logger) + + let channelManager = channelManagerConstructor.channelManager + let cmPersister = TestChannelManagerPersister(channelManager: channelManager) + + let header = Self.hexStringToBytes( + hexString: + "f5591ea0b69ae3edc0de11497ffb0fdd91f769ede96c5d662c805364e9bf8b2243e8e5b9d1833eff7cb19abd9fc9da3cd26fe84d718bbf8a336966ae4f7dea6a81372961ffff7f200400000001020000" + ) + channelManager.asConfirm() + .transactionsConfirmed( + header: header, + txdata: [ + ( + 2, + Self.hexStringToBytes( + hexString: + "020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff03530101ffffffff0200f2052a0100000017a9149e6d815a46cd349527961f58cc20d41d15fcb99e870000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000" + )! + ) + ], height: 525) + + /* let amtSat: NSNumber = 2 let sendRes = payer.pay_invoice(invoice: parsedInvoiceValue) if amtSat != 0 { @@ -122,289 +142,330 @@ class LDKSwiftTests: XCTestCase { } */ - channelManagerConstructor.chainSyncCompleted(persister: cmPersister) - channelManagerConstructor.interrupt() - } + channelManagerConstructor.chainSyncCompleted(persister: cmPersister) + channelManagerConstructor.interrupt() + } - func testMemoryLeaksIncrementally() throws { - // Bindings.suspendFreedom = true - try incrementalMemoryLeakTest() - } + func testMemoryLeaksIncrementally() throws { + // Bindings.suspendFreedom = true + try incrementalMemoryLeakTest() + } - func testInvoiceSerialization() throws { - let invoiceString = "lnbc100u1p38tg4pdqlf9h8vmmfvdjjqer9wd3hy6tsw35k7msnp4qvwaqdzmlur2m5hea2da3c4zhwhyxrgxe49yrq854vqw4kckrtvygpp58qkwaky9l09g332372qnr8kcdafvrf7re9z0l5vw9xa2kvdhglfqsp5axgjhklwf08jg7w57wvlk8yksgttcxkl7rjmjy8zqzpxslme5xcs9qyysgqcqpcrzjqve0ahnleay8csatqrugw062f43cyxhxq4gj6c4a2fgr5alr84a3wp66yqqqslcqqqqqqqlgqqqqqqqqfqfjudghme9fqk4mrqmw9n2g44navk3dnvn4en8yxxf7fcwhk7wp884j43etfyc5vzp2ss6g2dgrr285kd0lmsa5mjtnzd4d583rfjl3gpprr8ru" + func testInvoiceSerialization() throws { + let invoiceString = + "lnbc100u1p38tg4pdqlf9h8vmmfvdjjqer9wd3hy6tsw35k7msnp4qvwaqdzmlur2m5hea2da3c4zhwhyxrgxe49yrq854vqw4kckrtvygpp58qkwaky9l09g332372qnr8kcdafvrf7re9z0l5vw9xa2kvdhglfqsp5axgjhklwf08jg7w57wvlk8yksgttcxkl7rjmjy8zqzpxslme5xcs9qyysgqcqpcrzjqve0ahnleay8csatqrugw062f43cyxhxq4gj6c4a2fgr5alr84a3wp66yqqqslcqqqqqqqlgqqqqqqqqfqfjudghme9fqk4mrqmw9n2g44navk3dnvn4en8yxxf7fcwhk7wp884j43etfyc5vzp2ss6g2dgrr285kd0lmsa5mjtnzd4d583rfjl3gpprr8ru" let invoiceResult = Bolt11Invoice.fromStr(s: invoiceString) XCTAssertEqual(invoiceResult.isOk(), true) guard let invoice = invoiceResult.getValue() else { return } let regeneratedInvoiceString = invoice.toStr() print("restored invoice string: \(regeneratedInvoiceString)") - - let signedRawInvoice = invoice.intoSignedRaw() - let rawInvoice = signedRawInvoice.rawInvoice() - let description = rawInvoice.description() - let descriptionString = description?.intoInner() - XCTAssertEqual(descriptionString!.getA(), "Invoice description") - - let singleLineDescriptionString = invoice.intoSignedRaw().rawInvoice().description()?.intoInner() - XCTAssertEqual(singleLineDescriptionString!.getA(), "Invoice description") + + let signedRawInvoice = invoice.intoSignedRaw() + let rawInvoice = signedRawInvoice.rawInvoice() + let description = rawInvoice.description() + let descriptionString = description?.intoInner() + XCTAssertEqual(descriptionString!.getA(), "Invoice description") + + let singleLineDescriptionString = invoice.intoSignedRaw().rawInvoice().description()?.intoInner() + XCTAssertEqual(singleLineDescriptionString!.getA(), "Invoice description") } func testWeirdChannelManagerMemoryLeak() async throws { - let reversedGenesisHashHex = "6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000" - let reversedGenesisHash = Self.hexStringToBytes(hexString: reversedGenesisHashHex)! - - let seed: [UInt8] = [UInt8](Data(base64Encoded: "//////////////////////////////////////////8=")!) - let timestamp_seconds = UInt64(NSDate().timeIntervalSince1970) - let timestamp_nanos = UInt32(truncating: NSNumber(value: timestamp_seconds * 1000 * 1000)) - - let keysManager = KeysManager(seed: seed, startingTimeSecs: timestamp_seconds, startingTimeNanos: timestamp_nanos) - - let logger = TestLogger() - - let config = UserConfig.initWithDefault() - let networkGraph = NetworkGraph(network: .Bitcoin, logger: logger) - - let decayParams = ProbabilisticScoringDecayParameters.initWithDefault() - let probabalisticScorer = ProbabilisticScorer(decayParams: decayParams, networkGraph: networkGraph, logger: logger) - let score = probabalisticScorer.asScore() - let multiThreadedScorer = MultiThreadedLockableScore(score: score) - - let feeEstimator = TestFeeEstimator() - let broadcaster = TestBroadcasterInterface() - - let channelMonitorPersister = TestPersister() - let chainMonitor = ChainMonitor(chainSource: nil, broadcaster: broadcaster, logger: logger, feeest: feeEstimator, persister: channelMonitorPersister) - - let constructionParameters = ChannelManagerConstructionParameters( - config: config, - entropySource: keysManager.asEntropySource(), - nodeSigner: keysManager.asNodeSigner(), - signerProvider: keysManager.asSignerProvider(), - feeEstimator: feeEstimator, - chainMonitor: chainMonitor, - txBroadcaster: broadcaster, - logger: logger - ) - let channelManagerConstructor = ChannelManagerConstructor(network: .Bitcoin, currentBlockchainTipHash: reversedGenesisHash, currentBlockchainTipHeight: 0, netGraph: networkGraph, params: constructionParameters) - let channelManager = channelManagerConstructor.channelManager - let peerManager = channelManagerConstructor.peerManager - let tcpPeerHandler = channelManagerConstructor.getTCPPeerHandler() - if channelManagerConstructor.netGraph != nil { - print("net graph available!") - } + let reversedGenesisHashHex = "6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000" + let reversedGenesisHash = Self.hexStringToBytes(hexString: reversedGenesisHashHex)! - let channelManagerAndNetworkGraphPersisterAndEventHandler = FloatingChannelManagerPersister(channelManager: channelManager) - } - - func testChannelCreationResultError() async throws { - let reversedGenesisHashHex = "6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000" - let reversedGenesisHash = Self.hexStringToBytes(hexString: reversedGenesisHashHex)! - - let seed: [UInt8] = [UInt8](Data(base64Encoded: "//////////////////////////////////////////8=")!) - let timestamp_seconds = UInt64(NSDate().timeIntervalSince1970) - let timestamp_nanos = UInt32(truncating: NSNumber(value: timestamp_seconds * 1000 * 1000)) - - let keysManager = KeysManager(seed: seed, startingTimeSecs: timestamp_seconds, startingTimeNanos: timestamp_nanos) - - let logger = TestLogger() - - let config = UserConfig.initWithDefault() - let lightningNetwork = LDKNetwork_Bitcoin - let networkGraph = NetworkGraph(network: .Bitcoin, logger: logger) - - let decayParams = ProbabilisticScoringDecayParameters.initWithDefault() - let probabalisticScorer = ProbabilisticScorer(decayParams: decayParams, networkGraph: networkGraph, logger: logger) - let score = probabalisticScorer.asScore() - let multiThreadedScorer = MultiThreadedLockableScore(score: score) - - let feeEstimator = TestFeeEstimator() - let broadcaster = TestBroadcasterInterface() - - let channelMonitorPersister = TestPersister() - let chainMonitor = ChainMonitor(chainSource: nil, broadcaster: broadcaster, logger: logger, feeest: feeEstimator, persister: channelMonitorPersister) - - let constructionParameters = ChannelManagerConstructionParameters( - config: config, - entropySource: keysManager.asEntropySource(), - nodeSigner: keysManager.asNodeSigner(), - signerProvider: keysManager.asSignerProvider(), - feeEstimator: feeEstimator, - chainMonitor: chainMonitor, - txBroadcaster: broadcaster, - logger: logger - ) - let channelManagerConstructor = ChannelManagerConstructor(network: .Bitcoin, currentBlockchainTipHash: reversedGenesisHash, currentBlockchainTipHeight: 0, netGraph: networkGraph, params: constructionParameters) - let channelManager = channelManagerConstructor.channelManager - - let channelValue: UInt64 = 1_300_000 // 1.3 million satoshis, or 0.013 BTC - let channelValueBtcString = "0.013" - let reserveAmount: UInt64 = 1000 // a thousand satoshis rserve - let peerPubkey = Self.hexStringToBytes(hexString: "02deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef")! - let userChannelId: [UInt8] = [UInt8](repeating: 42, count: 16); - let channelOpenResult = channelManager.createChannel(theirNetworkKey: peerPubkey, channelValueSatoshis: channelValue, pushMsat: reserveAmount, userChannelId: userChannelId, temporaryChannelId: ChannelId.initWithZero(), overrideConfig: config) - - let channelOpenError = channelOpenResult.getError()! - print("error type: \(channelOpenError.getValueType())") - - // verify that error details can be accessed without memory access issues - if let misuseError = channelOpenError.getValueAsApiMisuseError() { - print("misuse error: \(misuseError.getErr())") - } else if let unavailableError = channelOpenError.getValueAsChannelUnavailable() { - print("channel unavailable error: \(unavailableError.getErr())") - } - } + let seed: [UInt8] = [UInt8](Data(base64Encoded: "//////////////////////////////////////////8=")!) + let timestamp_seconds = UInt64(NSDate().timeIntervalSince1970) + let timestamp_nanos = UInt32(truncating: NSNumber(value: timestamp_seconds * 1000 * 1000)) + let keysManager = KeysManager( + seed: seed, startingTimeSecs: timestamp_seconds, startingTimeNanos: timestamp_nanos) - func testMainnetGraphSync() async throws { - let reversedGenesisHashHex = "6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000" - let reversedGenesisHash = Self.hexStringToBytes(hexString: reversedGenesisHashHex)! + let logger = TestLogger() - let seed: [UInt8] = [UInt8](Data(base64Encoded: "//////////////////////////////////////////8=")!) - let timestamp_seconds = UInt64(NSDate().timeIntervalSince1970) - let timestamp_nanos = UInt32(truncating: NSNumber(value: timestamp_seconds * 1000 * 1000)) - let keysManager = KeysManager(seed: seed, startingTimeSecs: timestamp_seconds, startingTimeNanos: timestamp_nanos) - - let logger = MuteLogger() - - let config = UserConfig.initWithDefault() - let lightningNetwork: Bindings.Network = .Bitcoin - let networkGraph = NetworkGraph(network: .Bitcoin, logger: logger) - - let decayParams = ProbabilisticScoringDecayParameters.initWithDefault() - let probabalisticScorer = ProbabilisticScorer(decayParams: decayParams, networkGraph: networkGraph, logger: logger) - let score = probabalisticScorer.asScore() - let multiThreadedScorer = MultiThreadedLockableScore(score: score) - - let feeEstimator = TestFeeEstimator() - let broadcaster = TestBroadcasterInterface() - - let channelMonitorPersister = TestPersister() - let chainMonitor = ChainMonitor(chainSource: nil, broadcaster: broadcaster, logger: logger, feeest: feeEstimator, persister: channelMonitorPersister) - - let constructionParameters = ChannelManagerConstructionParameters( - config: config, - entropySource: keysManager.asEntropySource(), - nodeSigner: keysManager.asNodeSigner(), - signerProvider: keysManager.asSignerProvider(), - feeEstimator: feeEstimator, - chainMonitor: chainMonitor, - txBroadcaster: broadcaster, - logger: logger - ) - let channelManagerConstructor = ChannelManagerConstructor(network: .Bitcoin, currentBlockchainTipHash: reversedGenesisHash, currentBlockchainTipHeight: 0, netGraph: networkGraph, params: constructionParameters) - let channelManager = channelManagerConstructor.channelManager - let peerManager = channelManagerConstructor.peerManager - let tcpPeerHandler = channelManagerConstructor.getTCPPeerHandler() - if channelManagerConstructor.netGraph != nil { - print("net graph available!") - } + let config = UserConfig.initWithDefault() + let networkGraph = NetworkGraph(network: .Bitcoin, logger: logger) + + let decayParams = ProbabilisticScoringDecayParameters.initWithDefault() + let probabalisticScorer = ProbabilisticScorer( + decayParams: decayParams, networkGraph: networkGraph, logger: logger) + let score = probabalisticScorer.asScore() + let multiThreadedScorer = MultiThreadedLockableScore(score: score) + + let feeEstimator = TestFeeEstimator() + let broadcaster = TestBroadcasterInterface() + + let channelMonitorPersister = TestPersister() + let chainMonitor = ChainMonitor( + chainSource: nil, broadcaster: broadcaster, logger: logger, feeest: feeEstimator, + persister: channelMonitorPersister) + + let constructionParameters = ChannelManagerConstructionParameters( + config: config, + entropySource: keysManager.asEntropySource(), + nodeSigner: keysManager.asNodeSigner(), + signerProvider: keysManager.asSignerProvider(), + feeEstimator: feeEstimator, + chainMonitor: chainMonitor, + txBroadcaster: broadcaster, + logger: logger + ) + let channelManagerConstructor = ChannelManagerConstructor( + network: .Bitcoin, currentBlockchainTipHash: reversedGenesisHash, currentBlockchainTipHeight: 0, + netGraph: networkGraph, params: constructionParameters) + let channelManager = channelManagerConstructor.channelManager + let peerManager = channelManagerConstructor.peerManager + let tcpPeerHandler = channelManagerConstructor.getTCPPeerHandler() + if channelManagerConstructor.netGraph != nil { + print("net graph available!") + } - Bindings.setLogThreshold(severity: .WARNING) + let channelManagerAndNetworkGraphPersisterAndEventHandler = FloatingChannelManagerPersister( + channelManager: channelManager) + } - // bitrefill - tcpPeerHandler.connect(address: "52.50.244.44", port: 9735, theirNodeId: Self.hexStringToBytes(hexString: "030c3f19d742ca294a55c00376b3b355c3c90d61c6b6b39554dbc7ac19b141c14f")!) + func testChannelCreationResultError() async throws { + let reversedGenesisHashHex = "6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000" + let reversedGenesisHash = Self.hexStringToBytes(hexString: reversedGenesisHashHex)! + + let seed: [UInt8] = [UInt8](Data(base64Encoded: "//////////////////////////////////////////8=")!) + let timestamp_seconds = UInt64(NSDate().timeIntervalSince1970) + let timestamp_nanos = UInt32(truncating: NSNumber(value: timestamp_seconds * 1000 * 1000)) - // River - tcpPeerHandler.connect(address: "104.196.249.140", port: 9735, theirNodeId: Self.hexStringToBytes(hexString: "03037dc08e9ac63b82581f79b662a4d0ceca8a8ca162b1af3551595b8f2d97b70a")!) + let keysManager = KeysManager( + seed: seed, startingTimeSecs: timestamp_seconds, startingTimeNanos: timestamp_nanos) - // Acinq - tcpPeerHandler.connect(address: "3.33.236.230", port: 9735, theirNodeId: Self.hexStringToBytes(hexString: "03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f")!) + let logger = TestLogger() - // Kraken - tcpPeerHandler.connect(address: "52.13.118.208", port: 9735, theirNodeId: Self.hexStringToBytes(hexString: "02f1a8c87607f415c8f22c00593002775941dea48869ce23096af27b0cfdcc0b69")!) + let config = UserConfig.initWithDefault() + let lightningNetwork = LDKNetwork_Bitcoin + let networkGraph = NetworkGraph(network: .Bitcoin, logger: logger) + + let decayParams = ProbabilisticScoringDecayParameters.initWithDefault() + let probabalisticScorer = ProbabilisticScorer( + decayParams: decayParams, networkGraph: networkGraph, logger: logger) + let score = probabalisticScorer.asScore() + let multiThreadedScorer = MultiThreadedLockableScore(score: score) + + let feeEstimator = TestFeeEstimator() + let broadcaster = TestBroadcasterInterface() + + let channelMonitorPersister = TestPersister() + let chainMonitor = ChainMonitor( + chainSource: nil, broadcaster: broadcaster, logger: logger, feeest: feeEstimator, + persister: channelMonitorPersister) + + let constructionParameters = ChannelManagerConstructionParameters( + config: config, + entropySource: keysManager.asEntropySource(), + nodeSigner: keysManager.asNodeSigner(), + signerProvider: keysManager.asSignerProvider(), + feeEstimator: feeEstimator, + chainMonitor: chainMonitor, + txBroadcaster: broadcaster, + logger: logger + ) + let channelManagerConstructor = ChannelManagerConstructor( + network: .Bitcoin, currentBlockchainTipHash: reversedGenesisHash, currentBlockchainTipHeight: 0, + netGraph: networkGraph, params: constructionParameters) + let channelManager = channelManagerConstructor.channelManager + + let channelValue: UInt64 = 1_300_000 // 1.3 million satoshis, or 0.013 BTC + let channelValueBtcString = "0.013" + let reserveAmount: UInt64 = 1000 // a thousand satoshis rserve + let peerPubkey = Self.hexStringToBytes( + hexString: "02deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef")! + let userChannelId: [UInt8] = [UInt8](repeating: 42, count: 16) + let channelOpenResult = channelManager.createChannel( + theirNetworkKey: peerPubkey, channelValueSatoshis: channelValue, pushMsat: reserveAmount, + userChannelId: userChannelId, temporaryChannelId: ChannelId.initWithZero(), overrideConfig: config) + + let channelOpenError = channelOpenResult.getError()! + print("error type: \(channelOpenError.getValueType())") + + // verify that error details can be accessed without memory access issues + if let misuseError = channelOpenError.getValueAsApiMisuseError() { + print("misuse error: \(misuseError.getErr())") + } else if let unavailableError = channelOpenError.getValueAsChannelUnavailable() { + print("channel unavailable error: \(unavailableError.getErr())") + } + } - // Matt - tcpPeerHandler.connect(address: "69.59.18.80", port: 9735, theirNodeId: Self.hexStringToBytes(hexString: "03db10aa09ff04d3568b0621750794063df401e6853c79a21a83e1a3f3b5bfb0c8")!) + func testMainnetGraphSync() async throws { + let reversedGenesisHashHex = "6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000" + let reversedGenesisHash = Self.hexStringToBytes(hexString: reversedGenesisHashHex)! - let channelManagerAndNetworkGraphPersisterAndEventHandler = TestChannelManagerPersister() - channelManagerConstructor.chainSyncCompleted(persister: channelManagerAndNetworkGraphPersisterAndEventHandler) + let seed: [UInt8] = [UInt8](Data(base64Encoded: "//////////////////////////////////////////8=")!) + let timestamp_seconds = UInt64(NSDate().timeIntervalSince1970) + let timestamp_nanos = UInt32(truncating: NSNumber(value: timestamp_seconds * 1000 * 1000)) + let keysManager = KeysManager( + seed: seed, startingTimeSecs: timestamp_seconds, startingTimeNanos: timestamp_nanos) + + let logger = MuteLogger() + + let config = UserConfig.initWithDefault() + let lightningNetwork: Bindings.Network = .Bitcoin + let networkGraph = NetworkGraph(network: .Bitcoin, logger: logger) + + let decayParams = ProbabilisticScoringDecayParameters.initWithDefault() + let probabalisticScorer = ProbabilisticScorer( + decayParams: decayParams, networkGraph: networkGraph, logger: logger) + let score = probabalisticScorer.asScore() + let multiThreadedScorer = MultiThreadedLockableScore(score: score) + + let feeEstimator = TestFeeEstimator() + let broadcaster = TestBroadcasterInterface() + + let channelMonitorPersister = TestPersister() + let chainMonitor = ChainMonitor( + chainSource: nil, broadcaster: broadcaster, logger: logger, feeest: feeEstimator, + persister: channelMonitorPersister) + + let constructionParameters = ChannelManagerConstructionParameters( + config: config, + entropySource: keysManager.asEntropySource(), + nodeSigner: keysManager.asNodeSigner(), + signerProvider: keysManager.asSignerProvider(), + feeEstimator: feeEstimator, + chainMonitor: chainMonitor, + txBroadcaster: broadcaster, + logger: logger + ) + let channelManagerConstructor = ChannelManagerConstructor( + network: .Bitcoin, currentBlockchainTipHash: reversedGenesisHash, currentBlockchainTipHeight: 0, + netGraph: networkGraph, params: constructionParameters) + let channelManager = channelManagerConstructor.channelManager + let peerManager = channelManagerConstructor.peerManager + let tcpPeerHandler = channelManagerConstructor.getTCPPeerHandler() + if channelManagerConstructor.netGraph != nil { + print("net graph available!") + } - // run this for one minute - for _ in 0..<600 { - // sleep for 100ms - try! await Task.sleep(nanoseconds: 0_100_000_000) - } + Bindings.setLogThreshold(severity: .WARNING) + + // bitrefill + tcpPeerHandler.connect( + address: "52.50.244.44", port: 9735, + theirNodeId: Self.hexStringToBytes( + hexString: "030c3f19d742ca294a55c00376b3b355c3c90d61c6b6b39554dbc7ac19b141c14f")!) + + // River + tcpPeerHandler.connect( + address: "104.196.249.140", port: 9735, + theirNodeId: Self.hexStringToBytes( + hexString: "03037dc08e9ac63b82581f79b662a4d0ceca8a8ca162b1af3551595b8f2d97b70a")!) + + // Acinq + tcpPeerHandler.connect( + address: "3.33.236.230", port: 9735, + theirNodeId: Self.hexStringToBytes( + hexString: "03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f")!) + + // Kraken + tcpPeerHandler.connect( + address: "52.13.118.208", port: 9735, + theirNodeId: Self.hexStringToBytes( + hexString: "02f1a8c87607f415c8f22c00593002775941dea48869ce23096af27b0cfdcc0b69")!) + + // Matt + tcpPeerHandler.connect( + address: "69.59.18.80", port: 9735, + theirNodeId: Self.hexStringToBytes( + hexString: "03db10aa09ff04d3568b0621750794063df401e6853c79a21a83e1a3f3b5bfb0c8")!) + + + let channelManagerAndNetworkGraphPersisterAndEventHandler = TestChannelManagerPersister() + channelManagerConstructor.chainSyncCompleted(persister: channelManagerAndNetworkGraphPersisterAndEventHandler) + + // run this for one minute + for _ in 0..<600 { + // sleep for 100ms + try! await Task.sleep(nanoseconds: 0_100_000_000) + } Bindings.setLogThreshold(severity: .DEBUG) - } - - - func testRouteConstruction() throws { - - let destPubkeyHex = "03c2abfa93eacec04721c019644584424aab2ba4dff3ac9bdab4e9c97007491dda" - let short_channel_id_arg: UInt64 = 762615767524704256 - let paymentValueMsat: UInt64 = 666000 - let finalCltvValue: UInt32 = 40 - - let routeHop = RouteHop( - pubkeyArg: Self.hexStringToBytes(hexString: destPubkeyHex)!, - nodeFeaturesArg: NodeFeatures.initWithEmpty(), - shortChannelIdArg: short_channel_id_arg, - channelFeaturesArg: ChannelFeatures.initWithEmpty(), - feeMsatArg: paymentValueMsat, - cltvExpiryDeltaArg: finalCltvValue, - maybeAnnouncedChannelArg: false - ) - - var hops: [RouteHop] = [routeHop] - - for _ in 0..<3 { - - let extraHop = RouteHop( - pubkeyArg: Self.hexStringToBytes(hexString: destPubkeyHex)!, - nodeFeaturesArg: NodeFeatures.initWithEmpty(), - shortChannelIdArg: short_channel_id_arg, - channelFeaturesArg: ChannelFeatures.initWithEmpty(), - feeMsatArg: paymentValueMsat, - cltvExpiryDeltaArg: finalCltvValue, - maybeAnnouncedChannelArg: false - ) - hops.append(extraHop) - } + } - let pubkeyBytes = Self.hexStringToBytes(hexString: destPubkeyHex)! - let paymentParams = PaymentParameters.initForKeysend(payeePubkey: pubkeyBytes, finalCltvExpiryDelta: 3, allowMpp: false) - let routeParams = RouteParameters.init(paymentParamsArg: paymentParams, finalValueMsatArg: 1_000_000, maxTotalRoutingFeeMsatArg: nil) - let blindedTail = BlindedTail(hopsArg: [], blindingPointArg: pubkeyBytes, excessFinalCltvExpiryDeltaArg: 0, finalValueMsatArg: 0) - let path = Path(hopsArg: hops, blindedTailArg: blindedTail) - let route = Route(pathsArg: [path], routeParamsArg: routeParams) - } - - #if !SWIFT_PACKAGE - public func testRapidGossipSync() async throws { - // first, download the gossip data - print("Sending rapid gossip sync request…"); - var request = URLRequest(url: URL(string: "https://rapidsync.lightningdevkit.org/snapshot/0")!) - request.httpMethod = "GET" - - let startA = DispatchTime.now() - let (data, _) = try await URLSession.shared.data(for: request) - let finishA = DispatchTime.now() - let elapsedA = Double(finishA.uptimeNanoseconds-startA.uptimeNanoseconds)/1_000_000_000 - print("Received rapid gossip sync response: \(data.count) bytes! Time: \(elapsedA)s"); - let reversedGenesisHashHex = "6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000" - let reversedGenesisHash = LDKSwiftTests.hexStringToBytes(hexString: reversedGenesisHashHex)! - - let logger = TestLogger() - let networkGraph = NetworkGraph(network: .Bitcoin, logger: logger) - let rapidSync = RapidGossipSync(networkGraph: networkGraph, logger: logger) - - let gossipDataRaw = [UInt8](data) - print("Applying rapid sync data…") - let startB = DispatchTime.now() - let timestamp = rapidSync.updateNetworkGraphNoStd(updateData: gossipDataRaw, currentTimeUnix: nil) - if let error = timestamp.getError() { - print("error! type: \(error.getValueType())") - let specificError = error.getValueAsLightningError() - print("details: \(specificError?.getErr())") + func testRouteConstruction() throws { + + let destPubkeyHex = "03c2abfa93eacec04721c019644584424aab2ba4dff3ac9bdab4e9c97007491dda" + let short_channel_id_arg: UInt64 = 762_615_767_524_704_256 + let paymentValueMsat: UInt64 = 666000 + let finalCltvValue: UInt32 = 40 + + let routeHop = RouteHop( + pubkeyArg: Self.hexStringToBytes(hexString: destPubkeyHex)!, + nodeFeaturesArg: NodeFeatures.initWithEmpty(), + shortChannelIdArg: short_channel_id_arg, + channelFeaturesArg: ChannelFeatures.initWithEmpty(), + feeMsatArg: paymentValueMsat, + cltvExpiryDeltaArg: finalCltvValue, + maybeAnnouncedChannelArg: false + ) + + var hops: [RouteHop] = [routeHop] + + for _ in 0..<3 { + + let extraHop = RouteHop( + pubkeyArg: Self.hexStringToBytes(hexString: destPubkeyHex)!, + nodeFeaturesArg: NodeFeatures.initWithEmpty(), + shortChannelIdArg: short_channel_id_arg, + channelFeaturesArg: ChannelFeatures.initWithEmpty(), + feeMsatArg: paymentValueMsat, + cltvExpiryDeltaArg: finalCltvValue, + maybeAnnouncedChannelArg: false + ) + hops.append(extraHop) } - let finishB = DispatchTime.now() - let elapsedB = Double(finishB.uptimeNanoseconds-startB.uptimeNanoseconds)/1_000_000_000 - print("Applied rapid sync data: \(timestamp.getValue())! Time: \(elapsedB)s") - /* + let pubkeyBytes = Self.hexStringToBytes(hexString: destPubkeyHex)! + let paymentParams = PaymentParameters.initForKeysend( + payeePubkey: pubkeyBytes, finalCltvExpiryDelta: 3, allowMpp: false) + let routeParams = RouteParameters.init( + paymentParamsArg: paymentParams, finalValueMsatArg: 1_000_000, maxTotalRoutingFeeMsatArg: nil) + let blindedTail = BlindedTail( + hopsArg: [], blindingPointArg: pubkeyBytes, excessFinalCltvExpiryDeltaArg: 0, finalValueMsatArg: 0) + let path = Path(hopsArg: hops, blindedTailArg: blindedTail) + let route = Route(pathsArg: [path], routeParamsArg: routeParams) + } + + #if !SWIFT_PACKAGE + public func testRapidGossipSync() async throws { + // first, download the gossip data + print("Sending rapid gossip sync request…") + var request = URLRequest(url: URL(string: "https://rapidsync.lightningdevkit.org/snapshot/0")!) + request.httpMethod = "GET" + + let startA = DispatchTime.now() + let (data, _) = try await URLSession.shared.data(for: request) + let finishA = DispatchTime.now() + let elapsedA = Double(finishA.uptimeNanoseconds - startA.uptimeNanoseconds) / 1_000_000_000 + print("Received rapid gossip sync response: \(data.count) bytes! Time: \(elapsedA)s") + + let reversedGenesisHashHex = "6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000" + let reversedGenesisHash = LDKSwiftTests.hexStringToBytes(hexString: reversedGenesisHashHex)! + + let logger = TestLogger() + let networkGraph = NetworkGraph(network: .Bitcoin, logger: logger) + let rapidSync = RapidGossipSync(networkGraph: networkGraph, logger: logger) + + let gossipDataRaw = [UInt8](data) + print("Applying rapid sync data…") + let startB = DispatchTime.now() + let timestamp = rapidSync.updateNetworkGraphNoStd(updateData: gossipDataRaw, currentTimeUnix: nil) + if let error = timestamp.getError() { + print("error! type: \(error.getValueType())") + let specificError = error.getValueAsLightningError() + print("details: \(specificError?.getErr())") + } + let finishB = DispatchTime.now() + let elapsedB = Double(finishB.uptimeNanoseconds - startB.uptimeNanoseconds) / 1_000_000_000 + print("Applied rapid sync data: \(timestamp.getValue())! Time: \(elapsedB)s") + + /* print("Measuring graph size…") let startC = DispatchTime.now() let graphBytes = networkGraph.write() @@ -413,125 +474,135 @@ class LDKSwiftTests: XCTestCase { print("Network graph size: \(graphBytes.count)! Time: \(elapsedC)s") */ - let decayParams = ProbabilisticScoringDecayParameters.initWithDefault() - let scorer = ProbabilisticScorer(decayParams: decayParams, networkGraph: networkGraph, logger: logger) - let score = scorer.asScore() - // let lockableScore = LockableScore() - // let defaultRouter = DefaultRouter(network_graph: networkGraph, logger: logger, random_seed_bytes: [UInt8](repeating: 0, count: 32), scorer: lockableScore) - // let router = defaultRouter.as_Router() + let decayParams = ProbabilisticScoringDecayParameters.initWithDefault() + let scorer = ProbabilisticScorer(decayParams: decayParams, networkGraph: networkGraph, logger: logger) + let score = scorer.asScore() + // let lockableScore = LockableScore() + // let defaultRouter = DefaultRouter(network_graph: networkGraph, logger: logger, random_seed_bytes: [UInt8](repeating: 0, count: 32), scorer: lockableScore) + // let router = defaultRouter.as_Router() + + // let multiThreadedScorer = MultiThreadedLockableScore(score: score) + + + let payerPubkey = LDKSwiftTests.hexStringToBytes( + hexString: "0242a4ae0c5bef18048fbecf995094b74bfb0f7391418d71ed394784373f41e4f3")! + let recipientPubkey = LDKSwiftTests.hexStringToBytes( + hexString: "03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f")! + + let paymentParameters = PaymentParameters.initForKeysend( + payeePubkey: recipientPubkey, finalCltvExpiryDelta: 3, allowMpp: false) + let routeParameters = RouteParameters( + paymentParamsArg: paymentParameters, finalValueMsatArg: 500, maxTotalRoutingFeeMsatArg: nil) + + print("STEP A") + + let firstHops: [ChannelDetails]? = nil + print("STEP B") + let randomSeedBytes: [UInt8] = [UInt8](repeating: 0, count: 32) + let scoreParams = ProbabilisticScoringFeeParameters.initWithDefault() + let foundRoute = Bindings.findRoute( + ourNodePubkey: payerPubkey, routeParams: routeParameters, networkGraph: networkGraph, firstHops: [], + logger: logger, scorer: score.getScoreLookUp(), scoreParams: scoreParams, + randomSeedBytes: randomSeedBytes) + // let foundRoute = router.find_route(payer: payerPubkey, route_params: routeParameters, payment_hash: nil, first_hops: firstHops, inflight_htlcs: <#T##InFlightHtlcs#>) + + if let routeError = foundRoute.getError() { + print("routing error: \(routeError.getErr())") + } + + if let route = foundRoute.getValue() { + let paths = route.getPaths() + print("found route with \(paths.count) paths!") + for currentPath in paths { + print("\nPath Option:") + for currentHop in currentPath.getHops() { + print( + "scid: \(currentHop.getShortChannelId()), pubkey: \(currentHop.getPubkey()), fee (msat): \(currentHop.getFeeMsat()), CLTV delta: \(currentHop.getCltvExpiryDelta())" + ) + } + } + } + + } + #endif - // let multiThreadedScorer = MultiThreadedLockableScore(score: score) + func testExtendedActivity() async throws { + // for i in 0...(1 << 7) { + let combinationCount = HumanObjectPeerTestInstance.Configuration.combinationCount() + for i in 0..) + config.useRouter = (i & (1 << 2)) != 0 + print("useRouter: \(config.useRouter)") - if let routeError = foundRoute.getError() { - print("routing error: \(routeError.getErr())") - } + config.useWrappedSignerProvider = (i & (1 << 3)) != 0 + print("useWrappedSignerProvider: \(config.useWrappedSignerProvider)") - if let route = foundRoute.getValue() { - let paths = route.getPaths() - print("found route with \(paths.count) paths!") - for currentPath in paths { - print("\nPath Option:") - for currentHop in currentPath.getHops() { - print("scid: \(currentHop.getShortChannelId()), pubkey: \(currentHop.getPubkey()), fee (msat): \(currentHop.getFeeMsat()), CLTV delta: \(currentHop.getCltvExpiryDelta())") - } - } - } + config.ephemeralNetworkGraphForScorer = (i & (1 << 4)) != 0 + print("ephemeralNetworkGraphForScorer: \(config.ephemeralNetworkGraphForScorer)") - } - #endif + config.reserializedProbabilisticScorer = (i & (1 << 5)) != 0 + print("reserializedProbabilisticScorer: \(config.reserializedProbabilisticScorer)") - func testExtendedActivity() async throws { - // for i in 0...(1 << 7) { - let combinationCount = HumanObjectPeerTestInstance.Configuration.combinationCount() - for i in 0.. [UInt8]? { - let hexStr = hexString.dropFirst(hexString.hasPrefix("0x") ? 2 : 0) + internal class func hexStringToBytes(hexString: String) -> [UInt8]? { + let hexStr = hexString.dropFirst(hexString.hasPrefix("0x") ? 2 : 0) - guard hexStr.count % 2 == 0 else { - return nil - } + guard hexStr.count % 2 == 0 else { + return nil + } - var newData = [UInt8]() + var newData = [UInt8]() + + var indexIsEven = true + for i in hexStr.indices { + if indexIsEven { + let byteRange = i...hexStr.index(after: i) + guard let byte = UInt8(hexStr[byteRange], radix: 16) else { + return nil + } + newData.append(byte) + } + indexIsEven.toggle() + } + return newData + } - var indexIsEven = true - for i in hexStr.indices { - if indexIsEven { - let byteRange = i...hexStr.index(after: i) - guard let byte = UInt8(hexStr[byteRange], radix: 16) else { - return nil - } - newData.append(byte) - } - indexIsEven.toggle() - } - return newData - } - - internal class func bytesToHexString(bytes: [UInt8]) -> String { - let format = "%02hhx" // "%02hhX" (uppercase) - return bytes.map { - String(format: format, $0) - }.joined() - } + internal class func bytesToHexString(bytes: [UInt8]) -> String { + let format = "%02hhx" // "%02hhX" (uppercase) + return + bytes.map { + String(format: format, $0) + } + .joined() + } } diff --git a/ci/LDKSwift/Tests/LDKSwiftTests/LDKTestFixtures.swift b/ci/LDKSwift/Tests/LDKSwiftTests/LDKTestFixtures.swift index 58aaf7ca..62516887 100644 --- a/ci/LDKSwift/Tests/LDKSwiftTests/LDKTestFixtures.swift +++ b/ci/LDKSwift/Tests/LDKSwiftTests/LDKTestFixtures.swift @@ -8,20 +8,625 @@ import Foundation class LDKTestFixtures { - - static var serializedChannelManager: [UInt8] = [1, 1, 6, 34, 110, 70, 17, 26, 11, 89, 202, 175, 18, 96, 67, 235, 91, 191, 40, 195, 79, 58, 94, 51, 42, 31, 199, 178, 183, 60, 241, 136, 145, 15, 0, 0, 2, 12, 245, 89, 30, 160, 182, 154, 227, 237, 192, 222, 17, 73, 127, 251, 15, 221, 145, 247, 105, 237, 233, 108, 93, 102, 44, 128, 83, 100, 233, 191, 139, 34, 0, 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0, 0, 42, 0, 0, 0, 0, 0, 72, 0, 1, 191, 159, 45, 109, 250, 168, 172, 172, 111, 136, 62, 120, 42, 195, 204, 192, 75, 139, 45, 252, 75, 128, 100, 201, 101, 164, 34, 25, 123, 118, 72, 238, 0, 0, 0, 192, 0, 0, 0, 0, 0, 61, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 135, 1, 1, 147, 230, 63, 191, 15, 83, 126, 83, 56, 77, 244, 16, 203, 26, 120, 61, 110, 159, 96, 102, 208, 42, 82, 219, 25, 231, 84, 15, 52, 140, 28, 78, 57, 168, 53, 136, 69, 225, 211, 254, 158, 192, 210, 35, 192, 195, 39, 6, 152, 15, 119, 215, 69, 211, 64, 46, 92, 100, 212, 137, 250, 191, 196, 128, 221, 81, 68, 184, 94, 227, 133, 148, 123, 187, 3, 192, 142, 63, 75, 230, 135, 110, 89, 69, 117, 192, 101, 143, 160, 138, 107, 234, 215, 174, 191, 49, 194, 8, 77, 175, 34, 220, 38, 229, 18, 48, 135, 192, 16, 201, 249, 9, 213, 46, 208, 175, 250, 76, 229, 139, 241, 13, 19, 19, 47, 101, 254, 35, 74, 113, 245, 15, 196, 5, 95, 129, 67, 217, 245, 156, 121, 83, 201, 81, 20, 242, 100, 71, 120, 169, 162, 58, 109, 139, 107, 107, 139, 16, 218, 193, 80, 220, 107, 17, 18, 1, 217, 220, 76, 95, 220, 67, 170, 89, 170, 51, 61, 254, 8, 52, 100, 179, 249, 248, 78, 227, 128, 36, 42, 148, 113, 160, 253, 1, 154, 253, 1, 150, 0, 176, 175, 0, 33, 3, 65, 81, 5, 242, 6, 238, 79, 27, 32, 59, 118, 145, 167, 132, 137, 105, 177, 225, 227, 93, 193, 58, 227, 5, 53, 171, 42, 71, 159, 54, 228, 137, 2, 33, 2, 203, 126, 6, 106, 235, 15, 28, 5, 68, 63, 181, 110, 193, 14, 133, 104, 247, 56, 151, 127, 3, 206, 216, 112, 103, 135, 168, 4, 221, 40, 233, 168, 4, 33, 2, 199, 21, 85, 32, 48, 247, 98, 188, 50, 244, 157, 5, 216, 110, 59, 131, 207, 158, 110, 240, 52, 37, 9, 92, 74, 186, 97, 0, 75, 87, 159, 137, 6, 33, 3, 224, 171, 107, 195, 109, 139, 39, 25, 93, 126, 4, 226, 210, 240, 59, 87, 50, 46, 230, 140, 144, 21, 16, 139, 180, 199, 19, 170, 97, 177, 150, 42, 8, 33, 3, 228, 157, 142, 206, 28, 67, 191, 144, 5, 125, 60, 105, 113, 179, 143, 252, 28, 51, 34, 73, 180, 64, 176, 121, 58, 0, 218, 153, 216, 218, 113, 28, 2, 2, 0, 144, 4, 1, 1, 6, 183, 182, 0, 176, 175, 0, 33, 2, 9, 208, 118, 12, 208, 192, 203, 156, 37, 75, 5, 208, 168, 90, 146, 102, 106, 104, 201, 48, 199, 244, 78, 242, 183, 129, 96, 108, 69, 110, 70, 142, 2, 33, 3, 162, 188, 220, 100, 190, 42, 74, 213, 77, 38, 152, 16, 231, 110, 166, 29, 70, 240, 14, 32, 57, 24, 175, 118, 236, 36, 75, 201, 91, 93, 62, 106, 4, 33, 2, 178, 61, 18, 201, 74, 208, 113, 218, 152, 49, 92, 52, 150, 191, 56, 239, 168, 32, 96, 86, 114, 21, 19, 108, 171, 114, 233, 40, 204, 186, 169, 154, 6, 33, 2, 177, 134, 18, 222, 96, 37, 31, 216, 61, 52, 79, 185, 165, 149, 65, 241, 204, 140, 168, 200, 2, 5, 142, 253, 20, 47, 215, 54, 34, 44, 29, 174, 8, 33, 3, 66, 55, 59, 134, 74, 73, 106, 150, 243, 60, 164, 231, 147, 235, 222, 199, 46, 227, 12, 162, 208, 119, 225, 156, 136, 68, 61, 163, 190, 101, 34, 69, 2, 2, 1, 224, 8, 34, 191, 159, 45, 109, 250, 168, 172, 172, 111, 136, 62, 120, 42, 195, 204, 192, 75, 139, 45, 252, 75, 128, 100, 201, 101, 164, 34, 25, 123, 118, 72, 239, 0, 1, 0, 0, 0, 0, 0, 61, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 97, 72, 0, 0, 0, 0, 0, 96, 191, 196, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 245, 197, 45, 161, 157, 103, 143, 155, 92, 248, 240, 63, 44, 34, 79, 54, 82, 119, 12, 72, 194, 213, 81, 200, 20, 207, 114, 193, 202, 119, 214, 206, 0, 22, 0, 20, 145, 136, 195, 71, 179, 2, 109, 136, 237, 2, 125, 149, 143, 194, 231, 176, 239, 175, 178, 93, 0, 0, 255, 255, 255, 255, 255, 254, 0, 0, 255, 255, 255, 255, 255, 254, 0, 0, 0, 0, 238, 76, 163, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 97, 1, 175, 91, 0, 0, 0, 253, 0, 33, 202, 100, 121, 48, 88, 50, 11, 177, 75, 253, 189, 162, 177, 112, 244, 153, 140, 130, 154, 40, 94, 28, 41, 10, 32, 219, 115, 52, 116, 218, 30, 50, 0, 0, 0, 209, 9, 0, 0, 209, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 2, 61, 0, 0, 0, 0, 0, 0, 1, 74, 0, 0, 0, 0, 236, 8, 206, 0, 0, 0, 0, 0, 0, 0, 156, 64, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 227, 0, 0, 0, 3, 1, 0, 0, 3, 232, 0, 0, 0, 1, 0, 40, 253, 1, 150, 0, 176, 175, 0, 33, 3, 65, 81, 5, 242, 6, 238, 79, 27, 32, 59, 118, 145, 167, 132, 137, 105, 177, 225, 227, 93, 193, 58, 227, 5, 53, 171, 42, 71, 159, 54, 228, 137, 2, 33, 2, 203, 126, 6, 106, 235, 15, 28, 5, 68, 63, 181, 110, 193, 14, 133, 104, 247, 56, 151, 127, 3, 206, 216, 112, 103, 135, 168, 4, 221, 40, 233, 168, 4, 33, 2, 199, 21, 85, 32, 48, 247, 98, 188, 50, 244, 157, 5, 216, 110, 59, 131, 207, 158, 110, 240, 52, 37, 9, 92, 74, 186, 97, 0, 75, 87, 159, 137, 6, 33, 3, 224, 171, 107, 195, 109, 139, 39, 25, 93, 126, 4, 226, 210, 240, 59, 87, 50, 46, 230, 140, 144, 21, 16, 139, 180, 199, 19, 170, 97, 177, 150, 42, 8, 33, 3, 228, 157, 142, 206, 28, 67, 191, 144, 5, 125, 60, 105, 113, 179, 143, 252, 28, 51, 34, 73, 180, 64, 176, 121, 58, 0, 218, 153, 216, 218, 113, 28, 2, 2, 0, 144, 4, 1, 1, 6, 183, 182, 0, 176, 175, 0, 33, 2, 9, 208, 118, 12, 208, 192, 203, 156, 37, 75, 5, 208, 168, 90, 146, 102, 106, 104, 201, 48, 199, 244, 78, 242, 183, 129, 96, 108, 69, 110, 70, 142, 2, 33, 3, 162, 188, 220, 100, 190, 42, 74, 213, 77, 38, 152, 16, 231, 110, 166, 29, 70, 240, 14, 32, 57, 24, 175, 118, 236, 36, 75, 201, 91, 93, 62, 106, 4, 33, 2, 178, 61, 18, 201, 74, 208, 113, 218, 152, 49, 92, 52, 150, 191, 56, 239, 168, 32, 96, 86, 114, 21, 19, 108, 171, 114, 233, 40, 204, 186, 169, 154, 6, 33, 2, 177, 134, 18, 222, 96, 37, 31, 216, 61, 52, 79, 185, 165, 149, 65, 241, 204, 140, 168, 200, 2, 5, 142, 253, 20, 47, 215, 54, 34, 44, 29, 174, 8, 33, 3, 66, 55, 59, 134, 74, 73, 106, 150, 243, 60, 164, 231, 147, 235, 222, 199, 46, 227, 12, 162, 208, 119, 225, 156, 136, 68, 61, 163, 190, 101, 34, 69, 2, 2, 1, 224, 8, 34, 191, 159, 45, 109, 250, 168, 172, 172, 111, 136, 62, 120, 42, 195, 204, 192, 75, 139, 45, 252, 75, 128, 100, 201, 101, 164, 34, 25, 123, 118, 72, 239, 0, 1, 235, 2, 0, 0, 0, 0, 1, 1, 154, 13, 188, 186, 197, 129, 42, 234, 116, 174, 71, 179, 0, 176, 61, 111, 34, 233, 34, 212, 224, 37, 185, 65, 37, 30, 244, 58, 24, 36, 229, 161, 1, 0, 0, 0, 0, 254, 255, 255, 255, 2, 224, 155, 186, 19, 1, 0, 0, 0, 22, 0, 20, 185, 142, 196, 145, 56, 80, 17, 143, 150, 44, 150, 156, 89, 129, 82, 110, 103, 155, 34, 244, 0, 9, 61, 0, 0, 0, 0, 0, 34, 0, 32, 108, 196, 111, 93, 122, 59, 15, 44, 192, 196, 18, 129, 59, 19, 54, 189, 120, 12, 193, 178, 145, 181, 5, 118, 139, 228, 118, 195, 134, 243, 160, 36, 2, 71, 48, 68, 2, 32, 20, 230, 153, 69, 79, 91, 73, 64, 131, 114, 150, 5, 36, 97, 7, 254, 202, 226, 8, 155, 55, 147, 153, 250, 248, 30, 248, 103, 247, 102, 206, 172, 2, 32, 49, 90, 216, 205, 27, 63, 31, 87, 72, 33, 68, 5, 23, 227, 144, 105, 119, 159, 121, 225, 216, 65, 222, 83, 160, 60, 96, 8, 206, 38, 27, 19, 1, 33, 2, 107, 52, 102, 240, 125, 164, 53, 66, 145, 238, 132, 161, 173, 210, 61, 144, 141, 191, 118, 52, 39, 50, 206, 252, 54, 207, 141, 246, 5, 223, 65, 139, 208, 0, 0, 0, 34, 3, 132, 66, 188, 167, 156, 18, 9, 88, 231, 15, 24, 99, 219, 187, 36, 30, 218, 94, 205, 177, 25, 135, 34, 109, 27, 211, 58, 3, 151, 121, 9, 143, 34, 3, 0, 86, 208, 241, 10, 75, 8, 4, 213, 148, 254, 32, 58, 221, 32, 48, 55, 129, 243, 231, 55, 161, 22, 68, 123, 234, 112, 238, 27, 248, 205, 175, 3, 76, 1, 164, 167, 52, 78, 65, 176, 169, 137, 4, 159, 182, 49, 198, 72, 197, 210, 127, 107, 63, 166, 28, 124, 25, 59, 64, 220, 201, 106, 147, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 41, 1, 4, 0, 0, 0, 3, 3, 8, 0, 0, 0, 0, 0, 0, 156, 64, 5, 23, 22, 0, 4, 0, 0, 0, 0, 2, 2, 0, 72, 4, 1, 0, 6, 1, 1, 8, 4, 0, 0, 3, 232, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 76, 1, 164, 167, 52, 78, 65, 176, 169, 137, 4, 159, 182, 49, 198, 72, 197, 210, 127, 107, 63, 166, 28, 124, 25, 59, 64, 220, 201, 106, 147, 65, 0, 3, 2, 82, 161, 2, 89, 164, 108, 52, 253, 52, 16, 98, 113, 175, 141, 110, 220, 214, 130, 103, 215, 226, 16, 216, 211, 252, 9, 226, 0, 84, 129, 189, 201, 17, 146, 139, 0, 3, 2, 82, 161, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 97, 1, 175, 91, 97, 1, 175, 91, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] - - - - static var serializedChannelMonitors: [[UInt8]] = [ - [1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 30, 40, 198, 1, 17, 200, 0, 22, 0, 20, 145, 136, 195, 71, 179, 2, 109, 136, 237, 2, 125, 149, 143, 194, 231, 176, 239, 175, 178, 93, 1, 0, 22, 0, 20, 238, 90, 239, 18, 253, 215, 67, 147, 78, 231, 119, 204, 199, 41, 181, 112, 78, 156, 253, 165, 0, 22, 0, 20, 192, 215, 94, 229, 110, 19, 8, 215, 222, 48, 2, 152, 139, 221, 240, 206, 172, 36, 24, 151, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 97, 72, 0, 0, 0, 0, 0, 96, 191, 196, 32, 0, 0, 0, 0, 0, 0, 0, 0, 2, 203, 126, 6, 106, 235, 15, 28, 5, 68, 63, 181, 110, 193, 14, 133, 104, 247, 56, 151, 127, 3, 206, 216, 112, 103, 135, 168, 4, 221, 40, 233, 168, 191, 159, 45, 109, 250, 168, 172, 172, 111, 136, 62, 120, 42, 195, 204, 192, 75, 139, 45, 252, 75, 128, 100, 201, 101, 164, 34, 25, 123, 118, 72, 239, 0, 1, 0, 34, 0, 32, 108, 196, 111, 93, 122, 59, 15, 44, 192, 196, 18, 129, 59, 19, 54, 189, 120, 12, 193, 178, 145, 181, 5, 118, 139, 228, 118, 195, 134, 243, 160, 36, 33, 219, 63, 105, 199, 123, 141, 196, 55, 169, 197, 67, 7, 149, 119, 53, 171, 18, 48, 200, 15, 101, 107, 205, 5, 48, 3, 155, 165, 97, 42, 40, 171, 0, 0, 0, 0, 0, 0, 0, 0, 1, 219, 63, 105, 199, 123, 141, 196, 55, 169, 197, 67, 7, 149, 119, 53, 171, 18, 48, 200, 15, 101, 107, 205, 5, 48, 3, 155, 165, 97, 42, 40, 171, 0, 0, 0, 0, 0, 0, 0, 0, 74, 0, 33, 2, 177, 134, 18, 222, 96, 37, 31, 216, 61, 52, 79, 185, 165, 149, 65, 241, 204, 140, 168, 200, 2, 5, 142, 253, 20, 47, 215, 54, 34, 44, 29, 174, 2, 33, 3, 66, 55, 59, 134, 74, 73, 106, 150, 243, 60, 164, 231, 147, 235, 222, 199, 46, 227, 12, 162, 208, 119, 225, 156, 136, 68, 61, 163, 190, 101, 34, 69, 4, 2, 0, 144, 0, 71, 82, 33, 2, 9, 208, 118, 12, 208, 192, 203, 156, 37, 75, 5, 208, 168, 90, 146, 102, 106, 104, 201, 48, 199, 244, 78, 242, 183, 129, 96, 108, 69, 110, 70, 142, 33, 3, 65, 81, 5, 242, 6, 238, 79, 27, 32, 59, 118, 145, 167, 132, 137, 105, 177, 225, 227, 93, 193, 58, 227, 5, 53, 171, 42, 71, 159, 54, 228, 137, 82, 174, 0, 0, 0, 0, 0, 61, 9, 0, 255, 255, 255, 255, 255, 255, 3, 0, 86, 208, 241, 10, 75, 8, 4, 213, 148, 254, 32, 58, 221, 32, 48, 55, 129, 243, 231, 55, 161, 22, 68, 123, 234, 112, 238, 27, 248, 205, 175, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 219, 63, 105, 199, 123, 141, 196, 55, 169, 197, 67, 7, 149, 119, 53, 171, 18, 48, 200, 15, 101, 107, 205, 5, 48, 3, 155, 165, 97, 42, 40, 171, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 217, 0, 32, 159, 172, 119, 230, 40, 58, 126, 90, 111, 203, 46, 52, 112, 231, 92, 43, 111, 35, 175, 215, 162, 32, 49, 63, 57, 113, 52, 230, 222, 96, 187, 148, 2, 33, 2, 35, 64, 218, 18, 4, 249, 115, 136, 2, 54, 84, 146, 76, 195, 215, 96, 56, 163, 104, 200, 15, 236, 248, 96, 44, 233, 175, 85, 134, 23, 250, 36, 4, 33, 3, 197, 119, 156, 31, 150, 55, 152, 97, 249, 84, 166, 133, 29, 202, 191, 246, 151, 91, 211, 173, 24, 119, 183, 130, 203, 125, 106, 112, 18, 199, 121, 204, 6, 33, 3, 234, 174, 250, 108, 146, 112, 48, 206, 214, 198, 65, 49, 95, 253, 86, 144, 79, 91, 252, 58, 63, 168, 56, 74, 24, 225, 247, 74, 24, 57, 24, 13, 8, 33, 3, 12, 25, 187, 117, 210, 189, 73, 106, 109, 46, 84, 178, 53, 58, 54, 6, 167, 133, 229, 79, 99, 206, 98, 142, 173, 34, 152, 81, 255, 169, 63, 116, 10, 33, 3, 183, 124, 104, 211, 18, 92, 225, 209, 51, 56, 194, 230, 175, 231, 137, 80, 191, 21, 133, 145, 214, 173, 30, 183, 36, 188, 81, 232, 22, 193, 208, 182, 12, 4, 0, 0, 0, 253, 14, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 223, 70, 242, 96, 28, 101, 156, 25, 147, 227, 151, 92, 34, 232, 55, 154, 102, 10, 133, 4, 212, 55, 146, 220, 238, 62, 142, 84, 143, 27, 124, 37, 0, 0, 0, 208, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 191, 159, 45, 109, 250, 168, 172, 172, 111, 136, 62, 120, 42, 195, 204, 192, 75, 139, 45, 252, 75, 128, 100, 201, 101, 164, 34, 25, 123, 118, 72, 239, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 34, 0, 32, 108, 196, 111, 93, 122, 59, 15, 44, 192, 196, 18, 129, 59, 19, 54, 189, 120, 12, 193, 178, 145, 181, 5, 118, 139, 228, 118, 195, 134, 243, 160, 36, 1, 1, 0, 22, 0, 20, 145, 136, 195, 71, 179, 2, 109, 136, 237, 2, 125, 149, 143, 194, 231, 176, 239, 175, 178, 93, 253, 1, 202, 0, 253, 1, 127, 253, 1, 124, 0, 8, 0, 0, 255, 255, 255, 255, 255, 255, 2, 8, 0, 0, 0, 0, 0, 61, 0, 121, 4, 8, 0, 0, 0, 0, 0, 0, 7, 208, 6, 4, 0, 0, 0, 253, 8, 176, 175, 0, 33, 3, 183, 124, 104, 211, 18, 92, 225, 209, 51, 56, 194, 230, 175, 231, 137, 80, 191, 21, 133, 145, 214, 173, 30, 183, 36, 188, 81, 232, 22, 193, 208, 182, 2, 33, 2, 35, 64, 218, 18, 4, 249, 115, 136, 2, 54, 84, 146, 76, 195, 215, 96, 56, 163, 104, 200, 15, 236, 248, 96, 44, 233, 175, 85, 134, 23, 250, 36, 4, 33, 3, 197, 119, 156, 31, 150, 55, 152, 97, 249, 84, 166, 133, 29, 202, 191, 246, 151, 91, 211, 173, 24, 119, 183, 130, 203, 125, 106, 112, 18, 199, 121, 204, 6, 33, 3, 234, 174, 250, 108, 146, 112, 48, 206, 214, 198, 65, 49, 95, 253, 86, 144, 79, 91, 252, 58, 63, 168, 56, 74, 24, 225, 247, 74, 24, 57, 24, 13, 8, 33, 3, 12, 25, 187, 117, 210, 189, 73, 106, 109, 46, 84, 178, 53, 58, 54, 6, 167, 133, 229, 79, 99, 206, 98, 142, 173, 34, 152, 81, 255, 169, 63, 116, 10, 162, 161, 0, 125, 2, 0, 0, 0, 1, 191, 159, 45, 109, 250, 168, 172, 172, 111, 136, 62, 120, 42, 195, 204, 192, 75, 139, 45, 252, 75, 128, 100, 201, 101, 164, 34, 25, 123, 118, 72, 239, 1, 0, 0, 0, 0, 198, 40, 30, 128, 2, 208, 7, 0, 0, 0, 0, 0, 0, 22, 0, 20, 158, 138, 199, 245, 61, 146, 58, 33, 73, 28, 13, 90, 209, 193, 239, 77, 44, 183, 84, 73, 121, 0, 61, 0, 0, 0, 0, 0, 34, 0, 32, 127, 234, 213, 198, 76, 150, 85, 149, 212, 195, 188, 50, 11, 120, 159, 184, 238, 141, 165, 16, 149, 41, 94, 123, 67, 222, 133, 179, 15, 191, 148, 162, 200, 17, 1, 32, 2, 32, 159, 172, 119, 230, 40, 58, 126, 90, 111, 203, 46, 52, 112, 231, 92, 43, 111, 35, 175, 215, 162, 32, 49, 63, 57, 113, 52, 230, 222, 96, 187, 148, 12, 0, 2, 64, 36, 29, 16, 35, 185, 254, 159, 18, 170, 41, 231, 13, 99, 119, 111, 255, 254, 111, 151, 196, 28, 113, 242, 154, 241, 244, 123, 4, 100, 140, 47, 248, 59, 104, 147, 154, 140, 13, 209, 195, 14, 222, 11, 211, 163, 116, 234, 181, 25, 71, 247, 230, 223, 196, 158, 67, 199, 23, 187, 221, 72, 193, 234, 82, 4, 1, 0, 6, 0, 0, 0, 0, 253, 1, 150, 0, 176, 175, 0, 33, 3, 65, 81, 5, 242, 6, 238, 79, 27, 32, 59, 118, 145, 167, 132, 137, 105, 177, 225, 227, 93, 193, 58, 227, 5, 53, 171, 42, 71, 159, 54, 228, 137, 2, 33, 2, 203, 126, 6, 106, 235, 15, 28, 5, 68, 63, 181, 110, 193, 14, 133, 104, 247, 56, 151, 127, 3, 206, 216, 112, 103, 135, 168, 4, 221, 40, 233, 168, 4, 33, 2, 199, 21, 85, 32, 48, 247, 98, 188, 50, 244, 157, 5, 216, 110, 59, 131, 207, 158, 110, 240, 52, 37, 9, 92, 74, 186, 97, 0, 75, 87, 159, 137, 6, 33, 3, 224, 171, 107, 195, 109, 139, 39, 25, 93, 126, 4, 226, 210, 240, 59, 87, 50, 46, 230, 140, 144, 21, 16, 139, 180, 199, 19, 170, 97, 177, 150, 42, 8, 33, 3, 228, 157, 142, 206, 28, 67, 191, 144, 5, 125, 60, 105, 113, 179, 143, 252, 28, 51, 34, 73, 180, 64, 176, 121, 58, 0, 218, 153, 216, 218, 113, 28, 2, 2, 0, 144, 4, 1, 1, 6, 183, 182, 0, 176, 175, 0, 33, 2, 9, 208, 118, 12, 208, 192, 203, 156, 37, 75, 5, 208, 168, 90, 146, 102, 106, 104, 201, 48, 199, 244, 78, 242, 183, 129, 96, 108, 69, 110, 70, 142, 2, 33, 3, 162, 188, 220, 100, 190, 42, 74, 213, 77, 38, 152, 16, 231, 110, 166, 29, 70, 240, 14, 32, 57, 24, 175, 118, 236, 36, 75, 201, 91, 93, 62, 106, 4, 33, 2, 178, 61, 18, 201, 74, 208, 113, 218, 152, 49, 92, 52, 150, 191, 56, 239, 168, 32, 96, 86, 114, 21, 19, 108, 171, 114, 233, 40, 204, 186, 169, 154, 6, 33, 2, 177, 134, 18, 222, 96, 37, 31, 216, 61, 52, 79, 185, 165, 149, 65, 241, 204, 140, 168, 200, 2, 5, 142, 253, 20, 47, 215, 54, 34, 44, 29, 174, 8, 33, 3, 66, 55, 59, 134, 74, 73, 106, 150, 243, 60, 164, 231, 147, 235, 222, 199, 46, 227, 12, 162, 208, 119, 225, 156, 136, 68, 61, 163, 190, 101, 34, 69, 2, 2, 1, 224, 8, 34, 191, 159, 45, 109, 250, 168, 172, 172, 111, 136, 62, 120, 42, 195, 204, 192, 75, 139, 45, 252, 75, 128, 100, 201, 101, 164, 34, 25, 123, 118, 72, 239, 0, 1, 0, 0, 2, 135, 1, 1, 147, 230, 63, 191, 15, 83, 126, 83, 56, 77, 244, 16, 203, 26, 120, 61, 110, 159, 96, 102, 208, 42, 82, 219, 25, 231, 84, 15, 52, 140, 28, 78, 57, 168, 53, 136, 69, 225, 211, 254, 158, 192, 210, 35, 192, 195, 39, 6, 152, 15, 119, 215, 69, 211, 64, 46, 92, 100, 212, 137, 250, 191, 196, 128, 221, 81, 68, 184, 94, 227, 133, 148, 123, 187, 3, 192, 142, 63, 75, 230, 135, 110, 89, 69, 117, 192, 101, 143, 160, 138, 107, 234, 215, 174, 191, 49, 194, 8, 77, 175, 34, 220, 38, 229, 18, 48, 135, 192, 16, 201, 249, 9, 213, 46, 208, 175, 250, 76, 229, 139, 241, 13, 19, 19, 47, 101, 254, 35, 74, 113, 245, 15, 196, 5, 95, 129, 67, 217, 245, 156, 121, 83, 201, 81, 20, 242, 100, 71, 120, 169, 162, 58, 109, 139, 107, 107, 139, 16, 218, 193, 80, 220, 107, 17, 18, 1, 217, 220, 76, 95, 220, 67, 170, 89, 170, 51, 61, 254, 8, 52, 100, 179, 249, 248, 78, 227, 128, 36, 42, 148, 113, 160, 253, 1, 154, 253, 1, 150, 0, 176, 175, 0, 33, 3, 65, 81, 5, 242, 6, 238, 79, 27, 32, 59, 118, 145, 167, 132, 137, 105, 177, 225, 227, 93, 193, 58, 227, 5, 53, 171, 42, 71, 159, 54, 228, 137, 2, 33, 2, 203, 126, 6, 106, 235, 15, 28, 5, 68, 63, 181, 110, 193, 14, 133, 104, 247, 56, 151, 127, 3, 206, 216, 112, 103, 135, 168, 4, 221, 40, 233, 168, 4, 33, 2, 199, 21, 85, 32, 48, 247, 98, 188, 50, 244, 157, 5, 216, 110, 59, 131, 207, 158, 110, 240, 52, 37, 9, 92, 74, 186, 97, 0, 75, 87, 159, 137, 6, 33, 3, 224, 171, 107, 195, 109, 139, 39, 25, 93, 126, 4, 226, 210, 240, 59, 87, 50, 46, 230, 140, 144, 21, 16, 139, 180, 199, 19, 170, 97, 177, 150, 42, 8, 33, 3, 228, 157, 142, 206, 28, 67, 191, 144, 5, 125, 60, 105, 113, 179, 143, 252, 28, 51, 34, 73, 180, 64, 176, 121, 58, 0, 218, 153, 216, 218, 113, 28, 2, 2, 0, 144, 4, 1, 1, 6, 183, 182, 0, 176, 175, 0, 33, 2, 9, 208, 118, 12, 208, 192, 203, 156, 37, 75, 5, 208, 168, 90, 146, 102, 106, 104, 201, 48, 199, 244, 78, 242, 183, 129, 96, 108, 69, 110, 70, 142, 2, 33, 3, 162, 188, 220, 100, 190, 42, 74, 213, 77, 38, 152, 16, 231, 110, 166, 29, 70, 240, 14, 32, 57, 24, 175, 118, 236, 36, 75, 201, 91, 93, 62, 106, 4, 33, 2, 178, 61, 18, 201, 74, 208, 113, 218, 152, 49, 92, 52, 150, 191, 56, 239, 168, 32, 96, 86, 114, 21, 19, 108, 171, 114, 233, 40, 204, 186, 169, 154, 6, 33, 2, 177, 134, 18, 222, 96, 37, 31, 216, 61, 52, 79, 185, 165, 149, 65, 241, 204, 140, 168, 200, 2, 5, 142, 253, 20, 47, 215, 54, 34, 44, 29, 174, 8, 33, 3, 66, 55, 59, 134, 74, 73, 106, 150, 243, 60, 164, 231, 147, 235, 222, 199, 46, 227, 12, 162, 208, 119, 225, 156, 136, 68, 61, 163, 190, 101, 34, 69, 2, 2, 1, 224, 8, 34, 191, 159, 45, 109, 250, 168, 172, 172, 111, 136, 62, 120, 42, 195, 204, 192, 75, 139, 45, 252, 75, 128, 100, 201, 101, 164, 34, 25, 123, 118, 72, 239, 0, 1, 0, 0, 0, 0, 0, 61, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 97, 72, 0, 0, 0, 0, 0, 96, 191, 196, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - - - [1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 236, 131, 59, 149, 183, 100, 0, 22, 0, 20, 145, 136, 195, 71, 179, 2, 109, 136, 237, 2, 125, 149, 143, 194, 231, 176, 239, 175, 178, 93, 1, 0, 22, 0, 20, 161, 221, 204, 111, 11, 179, 246, 145, 99, 90, 218, 176, 191, 104, 67, 159, 97, 148, 87, 202, 0, 22, 0, 20, 192, 215, 94, 229, 110, 19, 8, 215, 222, 48, 2, 152, 139, 221, 240, 206, 172, 36, 24, 151, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 134, 68, 218, 0, 0, 0, 0, 0, 96, 191, 186, 40, 0, 0, 0, 0, 0, 0, 0, 0, 3, 23, 13, 54, 171, 102, 235, 221, 53, 72, 51, 123, 135, 168, 120, 42, 196, 140, 24, 148, 29, 145, 100, 130, 234, 216, 167, 232, 71, 57, 125, 56, 47, 91, 35, 204, 7, 40, 172, 92, 136, 96, 211, 177, 34, 85, 186, 249, 35, 131, 99, 50, 97, 93, 4, 51, 237, 227, 6, 200, 26, 22, 65, 195, 203, 0, 0, 0, 34, 0, 32, 82, 46, 216, 210, 105, 33, 126, 151, 160, 20, 7, 228, 176, 229, 72, 23, 105, 86, 73, 149, 223, 219, 16, 118, 197, 187, 107, 94, 209, 250, 216, 78, 33, 65, 156, 247, 109, 127, 254, 158, 108, 200, 38, 243, 31, 104, 221, 186, 93, 70, 193, 20, 123, 6, 4, 29, 172, 181, 252, 223, 45, 20, 107, 32, 135, 0, 0, 0, 0, 0, 0, 0, 0, 1, 65, 156, 247, 109, 127, 254, 158, 108, 200, 38, 243, 31, 104, 221, 186, 93, 70, 193, 20, 123, 6, 4, 29, 172, 181, 252, 223, 45, 20, 107, 32, 135, 0, 0, 0, 0, 0, 0, 0, 0, 74, 0, 33, 2, 152, 85, 104, 139, 85, 153, 234, 236, 90, 40, 197, 81, 80, 44, 83, 14, 122, 205, 217, 156, 94, 228, 98, 195, 142, 135, 238, 99, 67, 170, 105, 66, 2, 33, 3, 144, 195, 213, 137, 63, 56, 194, 230, 34, 123, 208, 88, 201, 237, 40, 139, 252, 84, 50, 195, 250, 132, 85, 190, 7, 124, 53, 193, 76, 135, 121, 47, 4, 2, 0, 144, 0, 71, 82, 33, 2, 164, 196, 150, 245, 129, 182, 7, 197, 177, 15, 83, 193, 210, 169, 117, 73, 65, 134, 69, 254, 31, 49, 113, 230, 125, 220, 109, 248, 62, 130, 29, 49, 33, 3, 207, 77, 43, 188, 237, 132, 89, 12, 108, 132, 241, 229, 2, 175, 137, 205, 77, 207, 248, 46, 129, 106, 11, 120, 84, 188, 31, 49, 63, 25, 54, 18, 82, 174, 0, 0, 0, 0, 0, 76, 75, 64, 255, 255, 255, 255, 255, 255, 2, 73, 220, 31, 99, 179, 103, 241, 56, 48, 5, 254, 221, 119, 137, 101, 225, 101, 105, 13, 131, 151, 152, 36, 137, 57, 168, 60, 187, 133, 188, 199, 130, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 88, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 65, 156, 247, 109, 127, 254, 158, 108, 200, 38, 243, 31, 104, 221, 186, 93, 70, 193, 20, 123, 6, 4, 29, 172, 181, 252, 223, 45, 20, 107, 32, 135, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 217, 0, 32, 155, 244, 216, 101, 5, 48, 111, 226, 29, 93, 119, 147, 99, 227, 192, 109, 17, 234, 31, 188, 129, 168, 122, 41, 1, 64, 101, 206, 39, 33, 255, 220, 2, 33, 2, 8, 238, 253, 199, 52, 195, 158, 109, 241, 83, 88, 217, 70, 156, 184, 137, 45, 148, 122, 15, 222, 224, 225, 162, 193, 73, 88, 144, 221, 15, 126, 249, 4, 33, 3, 241, 43, 220, 67, 128, 63, 217, 154, 222, 72, 132, 6, 180, 230, 135, 165, 143, 41, 61, 89, 68, 197, 31, 179, 51, 116, 206, 14, 18, 243, 140, 88, 6, 33, 3, 105, 237, 50, 191, 124, 241, 25, 13, 235, 218, 76, 194, 23, 169, 14, 219, 191, 190, 77, 48, 45, 180, 98, 13, 248, 136, 186, 65, 169, 139, 144, 151, 8, 33, 2, 218, 184, 146, 140, 238, 249, 174, 199, 13, 91, 216, 71, 208, 36, 136, 22, 81, 16, 241, 205, 134, 158, 5, 171, 136, 104, 25, 246, 112, 206, 153, 7, 10, 33, 3, 65, 97, 207, 152, 180, 230, 235, 248, 168, 122, 180, 208, 117, 127, 52, 219, 42, 180, 105, 146, 201, 237, 75, 209, 156, 97, 41, 182, 138, 244, 160, 125, 12, 4, 0, 0, 0, 253, 14, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 252, 210, 247, 104, 109, 8, 154, 117, 69, 168, 240, 210, 1, 217, 199, 86, 37, 35, 111, 204, 18, 181, 19, 9, 74, 135, 216, 183, 217, 240, 34, 85, 0, 0, 0, 185, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 91, 35, 204, 7, 40, 172, 92, 136, 96, 211, 177, 34, 85, 186, 249, 35, 131, 99, 50, 97, 93, 4, 51, 237, 227, 6, 200, 26, 22, 65, 195, 203, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 34, 0, 32, 82, 46, 216, 210, 105, 33, 126, 151, 160, 20, 7, 228, 176, 229, 72, 23, 105, 86, 73, 149, 223, 219, 16, 118, 197, 187, 107, 94, 209, 250, 216, 78, 1, 1, 0, 22, 0, 20, 145, 136, 195, 71, 179, 2, 109, 136, 237, 2, 125, 149, 143, 194, 231, 176, 239, 175, 178, 93, 253, 1, 171, 0, 253, 1, 96, 253, 1, 93, 0, 8, 0, 0, 255, 255, 255, 255, 255, 255, 2, 8, 0, 0, 0, 0, 0, 76, 74, 137, 4, 8, 0, 0, 0, 0, 0, 0, 0, 0, 6, 4, 0, 0, 0, 253, 8, 176, 175, 0, 33, 3, 65, 97, 207, 152, 180, 230, 235, 248, 168, 122, 180, 208, 117, 127, 52, 219, 42, 180, 105, 146, 201, 237, 75, 209, 156, 97, 41, 182, 138, 244, 160, 125, 2, 33, 2, 8, 238, 253, 199, 52, 195, 158, 109, 241, 83, 88, 217, 70, 156, 184, 137, 45, 148, 122, 15, 222, 224, 225, 162, 193, 73, 88, 144, 221, 15, 126, 249, 4, 33, 3, 241, 43, 220, 67, 128, 63, 217, 154, 222, 72, 132, 6, 180, 230, 135, 165, 143, 41, 61, 89, 68, 197, 31, 179, 51, 116, 206, 14, 18, 243, 140, 88, 6, 33, 3, 105, 237, 50, 191, 124, 241, 25, 13, 235, 218, 76, 194, 23, 169, 14, 219, 191, 190, 77, 48, 45, 180, 98, 13, 248, 136, 186, 65, 169, 139, 144, 151, 8, 33, 2, 218, 184, 146, 140, 238, 249, 174, 199, 13, 91, 216, 71, 208, 36, 136, 22, 81, 16, 241, 205, 134, 158, 5, 171, 136, 104, 25, 246, 112, 206, 153, 7, 10, 131, 130, 0, 94, 2, 0, 0, 0, 1, 91, 35, 204, 7, 40, 172, 92, 136, 96, 211, 177, 34, 85, 186, 249, 35, 131, 99, 50, 97, 93, 4, 51, 237, 227, 6, 200, 26, 22, 65, 195, 203, 0, 0, 0, 0, 0, 59, 131, 236, 128, 1, 137, 74, 76, 0, 0, 0, 0, 0, 34, 0, 32, 199, 233, 137, 95, 155, 13, 211, 124, 205, 61, 159, 18, 18, 1, 177, 165, 199, 45, 94, 199, 12, 153, 70, 38, 28, 92, 29, 59, 5, 189, 93, 21, 100, 183, 149, 32, 2, 32, 155, 244, 216, 101, 5, 48, 111, 226, 29, 93, 119, 147, 99, 227, 192, 109, 17, 234, 31, 188, 129, 168, 122, 41, 1, 64, 101, 206, 39, 33, 255, 220, 12, 0, 2, 64, 31, 9, 22, 9, 117, 55, 108, 96, 253, 58, 180, 222, 109, 69, 204, 74, 130, 239, 96, 253, 96, 169, 90, 102, 139, 213, 158, 193, 20, 168, 128, 229, 110, 230, 53, 239, 30, 72, 185, 237, 83, 104, 232, 203, 226, 14, 222, 24, 129, 219, 101, 121, 154, 240, 57, 78, 79, 26, 27, 58, 89, 152, 157, 98, 4, 1, 0, 6, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 253, 1, 150, 0, 176, 175, 0, 33, 3, 207, 77, 43, 188, 237, 132, 89, 12, 108, 132, 241, 229, 2, 175, 137, 205, 77, 207, 248, 46, 129, 106, 11, 120, 84, 188, 31, 49, 63, 25, 54, 18, 2, 33, 3, 23, 13, 54, 171, 102, 235, 221, 53, 72, 51, 123, 135, 168, 120, 42, 196, 140, 24, 148, 29, 145, 100, 130, 234, 216, 167, 232, 71, 57, 125, 56, 47, 4, 33, 3, 187, 164, 187, 3, 110, 52, 229, 43, 24, 114, 6, 145, 246, 57, 37, 172, 141, 9, 133, 48, 11, 119, 246, 98, 33, 51, 99, 197, 18, 56, 172, 221, 6, 33, 2, 101, 114, 44, 129, 63, 56, 252, 19, 86, 109, 254, 27, 72, 38, 51, 22, 165, 207, 136, 9, 57, 181, 188, 19, 234, 3, 12, 19, 79, 177, 106, 13, 8, 33, 2, 90, 230, 93, 154, 193, 232, 90, 118, 22, 154, 243, 207, 181, 143, 116, 105, 102, 164, 130, 200, 84, 178, 159, 186, 250, 165, 117, 186, 254, 189, 194, 122, 2, 2, 0, 144, 4, 1, 1, 6, 183, 182, 0, 176, 175, 0, 33, 2, 164, 196, 150, 245, 129, 182, 7, 197, 177, 15, 83, 193, 210, 169, 117, 73, 65, 134, 69, 254, 31, 49, 113, 230, 125, 220, 109, 248, 62, 130, 29, 49, 2, 33, 2, 217, 42, 5, 93, 111, 197, 212, 186, 244, 26, 75, 81, 239, 65, 189, 247, 190, 38, 250, 45, 167, 27, 107, 163, 138, 142, 159, 137, 27, 249, 89, 70, 4, 33, 2, 100, 85, 119, 59, 244, 239, 219, 228, 184, 151, 140, 250, 74, 100, 201, 239, 94, 13, 164, 2, 38, 36, 191, 167, 28, 125, 34, 72, 243, 111, 213, 245, 6, 33, 2, 152, 85, 104, 139, 85, 153, 234, 236, 90, 40, 197, 81, 80, 44, 83, 14, 122, 205, 217, 156, 94, 228, 98, 195, 142, 135, 238, 99, 67, 170, 105, 66, 8, 33, 3, 144, 195, 213, 137, 63, 56, 194, 230, 34, 123, 208, 88, 201, 237, 40, 139, 252, 84, 50, 195, 250, 132, 85, 190, 7, 124, 53, 193, 76, 135, 121, 47, 2, 2, 2, 88, 8, 34, 91, 35, 204, 7, 40, 172, 92, 136, 96, 211, 177, 34, 85, 186, 249, 35, 131, 99, 50, 97, 93, 4, 51, 237, 227, 6, 200, 26, 22, 65, 195, 203, 0, 0, 0, 0, 2, 135, 1, 1, 93, 46, 72, 106, 26, 123, 101, 185, 30, 186, 248, 204, 231, 156, 202, 142, 45, 144, 5, 101, 0, 47, 36, 180, 149, 105, 185, 84, 254, 140, 28, 198, 122, 178, 219, 13, 214, 224, 109, 222, 134, 124, 111, 87, 123, 65, 66, 207, 29, 233, 102, 31, 175, 229, 204, 95, 75, 150, 149, 139, 84, 28, 247, 223, 225, 133, 3, 176, 225, 155, 46, 173, 30, 93, 82, 153, 169, 186, 236, 245, 62, 75, 118, 157, 228, 135, 60, 189, 67, 188, 146, 123, 170, 132, 83, 236, 206, 88, 186, 92, 167, 23, 205, 159, 149, 102, 172, 66, 226, 42, 119, 142, 52, 42, 202, 0, 192, 87, 15, 45, 84, 78, 98, 214, 187, 142, 70, 31, 220, 16, 185, 91, 20, 222, 210, 110, 129, 134, 208, 94, 155, 234, 53, 52, 167, 244, 133, 111, 15, 196, 246, 107, 138, 24, 250, 22, 239, 207, 99, 174, 166, 81, 154, 220, 137, 189, 181, 135, 230, 189, 58, 47, 164, 212, 192, 222, 165, 39, 88, 76, 196, 77, 9, 92, 6, 169, 199, 136, 188, 140, 227, 56, 253, 1, 154, 253, 1, 150, 0, 176, 175, 0, 33, 3, 207, 77, 43, 188, 237, 132, 89, 12, 108, 132, 241, 229, 2, 175, 137, 205, 77, 207, 248, 46, 129, 106, 11, 120, 84, 188, 31, 49, 63, 25, 54, 18, 2, 33, 3, 23, 13, 54, 171, 102, 235, 221, 53, 72, 51, 123, 135, 168, 120, 42, 196, 140, 24, 148, 29, 145, 100, 130, 234, 216, 167, 232, 71, 57, 125, 56, 47, 4, 33, 3, 187, 164, 187, 3, 110, 52, 229, 43, 24, 114, 6, 145, 246, 57, 37, 172, 141, 9, 133, 48, 11, 119, 246, 98, 33, 51, 99, 197, 18, 56, 172, 221, 6, 33, 2, 101, 114, 44, 129, 63, 56, 252, 19, 86, 109, 254, 27, 72, 38, 51, 22, 165, 207, 136, 9, 57, 181, 188, 19, 234, 3, 12, 19, 79, 177, 106, 13, 8, 33, 2, 90, 230, 93, 154, 193, 232, 90, 118, 22, 154, 243, 207, 181, 143, 116, 105, 102, 164, 130, 200, 84, 178, 159, 186, 250, 165, 117, 186, 254, 189, 194, 122, 2, 2, 0, 144, 4, 1, 1, 6, 183, 182, 0, 176, 175, 0, 33, 2, 164, 196, 150, 245, 129, 182, 7, 197, 177, 15, 83, 193, 210, 169, 117, 73, 65, 134, 69, 254, 31, 49, 113, 230, 125, 220, 109, 248, 62, 130, 29, 49, 2, 33, 2, 217, 42, 5, 93, 111, 197, 212, 186, 244, 26, 75, 81, 239, 65, 189, 247, 190, 38, 250, 45, 167, 27, 107, 163, 138, 142, 159, 137, 27, 249, 89, 70, 4, 33, 2, 100, 85, 119, 59, 244, 239, 219, 228, 184, 151, 140, 250, 74, 100, 201, 239, 94, 13, 164, 2, 38, 36, 191, 167, 28, 125, 34, 72, 243, 111, 213, 245, 6, 33, 2, 152, 85, 104, 139, 85, 153, 234, 236, 90, 40, 197, 81, 80, 44, 83, 14, 122, 205, 217, 156, 94, 228, 98, 195, 142, 135, 238, 99, 67, 170, 105, 66, 8, 33, 3, 144, 195, 213, 137, 63, 56, 194, 230, 34, 123, 208, 88, 201, 237, 40, 139, 252, 84, 50, 195, 250, 132, 85, 190, 7, 124, 53, 193, 76, 135, 121, 47, 2, 2, 2, 88, 8, 34, 91, 35, 204, 7, 40, 172, 92, 136, 96, 211, 177, 34, 85, 186, 249, 35, 131, 99, 50, 97, 93, 4, 51, 237, 227, 6, 200, 26, 22, 65, 195, 203, 0, 0, 0, 0, 0, 0, 0, 76, 75, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 134, 68, 218, 0, 0, 0, 0, 0, 96, 191, 186, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0], - - - [1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 121, 243, 156, 122, 253, 33, 0, 22, 0, 20, 145, 136, 195, 71, 179, 2, 109, 136, 237, 2, 125, 149, 143, 194, 231, 176, 239, 175, 178, 93, 1, 0, 22, 0, 20, 156, 136, 155, 37, 207, 130, 41, 147, 38, 118, 132, 240, 2, 26, 207, 64, 43, 77, 213, 107, 0, 22, 0, 20, 192, 215, 94, 229, 110, 19, 8, 215, 222, 48, 2, 152, 139, 221, 240, 206, 172, 36, 24, 151, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 100, 134, 192, 0, 0, 0, 0, 96, 191, 195, 3, 0, 0, 0, 0, 0, 0, 0, 0, 3, 169, 210, 205, 107, 151, 19, 175, 99, 240, 89, 255, 218, 111, 14, 229, 169, 203, 192, 95, 238, 36, 218, 87, 24, 251, 188, 54, 20, 110, 134, 229, 112, 154, 13, 188, 186, 197, 129, 42, 234, 116, 174, 71, 179, 0, 176, 61, 111, 34, 233, 34, 212, 224, 37, 185, 65, 37, 30, 244, 58, 24, 36, 229, 161, 0, 0, 0, 34, 0, 32, 134, 213, 198, 74, 204, 68, 167, 226, 115, 12, 209, 241, 25, 111, 194, 124, 75, 139, 185, 58, 192, 212, 241, 159, 105, 48, 111, 220, 54, 195, 100, 57, 33, 182, 220, 153, 104, 164, 214, 74, 136, 37, 123, 219, 109, 224, 88, 133, 109, 145, 88, 101, 233, 60, 238, 104, 7, 175, 138, 175, 69, 223, 108, 52, 178, 0, 0, 0, 0, 0, 0, 0, 0, 1, 182, 220, 153, 104, 164, 214, 74, 136, 37, 123, 219, 109, 224, 88, 133, 109, 145, 88, 101, 233, 60, 238, 104, 7, 175, 138, 175, 69, 223, 108, 52, 178, 0, 0, 0, 0, 0, 0, 0, 0, 74, 0, 33, 3, 19, 72, 214, 48, 188, 23, 62, 190, 235, 38, 128, 5, 162, 68, 173, 136, 99, 223, 44, 123, 227, 17, 94, 234, 194, 64, 151, 77, 42, 176, 29, 177, 2, 33, 3, 166, 184, 55, 239, 104, 226, 8, 194, 210, 64, 148, 27, 5, 252, 244, 78, 156, 173, 2, 67, 101, 93, 32, 179, 251, 58, 65, 191, 151, 163, 195, 0, 4, 2, 0, 144, 0, 71, 82, 33, 2, 72, 97, 154, 95, 180, 18, 20, 112, 153, 254, 80, 92, 186, 183, 24, 213, 85, 123, 40, 150, 70, 158, 255, 45, 106, 112, 64, 156, 205, 249, 246, 162, 33, 3, 182, 71, 151, 63, 158, 166, 139, 175, 33, 191, 184, 2, 31, 80, 80, 175, 195, 243, 170, 48, 97, 168, 182, 205, 66, 74, 186, 234, 213, 155, 138, 0, 82, 174, 0, 0, 0, 0, 0, 61, 9, 0, 255, 255, 255, 255, 255, 255, 3, 159, 16, 206, 106, 109, 181, 15, 92, 31, 135, 79, 28, 102, 241, 196, 133, 60, 33, 133, 166, 217, 113, 148, 240, 43, 58, 127, 97, 247, 70, 206, 136, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 182, 220, 153, 104, 164, 214, 74, 136, 37, 123, 219, 109, 224, 88, 133, 109, 145, 88, 101, 233, 60, 238, 104, 7, 175, 138, 175, 69, 223, 108, 52, 178, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 217, 0, 32, 249, 249, 98, 229, 167, 62, 21, 67, 41, 61, 27, 231, 1, 75, 32, 12, 121, 144, 121, 160, 108, 43, 42, 255, 52, 202, 13, 47, 103, 235, 96, 221, 2, 33, 3, 107, 133, 180, 116, 127, 220, 77, 60, 217, 241, 148, 177, 122, 15, 156, 142, 180, 71, 207, 179, 118, 123, 171, 153, 14, 105, 124, 168, 221, 132, 54, 71, 4, 33, 2, 168, 149, 64, 202, 166, 184, 248, 35, 31, 253, 135, 157, 39, 164, 206, 28, 83, 140, 180, 60, 100, 36, 50, 82, 103, 17, 118, 243, 238, 34, 238, 208, 6, 33, 2, 2, 110, 79, 33, 39, 173, 95, 249, 89, 115, 209, 234, 57, 86, 52, 50, 49, 164, 74, 23, 134, 12, 205, 252, 111, 83, 167, 129, 63, 195, 111, 179, 8, 33, 3, 14, 110, 108, 28, 86, 183, 101, 122, 104, 241, 23, 63, 216, 202, 199, 190, 166, 239, 224, 147, 63, 228, 254, 177, 31, 210, 27, 47, 34, 130, 107, 46, 10, 33, 2, 18, 127, 119, 213, 63, 240, 181, 230, 149, 71, 42, 224, 5, 229, 239, 246, 149, 190, 52, 176, 137, 178, 175, 85, 233, 216, 198, 187, 165, 136, 145, 140, 12, 4, 0, 0, 0, 253, 14, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 248, 203, 59, 40, 150, 135, 230, 177, 80, 234, 252, 113, 114, 69, 68, 1, 163, 209, 116, 69, 127, 58, 225, 200, 202, 100, 1, 43, 84, 169, 165, 2, 0, 0, 0, 205, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 154, 13, 188, 186, 197, 129, 42, 234, 116, 174, 71, 179, 0, 176, 61, 111, 34, 233, 34, 212, 224, 37, 185, 65, 37, 30, 244, 58, 24, 36, 229, 161, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 34, 0, 32, 134, 213, 198, 74, 204, 68, 167, 226, 115, 12, 209, 241, 25, 111, 194, 124, 75, 139, 185, 58, 192, 212, 241, 159, 105, 48, 111, 220, 54, 195, 100, 57, 1, 1, 0, 22, 0, 20, 145, 136, 195, 71, 179, 2, 109, 136, 237, 2, 125, 149, 143, 194, 231, 176, 239, 175, 178, 93, 253, 1, 202, 0, 253, 1, 127, 253, 1, 124, 0, 8, 0, 0, 255, 255, 255, 255, 255, 255, 2, 8, 0, 0, 0, 0, 0, 61, 0, 121, 4, 8, 0, 0, 0, 0, 0, 0, 7, 208, 6, 4, 0, 0, 0, 253, 8, 176, 175, 0, 33, 2, 18, 127, 119, 213, 63, 240, 181, 230, 149, 71, 42, 224, 5, 229, 239, 246, 149, 190, 52, 176, 137, 178, 175, 85, 233, 216, 198, 187, 165, 136, 145, 140, 2, 33, 3, 107, 133, 180, 116, 127, 220, 77, 60, 217, 241, 148, 177, 122, 15, 156, 142, 180, 71, 207, 179, 118, 123, 171, 153, 14, 105, 124, 168, 221, 132, 54, 71, 4, 33, 2, 168, 149, 64, 202, 166, 184, 248, 35, 31, 253, 135, 157, 39, 164, 206, 28, 83, 140, 180, 60, 100, 36, 50, 82, 103, 17, 118, 243, 238, 34, 238, 208, 6, 33, 2, 2, 110, 79, 33, 39, 173, 95, 249, 89, 115, 209, 234, 57, 86, 52, 50, 49, 164, 74, 23, 134, 12, 205, 252, 111, 83, 167, 129, 63, 195, 111, 179, 8, 33, 3, 14, 110, 108, 28, 86, 183, 101, 122, 104, 241, 23, 63, 216, 202, 199, 190, 166, 239, 224, 147, 63, 228, 254, 177, 31, 210, 27, 47, 34, 130, 107, 46, 10, 162, 161, 0, 125, 2, 0, 0, 0, 1, 154, 13, 188, 186, 197, 129, 42, 234, 116, 174, 71, 179, 0, 176, 61, 111, 34, 233, 34, 212, 224, 37, 185, 65, 37, 30, 244, 58, 24, 36, 229, 161, 0, 0, 0, 0, 0, 156, 243, 121, 128, 2, 208, 7, 0, 0, 0, 0, 0, 0, 22, 0, 20, 98, 50, 119, 61, 44, 2, 246, 167, 42, 1, 128, 109, 34, 5, 34, 124, 146, 87, 32, 203, 121, 0, 61, 0, 0, 0, 0, 0, 34, 0, 32, 108, 52, 108, 150, 106, 214, 79, 135, 86, 65, 77, 225, 207, 143, 57, 208, 49, 112, 249, 244, 58, 79, 142, 216, 158, 50, 87, 49, 179, 39, 52, 49, 33, 253, 122, 32, 2, 32, 249, 249, 98, 229, 167, 62, 21, 67, 41, 61, 27, 231, 1, 75, 32, 12, 121, 144, 121, 160, 108, 43, 42, 255, 52, 202, 13, 47, 103, 235, 96, 221, 12, 0, 2, 64, 221, 10, 232, 28, 181, 168, 115, 35, 179, 141, 173, 166, 216, 132, 59, 0, 97, 42, 101, 126, 70, 9, 250, 191, 81, 103, 123, 209, 185, 116, 63, 190, 60, 217, 200, 37, 230, 153, 130, 117, 60, 247, 71, 213, 56, 221, 229, 200, 209, 192, 78, 246, 201, 140, 206, 250, 150, 94, 11, 77, 22, 228, 44, 21, 4, 1, 0, 6, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 253, 1, 150, 0, 176, 175, 0, 33, 3, 182, 71, 151, 63, 158, 166, 139, 175, 33, 191, 184, 2, 31, 80, 80, 175, 195, 243, 170, 48, 97, 168, 182, 205, 66, 74, 186, 234, 213, 155, 138, 0, 2, 33, 3, 169, 210, 205, 107, 151, 19, 175, 99, 240, 89, 255, 218, 111, 14, 229, 169, 203, 192, 95, 238, 36, 218, 87, 24, 251, 188, 54, 20, 110, 134, 229, 112, 4, 33, 2, 177, 247, 78, 252, 70, 132, 241, 79, 113, 190, 46, 213, 133, 68, 86, 43, 194, 235, 0, 80, 69, 40, 69, 23, 188, 138, 121, 221, 6, 84, 66, 175, 6, 33, 2, 59, 65, 183, 149, 220, 177, 69, 154, 54, 190, 95, 89, 221, 203, 167, 125, 138, 87, 85, 99, 254, 40, 147, 252, 123, 135, 198, 88, 210, 54, 6, 112, 8, 33, 2, 240, 165, 48, 220, 63, 140, 161, 160, 241, 27, 205, 177, 39, 58, 202, 232, 142, 55, 170, 78, 111, 100, 240, 161, 123, 148, 135, 227, 240, 132, 45, 180, 2, 2, 0, 144, 4, 1, 1, 6, 183, 182, 0, 176, 175, 0, 33, 2, 72, 97, 154, 95, 180, 18, 20, 112, 153, 254, 80, 92, 186, 183, 24, 213, 85, 123, 40, 150, 70, 158, 255, 45, 106, 112, 64, 156, 205, 249, 246, 162, 2, 33, 2, 131, 107, 20, 49, 73, 115, 255, 202, 251, 78, 27, 94, 59, 30, 0, 52, 234, 215, 127, 226, 35, 2, 123, 125, 132, 136, 119, 76, 199, 212, 121, 29, 4, 33, 3, 47, 54, 100, 94, 50, 107, 239, 164, 12, 188, 179, 239, 88, 99, 188, 152, 241, 183, 53, 150, 111, 248, 250, 102, 107, 204, 32, 226, 130, 199, 102, 79, 6, 33, 3, 19, 72, 214, 48, 188, 23, 62, 190, 235, 38, 128, 5, 162, 68, 173, 136, 99, 223, 44, 123, 227, 17, 94, 234, 194, 64, 151, 77, 42, 176, 29, 177, 8, 33, 3, 166, 184, 55, 239, 104, 226, 8, 194, 210, 64, 148, 27, 5, 252, 244, 78, 156, 173, 2, 67, 101, 93, 32, 179, 251, 58, 65, 191, 151, 163, 195, 0, 2, 2, 1, 224, 8, 34, 154, 13, 188, 186, 197, 129, 42, 234, 116, 174, 71, 179, 0, 176, 61, 111, 34, 233, 34, 212, 224, 37, 185, 65, 37, 30, 244, 58, 24, 36, 229, 161, 0, 0, 0, 0, 2, 135, 1, 1, 251, 107, 237, 225, 215, 54, 212, 93, 169, 173, 237, 150, 43, 247, 64, 48, 229, 120, 101, 28, 60, 49, 187, 232, 102, 79, 255, 132, 11, 17, 187, 214, 126, 100, 59, 136, 206, 203, 103, 189, 117, 166, 240, 152, 174, 147, 46, 241, 37, 118, 48, 184, 33, 8, 126, 102, 238, 123, 127, 172, 111, 82, 199, 5, 31, 209, 154, 252, 185, 80, 149, 1, 13, 40, 116, 21, 143, 7, 158, 107, 2, 77, 228, 204, 228, 199, 168, 21, 162, 157, 113, 98, 52, 251, 208, 180, 202, 13, 139, 42, 217, 150, 91, 117, 177, 58, 181, 224, 70, 67, 18, 83, 124, 87, 91, 11, 183, 176, 66, 126, 52, 96, 105, 253, 90, 136, 30, 138, 189, 200, 46, 101, 108, 82, 207, 241, 157, 107, 103, 234, 121, 140, 232, 202, 78, 224, 135, 118, 102, 88, 202, 212, 224, 102, 19, 215, 25, 77, 123, 23, 182, 136, 34, 103, 130, 48, 22, 87, 188, 146, 104, 162, 159, 188, 173, 123, 51, 0, 173, 195, 102, 235, 217, 180, 199, 41, 132, 26, 187, 140, 17, 106, 253, 1, 154, 253, 1, 150, 0, 176, 175, 0, 33, 3, 182, 71, 151, 63, 158, 166, 139, 175, 33, 191, 184, 2, 31, 80, 80, 175, 195, 243, 170, 48, 97, 168, 182, 205, 66, 74, 186, 234, 213, 155, 138, 0, 2, 33, 3, 169, 210, 205, 107, 151, 19, 175, 99, 240, 89, 255, 218, 111, 14, 229, 169, 203, 192, 95, 238, 36, 218, 87, 24, 251, 188, 54, 20, 110, 134, 229, 112, 4, 33, 2, 177, 247, 78, 252, 70, 132, 241, 79, 113, 190, 46, 213, 133, 68, 86, 43, 194, 235, 0, 80, 69, 40, 69, 23, 188, 138, 121, 221, 6, 84, 66, 175, 6, 33, 2, 59, 65, 183, 149, 220, 177, 69, 154, 54, 190, 95, 89, 221, 203, 167, 125, 138, 87, 85, 99, 254, 40, 147, 252, 123, 135, 198, 88, 210, 54, 6, 112, 8, 33, 2, 240, 165, 48, 220, 63, 140, 161, 160, 241, 27, 205, 177, 39, 58, 202, 232, 142, 55, 170, 78, 111, 100, 240, 161, 123, 148, 135, 227, 240, 132, 45, 180, 2, 2, 0, 144, 4, 1, 1, 6, 183, 182, 0, 176, 175, 0, 33, 2, 72, 97, 154, 95, 180, 18, 20, 112, 153, 254, 80, 92, 186, 183, 24, 213, 85, 123, 40, 150, 70, 158, 255, 45, 106, 112, 64, 156, 205, 249, 246, 162, 2, 33, 2, 131, 107, 20, 49, 73, 115, 255, 202, 251, 78, 27, 94, 59, 30, 0, 52, 234, 215, 127, 226, 35, 2, 123, 125, 132, 136, 119, 76, 199, 212, 121, 29, 4, 33, 3, 47, 54, 100, 94, 50, 107, 239, 164, 12, 188, 179, 239, 88, 99, 188, 152, 241, 183, 53, 150, 111, 248, 250, 102, 107, 204, 32, 226, 130, 199, 102, 79, 6, 33, 3, 19, 72, 214, 48, 188, 23, 62, 190, 235, 38, 128, 5, 162, 68, 173, 136, 99, 223, 44, 123, 227, 17, 94, 234, 194, 64, 151, 77, 42, 176, 29, 177, 8, 33, 3, 166, 184, 55, 239, 104, 226, 8, 194, 210, 64, 148, 27, 5, 252, 244, 78, 156, 173, 2, 67, 101, 93, 32, 179, 251, 58, 65, 191, 151, 163, 195, 0, 2, 2, 1, 224, 8, 34, 154, 13, 188, 186, 197, 129, 42, 234, 116, 174, 71, 179, 0, 176, 61, 111, 34, 233, 34, 212, 224, 37, 185, 65, 37, 30, 244, 58, 24, 36, 229, 161, 0, 0, 0, 0, 0, 0, 0, 61, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 100, 134, 192, 0, 0, 0, 0, 96, 191, 195, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0] - - ] - + + static var serializedChannelManager: [UInt8] = [ + 1, 1, 6, 34, 110, 70, 17, 26, 11, 89, 202, 175, 18, 96, 67, 235, 91, 191, 40, 195, 79, 58, 94, 51, 42, 31, 199, + 178, 183, 60, 241, 136, 145, 15, 0, 0, 2, 12, 245, 89, 30, 160, 182, 154, 227, 237, 192, 222, 17, 73, 127, 251, + 15, 221, 145, 247, 105, 237, 233, 108, 93, 102, 44, 128, 83, 100, 233, 191, 139, 34, 0, 0, 0, 0, 0, 0, 0, 1, 2, + 1, 0, 0, 0, 0, 0, 0, 0, 42, 0, 0, 0, 0, 0, 72, 0, 1, 191, 159, 45, 109, 250, 168, 172, 172, 111, 136, 62, 120, + 42, 195, 204, 192, 75, 139, 45, 252, 75, 128, 100, 201, 101, 164, 34, 25, 123, 118, 72, 238, 0, 0, 0, 192, 0, 0, + 0, 0, 0, 61, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 135, 1, 1, 147, 230, 63, 191, 15, 83, 126, 83, 56, 77, 244, + 16, 203, 26, 120, 61, 110, 159, 96, 102, 208, 42, 82, 219, 25, 231, 84, 15, 52, 140, 28, 78, 57, 168, 53, 136, + 69, 225, 211, 254, 158, 192, 210, 35, 192, 195, 39, 6, 152, 15, 119, 215, 69, 211, 64, 46, 92, 100, 212, 137, + 250, 191, 196, 128, 221, 81, 68, 184, 94, 227, 133, 148, 123, 187, 3, 192, 142, 63, 75, 230, 135, 110, 89, 69, + 117, 192, 101, 143, 160, 138, 107, 234, 215, 174, 191, 49, 194, 8, 77, 175, 34, 220, 38, 229, 18, 48, 135, 192, + 16, 201, 249, 9, 213, 46, 208, 175, 250, 76, 229, 139, 241, 13, 19, 19, 47, 101, 254, 35, 74, 113, 245, 15, 196, + 5, 95, 129, 67, 217, 245, 156, 121, 83, 201, 81, 20, 242, 100, 71, 120, 169, 162, 58, 109, 139, 107, 107, 139, + 16, 218, 193, 80, 220, 107, 17, 18, 1, 217, 220, 76, 95, 220, 67, 170, 89, 170, 51, 61, 254, 8, 52, 100, 179, + 249, 248, 78, 227, 128, 36, 42, 148, 113, 160, 253, 1, 154, 253, 1, 150, 0, 176, 175, 0, 33, 3, 65, 81, 5, 242, + 6, 238, 79, 27, 32, 59, 118, 145, 167, 132, 137, 105, 177, 225, 227, 93, 193, 58, 227, 5, 53, 171, 42, 71, 159, + 54, 228, 137, 2, 33, 2, 203, 126, 6, 106, 235, 15, 28, 5, 68, 63, 181, 110, 193, 14, 133, 104, 247, 56, 151, + 127, 3, 206, 216, 112, 103, 135, 168, 4, 221, 40, 233, 168, 4, 33, 2, 199, 21, 85, 32, 48, 247, 98, 188, 50, + 244, 157, 5, 216, 110, 59, 131, 207, 158, 110, 240, 52, 37, 9, 92, 74, 186, 97, 0, 75, 87, 159, 137, 6, 33, 3, + 224, 171, 107, 195, 109, 139, 39, 25, 93, 126, 4, 226, 210, 240, 59, 87, 50, 46, 230, 140, 144, 21, 16, 139, + 180, 199, 19, 170, 97, 177, 150, 42, 8, 33, 3, 228, 157, 142, 206, 28, 67, 191, 144, 5, 125, 60, 105, 113, 179, + 143, 252, 28, 51, 34, 73, 180, 64, 176, 121, 58, 0, 218, 153, 216, 218, 113, 28, 2, 2, 0, 144, 4, 1, 1, 6, 183, + 182, 0, 176, 175, 0, 33, 2, 9, 208, 118, 12, 208, 192, 203, 156, 37, 75, 5, 208, 168, 90, 146, 102, 106, 104, + 201, 48, 199, 244, 78, 242, 183, 129, 96, 108, 69, 110, 70, 142, 2, 33, 3, 162, 188, 220, 100, 190, 42, 74, 213, + 77, 38, 152, 16, 231, 110, 166, 29, 70, 240, 14, 32, 57, 24, 175, 118, 236, 36, 75, 201, 91, 93, 62, 106, 4, 33, + 2, 178, 61, 18, 201, 74, 208, 113, 218, 152, 49, 92, 52, 150, 191, 56, 239, 168, 32, 96, 86, 114, 21, 19, 108, + 171, 114, 233, 40, 204, 186, 169, 154, 6, 33, 2, 177, 134, 18, 222, 96, 37, 31, 216, 61, 52, 79, 185, 165, 149, + 65, 241, 204, 140, 168, 200, 2, 5, 142, 253, 20, 47, 215, 54, 34, 44, 29, 174, 8, 33, 3, 66, 55, 59, 134, 74, + 73, 106, 150, 243, 60, 164, 231, 147, 235, 222, 199, 46, 227, 12, 162, 208, 119, 225, 156, 136, 68, 61, 163, + 190, 101, 34, 69, 2, 2, 1, 224, 8, 34, 191, 159, 45, 109, 250, 168, 172, 172, 111, 136, 62, 120, 42, 195, 204, + 192, 75, 139, 45, 252, 75, 128, 100, 201, 101, 164, 34, 25, 123, 118, 72, 239, 0, 1, 0, 0, 0, 0, 0, 61, 9, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 97, 72, 0, 0, 0, 0, 0, 96, 191, 196, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 245, + 197, 45, 161, 157, 103, 143, 155, 92, 248, 240, 63, 44, 34, 79, 54, 82, 119, 12, 72, 194, 213, 81, 200, 20, 207, + 114, 193, 202, 119, 214, 206, 0, 22, 0, 20, 145, 136, 195, 71, 179, 2, 109, 136, 237, 2, 125, 149, 143, 194, + 231, 176, 239, 175, 178, 93, 0, 0, 255, 255, 255, 255, 255, 254, 0, 0, 255, 255, 255, 255, 255, 254, 0, 0, 0, 0, + 238, 76, 163, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 97, 1, 175, 91, 0, + 0, 0, 253, 0, 33, 202, 100, 121, 48, 88, 50, 11, 177, 75, 253, 189, 162, 177, 112, 244, 153, 140, 130, 154, 40, + 94, 28, 41, 10, 32, 219, 115, 52, 116, 218, 30, 50, 0, 0, 0, 209, 9, 0, 0, 209, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, + 2, 61, 0, 0, 0, 0, 0, 0, 1, 74, 0, 0, 0, 0, 236, 8, 206, 0, 0, 0, 0, 0, 0, 0, 156, 64, 0, 0, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 1, 1, 227, 0, 0, 0, 3, 1, 0, 0, 3, 232, 0, 0, 0, 1, 0, 40, 253, 1, 150, 0, 176, 175, 0, 33, + 3, 65, 81, 5, 242, 6, 238, 79, 27, 32, 59, 118, 145, 167, 132, 137, 105, 177, 225, 227, 93, 193, 58, 227, 5, 53, + 171, 42, 71, 159, 54, 228, 137, 2, 33, 2, 203, 126, 6, 106, 235, 15, 28, 5, 68, 63, 181, 110, 193, 14, 133, 104, + 247, 56, 151, 127, 3, 206, 216, 112, 103, 135, 168, 4, 221, 40, 233, 168, 4, 33, 2, 199, 21, 85, 32, 48, 247, + 98, 188, 50, 244, 157, 5, 216, 110, 59, 131, 207, 158, 110, 240, 52, 37, 9, 92, 74, 186, 97, 0, 75, 87, 159, + 137, 6, 33, 3, 224, 171, 107, 195, 109, 139, 39, 25, 93, 126, 4, 226, 210, 240, 59, 87, 50, 46, 230, 140, 144, + 21, 16, 139, 180, 199, 19, 170, 97, 177, 150, 42, 8, 33, 3, 228, 157, 142, 206, 28, 67, 191, 144, 5, 125, 60, + 105, 113, 179, 143, 252, 28, 51, 34, 73, 180, 64, 176, 121, 58, 0, 218, 153, 216, 218, 113, 28, 2, 2, 0, 144, 4, + 1, 1, 6, 183, 182, 0, 176, 175, 0, 33, 2, 9, 208, 118, 12, 208, 192, 203, 156, 37, 75, 5, 208, 168, 90, 146, + 102, 106, 104, 201, 48, 199, 244, 78, 242, 183, 129, 96, 108, 69, 110, 70, 142, 2, 33, 3, 162, 188, 220, 100, + 190, 42, 74, 213, 77, 38, 152, 16, 231, 110, 166, 29, 70, 240, 14, 32, 57, 24, 175, 118, 236, 36, 75, 201, 91, + 93, 62, 106, 4, 33, 2, 178, 61, 18, 201, 74, 208, 113, 218, 152, 49, 92, 52, 150, 191, 56, 239, 168, 32, 96, 86, + 114, 21, 19, 108, 171, 114, 233, 40, 204, 186, 169, 154, 6, 33, 2, 177, 134, 18, 222, 96, 37, 31, 216, 61, 52, + 79, 185, 165, 149, 65, 241, 204, 140, 168, 200, 2, 5, 142, 253, 20, 47, 215, 54, 34, 44, 29, 174, 8, 33, 3, 66, + 55, 59, 134, 74, 73, 106, 150, 243, 60, 164, 231, 147, 235, 222, 199, 46, 227, 12, 162, 208, 119, 225, 156, 136, + 68, 61, 163, 190, 101, 34, 69, 2, 2, 1, 224, 8, 34, 191, 159, 45, 109, 250, 168, 172, 172, 111, 136, 62, 120, + 42, 195, 204, 192, 75, 139, 45, 252, 75, 128, 100, 201, 101, 164, 34, 25, 123, 118, 72, 239, 0, 1, 235, 2, 0, 0, + 0, 0, 1, 1, 154, 13, 188, 186, 197, 129, 42, 234, 116, 174, 71, 179, 0, 176, 61, 111, 34, 233, 34, 212, 224, 37, + 185, 65, 37, 30, 244, 58, 24, 36, 229, 161, 1, 0, 0, 0, 0, 254, 255, 255, 255, 2, 224, 155, 186, 19, 1, 0, 0, 0, + 22, 0, 20, 185, 142, 196, 145, 56, 80, 17, 143, 150, 44, 150, 156, 89, 129, 82, 110, 103, 155, 34, 244, 0, 9, + 61, 0, 0, 0, 0, 0, 34, 0, 32, 108, 196, 111, 93, 122, 59, 15, 44, 192, 196, 18, 129, 59, 19, 54, 189, 120, 12, + 193, 178, 145, 181, 5, 118, 139, 228, 118, 195, 134, 243, 160, 36, 2, 71, 48, 68, 2, 32, 20, 230, 153, 69, 79, + 91, 73, 64, 131, 114, 150, 5, 36, 97, 7, 254, 202, 226, 8, 155, 55, 147, 153, 250, 248, 30, 248, 103, 247, 102, + 206, 172, 2, 32, 49, 90, 216, 205, 27, 63, 31, 87, 72, 33, 68, 5, 23, 227, 144, 105, 119, 159, 121, 225, 216, + 65, 222, 83, 160, 60, 96, 8, 206, 38, 27, 19, 1, 33, 2, 107, 52, 102, 240, 125, 164, 53, 66, 145, 238, 132, 161, + 173, 210, 61, 144, 141, 191, 118, 52, 39, 50, 206, 252, 54, 207, 141, 246, 5, 223, 65, 139, 208, 0, 0, 0, 34, 3, + 132, 66, 188, 167, 156, 18, 9, 88, 231, 15, 24, 99, 219, 187, 36, 30, 218, 94, 205, 177, 25, 135, 34, 109, 27, + 211, 58, 3, 151, 121, 9, 143, 34, 3, 0, 86, 208, 241, 10, 75, 8, 4, 213, 148, 254, 32, 58, 221, 32, 48, 55, 129, + 243, 231, 55, 161, 22, 68, 123, 234, 112, 238, 27, 248, 205, 175, 3, 76, 1, 164, 167, 52, 78, 65, 176, 169, 137, + 4, 159, 182, 49, 198, 72, 197, 210, 127, 107, 63, 166, 28, 124, 25, 59, 64, 220, 201, 106, 147, 65, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 41, 1, + 4, 0, 0, 0, 3, 3, 8, 0, 0, 0, 0, 0, 0, 156, 64, 5, 23, 22, 0, 4, 0, 0, 0, 0, 2, 2, 0, 72, 4, 1, 0, 6, 1, 1, 8, + 4, 0, 0, 3, 232, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 76, 1, 164, 167, 52, + 78, 65, 176, 169, 137, 4, 159, 182, 49, 198, 72, 197, 210, 127, 107, 63, 166, 28, 124, 25, 59, 64, 220, 201, + 106, 147, 65, 0, 3, 2, 82, 161, 2, 89, 164, 108, 52, 253, 52, 16, 98, 113, 175, 141, 110, 220, 214, 130, 103, + 215, 226, 16, 216, 211, 252, 9, 226, 0, 84, 129, 189, 201, 17, 146, 139, 0, 3, 2, 82, 161, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 97, 1, 175, 91, 97, 1, 175, 91, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ] + + + static var serializedChannelMonitors: [[UInt8]] = [ + [ + 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 30, 40, 198, 1, 17, 200, 0, 22, 0, 20, 145, 136, 195, 71, 179, 2, 109, 136, + 237, 2, 125, 149, 143, 194, 231, 176, 239, 175, 178, 93, 1, 0, 22, 0, 20, 238, 90, 239, 18, 253, 215, 67, + 147, 78, 231, 119, 204, 199, 41, 181, 112, 78, 156, 253, 165, 0, 22, 0, 20, 192, 215, 94, 229, 110, 19, 8, + 215, 222, 48, 2, 152, 139, 221, 240, 206, 172, 36, 24, 151, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 97, 72, + 0, 0, 0, 0, 0, 96, 191, 196, 32, 0, 0, 0, 0, 0, 0, 0, 0, 2, 203, 126, 6, 106, 235, 15, 28, 5, 68, 63, 181, + 110, 193, 14, 133, 104, 247, 56, 151, 127, 3, 206, 216, 112, 103, 135, 168, 4, 221, 40, 233, 168, 191, 159, + 45, 109, 250, 168, 172, 172, 111, 136, 62, 120, 42, 195, 204, 192, 75, 139, 45, 252, 75, 128, 100, 201, 101, + 164, 34, 25, 123, 118, 72, 239, 0, 1, 0, 34, 0, 32, 108, 196, 111, 93, 122, 59, 15, 44, 192, 196, 18, 129, + 59, 19, 54, 189, 120, 12, 193, 178, 145, 181, 5, 118, 139, 228, 118, 195, 134, 243, 160, 36, 33, 219, 63, + 105, 199, 123, 141, 196, 55, 169, 197, 67, 7, 149, 119, 53, 171, 18, 48, 200, 15, 101, 107, 205, 5, 48, 3, + 155, 165, 97, 42, 40, 171, 0, 0, 0, 0, 0, 0, 0, 0, 1, 219, 63, 105, 199, 123, 141, 196, 55, 169, 197, 67, 7, + 149, 119, 53, 171, 18, 48, 200, 15, 101, 107, 205, 5, 48, 3, 155, 165, 97, 42, 40, 171, 0, 0, 0, 0, 0, 0, 0, + 0, 74, 0, 33, 2, 177, 134, 18, 222, 96, 37, 31, 216, 61, 52, 79, 185, 165, 149, 65, 241, 204, 140, 168, 200, + 2, 5, 142, 253, 20, 47, 215, 54, 34, 44, 29, 174, 2, 33, 3, 66, 55, 59, 134, 74, 73, 106, 150, 243, 60, 164, + 231, 147, 235, 222, 199, 46, 227, 12, 162, 208, 119, 225, 156, 136, 68, 61, 163, 190, 101, 34, 69, 4, 2, 0, + 144, 0, 71, 82, 33, 2, 9, 208, 118, 12, 208, 192, 203, 156, 37, 75, 5, 208, 168, 90, 146, 102, 106, 104, + 201, 48, 199, 244, 78, 242, 183, 129, 96, 108, 69, 110, 70, 142, 33, 3, 65, 81, 5, 242, 6, 238, 79, 27, 32, + 59, 118, 145, 167, 132, 137, 105, 177, 225, 227, 93, 193, 58, 227, 5, 53, 171, 42, 71, 159, 54, 228, 137, + 82, 174, 0, 0, 0, 0, 0, 61, 9, 0, 255, 255, 255, 255, 255, 255, 3, 0, 86, 208, 241, 10, 75, 8, 4, 213, 148, + 254, 32, 58, 221, 32, 48, 55, 129, 243, 231, 55, 161, 22, 68, 123, 234, 112, 238, 27, 248, 205, 175, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 224, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 219, 63, 105, 199, 123, 141, 196, 55, + 169, 197, 67, 7, 149, 119, 53, 171, 18, 48, 200, 15, 101, 107, 205, 5, 48, 3, 155, 165, 97, 42, 40, 171, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 217, 0, 32, 159, 172, 119, 230, 40, + 58, 126, 90, 111, 203, 46, 52, 112, 231, 92, 43, 111, 35, 175, 215, 162, 32, 49, 63, 57, 113, 52, 230, 222, + 96, 187, 148, 2, 33, 2, 35, 64, 218, 18, 4, 249, 115, 136, 2, 54, 84, 146, 76, 195, 215, 96, 56, 163, 104, + 200, 15, 236, 248, 96, 44, 233, 175, 85, 134, 23, 250, 36, 4, 33, 3, 197, 119, 156, 31, 150, 55, 152, 97, + 249, 84, 166, 133, 29, 202, 191, 246, 151, 91, 211, 173, 24, 119, 183, 130, 203, 125, 106, 112, 18, 199, + 121, 204, 6, 33, 3, 234, 174, 250, 108, 146, 112, 48, 206, 214, 198, 65, 49, 95, 253, 86, 144, 79, 91, 252, + 58, 63, 168, 56, 74, 24, 225, 247, 74, 24, 57, 24, 13, 8, 33, 3, 12, 25, 187, 117, 210, 189, 73, 106, 109, + 46, 84, 178, 53, 58, 54, 6, 167, 133, 229, 79, 99, 206, 98, 142, 173, 34, 152, 81, 255, 169, 63, 116, 10, + 33, 3, 183, 124, 104, 211, 18, 92, 225, 209, 51, 56, 194, 230, 175, 231, 137, 80, 191, 21, 133, 145, 214, + 173, 30, 183, 36, 188, 81, 232, 22, 193, 208, 182, 12, 4, 0, 0, 0, 253, 14, 0, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 223, + 70, 242, 96, 28, 101, 156, 25, 147, 227, 151, 92, 34, 232, 55, 154, 102, 10, 133, 4, 212, 55, 146, 220, 238, + 62, 142, 84, 143, 27, 124, 37, 0, 0, 0, 208, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 191, 159, 45, + 109, 250, 168, 172, 172, 111, 136, 62, 120, 42, 195, 204, 192, 75, 139, 45, 252, 75, 128, 100, 201, 101, + 164, 34, 25, 123, 118, 72, 239, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 34, 0, 32, 108, 196, 111, 93, 122, + 59, 15, 44, 192, 196, 18, 129, 59, 19, 54, 189, 120, 12, 193, 178, 145, 181, 5, 118, 139, 228, 118, 195, + 134, 243, 160, 36, 1, 1, 0, 22, 0, 20, 145, 136, 195, 71, 179, 2, 109, 136, 237, 2, 125, 149, 143, 194, 231, + 176, 239, 175, 178, 93, 253, 1, 202, 0, 253, 1, 127, 253, 1, 124, 0, 8, 0, 0, 255, 255, 255, 255, 255, 255, + 2, 8, 0, 0, 0, 0, 0, 61, 0, 121, 4, 8, 0, 0, 0, 0, 0, 0, 7, 208, 6, 4, 0, 0, 0, 253, 8, 176, 175, 0, 33, 3, + 183, 124, 104, 211, 18, 92, 225, 209, 51, 56, 194, 230, 175, 231, 137, 80, 191, 21, 133, 145, 214, 173, 30, + 183, 36, 188, 81, 232, 22, 193, 208, 182, 2, 33, 2, 35, 64, 218, 18, 4, 249, 115, 136, 2, 54, 84, 146, 76, + 195, 215, 96, 56, 163, 104, 200, 15, 236, 248, 96, 44, 233, 175, 85, 134, 23, 250, 36, 4, 33, 3, 197, 119, + 156, 31, 150, 55, 152, 97, 249, 84, 166, 133, 29, 202, 191, 246, 151, 91, 211, 173, 24, 119, 183, 130, 203, + 125, 106, 112, 18, 199, 121, 204, 6, 33, 3, 234, 174, 250, 108, 146, 112, 48, 206, 214, 198, 65, 49, 95, + 253, 86, 144, 79, 91, 252, 58, 63, 168, 56, 74, 24, 225, 247, 74, 24, 57, 24, 13, 8, 33, 3, 12, 25, 187, + 117, 210, 189, 73, 106, 109, 46, 84, 178, 53, 58, 54, 6, 167, 133, 229, 79, 99, 206, 98, 142, 173, 34, 152, + 81, 255, 169, 63, 116, 10, 162, 161, 0, 125, 2, 0, 0, 0, 1, 191, 159, 45, 109, 250, 168, 172, 172, 111, 136, + 62, 120, 42, 195, 204, 192, 75, 139, 45, 252, 75, 128, 100, 201, 101, 164, 34, 25, 123, 118, 72, 239, 1, 0, + 0, 0, 0, 198, 40, 30, 128, 2, 208, 7, 0, 0, 0, 0, 0, 0, 22, 0, 20, 158, 138, 199, 245, 61, 146, 58, 33, 73, + 28, 13, 90, 209, 193, 239, 77, 44, 183, 84, 73, 121, 0, 61, 0, 0, 0, 0, 0, 34, 0, 32, 127, 234, 213, 198, + 76, 150, 85, 149, 212, 195, 188, 50, 11, 120, 159, 184, 238, 141, 165, 16, 149, 41, 94, 123, 67, 222, 133, + 179, 15, 191, 148, 162, 200, 17, 1, 32, 2, 32, 159, 172, 119, 230, 40, 58, 126, 90, 111, 203, 46, 52, 112, + 231, 92, 43, 111, 35, 175, 215, 162, 32, 49, 63, 57, 113, 52, 230, 222, 96, 187, 148, 12, 0, 2, 64, 36, 29, + 16, 35, 185, 254, 159, 18, 170, 41, 231, 13, 99, 119, 111, 255, 254, 111, 151, 196, 28, 113, 242, 154, 241, + 244, 123, 4, 100, 140, 47, 248, 59, 104, 147, 154, 140, 13, 209, 195, 14, 222, 11, 211, 163, 116, 234, 181, + 25, 71, 247, 230, 223, 196, 158, 67, 199, 23, 187, 221, 72, 193, 234, 82, 4, 1, 0, 6, 0, 0, 0, 0, 253, 1, + 150, 0, 176, 175, 0, 33, 3, 65, 81, 5, 242, 6, 238, 79, 27, 32, 59, 118, 145, 167, 132, 137, 105, 177, 225, + 227, 93, 193, 58, 227, 5, 53, 171, 42, 71, 159, 54, 228, 137, 2, 33, 2, 203, 126, 6, 106, 235, 15, 28, 5, + 68, 63, 181, 110, 193, 14, 133, 104, 247, 56, 151, 127, 3, 206, 216, 112, 103, 135, 168, 4, 221, 40, 233, + 168, 4, 33, 2, 199, 21, 85, 32, 48, 247, 98, 188, 50, 244, 157, 5, 216, 110, 59, 131, 207, 158, 110, 240, + 52, 37, 9, 92, 74, 186, 97, 0, 75, 87, 159, 137, 6, 33, 3, 224, 171, 107, 195, 109, 139, 39, 25, 93, 126, 4, + 226, 210, 240, 59, 87, 50, 46, 230, 140, 144, 21, 16, 139, 180, 199, 19, 170, 97, 177, 150, 42, 8, 33, 3, + 228, 157, 142, 206, 28, 67, 191, 144, 5, 125, 60, 105, 113, 179, 143, 252, 28, 51, 34, 73, 180, 64, 176, + 121, 58, 0, 218, 153, 216, 218, 113, 28, 2, 2, 0, 144, 4, 1, 1, 6, 183, 182, 0, 176, 175, 0, 33, 2, 9, 208, + 118, 12, 208, 192, 203, 156, 37, 75, 5, 208, 168, 90, 146, 102, 106, 104, 201, 48, 199, 244, 78, 242, 183, + 129, 96, 108, 69, 110, 70, 142, 2, 33, 3, 162, 188, 220, 100, 190, 42, 74, 213, 77, 38, 152, 16, 231, 110, + 166, 29, 70, 240, 14, 32, 57, 24, 175, 118, 236, 36, 75, 201, 91, 93, 62, 106, 4, 33, 2, 178, 61, 18, 201, + 74, 208, 113, 218, 152, 49, 92, 52, 150, 191, 56, 239, 168, 32, 96, 86, 114, 21, 19, 108, 171, 114, 233, 40, + 204, 186, 169, 154, 6, 33, 2, 177, 134, 18, 222, 96, 37, 31, 216, 61, 52, 79, 185, 165, 149, 65, 241, 204, + 140, 168, 200, 2, 5, 142, 253, 20, 47, 215, 54, 34, 44, 29, 174, 8, 33, 3, 66, 55, 59, 134, 74, 73, 106, + 150, 243, 60, 164, 231, 147, 235, 222, 199, 46, 227, 12, 162, 208, 119, 225, 156, 136, 68, 61, 163, 190, + 101, 34, 69, 2, 2, 1, 224, 8, 34, 191, 159, 45, 109, 250, 168, 172, 172, 111, 136, 62, 120, 42, 195, 204, + 192, 75, 139, 45, 252, 75, 128, 100, 201, 101, 164, 34, 25, 123, 118, 72, 239, 0, 1, 0, 0, 2, 135, 1, 1, + 147, 230, 63, 191, 15, 83, 126, 83, 56, 77, 244, 16, 203, 26, 120, 61, 110, 159, 96, 102, 208, 42, 82, 219, + 25, 231, 84, 15, 52, 140, 28, 78, 57, 168, 53, 136, 69, 225, 211, 254, 158, 192, 210, 35, 192, 195, 39, 6, + 152, 15, 119, 215, 69, 211, 64, 46, 92, 100, 212, 137, 250, 191, 196, 128, 221, 81, 68, 184, 94, 227, 133, + 148, 123, 187, 3, 192, 142, 63, 75, 230, 135, 110, 89, 69, 117, 192, 101, 143, 160, 138, 107, 234, 215, 174, + 191, 49, 194, 8, 77, 175, 34, 220, 38, 229, 18, 48, 135, 192, 16, 201, 249, 9, 213, 46, 208, 175, 250, 76, + 229, 139, 241, 13, 19, 19, 47, 101, 254, 35, 74, 113, 245, 15, 196, 5, 95, 129, 67, 217, 245, 156, 121, 83, + 201, 81, 20, 242, 100, 71, 120, 169, 162, 58, 109, 139, 107, 107, 139, 16, 218, 193, 80, 220, 107, 17, 18, + 1, 217, 220, 76, 95, 220, 67, 170, 89, 170, 51, 61, 254, 8, 52, 100, 179, 249, 248, 78, 227, 128, 36, 42, + 148, 113, 160, 253, 1, 154, 253, 1, 150, 0, 176, 175, 0, 33, 3, 65, 81, 5, 242, 6, 238, 79, 27, 32, 59, 118, + 145, 167, 132, 137, 105, 177, 225, 227, 93, 193, 58, 227, 5, 53, 171, 42, 71, 159, 54, 228, 137, 2, 33, 2, + 203, 126, 6, 106, 235, 15, 28, 5, 68, 63, 181, 110, 193, 14, 133, 104, 247, 56, 151, 127, 3, 206, 216, 112, + 103, 135, 168, 4, 221, 40, 233, 168, 4, 33, 2, 199, 21, 85, 32, 48, 247, 98, 188, 50, 244, 157, 5, 216, 110, + 59, 131, 207, 158, 110, 240, 52, 37, 9, 92, 74, 186, 97, 0, 75, 87, 159, 137, 6, 33, 3, 224, 171, 107, 195, + 109, 139, 39, 25, 93, 126, 4, 226, 210, 240, 59, 87, 50, 46, 230, 140, 144, 21, 16, 139, 180, 199, 19, 170, + 97, 177, 150, 42, 8, 33, 3, 228, 157, 142, 206, 28, 67, 191, 144, 5, 125, 60, 105, 113, 179, 143, 252, 28, + 51, 34, 73, 180, 64, 176, 121, 58, 0, 218, 153, 216, 218, 113, 28, 2, 2, 0, 144, 4, 1, 1, 6, 183, 182, 0, + 176, 175, 0, 33, 2, 9, 208, 118, 12, 208, 192, 203, 156, 37, 75, 5, 208, 168, 90, 146, 102, 106, 104, 201, + 48, 199, 244, 78, 242, 183, 129, 96, 108, 69, 110, 70, 142, 2, 33, 3, 162, 188, 220, 100, 190, 42, 74, 213, + 77, 38, 152, 16, 231, 110, 166, 29, 70, 240, 14, 32, 57, 24, 175, 118, 236, 36, 75, 201, 91, 93, 62, 106, 4, + 33, 2, 178, 61, 18, 201, 74, 208, 113, 218, 152, 49, 92, 52, 150, 191, 56, 239, 168, 32, 96, 86, 114, 21, + 19, 108, 171, 114, 233, 40, 204, 186, 169, 154, 6, 33, 2, 177, 134, 18, 222, 96, 37, 31, 216, 61, 52, 79, + 185, 165, 149, 65, 241, 204, 140, 168, 200, 2, 5, 142, 253, 20, 47, 215, 54, 34, 44, 29, 174, 8, 33, 3, 66, + 55, 59, 134, 74, 73, 106, 150, 243, 60, 164, 231, 147, 235, 222, 199, 46, 227, 12, 162, 208, 119, 225, 156, + 136, 68, 61, 163, 190, 101, 34, 69, 2, 2, 1, 224, 8, 34, 191, 159, 45, 109, 250, 168, 172, 172, 111, 136, + 62, 120, 42, 195, 204, 192, 75, 139, 45, 252, 75, 128, 100, 201, 101, 164, 34, 25, 123, 118, 72, 239, 0, 1, + 0, 0, 0, 0, 0, 61, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 97, 72, 0, 0, 0, 0, 0, 96, 191, 196, 32, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + ], + + + [ + 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 236, 131, 59, 149, 183, 100, 0, 22, 0, 20, 145, 136, 195, 71, 179, 2, 109, + 136, 237, 2, 125, 149, 143, 194, 231, 176, 239, 175, 178, 93, 1, 0, 22, 0, 20, 161, 221, 204, 111, 11, 179, + 246, 145, 99, 90, 218, 176, 191, 104, 67, 159, 97, 148, 87, 202, 0, 22, 0, 20, 192, 215, 94, 229, 110, 19, + 8, 215, 222, 48, 2, 152, 139, 221, 240, 206, 172, 36, 24, 151, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 134, 68, + 218, 0, 0, 0, 0, 0, 96, 191, 186, 40, 0, 0, 0, 0, 0, 0, 0, 0, 3, 23, 13, 54, 171, 102, 235, 221, 53, 72, 51, + 123, 135, 168, 120, 42, 196, 140, 24, 148, 29, 145, 100, 130, 234, 216, 167, 232, 71, 57, 125, 56, 47, 91, + 35, 204, 7, 40, 172, 92, 136, 96, 211, 177, 34, 85, 186, 249, 35, 131, 99, 50, 97, 93, 4, 51, 237, 227, 6, + 200, 26, 22, 65, 195, 203, 0, 0, 0, 34, 0, 32, 82, 46, 216, 210, 105, 33, 126, 151, 160, 20, 7, 228, 176, + 229, 72, 23, 105, 86, 73, 149, 223, 219, 16, 118, 197, 187, 107, 94, 209, 250, 216, 78, 33, 65, 156, 247, + 109, 127, 254, 158, 108, 200, 38, 243, 31, 104, 221, 186, 93, 70, 193, 20, 123, 6, 4, 29, 172, 181, 252, + 223, 45, 20, 107, 32, 135, 0, 0, 0, 0, 0, 0, 0, 0, 1, 65, 156, 247, 109, 127, 254, 158, 108, 200, 38, 243, + 31, 104, 221, 186, 93, 70, 193, 20, 123, 6, 4, 29, 172, 181, 252, 223, 45, 20, 107, 32, 135, 0, 0, 0, 0, 0, + 0, 0, 0, 74, 0, 33, 2, 152, 85, 104, 139, 85, 153, 234, 236, 90, 40, 197, 81, 80, 44, 83, 14, 122, 205, 217, + 156, 94, 228, 98, 195, 142, 135, 238, 99, 67, 170, 105, 66, 2, 33, 3, 144, 195, 213, 137, 63, 56, 194, 230, + 34, 123, 208, 88, 201, 237, 40, 139, 252, 84, 50, 195, 250, 132, 85, 190, 7, 124, 53, 193, 76, 135, 121, 47, + 4, 2, 0, 144, 0, 71, 82, 33, 2, 164, 196, 150, 245, 129, 182, 7, 197, 177, 15, 83, 193, 210, 169, 117, 73, + 65, 134, 69, 254, 31, 49, 113, 230, 125, 220, 109, 248, 62, 130, 29, 49, 33, 3, 207, 77, 43, 188, 237, 132, + 89, 12, 108, 132, 241, 229, 2, 175, 137, 205, 77, 207, 248, 46, 129, 106, 11, 120, 84, 188, 31, 49, 63, 25, + 54, 18, 82, 174, 0, 0, 0, 0, 0, 76, 75, 64, 255, 255, 255, 255, 255, 255, 2, 73, 220, 31, 99, 179, 103, 241, + 56, 48, 5, 254, 221, 119, 137, 101, 225, 101, 105, 13, 131, 151, 152, 36, 137, 57, 168, 60, 187, 133, 188, + 199, 130, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2, 88, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 65, 156, 247, 109, 127, + 254, 158, 108, 200, 38, 243, 31, 104, 221, 186, 93, 70, 193, 20, 123, 6, 4, 29, 172, 181, 252, 223, 45, 20, + 107, 32, 135, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 217, 0, 32, 155, + 244, 216, 101, 5, 48, 111, 226, 29, 93, 119, 147, 99, 227, 192, 109, 17, 234, 31, 188, 129, 168, 122, 41, 1, + 64, 101, 206, 39, 33, 255, 220, 2, 33, 2, 8, 238, 253, 199, 52, 195, 158, 109, 241, 83, 88, 217, 70, 156, + 184, 137, 45, 148, 122, 15, 222, 224, 225, 162, 193, 73, 88, 144, 221, 15, 126, 249, 4, 33, 3, 241, 43, 220, + 67, 128, 63, 217, 154, 222, 72, 132, 6, 180, 230, 135, 165, 143, 41, 61, 89, 68, 197, 31, 179, 51, 116, 206, + 14, 18, 243, 140, 88, 6, 33, 3, 105, 237, 50, 191, 124, 241, 25, 13, 235, 218, 76, 194, 23, 169, 14, 219, + 191, 190, 77, 48, 45, 180, 98, 13, 248, 136, 186, 65, 169, 139, 144, 151, 8, 33, 2, 218, 184, 146, 140, 238, + 249, 174, 199, 13, 91, 216, 71, 208, 36, 136, 22, 81, 16, 241, 205, 134, 158, 5, 171, 136, 104, 25, 246, + 112, 206, 153, 7, 10, 33, 3, 65, 97, 207, 152, 180, 230, 235, 248, 168, 122, 180, 208, 117, 127, 52, 219, + 42, 180, 105, 146, 201, 237, 75, 209, 156, 97, 41, 182, 138, 244, 160, 125, 12, 4, 0, 0, 0, 253, 14, 0, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 252, 210, + 247, 104, 109, 8, 154, 117, 69, 168, 240, 210, 1, 217, 199, 86, 37, 35, 111, 204, 18, 181, 19, 9, 74, 135, + 216, 183, 217, 240, 34, 85, 0, 0, 0, 185, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 91, 35, 204, 7, + 40, 172, 92, 136, 96, 211, 177, 34, 85, 186, 249, 35, 131, 99, 50, 97, 93, 4, 51, 237, 227, 6, 200, 26, 22, + 65, 195, 203, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 34, 0, 32, 82, 46, 216, 210, 105, 33, 126, 151, 160, + 20, 7, 228, 176, 229, 72, 23, 105, 86, 73, 149, 223, 219, 16, 118, 197, 187, 107, 94, 209, 250, 216, 78, 1, + 1, 0, 22, 0, 20, 145, 136, 195, 71, 179, 2, 109, 136, 237, 2, 125, 149, 143, 194, 231, 176, 239, 175, 178, + 93, 253, 1, 171, 0, 253, 1, 96, 253, 1, 93, 0, 8, 0, 0, 255, 255, 255, 255, 255, 255, 2, 8, 0, 0, 0, 0, 0, + 76, 74, 137, 4, 8, 0, 0, 0, 0, 0, 0, 0, 0, 6, 4, 0, 0, 0, 253, 8, 176, 175, 0, 33, 3, 65, 97, 207, 152, 180, + 230, 235, 248, 168, 122, 180, 208, 117, 127, 52, 219, 42, 180, 105, 146, 201, 237, 75, 209, 156, 97, 41, + 182, 138, 244, 160, 125, 2, 33, 2, 8, 238, 253, 199, 52, 195, 158, 109, 241, 83, 88, 217, 70, 156, 184, 137, + 45, 148, 122, 15, 222, 224, 225, 162, 193, 73, 88, 144, 221, 15, 126, 249, 4, 33, 3, 241, 43, 220, 67, 128, + 63, 217, 154, 222, 72, 132, 6, 180, 230, 135, 165, 143, 41, 61, 89, 68, 197, 31, 179, 51, 116, 206, 14, 18, + 243, 140, 88, 6, 33, 3, 105, 237, 50, 191, 124, 241, 25, 13, 235, 218, 76, 194, 23, 169, 14, 219, 191, 190, + 77, 48, 45, 180, 98, 13, 248, 136, 186, 65, 169, 139, 144, 151, 8, 33, 2, 218, 184, 146, 140, 238, 249, 174, + 199, 13, 91, 216, 71, 208, 36, 136, 22, 81, 16, 241, 205, 134, 158, 5, 171, 136, 104, 25, 246, 112, 206, + 153, 7, 10, 131, 130, 0, 94, 2, 0, 0, 0, 1, 91, 35, 204, 7, 40, 172, 92, 136, 96, 211, 177, 34, 85, 186, + 249, 35, 131, 99, 50, 97, 93, 4, 51, 237, 227, 6, 200, 26, 22, 65, 195, 203, 0, 0, 0, 0, 0, 59, 131, 236, + 128, 1, 137, 74, 76, 0, 0, 0, 0, 0, 34, 0, 32, 199, 233, 137, 95, 155, 13, 211, 124, 205, 61, 159, 18, 18, + 1, 177, 165, 199, 45, 94, 199, 12, 153, 70, 38, 28, 92, 29, 59, 5, 189, 93, 21, 100, 183, 149, 32, 2, 32, + 155, 244, 216, 101, 5, 48, 111, 226, 29, 93, 119, 147, 99, 227, 192, 109, 17, 234, 31, 188, 129, 168, 122, + 41, 1, 64, 101, 206, 39, 33, 255, 220, 12, 0, 2, 64, 31, 9, 22, 9, 117, 55, 108, 96, 253, 58, 180, 222, 109, + 69, 204, 74, 130, 239, 96, 253, 96, 169, 90, 102, 139, 213, 158, 193, 20, 168, 128, 229, 110, 230, 53, 239, + 30, 72, 185, 237, 83, 104, 232, 203, 226, 14, 222, 24, 129, 219, 101, 121, 154, 240, 57, 78, 79, 26, 27, 58, + 89, 152, 157, 98, 4, 1, 0, 6, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 253, 1, 150, 0, 176, 175, 0, 33, 3, 207, + 77, 43, 188, 237, 132, 89, 12, 108, 132, 241, 229, 2, 175, 137, 205, 77, 207, 248, 46, 129, 106, 11, 120, + 84, 188, 31, 49, 63, 25, 54, 18, 2, 33, 3, 23, 13, 54, 171, 102, 235, 221, 53, 72, 51, 123, 135, 168, 120, + 42, 196, 140, 24, 148, 29, 145, 100, 130, 234, 216, 167, 232, 71, 57, 125, 56, 47, 4, 33, 3, 187, 164, 187, + 3, 110, 52, 229, 43, 24, 114, 6, 145, 246, 57, 37, 172, 141, 9, 133, 48, 11, 119, 246, 98, 33, 51, 99, 197, + 18, 56, 172, 221, 6, 33, 2, 101, 114, 44, 129, 63, 56, 252, 19, 86, 109, 254, 27, 72, 38, 51, 22, 165, 207, + 136, 9, 57, 181, 188, 19, 234, 3, 12, 19, 79, 177, 106, 13, 8, 33, 2, 90, 230, 93, 154, 193, 232, 90, 118, + 22, 154, 243, 207, 181, 143, 116, 105, 102, 164, 130, 200, 84, 178, 159, 186, 250, 165, 117, 186, 254, 189, + 194, 122, 2, 2, 0, 144, 4, 1, 1, 6, 183, 182, 0, 176, 175, 0, 33, 2, 164, 196, 150, 245, 129, 182, 7, 197, + 177, 15, 83, 193, 210, 169, 117, 73, 65, 134, 69, 254, 31, 49, 113, 230, 125, 220, 109, 248, 62, 130, 29, + 49, 2, 33, 2, 217, 42, 5, 93, 111, 197, 212, 186, 244, 26, 75, 81, 239, 65, 189, 247, 190, 38, 250, 45, 167, + 27, 107, 163, 138, 142, 159, 137, 27, 249, 89, 70, 4, 33, 2, 100, 85, 119, 59, 244, 239, 219, 228, 184, 151, + 140, 250, 74, 100, 201, 239, 94, 13, 164, 2, 38, 36, 191, 167, 28, 125, 34, 72, 243, 111, 213, 245, 6, 33, + 2, 152, 85, 104, 139, 85, 153, 234, 236, 90, 40, 197, 81, 80, 44, 83, 14, 122, 205, 217, 156, 94, 228, 98, + 195, 142, 135, 238, 99, 67, 170, 105, 66, 8, 33, 3, 144, 195, 213, 137, 63, 56, 194, 230, 34, 123, 208, 88, + 201, 237, 40, 139, 252, 84, 50, 195, 250, 132, 85, 190, 7, 124, 53, 193, 76, 135, 121, 47, 2, 2, 2, 88, 8, + 34, 91, 35, 204, 7, 40, 172, 92, 136, 96, 211, 177, 34, 85, 186, 249, 35, 131, 99, 50, 97, 93, 4, 51, 237, + 227, 6, 200, 26, 22, 65, 195, 203, 0, 0, 0, 0, 2, 135, 1, 1, 93, 46, 72, 106, 26, 123, 101, 185, 30, 186, + 248, 204, 231, 156, 202, 142, 45, 144, 5, 101, 0, 47, 36, 180, 149, 105, 185, 84, 254, 140, 28, 198, 122, + 178, 219, 13, 214, 224, 109, 222, 134, 124, 111, 87, 123, 65, 66, 207, 29, 233, 102, 31, 175, 229, 204, 95, + 75, 150, 149, 139, 84, 28, 247, 223, 225, 133, 3, 176, 225, 155, 46, 173, 30, 93, 82, 153, 169, 186, 236, + 245, 62, 75, 118, 157, 228, 135, 60, 189, 67, 188, 146, 123, 170, 132, 83, 236, 206, 88, 186, 92, 167, 23, + 205, 159, 149, 102, 172, 66, 226, 42, 119, 142, 52, 42, 202, 0, 192, 87, 15, 45, 84, 78, 98, 214, 187, 142, + 70, 31, 220, 16, 185, 91, 20, 222, 210, 110, 129, 134, 208, 94, 155, 234, 53, 52, 167, 244, 133, 111, 15, + 196, 246, 107, 138, 24, 250, 22, 239, 207, 99, 174, 166, 81, 154, 220, 137, 189, 181, 135, 230, 189, 58, 47, + 164, 212, 192, 222, 165, 39, 88, 76, 196, 77, 9, 92, 6, 169, 199, 136, 188, 140, 227, 56, 253, 1, 154, 253, + 1, 150, 0, 176, 175, 0, 33, 3, 207, 77, 43, 188, 237, 132, 89, 12, 108, 132, 241, 229, 2, 175, 137, 205, 77, + 207, 248, 46, 129, 106, 11, 120, 84, 188, 31, 49, 63, 25, 54, 18, 2, 33, 3, 23, 13, 54, 171, 102, 235, 221, + 53, 72, 51, 123, 135, 168, 120, 42, 196, 140, 24, 148, 29, 145, 100, 130, 234, 216, 167, 232, 71, 57, 125, + 56, 47, 4, 33, 3, 187, 164, 187, 3, 110, 52, 229, 43, 24, 114, 6, 145, 246, 57, 37, 172, 141, 9, 133, 48, + 11, 119, 246, 98, 33, 51, 99, 197, 18, 56, 172, 221, 6, 33, 2, 101, 114, 44, 129, 63, 56, 252, 19, 86, 109, + 254, 27, 72, 38, 51, 22, 165, 207, 136, 9, 57, 181, 188, 19, 234, 3, 12, 19, 79, 177, 106, 13, 8, 33, 2, 90, + 230, 93, 154, 193, 232, 90, 118, 22, 154, 243, 207, 181, 143, 116, 105, 102, 164, 130, 200, 84, 178, 159, + 186, 250, 165, 117, 186, 254, 189, 194, 122, 2, 2, 0, 144, 4, 1, 1, 6, 183, 182, 0, 176, 175, 0, 33, 2, 164, + 196, 150, 245, 129, 182, 7, 197, 177, 15, 83, 193, 210, 169, 117, 73, 65, 134, 69, 254, 31, 49, 113, 230, + 125, 220, 109, 248, 62, 130, 29, 49, 2, 33, 2, 217, 42, 5, 93, 111, 197, 212, 186, 244, 26, 75, 81, 239, 65, + 189, 247, 190, 38, 250, 45, 167, 27, 107, 163, 138, 142, 159, 137, 27, 249, 89, 70, 4, 33, 2, 100, 85, 119, + 59, 244, 239, 219, 228, 184, 151, 140, 250, 74, 100, 201, 239, 94, 13, 164, 2, 38, 36, 191, 167, 28, 125, + 34, 72, 243, 111, 213, 245, 6, 33, 2, 152, 85, 104, 139, 85, 153, 234, 236, 90, 40, 197, 81, 80, 44, 83, 14, + 122, 205, 217, 156, 94, 228, 98, 195, 142, 135, 238, 99, 67, 170, 105, 66, 8, 33, 3, 144, 195, 213, 137, 63, + 56, 194, 230, 34, 123, 208, 88, 201, 237, 40, 139, 252, 84, 50, 195, 250, 132, 85, 190, 7, 124, 53, 193, 76, + 135, 121, 47, 2, 2, 2, 88, 8, 34, 91, 35, 204, 7, 40, 172, 92, 136, 96, 211, 177, 34, 85, 186, 249, 35, 131, + 99, 50, 97, 93, 4, 51, 237, 227, 6, 200, 26, 22, 65, 195, 203, 0, 0, 0, 0, 0, 0, 0, 76, 75, 64, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 134, 68, 218, 0, 0, 0, 0, 0, 96, 191, 186, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, + ], + + + [ + 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 121, 243, 156, 122, 253, 33, 0, 22, 0, 20, 145, 136, 195, 71, 179, 2, 109, + 136, 237, 2, 125, 149, 143, 194, 231, 176, 239, 175, 178, 93, 1, 0, 22, 0, 20, 156, 136, 155, 37, 207, 130, + 41, 147, 38, 118, 132, 240, 2, 26, 207, 64, 43, 77, 213, 107, 0, 22, 0, 20, 192, 215, 94, 229, 110, 19, 8, + 215, 222, 48, 2, 152, 139, 221, 240, 206, 172, 36, 24, 151, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 100, + 134, 192, 0, 0, 0, 0, 96, 191, 195, 3, 0, 0, 0, 0, 0, 0, 0, 0, 3, 169, 210, 205, 107, 151, 19, 175, 99, 240, + 89, 255, 218, 111, 14, 229, 169, 203, 192, 95, 238, 36, 218, 87, 24, 251, 188, 54, 20, 110, 134, 229, 112, + 154, 13, 188, 186, 197, 129, 42, 234, 116, 174, 71, 179, 0, 176, 61, 111, 34, 233, 34, 212, 224, 37, 185, + 65, 37, 30, 244, 58, 24, 36, 229, 161, 0, 0, 0, 34, 0, 32, 134, 213, 198, 74, 204, 68, 167, 226, 115, 12, + 209, 241, 25, 111, 194, 124, 75, 139, 185, 58, 192, 212, 241, 159, 105, 48, 111, 220, 54, 195, 100, 57, 33, + 182, 220, 153, 104, 164, 214, 74, 136, 37, 123, 219, 109, 224, 88, 133, 109, 145, 88, 101, 233, 60, 238, + 104, 7, 175, 138, 175, 69, 223, 108, 52, 178, 0, 0, 0, 0, 0, 0, 0, 0, 1, 182, 220, 153, 104, 164, 214, 74, + 136, 37, 123, 219, 109, 224, 88, 133, 109, 145, 88, 101, 233, 60, 238, 104, 7, 175, 138, 175, 69, 223, 108, + 52, 178, 0, 0, 0, 0, 0, 0, 0, 0, 74, 0, 33, 3, 19, 72, 214, 48, 188, 23, 62, 190, 235, 38, 128, 5, 162, 68, + 173, 136, 99, 223, 44, 123, 227, 17, 94, 234, 194, 64, 151, 77, 42, 176, 29, 177, 2, 33, 3, 166, 184, 55, + 239, 104, 226, 8, 194, 210, 64, 148, 27, 5, 252, 244, 78, 156, 173, 2, 67, 101, 93, 32, 179, 251, 58, 65, + 191, 151, 163, 195, 0, 4, 2, 0, 144, 0, 71, 82, 33, 2, 72, 97, 154, 95, 180, 18, 20, 112, 153, 254, 80, 92, + 186, 183, 24, 213, 85, 123, 40, 150, 70, 158, 255, 45, 106, 112, 64, 156, 205, 249, 246, 162, 33, 3, 182, + 71, 151, 63, 158, 166, 139, 175, 33, 191, 184, 2, 31, 80, 80, 175, 195, 243, 170, 48, 97, 168, 182, 205, 66, + 74, 186, 234, 213, 155, 138, 0, 82, 174, 0, 0, 0, 0, 0, 61, 9, 0, 255, 255, 255, 255, 255, 255, 3, 159, 16, + 206, 106, 109, 181, 15, 92, 31, 135, 79, 28, 102, 241, 196, 133, 60, 33, 133, 166, 217, 113, 148, 240, 43, + 58, 127, 97, 247, 70, 206, 136, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 182, 220, 153, 104, 164, 214, 74, 136, 37, 123, 219, 109, 224, 88, 133, 109, 145, 88, 101, 233, 60, 238, + 104, 7, 175, 138, 175, 69, 223, 108, 52, 178, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 217, 0, 32, 249, 249, 98, 229, 167, 62, 21, 67, 41, 61, 27, 231, 1, 75, 32, 12, 121, 144, 121, + 160, 108, 43, 42, 255, 52, 202, 13, 47, 103, 235, 96, 221, 2, 33, 3, 107, 133, 180, 116, 127, 220, 77, 60, + 217, 241, 148, 177, 122, 15, 156, 142, 180, 71, 207, 179, 118, 123, 171, 153, 14, 105, 124, 168, 221, 132, + 54, 71, 4, 33, 2, 168, 149, 64, 202, 166, 184, 248, 35, 31, 253, 135, 157, 39, 164, 206, 28, 83, 140, 180, + 60, 100, 36, 50, 82, 103, 17, 118, 243, 238, 34, 238, 208, 6, 33, 2, 2, 110, 79, 33, 39, 173, 95, 249, 89, + 115, 209, 234, 57, 86, 52, 50, 49, 164, 74, 23, 134, 12, 205, 252, 111, 83, 167, 129, 63, 195, 111, 179, 8, + 33, 3, 14, 110, 108, 28, 86, 183, 101, 122, 104, 241, 23, 63, 216, 202, 199, 190, 166, 239, 224, 147, 63, + 228, 254, 177, 31, 210, 27, 47, 34, 130, 107, 46, 10, 33, 2, 18, 127, 119, 213, 63, 240, 181, 230, 149, 71, + 42, 224, 5, 229, 239, 246, 149, 190, 52, 176, 137, 178, 175, 85, 233, 216, 198, 187, 165, 136, 145, 140, 12, + 4, 0, 0, 0, 253, 14, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 24, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 248, 203, 59, 40, 150, 135, 230, 177, 80, 234, 252, 113, 114, 69, 68, 1, 163, 209, 116, 69, 127, + 58, 225, 200, 202, 100, 1, 43, 84, 169, 165, 2, 0, 0, 0, 205, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 154, 13, 188, 186, 197, 129, 42, 234, 116, 174, 71, 179, 0, 176, 61, 111, 34, 233, 34, 212, 224, 37, 185, + 65, 37, 30, 244, 58, 24, 36, 229, 161, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 34, 0, 32, 134, 213, 198, 74, + 204, 68, 167, 226, 115, 12, 209, 241, 25, 111, 194, 124, 75, 139, 185, 58, 192, 212, 241, 159, 105, 48, 111, + 220, 54, 195, 100, 57, 1, 1, 0, 22, 0, 20, 145, 136, 195, 71, 179, 2, 109, 136, 237, 2, 125, 149, 143, 194, + 231, 176, 239, 175, 178, 93, 253, 1, 202, 0, 253, 1, 127, 253, 1, 124, 0, 8, 0, 0, 255, 255, 255, 255, 255, + 255, 2, 8, 0, 0, 0, 0, 0, 61, 0, 121, 4, 8, 0, 0, 0, 0, 0, 0, 7, 208, 6, 4, 0, 0, 0, 253, 8, 176, 175, 0, + 33, 2, 18, 127, 119, 213, 63, 240, 181, 230, 149, 71, 42, 224, 5, 229, 239, 246, 149, 190, 52, 176, 137, + 178, 175, 85, 233, 216, 198, 187, 165, 136, 145, 140, 2, 33, 3, 107, 133, 180, 116, 127, 220, 77, 60, 217, + 241, 148, 177, 122, 15, 156, 142, 180, 71, 207, 179, 118, 123, 171, 153, 14, 105, 124, 168, 221, 132, 54, + 71, 4, 33, 2, 168, 149, 64, 202, 166, 184, 248, 35, 31, 253, 135, 157, 39, 164, 206, 28, 83, 140, 180, 60, + 100, 36, 50, 82, 103, 17, 118, 243, 238, 34, 238, 208, 6, 33, 2, 2, 110, 79, 33, 39, 173, 95, 249, 89, 115, + 209, 234, 57, 86, 52, 50, 49, 164, 74, 23, 134, 12, 205, 252, 111, 83, 167, 129, 63, 195, 111, 179, 8, 33, + 3, 14, 110, 108, 28, 86, 183, 101, 122, 104, 241, 23, 63, 216, 202, 199, 190, 166, 239, 224, 147, 63, 228, + 254, 177, 31, 210, 27, 47, 34, 130, 107, 46, 10, 162, 161, 0, 125, 2, 0, 0, 0, 1, 154, 13, 188, 186, 197, + 129, 42, 234, 116, 174, 71, 179, 0, 176, 61, 111, 34, 233, 34, 212, 224, 37, 185, 65, 37, 30, 244, 58, 24, + 36, 229, 161, 0, 0, 0, 0, 0, 156, 243, 121, 128, 2, 208, 7, 0, 0, 0, 0, 0, 0, 22, 0, 20, 98, 50, 119, 61, + 44, 2, 246, 167, 42, 1, 128, 109, 34, 5, 34, 124, 146, 87, 32, 203, 121, 0, 61, 0, 0, 0, 0, 0, 34, 0, 32, + 108, 52, 108, 150, 106, 214, 79, 135, 86, 65, 77, 225, 207, 143, 57, 208, 49, 112, 249, 244, 58, 79, 142, + 216, 158, 50, 87, 49, 179, 39, 52, 49, 33, 253, 122, 32, 2, 32, 249, 249, 98, 229, 167, 62, 21, 67, 41, 61, + 27, 231, 1, 75, 32, 12, 121, 144, 121, 160, 108, 43, 42, 255, 52, 202, 13, 47, 103, 235, 96, 221, 12, 0, 2, + 64, 221, 10, 232, 28, 181, 168, 115, 35, 179, 141, 173, 166, 216, 132, 59, 0, 97, 42, 101, 126, 70, 9, 250, + 191, 81, 103, 123, 209, 185, 116, 63, 190, 60, 217, 200, 37, 230, 153, 130, 117, 60, 247, 71, 213, 56, 221, + 229, 200, 209, 192, 78, 246, 201, 140, 206, 250, 150, 94, 11, 77, 22, 228, 44, 21, 4, 1, 0, 6, 0, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 253, 1, 150, 0, 176, 175, 0, 33, 3, 182, 71, 151, 63, 158, 166, 139, 175, 33, 191, + 184, 2, 31, 80, 80, 175, 195, 243, 170, 48, 97, 168, 182, 205, 66, 74, 186, 234, 213, 155, 138, 0, 2, 33, 3, + 169, 210, 205, 107, 151, 19, 175, 99, 240, 89, 255, 218, 111, 14, 229, 169, 203, 192, 95, 238, 36, 218, 87, + 24, 251, 188, 54, 20, 110, 134, 229, 112, 4, 33, 2, 177, 247, 78, 252, 70, 132, 241, 79, 113, 190, 46, 213, + 133, 68, 86, 43, 194, 235, 0, 80, 69, 40, 69, 23, 188, 138, 121, 221, 6, 84, 66, 175, 6, 33, 2, 59, 65, 183, + 149, 220, 177, 69, 154, 54, 190, 95, 89, 221, 203, 167, 125, 138, 87, 85, 99, 254, 40, 147, 252, 123, 135, + 198, 88, 210, 54, 6, 112, 8, 33, 2, 240, 165, 48, 220, 63, 140, 161, 160, 241, 27, 205, 177, 39, 58, 202, + 232, 142, 55, 170, 78, 111, 100, 240, 161, 123, 148, 135, 227, 240, 132, 45, 180, 2, 2, 0, 144, 4, 1, 1, 6, + 183, 182, 0, 176, 175, 0, 33, 2, 72, 97, 154, 95, 180, 18, 20, 112, 153, 254, 80, 92, 186, 183, 24, 213, 85, + 123, 40, 150, 70, 158, 255, 45, 106, 112, 64, 156, 205, 249, 246, 162, 2, 33, 2, 131, 107, 20, 49, 73, 115, + 255, 202, 251, 78, 27, 94, 59, 30, 0, 52, 234, 215, 127, 226, 35, 2, 123, 125, 132, 136, 119, 76, 199, 212, + 121, 29, 4, 33, 3, 47, 54, 100, 94, 50, 107, 239, 164, 12, 188, 179, 239, 88, 99, 188, 152, 241, 183, 53, + 150, 111, 248, 250, 102, 107, 204, 32, 226, 130, 199, 102, 79, 6, 33, 3, 19, 72, 214, 48, 188, 23, 62, 190, + 235, 38, 128, 5, 162, 68, 173, 136, 99, 223, 44, 123, 227, 17, 94, 234, 194, 64, 151, 77, 42, 176, 29, 177, + 8, 33, 3, 166, 184, 55, 239, 104, 226, 8, 194, 210, 64, 148, 27, 5, 252, 244, 78, 156, 173, 2, 67, 101, 93, + 32, 179, 251, 58, 65, 191, 151, 163, 195, 0, 2, 2, 1, 224, 8, 34, 154, 13, 188, 186, 197, 129, 42, 234, 116, + 174, 71, 179, 0, 176, 61, 111, 34, 233, 34, 212, 224, 37, 185, 65, 37, 30, 244, 58, 24, 36, 229, 161, 0, 0, + 0, 0, 2, 135, 1, 1, 251, 107, 237, 225, 215, 54, 212, 93, 169, 173, 237, 150, 43, 247, 64, 48, 229, 120, + 101, 28, 60, 49, 187, 232, 102, 79, 255, 132, 11, 17, 187, 214, 126, 100, 59, 136, 206, 203, 103, 189, 117, + 166, 240, 152, 174, 147, 46, 241, 37, 118, 48, 184, 33, 8, 126, 102, 238, 123, 127, 172, 111, 82, 199, 5, + 31, 209, 154, 252, 185, 80, 149, 1, 13, 40, 116, 21, 143, 7, 158, 107, 2, 77, 228, 204, 228, 199, 168, 21, + 162, 157, 113, 98, 52, 251, 208, 180, 202, 13, 139, 42, 217, 150, 91, 117, 177, 58, 181, 224, 70, 67, 18, + 83, 124, 87, 91, 11, 183, 176, 66, 126, 52, 96, 105, 253, 90, 136, 30, 138, 189, 200, 46, 101, 108, 82, 207, + 241, 157, 107, 103, 234, 121, 140, 232, 202, 78, 224, 135, 118, 102, 88, 202, 212, 224, 102, 19, 215, 25, + 77, 123, 23, 182, 136, 34, 103, 130, 48, 22, 87, 188, 146, 104, 162, 159, 188, 173, 123, 51, 0, 173, 195, + 102, 235, 217, 180, 199, 41, 132, 26, 187, 140, 17, 106, 253, 1, 154, 253, 1, 150, 0, 176, 175, 0, 33, 3, + 182, 71, 151, 63, 158, 166, 139, 175, 33, 191, 184, 2, 31, 80, 80, 175, 195, 243, 170, 48, 97, 168, 182, + 205, 66, 74, 186, 234, 213, 155, 138, 0, 2, 33, 3, 169, 210, 205, 107, 151, 19, 175, 99, 240, 89, 255, 218, + 111, 14, 229, 169, 203, 192, 95, 238, 36, 218, 87, 24, 251, 188, 54, 20, 110, 134, 229, 112, 4, 33, 2, 177, + 247, 78, 252, 70, 132, 241, 79, 113, 190, 46, 213, 133, 68, 86, 43, 194, 235, 0, 80, 69, 40, 69, 23, 188, + 138, 121, 221, 6, 84, 66, 175, 6, 33, 2, 59, 65, 183, 149, 220, 177, 69, 154, 54, 190, 95, 89, 221, 203, + 167, 125, 138, 87, 85, 99, 254, 40, 147, 252, 123, 135, 198, 88, 210, 54, 6, 112, 8, 33, 2, 240, 165, 48, + 220, 63, 140, 161, 160, 241, 27, 205, 177, 39, 58, 202, 232, 142, 55, 170, 78, 111, 100, 240, 161, 123, 148, + 135, 227, 240, 132, 45, 180, 2, 2, 0, 144, 4, 1, 1, 6, 183, 182, 0, 176, 175, 0, 33, 2, 72, 97, 154, 95, + 180, 18, 20, 112, 153, 254, 80, 92, 186, 183, 24, 213, 85, 123, 40, 150, 70, 158, 255, 45, 106, 112, 64, + 156, 205, 249, 246, 162, 2, 33, 2, 131, 107, 20, 49, 73, 115, 255, 202, 251, 78, 27, 94, 59, 30, 0, 52, 234, + 215, 127, 226, 35, 2, 123, 125, 132, 136, 119, 76, 199, 212, 121, 29, 4, 33, 3, 47, 54, 100, 94, 50, 107, + 239, 164, 12, 188, 179, 239, 88, 99, 188, 152, 241, 183, 53, 150, 111, 248, 250, 102, 107, 204, 32, 226, + 130, 199, 102, 79, 6, 33, 3, 19, 72, 214, 48, 188, 23, 62, 190, 235, 38, 128, 5, 162, 68, 173, 136, 99, 223, + 44, 123, 227, 17, 94, 234, 194, 64, 151, 77, 42, 176, 29, 177, 8, 33, 3, 166, 184, 55, 239, 104, 226, 8, + 194, 210, 64, 148, 27, 5, 252, 244, 78, 156, 173, 2, 67, 101, 93, 32, 179, 251, 58, 65, 191, 151, 163, 195, + 0, 2, 2, 1, 224, 8, 34, 154, 13, 188, 186, 197, 129, 42, 234, 116, 174, 71, 179, 0, 176, 61, 111, 34, 233, + 34, 212, 224, 37, 185, 65, 37, 30, 244, 58, 24, 36, 229, 161, 0, 0, 0, 0, 0, 0, 0, 61, 9, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 13, 100, 134, 192, 0, 0, 0, 0, 96, 191, 195, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, + ], + + ] + } diff --git a/ci/LDKSwift/Tests/LDKSwiftTests/TestBroadcasterInterface.swift b/ci/LDKSwift/Tests/LDKSwiftTests/TestBroadcasterInterface.swift index 0b456a9d..86cbd490 100644 --- a/ci/LDKSwift/Tests/LDKSwiftTests/TestBroadcasterInterface.swift +++ b/ci/LDKSwift/Tests/LDKSwiftTests/TestBroadcasterInterface.swift @@ -6,14 +6,14 @@ // #if SWIFT_PACKAGE -import LDKSwift -import LDKHeaders + import LDKSwift + import LDKHeaders #endif class TestBroadcasterInterface: BroadcasterInterface { - - override func broadcastTransactions(txs: [[UInt8]]) { - // insert code to broadcast transaction - } - + + override func broadcastTransactions(txs: [[UInt8]]) { + // insert code to broadcast transaction + } + } diff --git a/ci/LDKSwift/Tests/LDKSwiftTests/TestChannelManagerPersister.swift b/ci/LDKSwift/Tests/LDKSwiftTests/TestChannelManagerPersister.swift index 01c9b893..ca2cf55d 100644 --- a/ci/LDKSwift/Tests/LDKSwiftTests/TestChannelManagerPersister.swift +++ b/ci/LDKSwift/Tests/LDKSwiftTests/TestChannelManagerPersister.swift @@ -6,11 +6,11 @@ // #if SWIFT_PACKAGE -import LDKSwift -import LDKHeaders + import LDKSwift + import LDKHeaders #endif -class TestChannelManagerPersister : Persister, ExtendedChannelManagerPersister { +class TestChannelManagerPersister: Persister, ExtendedChannelManagerPersister { private let channelManager: ChannelManager? @@ -18,25 +18,25 @@ class TestChannelManagerPersister : Persister, ExtendedChannelManagerPersister { self.channelManager = channelManager super.init() } - - func handleEvent(event: Event) -> Result_NoneReplayEventZ { - .initWithOk() - } - - override func persistScorer(scorer: Bindings.WriteableScore) -> Bindings.Result_NoneIOErrorZ { - .initWithOk() - } - - override func persistGraph(networkGraph: Bindings.NetworkGraph) -> Bindings.Result_NoneIOErrorZ { - .initWithOk() - } - - override func persistManager(channelManager: Bindings.ChannelManager) -> Bindings.Result_NoneIOErrorZ { - .initWithOk() - } + + func handleEvent(event: Event) -> Result_NoneReplayEventZ { + .initWithOk() + } + + override func persistScorer(scorer: Bindings.WriteableScore) -> Bindings.Result_NoneIOErrorZ { + .initWithOk() + } + + override func persistGraph(networkGraph: Bindings.NetworkGraph) -> Bindings.Result_NoneIOErrorZ { + .initWithOk() + } + + override func persistManager(channelManager: Bindings.ChannelManager) -> Bindings.Result_NoneIOErrorZ { + .initWithOk() + } } -class FloatingChannelManagerPersister : Persister{ +class FloatingChannelManagerPersister: Persister { private let channelManager: ChannelManager? diff --git a/ci/LDKSwift/Tests/LDKSwiftTests/TestFeeEstimator.swift b/ci/LDKSwift/Tests/LDKSwiftTests/TestFeeEstimator.swift index 8757034b..5ac1b7fa 100644 --- a/ci/LDKSwift/Tests/LDKSwiftTests/TestFeeEstimator.swift +++ b/ci/LDKSwift/Tests/LDKSwiftTests/TestFeeEstimator.swift @@ -6,14 +6,14 @@ // #if SWIFT_PACKAGE -import LDKSwift -import LDKHeaders + import LDKSwift + import LDKHeaders #endif class TestFeeEstimator: FeeEstimator { - override func getEstSatPer1000Weight(confirmationTarget: Bindings.ConfirmationTarget) -> UInt32 { - return 253 - } + override func getEstSatPer1000Weight(confirmationTarget: Bindings.ConfirmationTarget) -> UInt32 { + return 253 + } } diff --git a/ci/LDKSwift/Tests/LDKSwiftTests/TestFilter.swift b/ci/LDKSwift/Tests/LDKSwiftTests/TestFilter.swift index 0791d6ea..369a3244 100644 --- a/ci/LDKSwift/Tests/LDKSwiftTests/TestFilter.swift +++ b/ci/LDKSwift/Tests/LDKSwiftTests/TestFilter.swift @@ -6,25 +6,25 @@ // #if SWIFT_PACKAGE -import LDKSwift -import LDKHeaders + import LDKSwift + import LDKHeaders #endif class TestFilter: Filter { - - override func registerTx(txid: [UInt8]?, scriptPubkey: [UInt8]) { - // watch this transaction on-chain - } - - override func registerOutput(output: Bindings.WatchedOutput) { - let scriptPubkeyBytes = output.getScriptPubkey() - let outpoint = output.getOutpoint() - let txid = outpoint.getTxid() - let outputIndex = outpoint.getIndex() - - // watch for any transactions that spend this output on-chain - - let blockHashBytes = output.getBlockHash() - // if block hash bytes are not null, return any transaction spending the output that is found in the corresponding block along with its index - } + + override func registerTx(txid: [UInt8]?, scriptPubkey: [UInt8]) { + // watch this transaction on-chain + } + + override func registerOutput(output: Bindings.WatchedOutput) { + let scriptPubkeyBytes = output.getScriptPubkey() + let outpoint = output.getOutpoint() + let txid = outpoint.getTxid() + let outputIndex = outpoint.getIndex() + + // watch for any transactions that spend this output on-chain + + let blockHashBytes = output.getBlockHash() + // if block hash bytes are not null, return any transaction spending the output that is found in the corresponding block along with its index + } } diff --git a/ci/LDKSwift/Tests/LDKSwiftTests/TestLogger.swift b/ci/LDKSwift/Tests/LDKSwiftTests/TestLogger.swift index 6b5a6b67..21f0abf8 100644 --- a/ci/LDKSwift/Tests/LDKSwiftTests/TestLogger.swift +++ b/ci/LDKSwift/Tests/LDKSwiftTests/TestLogger.swift @@ -6,21 +6,22 @@ // #if SWIFT_PACKAGE -import LDKSwift -import LDKHeaders + import LDKSwift + import LDKHeaders #endif class TestLogger: Logger { - override func log(record: Record) { - print("\nRLTestLogger (\(record.getLevel())): \(record.getFile()):\(record.getLine()):\n> \(record.getArgs())\n") + override func log(record: Record) { + print( + "\nRLTestLogger (\(record.getLevel())): \(record.getFile()):\(record.getLine()):\n> \(record.getArgs())\n") } } class MuteLogger: Logger { - override func log(record: Record) { + override func log(record: Record) { // do nothing } diff --git a/ci/LDKSwift/Tests/LDKSwiftTests/TestPersister.swift b/ci/LDKSwift/Tests/LDKSwiftTests/TestPersister.swift index 0d00074c..c856520b 100644 --- a/ci/LDKSwift/Tests/LDKSwiftTests/TestPersister.swift +++ b/ci/LDKSwift/Tests/LDKSwiftTests/TestPersister.swift @@ -6,18 +6,23 @@ // #if SWIFT_PACKAGE -import LDKSwift -import LDKHeaders + import LDKSwift + import LDKHeaders #endif class TestPersister: Persist { - - override func persistNewChannel(channelFundingOutpoint: Bindings.OutPoint, monitor: Bindings.ChannelMonitor) -> Bindings.ChannelMonitorUpdateStatus { - .Completed - } - - override func updatePersistedChannel(channelFundingOutpoint: Bindings.OutPoint, monitorUpdate: Bindings.ChannelMonitorUpdate, monitor: Bindings.ChannelMonitor) -> Bindings.ChannelMonitorUpdateStatus { - .Completed - } + + override func persistNewChannel(channelFundingOutpoint: Bindings.OutPoint, monitor: Bindings.ChannelMonitor) + -> Bindings.ChannelMonitorUpdateStatus + { + .Completed + } + + override func updatePersistedChannel( + channelFundingOutpoint: Bindings.OutPoint, monitorUpdate: Bindings.ChannelMonitorUpdate, + monitor: Bindings.ChannelMonitor + ) -> Bindings.ChannelMonitorUpdateStatus { + .Completed + } } diff --git a/ci/LDKSwift/Tests/LDKSwiftTests/WrappedSignerProviderTests.swift b/ci/LDKSwift/Tests/LDKSwiftTests/WrappedSignerProviderTests.swift index 0c6c94c4..d3aef034 100644 --- a/ci/LDKSwift/Tests/LDKSwiftTests/WrappedSignerProviderTests.swift +++ b/ci/LDKSwift/Tests/LDKSwiftTests/WrappedSignerProviderTests.swift @@ -4,155 +4,178 @@ // Created by Arik Sosman on 7/27/23. // +import XCTest + #if SWIFT_PACKAGE -import LDKSwift -import LDKHeaders + import LDKSwift + import LDKHeaders #endif -import XCTest class WrappedSignerProviderTests: XCTestCase { - override func setUpWithError() throws { - // Put setup code here. This method is called before the invocation of each test method in the class. - Bindings.setLogThreshold(severity: .DEBUG) - } - - override func tearDownWithError() throws { - // Put teardown code here. This method is called after the invocation of each test method in the class. - } - - func testChannelManagerConstruction() { - let feeEstimator = TestFeeEstimator() - let logger = TestLogger() - let broadcaster = TestBroadcasterInterface() - let persister = TestPersister() - let filter = TestFilter() - - let chainMonitor = ChainMonitor(chainSource: filter, broadcaster: broadcaster, logger: logger, feeest: feeEstimator, persister: persister) - - let seed = [UInt8](Data(base64Encoded: "//////////////////////////////////////////8=")!) - let timestampSeconds = UInt64(NSDate().timeIntervalSince1970) - let timestampNanos = UInt32.init(truncating: NSNumber(value: timestampSeconds * 1000 * 1000)) - let myKeysManager = MyKeysManager(seed: seed, startingTimeSecs: timestampSeconds, startingTimeNanos: timestampNanos) - - let handshakeConfig = ChannelHandshakeConfig.initWithDefault() - handshakeConfig.setMinimumDepth(val: 1) - // handshakeConfig.setAnnouncedChannel(val: false) - - let handshakeLimits = ChannelHandshakeLimits.initWithDefault() - handshakeLimits.setForceAnnouncedChannelPreference(val: false) - - let userConfig = UserConfig.initWithDefault() - userConfig.setChannelHandshakeConfig(val: handshakeConfig) - userConfig.setChannelHandshakeLimits(val: handshakeLimits) - userConfig.setAcceptInboundChannels(val: true) - - let network = Network.Testnet - let netGraph = NetworkGraph(network: network, logger: logger) - - let decayParams = ProbabilisticScoringDecayParameters.initWithDefault() - let probabilisticScorer = ProbabilisticScorer(decayParams: decayParams, networkGraph: netGraph, logger: logger) - let score = probabilisticScorer.asScore() - let scorer = MultiThreadedLockableScore(score: score) - - let channelManagerConstructionParameters = ChannelManagerConstructionParameters(config: userConfig, entropySource: myKeysManager.entropySource, nodeSigner: myKeysManager.nodeSigner, signerProvider: myKeysManager.signerProvider, feeEstimator: feeEstimator, chainMonitor: chainMonitor, txBroadcaster: broadcaster, logger: logger, enableP2PGossip: true, scorer: scorer) - - let reversedGenesisHashHex = "6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000" - let reversedGenesisHash = LDKSwiftTests.hexStringToBytes(hexString: reversedGenesisHashHex)! - let latestBlockHash = reversedGenesisHash - let channelManagerConstructor = ChannelManagerConstructor(network: network, currentBlockchainTipHash: latestBlockHash, currentBlockchainTipHeight: 0, netGraph: netGraph, params: channelManagerConstructionParameters) - - let channelManager = channelManagerConstructor.channelManager - } - - class MyKeysManager { - let keysManager: KeysManager - let nodeSigner: MyNodeSigner - let entropySource: MyEntropySource - let signerProvider: MySignerProvider - - init(seed: [UInt8], startingTimeSecs: UInt64, startingTimeNanos: UInt32) { - self.keysManager = KeysManager(seed: seed, startingTimeSecs: startingTimeSecs, startingTimeNanos: startingTimeNanos) - nodeSigner = MyNodeSigner() - entropySource = MyEntropySource() - signerProvider = MySignerProvider() - nodeSigner.myKeysManager = self - entropySource.myKeysManager = self - signerProvider.myKeysManager = self - } - } - - class MyNodeSigner: NodeSigner { - weak var myKeysManager: MyKeysManager? - - override func ecdh(recipient: Bindings.Recipient, otherKey: [UInt8], tweak: [UInt8]?) -> Bindings.Result_ThirtyTwoBytesNoneZ { - print("entering wrapper: ecdh()") - return myKeysManager!.keysManager.asNodeSigner().ecdh(recipient: recipient, otherKey: otherKey, tweak: tweak) - } - - override func getNodeId(recipient: Bindings.Recipient) -> Bindings.Result_PublicKeyNoneZ { - print("entering wrapper: getNodeId()") - let nodeId = myKeysManager!.keysManager.asNodeSigner().getNodeId(recipient: recipient) - return nodeId - } - - override func getInboundPaymentKeyMaterial() -> [UInt8] { - print("entering wrapper: getInboundPaymentKeyMaterial()") - return myKeysManager!.keysManager.asNodeSigner().getInboundPaymentKeyMaterial() - } - - override func signGossipMessage(msg: Bindings.UnsignedGossipMessage) -> Bindings.Result_ECDSASignatureNoneZ { - print("entering wrapper: signGossipMessage()") - return myKeysManager!.keysManager.asNodeSigner().signGossipMessage(msg: msg) - } - - override func signInvoice(invoice: Bindings.RawBolt11Invoice, recipient: Bindings.Recipient) -> Bindings.Result_RecoverableSignatureNoneZ { - print("entering wrapper: signInvoice()") - return myKeysManager!.keysManager.asNodeSigner().signInvoice(invoice: invoice, recipient: recipient) - } - } - - class MyEntropySource: EntropySource { - weak var myKeysManager: MyKeysManager? - override func getSecureRandomBytes() -> [UInt8] { - print("entering wrapper: getSecureRandomBytes()") - return myKeysManager!.keysManager.asEntropySource().getSecureRandomBytes() - } - } - - class MySignerProvider: SignerProvider { - weak var myKeysManager: MyKeysManager? - - override func deriveChannelSigner(channelValueSatoshis: UInt64, channelKeysId: [UInt8]) -> Bindings.EcdsaChannelSigner { - print("entering wrapper: deriveChannelSigner()") - return myKeysManager!.keysManager.deriveChannelKeys(channelValueSatoshis: channelValueSatoshis, params: channelKeysId).asEcdsaChannelSigner() - } - - override func generateChannelKeysId(inbound: Bool, channelValueSatoshis: UInt64, userChannelId: [UInt8]) -> [UInt8] { - print("entering wrapper: generateChannelKeysId()") - return myKeysManager!.keysManager.asSignerProvider().generateChannelKeysId(inbound: inbound, channelValueSatoshis: channelValueSatoshis, userChannelId: userChannelId) - } - - override func readChanSigner(reader: [UInt8]) -> Bindings.Result_EcdsaChannelSignerDecodeErrorZ { - print("entering wrapper: readChanSigner()") - return myKeysManager!.keysManager.asSignerProvider().readChanSigner(reader: reader) - } - - override func getDestinationScript(channelKeysId: [UInt8]) -> Bindings.Result_CVec_u8ZNoneZ { - print("entering wrapper: getDestinationScript()") - return myKeysManager!.keysManager.asSignerProvider().getDestinationScript(channelKeysId: channelKeysId) - } - - override func getShutdownScriptpubkey() -> Bindings.Result_ShutdownScriptNoneZ { - print("entering wrapper: getShutdownScriptpubkey()") - - let randomHex = "6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000" - let randomHexBytes = LDKSwiftTests.hexStringToBytes(hexString: randomHex)! - let witnessProgram = ShutdownScript.newWitnessProgram(witnessProgram: WitnessProgram(version: 1, program: randomHexBytes)) - let witnessBasedScript = witnessProgram.getValue()! - - return Result_ShutdownScriptNoneZ.initWithOk(o: witnessBasedScript) - } - } + override func setUpWithError() throws { + // Put setup code here. This method is called before the invocation of each test method in the class. + Bindings.setLogThreshold(severity: .DEBUG) + } + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testChannelManagerConstruction() { + let feeEstimator = TestFeeEstimator() + let logger = TestLogger() + let broadcaster = TestBroadcasterInterface() + let persister = TestPersister() + let filter = TestFilter() + + let chainMonitor = ChainMonitor( + chainSource: filter, broadcaster: broadcaster, logger: logger, feeest: feeEstimator, persister: persister) + + let seed = [UInt8](Data(base64Encoded: "//////////////////////////////////////////8=")!) + let timestampSeconds = UInt64(NSDate().timeIntervalSince1970) + let timestampNanos = UInt32.init(truncating: NSNumber(value: timestampSeconds * 1000 * 1000)) + let myKeysManager = MyKeysManager( + seed: seed, startingTimeSecs: timestampSeconds, startingTimeNanos: timestampNanos) + + let handshakeConfig = ChannelHandshakeConfig.initWithDefault() + handshakeConfig.setMinimumDepth(val: 1) + // handshakeConfig.setAnnouncedChannel(val: false) + + let handshakeLimits = ChannelHandshakeLimits.initWithDefault() + handshakeLimits.setForceAnnouncedChannelPreference(val: false) + + let userConfig = UserConfig.initWithDefault() + userConfig.setChannelHandshakeConfig(val: handshakeConfig) + userConfig.setChannelHandshakeLimits(val: handshakeLimits) + userConfig.setAcceptInboundChannels(val: true) + + let network = Network.Testnet + let netGraph = NetworkGraph(network: network, logger: logger) + + let decayParams = ProbabilisticScoringDecayParameters.initWithDefault() + let probabilisticScorer = ProbabilisticScorer(decayParams: decayParams, networkGraph: netGraph, logger: logger) + let score = probabilisticScorer.asScore() + let scorer = MultiThreadedLockableScore(score: score) + + let channelManagerConstructionParameters = ChannelManagerConstructionParameters( + config: userConfig, entropySource: myKeysManager.entropySource, nodeSigner: myKeysManager.nodeSigner, + signerProvider: myKeysManager.signerProvider, feeEstimator: feeEstimator, chainMonitor: chainMonitor, + txBroadcaster: broadcaster, logger: logger, enableP2PGossip: true, scorer: scorer) + + let reversedGenesisHashHex = "6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000" + let reversedGenesisHash = LDKSwiftTests.hexStringToBytes(hexString: reversedGenesisHashHex)! + let latestBlockHash = reversedGenesisHash + let channelManagerConstructor = ChannelManagerConstructor( + network: network, currentBlockchainTipHash: latestBlockHash, currentBlockchainTipHeight: 0, + netGraph: netGraph, params: channelManagerConstructionParameters) + + let channelManager = channelManagerConstructor.channelManager + } + + class MyKeysManager { + let keysManager: KeysManager + let nodeSigner: MyNodeSigner + let entropySource: MyEntropySource + let signerProvider: MySignerProvider + + init(seed: [UInt8], startingTimeSecs: UInt64, startingTimeNanos: UInt32) { + self.keysManager = KeysManager( + seed: seed, startingTimeSecs: startingTimeSecs, startingTimeNanos: startingTimeNanos) + nodeSigner = MyNodeSigner() + entropySource = MyEntropySource() + signerProvider = MySignerProvider() + nodeSigner.myKeysManager = self + entropySource.myKeysManager = self + signerProvider.myKeysManager = self + } + } + + class MyNodeSigner: NodeSigner { + weak var myKeysManager: MyKeysManager? + + override func ecdh(recipient: Bindings.Recipient, otherKey: [UInt8], tweak: [UInt8]?) + -> Bindings.Result_ThirtyTwoBytesNoneZ + { + print("entering wrapper: ecdh()") + return myKeysManager!.keysManager.asNodeSigner() + .ecdh(recipient: recipient, otherKey: otherKey, tweak: tweak) + } + + override func getNodeId(recipient: Bindings.Recipient) -> Bindings.Result_PublicKeyNoneZ { + print("entering wrapper: getNodeId()") + let nodeId = myKeysManager!.keysManager.asNodeSigner().getNodeId(recipient: recipient) + return nodeId + } + + override func getInboundPaymentKeyMaterial() -> [UInt8] { + print("entering wrapper: getInboundPaymentKeyMaterial()") + return myKeysManager!.keysManager.asNodeSigner().getInboundPaymentKeyMaterial() + } + + override func signGossipMessage(msg: Bindings.UnsignedGossipMessage) -> Bindings.Result_ECDSASignatureNoneZ { + print("entering wrapper: signGossipMessage()") + return myKeysManager!.keysManager.asNodeSigner().signGossipMessage(msg: msg) + } + + override func signInvoice(invoice: Bindings.RawBolt11Invoice, recipient: Bindings.Recipient) + -> Bindings.Result_RecoverableSignatureNoneZ + { + print("entering wrapper: signInvoice()") + return myKeysManager!.keysManager.asNodeSigner().signInvoice(invoice: invoice, recipient: recipient) + } + } + + class MyEntropySource: EntropySource { + weak var myKeysManager: MyKeysManager? + override func getSecureRandomBytes() -> [UInt8] { + print("entering wrapper: getSecureRandomBytes()") + return myKeysManager!.keysManager.asEntropySource().getSecureRandomBytes() + } + } + + class MySignerProvider: SignerProvider { + weak var myKeysManager: MyKeysManager? + + override func deriveChannelSigner(channelValueSatoshis: UInt64, channelKeysId: [UInt8]) + -> Bindings.EcdsaChannelSigner + { + print("entering wrapper: deriveChannelSigner()") + return myKeysManager!.keysManager + .deriveChannelKeys(channelValueSatoshis: channelValueSatoshis, params: channelKeysId) + .asEcdsaChannelSigner() + } + + override func generateChannelKeysId(inbound: Bool, channelValueSatoshis: UInt64, userChannelId: [UInt8]) + -> [UInt8] + { + print("entering wrapper: generateChannelKeysId()") + return myKeysManager!.keysManager.asSignerProvider() + .generateChannelKeysId( + inbound: inbound, channelValueSatoshis: channelValueSatoshis, userChannelId: userChannelId) + } + + override func readChanSigner(reader: [UInt8]) -> Bindings.Result_EcdsaChannelSignerDecodeErrorZ { + print("entering wrapper: readChanSigner()") + return myKeysManager!.keysManager.asSignerProvider().readChanSigner(reader: reader) + } + + override func getDestinationScript(channelKeysId: [UInt8]) -> Bindings.Result_CVec_u8ZNoneZ { + print("entering wrapper: getDestinationScript()") + return myKeysManager!.keysManager.asSignerProvider().getDestinationScript(channelKeysId: channelKeysId) + } + + override func getShutdownScriptpubkey() -> Bindings.Result_ShutdownScriptNoneZ { + print("entering wrapper: getShutdownScriptpubkey()") + + let randomHex = "6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000" + let randomHexBytes = LDKSwiftTests.hexStringToBytes(hexString: randomHex)! + let witnessProgram = ShutdownScript.newWitnessProgram( + witnessProgram: WitnessProgram(version: 1, program: randomHexBytes)) + let witnessBasedScript = witnessProgram.getValue()! + + return Result_ShutdownScriptNoneZ.initWithOk(o: witnessBasedScript) + } + } } diff --git a/ci/LDKSwift/Tests/LDKSwiftTests/bitcoin/BTCBlock.swift b/ci/LDKSwift/Tests/LDKSwiftTests/bitcoin/BTCBlock.swift index 78f8abfc..e63db64a 100644 --- a/ci/LDKSwift/Tests/LDKSwiftTests/bitcoin/BTCBlock.swift +++ b/ci/LDKSwift/Tests/LDKSwiftTests/bitcoin/BTCBlock.swift @@ -5,11 +5,12 @@ // Created by Arik Sosman on 3/28/22. // +import XCTest + #if SWIFT_PACKAGE -import LDKSwift -import LDKHeaders + import LDKSwift + import LDKHeaders #endif -import XCTest /// serialization: /// 4 bytes: 0xD9B4BEF9 @@ -19,89 +20,89 @@ import XCTest /// transactions class BTCBlock: Equatable { - - var version: UInt32 = 2 - var previousBlockHash: [UInt8] = [] - var merkleRoot: [UInt8]? - var timestamp: UInt32 = 0 - var difficultyTarget: UInt32 = 0 - var nonce: UInt32 = 0 - var height: UInt32 = 0 - - // each transaction is a uint8 array - var transactions: [BTCTransaction] = [] - - func calculateMerkleRoot() -> [UInt8] { - // TODO: merkleize the transactions - if let merkleRoot = self.merkleRoot { - return merkleRoot - } - return [UInt8](repeating: 0, count: 32) - } - - func calculateHeader() -> [UInt8] { - var header = [UInt8]() - - do { - let versionBytes = withUnsafeBytes(of: self.version.littleEndian) { bytes in - Array(bytes) - } - assert(versionBytes.count == 4) - header.append(contentsOf: versionBytes) - } - - do { - assert(self.previousBlockHash.count == 32) - header.append(contentsOf: self.previousBlockHash) - } - - do { - let merkleRoot = self.calculateMerkleRoot() - assert(merkleRoot.count == 32) - header.append(contentsOf: merkleRoot) - } - - do { - let timestampBytes = withUnsafeBytes(of: self.timestamp.littleEndian) { bytes in - Array(bytes) - } - assert(timestampBytes.count == 4) - header.append(contentsOf: timestampBytes) - } - - do{ - let difficultyTargetBytes = withUnsafeBytes(of: self.difficultyTarget.littleEndian) { bytes in - Array(bytes) - } - assert(difficultyTargetBytes.count == 4) - header.append(contentsOf: difficultyTargetBytes) - } - - do { - let nonceBytes = withUnsafeBytes(of: self.nonce.littleEndian) { bytes in - Array(bytes) - } - assert(nonceBytes.count == 4) - header.append(contentsOf: nonceBytes) - } - - assert(header.count == 80) - return header - } - - /// The double sha256 hash of the block header (note: not the entire block) - /// In a well-mined block, the zeroes should be trailing, as this hash is little-endian-equivalent - func calculateHash() -> [UInt8] { - let blockHeader = self.calculateHeader() - return BTCHashing.doubleSha256(blockHeader) - } - - func serialize() -> [UInt8] { - - var block = [UInt8]() - - // apparently, this only needs to be appended when it's actually part of a block - /* + + var version: UInt32 = 2 + var previousBlockHash: [UInt8] = [] + var merkleRoot: [UInt8]? + var timestamp: UInt32 = 0 + var difficultyTarget: UInt32 = 0 + var nonce: UInt32 = 0 + var height: UInt32 = 0 + + // each transaction is a uint8 array + var transactions: [BTCTransaction] = [] + + func calculateMerkleRoot() -> [UInt8] { + // TODO: merkleize the transactions + if let merkleRoot = self.merkleRoot { + return merkleRoot + } + return [UInt8](repeating: 0, count: 32) + } + + func calculateHeader() -> [UInt8] { + var header = [UInt8]() + + do { + let versionBytes = withUnsafeBytes(of: self.version.littleEndian) { bytes in + Array(bytes) + } + assert(versionBytes.count == 4) + header.append(contentsOf: versionBytes) + } + + do { + assert(self.previousBlockHash.count == 32) + header.append(contentsOf: self.previousBlockHash) + } + + do { + let merkleRoot = self.calculateMerkleRoot() + assert(merkleRoot.count == 32) + header.append(contentsOf: merkleRoot) + } + + do { + let timestampBytes = withUnsafeBytes(of: self.timestamp.littleEndian) { bytes in + Array(bytes) + } + assert(timestampBytes.count == 4) + header.append(contentsOf: timestampBytes) + } + + do { + let difficultyTargetBytes = withUnsafeBytes(of: self.difficultyTarget.littleEndian) { bytes in + Array(bytes) + } + assert(difficultyTargetBytes.count == 4) + header.append(contentsOf: difficultyTargetBytes) + } + + do { + let nonceBytes = withUnsafeBytes(of: self.nonce.littleEndian) { bytes in + Array(bytes) + } + assert(nonceBytes.count == 4) + header.append(contentsOf: nonceBytes) + } + + assert(header.count == 80) + return header + } + + /// The double sha256 hash of the block header (note: not the entire block) + /// In a well-mined block, the zeroes should be trailing, as this hash is little-endian-equivalent + func calculateHash() -> [UInt8] { + let blockHeader = self.calculateHeader() + return BTCHashing.doubleSha256(blockHeader) + } + + func serialize() -> [UInt8] { + + var block = [UInt8]() + + // apparently, this only needs to be appended when it's actually part of a block + /* do { let magicNumber: UInt32 = 4190024921 let magicNumberBytes = withUnsafeBytes(of: magicNumber.littleEndian) { bytes in @@ -109,18 +110,18 @@ class BTCBlock: Equatable { } assert(magicNumberBytes.count == 4) assert(magicNumberBytes == [0xf9, 0xbe, 0xb4, 0xd9]) - + block.append(contentsOf: magicNumberBytes) } */ - - let blockHeader = self.calculateHeader() - let transactionCounter = BTCVarInt(UInt64(self.transactions.count)).serialize() - let transactionData = self.transactions.reduce(into: [UInt8]()) { partialResult, currentValue in - return partialResult.append(contentsOf: currentValue.serialize()) - } - - /* + + let blockHeader = self.calculateHeader() + let transactionCounter = BTCVarInt(UInt64(self.transactions.count)).serialize() + let transactionData = self.transactions.reduce(into: [UInt8]()) { partialResult, currentValue in + return partialResult.append(contentsOf: currentValue.serialize()) + } + + /* do { let blockSize = blockHeader.count + transactionCounter.count + transactionData.count let blockSizeBytes = withUnsafeBytes(of: UInt32(blockSize).littleEndian) { bytes in @@ -130,16 +131,16 @@ class BTCBlock: Equatable { block.append(contentsOf: blockSizeBytes) } */ - - block.append(contentsOf: blockHeader) - block.append(contentsOf: transactionCounter) - block.append(contentsOf: transactionData) - - return block - } - - static func == (lhs: BTCBlock, rhs: BTCBlock) -> Bool { - return lhs.calculateHash() == rhs.calculateHash() - } - + + block.append(contentsOf: blockHeader) + block.append(contentsOf: transactionCounter) + block.append(contentsOf: transactionData) + + return block + } + + static func == (lhs: BTCBlock, rhs: BTCBlock) -> Bool { + return lhs.calculateHash() == rhs.calculateHash() + } + } diff --git a/ci/LDKSwift/Tests/LDKSwiftTests/bitcoin/BTCHashing.swift b/ci/LDKSwift/Tests/LDKSwiftTests/bitcoin/BTCHashing.swift index 3d32ba52..92856dee 100644 --- a/ci/LDKSwift/Tests/LDKSwiftTests/bitcoin/BTCHashing.swift +++ b/ci/LDKSwift/Tests/LDKSwiftTests/bitcoin/BTCHashing.swift @@ -5,27 +5,27 @@ // Created by Arik Sosman on 3/29/22. // +import Crypto + #if SWIFT_PACKAGE -import LDKSwift -import LDKHeaders + import LDKSwift + import LDKHeaders #endif -import Crypto class BTCHashing { - public static let SHA_ZERO_HASH = [UInt8](repeating: 0, count: 32) - public static let RIPEMD_ZERO_HASH = [UInt8](repeating: 0, count: 20) + public static let SHA_ZERO_HASH = [UInt8](repeating: 0, count: 32) + public static let RIPEMD_ZERO_HASH = [UInt8](repeating: 0, count: 20) - private static func sha256(_ input: [UInt8]) -> [UInt8] { - let hash = Crypto.SHA256.hash(data: input) - let bytes = Array(hash) - assert(bytes.count == 32) - return bytes - } + private static func sha256(_ input: [UInt8]) -> [UInt8] { + let hash = Crypto.SHA256.hash(data: input) + let bytes = Array(hash) + assert(bytes.count == 32) + return bytes + } - static func doubleSha256(_ input: [UInt8]) -> [UInt8] { - return sha256(sha256(input)) - } + static func doubleSha256(_ input: [UInt8]) -> [UInt8] { + return sha256(sha256(input)) + } } - diff --git a/ci/LDKSwift/Tests/LDKSwiftTests/bitcoin/BTCTransaction.swift b/ci/LDKSwift/Tests/LDKSwiftTests/bitcoin/BTCTransaction.swift index af2e2eca..1f457560 100644 --- a/ci/LDKSwift/Tests/LDKSwiftTests/bitcoin/BTCTransaction.swift +++ b/ci/LDKSwift/Tests/LDKSwiftTests/bitcoin/BTCTransaction.swift @@ -6,181 +6,181 @@ // #if SWIFT_PACKAGE -import LDKSwift -import LDKHeaders + import LDKSwift + import LDKHeaders #endif class BTCTransaction: Equatable { - - var version: UInt32 = 1 - var flag: UInt16? // let's not support witness data quite yet - var inputs: [Input] = [] - var outputs: [Output] = [] - var lockTime: UInt32 = 0 - - public func serialize(useSegwit: Bool = true) -> [UInt8] { - var transaction = [UInt8]() - - var witnessData = [UInt8]() - - do { - let versionBytes = withUnsafeBytes(of: self.version.littleEndian) { bytes in - Array(bytes) - } - assert(versionBytes.count == 4) - transaction.append(contentsOf: versionBytes) - } - - if let flag = self.flag, useSegwit { - let flagBytes = withUnsafeBytes(of: flag.littleEndian) { bytes in - Array(bytes) - } - assert(flagBytes.count == 2) - transaction.append(contentsOf: flagBytes) - } - - do { - let inputCounter = BTCVarInt(UInt64(self.inputs.count)).serialize() - transaction.append(contentsOf: inputCounter) - - self.inputs.map { currentInput in - transaction.append(contentsOf: currentInput.serialize()) - if let witness = currentInput.witness, useSegwit { - witnessData.append(contentsOf: witness.serialize()) - } else if let _ = self.flag, useSegwit { - witnessData.append(0) // empty witness for this particular input - } - } - } - - do { - let outputCounter = BTCVarInt(UInt64(self.outputs.count)).serialize() - transaction.append(contentsOf: outputCounter) - - self.outputs.map { currentOutput in - transaction.append(contentsOf: currentOutput.serialize()) - } - } - - transaction.append(contentsOf: witnessData) - - do { - let lockTimeBytes = withUnsafeBytes(of: self.lockTime.littleEndian) { bytes in - Array(bytes) - } - assert(lockTimeBytes.count == 4) - transaction.append(contentsOf: lockTimeBytes) - } - - return transaction - } - - public func setWitnessForInput(inputIndex: Int, witness: Witness) { - self.flag = 256 - self.inputs[inputIndex].witness = witness - } - - public func calculateId() -> [UInt8] { - let transactionData = self.serialize(useSegwit: false) - return BTCHashing.doubleSha256(transactionData) - } - - public func calculateHash() -> [UInt8] { - let transactionData = self.serialize() - return BTCHashing.doubleSha256(transactionData) - } - - static func == (lhs: BTCTransaction, rhs: BTCTransaction) -> Bool { - return lhs.calculateHash() == rhs.calculateHash() - } - - public class Input { - var previousTransactionHash: [UInt8] - var previousOutputIndex: UInt32 - var script: [UInt8] - var sequence: UInt32 = 0xffffffff - fileprivate var witness: Witness? - - init(previousTransactionHash: [UInt8], previousOutputIndex: UInt32, script: [UInt8]) { - self.previousTransactionHash = previousTransactionHash - self.previousOutputIndex = previousOutputIndex - self.script = script - } - - fileprivate func serialize() -> [UInt8] { - var input = [UInt8]() - - input.append(contentsOf: previousTransactionHash) - - do { - let outputIndexBytes = withUnsafeBytes(of: self.previousOutputIndex.littleEndian) { bytes in - Array(bytes) - } - assert(outputIndexBytes.count == 4) - input.append(contentsOf: outputIndexBytes) - } - - do { - let scriptLengthCounter = BTCVarInt(UInt64(self.script.count)).serialize() - input.append(contentsOf: scriptLengthCounter) - input.append(contentsOf: self.script) - } - - do { - let sequenceBytes = withUnsafeBytes(of: self.sequence.littleEndian) { bytes in - Array(bytes) - } - assert(sequenceBytes.count == 4) - input.append(contentsOf: sequenceBytes) - } - - return input - } - } - - public struct Witness { - var stackElements: [[UInt8]] - - fileprivate func serialize() -> [UInt8] { - var witness = [UInt8]() - - let stackSizeCounter = BTCVarInt(UInt64(self.stackElements.count)).serialize() - witness.append(contentsOf: stackSizeCounter) - - self.stackElements.map { currentElement in - let elementSizeCounter = BTCVarInt(UInt64(currentElement.count)).serialize() - witness.append(contentsOf: elementSizeCounter) - witness.append(contentsOf: currentElement) - } - - return witness - } - - } - - public struct Output { - var value: UInt64 - var script: [UInt8] - - fileprivate func serialize() -> [UInt8] { - var output = [UInt8]() - - do { - let valueBytes = withUnsafeBytes(of: self.value.littleEndian) { bytes in - Array(bytes) - } - assert(valueBytes.count == 8) - output.append(contentsOf: valueBytes) - } - - do { - let scriptLengthCounter = BTCVarInt(UInt64(self.script.count)).serialize() - output.append(contentsOf: scriptLengthCounter) - output.append(contentsOf: self.script) - } - - return output - } - } - + + var version: UInt32 = 1 + var flag: UInt16? // let's not support witness data quite yet + var inputs: [Input] = [] + var outputs: [Output] = [] + var lockTime: UInt32 = 0 + + public func serialize(useSegwit: Bool = true) -> [UInt8] { + var transaction = [UInt8]() + + var witnessData = [UInt8]() + + do { + let versionBytes = withUnsafeBytes(of: self.version.littleEndian) { bytes in + Array(bytes) + } + assert(versionBytes.count == 4) + transaction.append(contentsOf: versionBytes) + } + + if let flag = self.flag, useSegwit { + let flagBytes = withUnsafeBytes(of: flag.littleEndian) { bytes in + Array(bytes) + } + assert(flagBytes.count == 2) + transaction.append(contentsOf: flagBytes) + } + + do { + let inputCounter = BTCVarInt(UInt64(self.inputs.count)).serialize() + transaction.append(contentsOf: inputCounter) + + self.inputs.map { currentInput in + transaction.append(contentsOf: currentInput.serialize()) + if let witness = currentInput.witness, useSegwit { + witnessData.append(contentsOf: witness.serialize()) + } else if let _ = self.flag, useSegwit { + witnessData.append(0) // empty witness for this particular input + } + } + } + + do { + let outputCounter = BTCVarInt(UInt64(self.outputs.count)).serialize() + transaction.append(contentsOf: outputCounter) + + self.outputs.map { currentOutput in + transaction.append(contentsOf: currentOutput.serialize()) + } + } + + transaction.append(contentsOf: witnessData) + + do { + let lockTimeBytes = withUnsafeBytes(of: self.lockTime.littleEndian) { bytes in + Array(bytes) + } + assert(lockTimeBytes.count == 4) + transaction.append(contentsOf: lockTimeBytes) + } + + return transaction + } + + public func setWitnessForInput(inputIndex: Int, witness: Witness) { + self.flag = 256 + self.inputs[inputIndex].witness = witness + } + + public func calculateId() -> [UInt8] { + let transactionData = self.serialize(useSegwit: false) + return BTCHashing.doubleSha256(transactionData) + } + + public func calculateHash() -> [UInt8] { + let transactionData = self.serialize() + return BTCHashing.doubleSha256(transactionData) + } + + static func == (lhs: BTCTransaction, rhs: BTCTransaction) -> Bool { + return lhs.calculateHash() == rhs.calculateHash() + } + + public class Input { + var previousTransactionHash: [UInt8] + var previousOutputIndex: UInt32 + var script: [UInt8] + var sequence: UInt32 = 0xffff_ffff + fileprivate var witness: Witness? + + init(previousTransactionHash: [UInt8], previousOutputIndex: UInt32, script: [UInt8]) { + self.previousTransactionHash = previousTransactionHash + self.previousOutputIndex = previousOutputIndex + self.script = script + } + + fileprivate func serialize() -> [UInt8] { + var input = [UInt8]() + + input.append(contentsOf: previousTransactionHash) + + do { + let outputIndexBytes = withUnsafeBytes(of: self.previousOutputIndex.littleEndian) { bytes in + Array(bytes) + } + assert(outputIndexBytes.count == 4) + input.append(contentsOf: outputIndexBytes) + } + + do { + let scriptLengthCounter = BTCVarInt(UInt64(self.script.count)).serialize() + input.append(contentsOf: scriptLengthCounter) + input.append(contentsOf: self.script) + } + + do { + let sequenceBytes = withUnsafeBytes(of: self.sequence.littleEndian) { bytes in + Array(bytes) + } + assert(sequenceBytes.count == 4) + input.append(contentsOf: sequenceBytes) + } + + return input + } + } + + public struct Witness { + var stackElements: [[UInt8]] + + fileprivate func serialize() -> [UInt8] { + var witness = [UInt8]() + + let stackSizeCounter = BTCVarInt(UInt64(self.stackElements.count)).serialize() + witness.append(contentsOf: stackSizeCounter) + + self.stackElements.map { currentElement in + let elementSizeCounter = BTCVarInt(UInt64(currentElement.count)).serialize() + witness.append(contentsOf: elementSizeCounter) + witness.append(contentsOf: currentElement) + } + + return witness + } + + } + + public struct Output { + var value: UInt64 + var script: [UInt8] + + fileprivate func serialize() -> [UInt8] { + var output = [UInt8]() + + do { + let valueBytes = withUnsafeBytes(of: self.value.littleEndian) { bytes in + Array(bytes) + } + assert(valueBytes.count == 8) + output.append(contentsOf: valueBytes) + } + + do { + let scriptLengthCounter = BTCVarInt(UInt64(self.script.count)).serialize() + output.append(contentsOf: scriptLengthCounter) + output.append(contentsOf: self.script) + } + + return output + } + } + } diff --git a/ci/LDKSwift/Tests/LDKSwiftTests/bitcoin/BTCVarInt.swift b/ci/LDKSwift/Tests/LDKSwiftTests/bitcoin/BTCVarInt.swift index 83b19cf7..14a4a46f 100644 --- a/ci/LDKSwift/Tests/LDKSwiftTests/bitcoin/BTCVarInt.swift +++ b/ci/LDKSwift/Tests/LDKSwiftTests/bitcoin/BTCVarInt.swift @@ -6,42 +6,42 @@ // #if SWIFT_PACKAGE -import LDKSwift -import LDKHeaders + import LDKSwift + import LDKHeaders #endif public class BTCVarInt { - - private let value: UInt64 - - public init(_ value: UInt64) { - self.value = value - } - - public func serialize() -> [UInt8] { - var varInt = [UInt8]() - var valueBytes: [UInt8] - - if self.value < 0xfd { - return [UInt8(self.value)] - } else if self.value <= 0xffff { - varInt.append(0xfd) - valueBytes = withUnsafeBytes(of: UInt16(self.value).littleEndian) { bytes in - Array(bytes) - } - } else if self.value <= 0xffffffff { - varInt.append(0xfe) - valueBytes = withUnsafeBytes(of: UInt32(self.value).littleEndian) { bytes in - Array(bytes) - } - } else { - varInt.append(0xff) - valueBytes = withUnsafeBytes(of: self.value.littleEndian) { bytes in - Array(bytes) - } - } - varInt.append(contentsOf: valueBytes) - return varInt - } - + + private let value: UInt64 + + public init(_ value: UInt64) { + self.value = value + } + + public func serialize() -> [UInt8] { + var varInt = [UInt8]() + var valueBytes: [UInt8] + + if self.value < 0xfd { + return [UInt8(self.value)] + } else if self.value <= 0xffff { + varInt.append(0xfd) + valueBytes = withUnsafeBytes(of: UInt16(self.value).littleEndian) { bytes in + Array(bytes) + } + } else if self.value <= 0xffff_ffff { + varInt.append(0xfe) + valueBytes = withUnsafeBytes(of: UInt32(self.value).littleEndian) { bytes in + Array(bytes) + } + } else { + varInt.append(0xff) + valueBytes = withUnsafeBytes(of: self.value.littleEndian) { bytes in + Array(bytes) + } + } + varInt.append(contentsOf: valueBytes) + return varInt + } + } diff --git a/ci/LDKSwift/Tests/LDKSwiftTests/bitcoin/BitcoinTests.swift b/ci/LDKSwift/Tests/LDKSwiftTests/bitcoin/BitcoinTests.swift index 39f6eddd..f895c1bc 100644 --- a/ci/LDKSwift/Tests/LDKSwiftTests/bitcoin/BitcoinTests.swift +++ b/ci/LDKSwift/Tests/LDKSwiftTests/bitcoin/BitcoinTests.swift @@ -5,209 +5,255 @@ // Created by Arik Sosman on 3/29/22. // +import XCTest + #if SWIFT_PACKAGE -import LDKSwift -import LDKHeaders + import LDKSwift + import LDKHeaders #endif -import XCTest public class BitcoinTests: XCTestCase { - func testVarInt() throws { - do { - let varInt = BTCVarInt(0xfc) - let serialization = varInt.serialize() - XCTAssertEqual(serialization, [0xfc]) - } - - do { - let varInt = BTCVarInt(0xfd) - let serialization = varInt.serialize() - XCTAssertEqual(serialization, [0xfd, 0xfd, 0]) - } - - do { - let varInt = BTCVarInt(0xfe) - let serialization = varInt.serialize() - XCTAssertEqual(serialization, [0xfd, 0xfe, 0]) - } - } - - func testBlockHashing() throws { - let block = BTCBlock() - block.version = 1 - block.previousBlockHash = [129, 205, 2, 171, 126, 86, 158, 139, 205, 147, 23, 226, 254, 153, 242, 222, 68, 212, 154, 178, 184, 133, 27, 164, 163, 8, 0, 0, 0, 0, 0, 0] - block.merkleRoot = [227, 32, 182, 194, 255, 252, 141, 117, 4, 35, 219, 139, 30, 185, 66, 174, 113, 14, 149, 30, 215, 151, 247, 175, 252, 136, 146, 176, 241, 252, 18, 43] - block.timestamp = 1305998791 - block.difficultyTarget = 440711666 - block.nonce = 2504433986 - let hash = block.calculateHash(); - - for i in 1...8 { - // verify that the hash worked correctly with enough trailing 0s - XCTAssertEqual(hash[hash.count - i], 0) - } - - // sanity check that the hash is not all 0s - XCTAssertNotEqual(hash[hash.count - 9], 0) - - // verify that the leading byte is nonzero - XCTAssertNotEqual(hash[0], 0) - } - - func testBlockSerialization() throws { - let block = BTCBlock() - block.version = 2 - block.previousBlockHash = BTCHashing.SHA_ZERO_HASH - block.merkleRoot = BTCHashing.SHA_ZERO_HASH - block.timestamp = 42 - block.difficultyTarget = 0 - block.nonce = 0 - - let serialization = block.serialize() - XCTAssertEqual(serialization, [2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) - } - - func testTxSerialization() throws { - let tx = BTCTransaction() - tx.version = 1 - let input = BTCTransaction.Input( - previousTransactionHash: BTCHashing.SHA_ZERO_HASH, - previousOutputIndex: 0xffffffff, // coinbase transaction has a u32::MAX prev output index - script: [] - ) - tx.inputs = [input] - tx.setWitnessForInput(inputIndex: 0, witness: BTCTransaction.Witness(stackElements: [[1]])) - - let output = BTCTransaction.Output(value: 100000, script: [0, 32, 214, 232, 96, 182, 226, 231, 42, 33, 219, 246, 121, 242, 58, 123, 110, 124, 118, 63, 70, 117, 109, 247, 0, 58, 172, 198, 127, 254, 216, 194, 41, 14]) - tx.outputs = [output] - let txSerialization = tx.serialize() - XCTAssertEqual(txSerialization.count, 99) - XCTAssertEqual(txSerialization, [1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 1, 160, 134, 1, 0, 0, 0, 0, 0, 34, 0, 32, 214, 232, 96, 182, 226, 231, 42, 33, 219, 246, 121, 242, 58, 123, 110, 124, 118, 63, 70, 117, 109, 247, 0, 58, 172, 198, 127, 254, 216, 194, 41, 14, 1, 1, 1, 0, 0, 0, 0]) - - let block = BTCBlock() - block.version = 2 - block.previousBlockHash = BTCHashing.SHA_ZERO_HASH - block.merkleRoot = BTCHashing.SHA_ZERO_HASH - block.timestamp = 42 - block.difficultyTarget = 0 - block.nonce = 0 - block.transactions = [tx] - let blockSerialization = block.serialize() - XCTAssertEqual(blockSerialization, [2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 1, 160, 134, 1, 0, 0, 0, 0, 0, 34, 0, 32, 214, 232, 96, 182, 226, 231, 42, 33, 219, 246, 121, 242, 58, 123, 110, 124, 118, 63, 70, 117, 109, 247, 0, 58, 172, 198, 127, 254, 216, 194, 41, 14, 1, 1, 1, 0, 0, 0, 0]) - } - - #if !SWIFT_PACKAGE - @available(iOS 15.0, *) - func testRpcCalls() async throws { - let username = ProcessInfo.processInfo.environment["BITCOIN_REGTEST_RPC_USERNAME"] ?? "polaruser" // "alice" - let password = ProcessInfo.processInfo.environment["BITCOIN_REGTEST_RPC_PASSWORD"] ?? "polarpass" // "DONT_USE_THIS_YOU_WILL_GET_ROBBED" - let port = NumberFormatter().number(from: ProcessInfo.processInfo.environment["BITCOIN_REGTEST_RPC_PORT"] ?? "") ?? 18443 - let rpcInterface = try RegtestBlockchainManager(rpcProtocol: .http, rpcDomain: "localhost", rpcPort: (port as! UInt), rpcUsername: username, rpcPassword: password) - let chaintipHeight = try await rpcInterface.getChaintipHeight() - XCTAssertGreaterThanOrEqual(chaintipHeight, 0) - let chaintipHashImplicit = try await rpcInterface.getBlockHashHex(height: chaintipHeight) - let chaintipHashExplicit = try await rpcInterface.getChaintipHashHex() - XCTAssertEqual(chaintipHashImplicit, chaintipHashExplicit) - let blockDetails = try await rpcInterface.getBlock(hash: chaintipHashExplicit) - XCTAssertEqual(blockDetails.height, chaintipHeight) - - let chainInfo = try await rpcInterface.getChainInfo() - - let genesisHash = try await rpcInterface.getBlockHashHex(height: 1) - let genesisDetails = try await rpcInterface.getBlock(hash: genesisHash) - let genesisBinary = try await rpcInterface.getBlockBinary(hash: genesisHash) - let genesisHeader = try await rpcInterface.getBlockHeader(hash: genesisHash) - - let genesisCoinbaseTxHash = genesisDetails.tx.first! - let genesisCoinbaseTx = try await rpcInterface.getTransaction(hash: genesisCoinbaseTxHash) - - - // try? await rpcInterface.submitTransaction(transaction: modifiedCoinbaseTx) - - // let address = try await rpcInterface.generateAddress() - // let details = try await rpcInterface.mineBlocks(number: 1, coinbaseDestinationAddress: address) - let helpDetails = try await rpcInterface.getHelp() - // print(helpDetails) - - let exampleFundingTx: [UInt8] = [1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 1, 160, 134, 1, 0, 0, 0, 0, 0, 34, 0, 32, 15, 45, 153, 123, 98, 37, 242, 142, 113, 51, 126, 45, 206, 175, 66, 247, 173, 2, 141, 2, 27, 84, 38, 188, 34, 74, 82, 164, 28, 229, 39, 139, 1, 1, 1, 0, 0, 0, 0] - - // try? await rpcInterface.submitTransaction(transaction: exampleFundingTx) - - - class Listener: BlockchainListener { - var initializationComplete = false - var newBlocksDetected = 0 - var blocksLost = 0 - - func blockConnected(block: [UInt8], height: UInt32) { - if self.initializationComplete { - self.newBlocksDetected += 1 - } - let blockHash = LDKSwiftTests.bytesToHexString(bytes: BTCHashing.doubleSha256(block)) - // print("block connected at height \(height): \(blockHash)") - } + func testVarInt() throws { + do { + let varInt = BTCVarInt(0xfc) + let serialization = varInt.serialize() + XCTAssertEqual(serialization, [0xfc]) + } - func blockDisconnected(header: [UInt8]?, height: UInt32) { - self.blocksLost += 1 - // print("block disconnected from height \(height): \(header)") - } + do { + let varInt = BTCVarInt(0xfd) + let serialization = varInt.serialize() + XCTAssertEqual(serialization, [0xfd, 0xfd, 0]) } - var listener = Listener() - rpcInterface.registerListener(listener) + do { + let varInt = BTCVarInt(0xfe) + let serialization = varInt.serialize() + XCTAssertEqual(serialization, [0xfd, 0xfe, 0]) + } + } - try await rpcInterface.preloadMonitor(); - // async let monitor = try rpcInterface.monitorBlockchain() - listener.initializationComplete = true + func testBlockHashing() throws { + let block = BTCBlock() + block.version = 1 + block.previousBlockHash = [ + 129, 205, 2, 171, 126, 86, 158, 139, 205, 147, 23, 226, 254, 153, 242, 222, 68, 212, 154, 178, 184, 133, 27, + 164, 163, 8, 0, 0, 0, 0, 0, 0, + ] + block.merkleRoot = [ + 227, 32, 182, 194, 255, 252, 141, 117, 4, 35, 219, 139, 30, 185, 66, 174, 113, 14, 149, 30, 215, 151, 247, + 175, 252, 136, 146, 176, 241, 252, 18, 43, + ] + block.timestamp = 1_305_998_791 + block.difficultyTarget = 440_711_666 + block.nonce = 2_504_433_986 + let hash = block.calculateHash() + + for i in 1...8 { + // verify that the hash worked correctly with enough trailing 0s + XCTAssertEqual(hash[hash.count - i], 0) + } - let testAddress = try await rpcInterface.generateAddress() - let exampleOutputScript: [UInt8] = [0, 32, 200, 194, 75, 55, 227, 33, 251, 71, 196, 33, 177, 196, 155, 145, 17, 78, 244, 226, 155, 141, 216, 230, 180, 183, 149, 172, 116, 249, 56, 6, 118, 255] - let scriptInfo = try await rpcInterface.decodeScript(script: exampleOutputScript) - let outputAddress = (scriptInfo["addresses"] as! [String]).first! - try await rpcInterface.mineBlocks(number: 1, coinbaseDestinationAddress: testAddress) - try await rpcInterface.mineBlocks(number: 1, coinbaseDestinationAddress: outputAddress) + // sanity check that the hash is not all 0s + XCTAssertNotEqual(hash[hash.count - 9], 0) - // sleep for six seconds - // try await Task.sleep(nanoseconds: 6_000_000_000) - try await rpcInterface.reconcileChaintips() - XCTAssertEqual(listener.newBlocksDetected, 2) - XCTAssertEqual(listener.blocksLost, 0) + // verify that the leading byte is nonzero + XCTAssertNotEqual(hash[0], 0) + } + + func testBlockSerialization() throws { + let block = BTCBlock() + block.version = 2 + block.previousBlockHash = BTCHashing.SHA_ZERO_HASH + block.merkleRoot = BTCHashing.SHA_ZERO_HASH + block.timestamp = 42 + block.difficultyTarget = 0 + block.nonce = 0 + + let serialization = block.serialize() + XCTAssertEqual( + serialization, + [ + 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ]) + } + + func testTxSerialization() throws { + let tx = BTCTransaction() + tx.version = 1 + let input = BTCTransaction.Input( + previousTransactionHash: BTCHashing.SHA_ZERO_HASH, + previousOutputIndex: 0xffff_ffff, // coinbase transaction has a u32::MAX prev output index + script: [] + ) + tx.inputs = [input] + tx.setWitnessForInput(inputIndex: 0, witness: BTCTransaction.Witness(stackElements: [[1]])) + + let output = BTCTransaction.Output( + value: 100000, + script: [ + 0, 32, 214, 232, 96, 182, 226, 231, 42, 33, 219, 246, 121, 242, 58, 123, 110, 124, 118, 63, 70, 117, + 109, 247, 0, 58, 172, 198, 127, 254, 216, 194, 41, 14, + ]) + tx.outputs = [output] + let txSerialization = tx.serialize() + XCTAssertEqual(txSerialization.count, 99) + XCTAssertEqual( + txSerialization, + [ + 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 1, 160, 134, 1, 0, 0, 0, 0, 0, 34, 0, 32, 214, + 232, 96, 182, 226, 231, 42, 33, 219, 246, 121, 242, 58, 123, 110, 124, 118, 63, 70, 117, 109, 247, 0, + 58, 172, 198, 127, 254, 216, 194, 41, 14, 1, 1, 1, 0, 0, 0, 0, + ]) + + let block = BTCBlock() + block.version = 2 + block.previousBlockHash = BTCHashing.SHA_ZERO_HASH + block.merkleRoot = BTCHashing.SHA_ZERO_HASH + block.timestamp = 42 + block.difficultyTarget = 0 + block.nonce = 0 + block.transactions = [tx] + let blockSerialization = block.serialize() + XCTAssertEqual( + blockSerialization, + [ + 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 1, 160, 134, + 1, 0, 0, 0, 0, 0, 34, 0, 32, 214, 232, 96, 182, 226, 231, 42, 33, 219, 246, 121, 242, 58, 123, 110, 124, + 118, 63, 70, 117, 109, 247, 0, 58, 172, 198, 127, 254, 216, 194, 41, 14, 1, 1, 1, 0, 0, 0, 0, + ]) + } + + #if !SWIFT_PACKAGE + @available(iOS 15.0, *) + func testRpcCalls() async throws { + let username = ProcessInfo.processInfo.environment["BITCOIN_REGTEST_RPC_USERNAME"] ?? "polaruser" // "alice" + let password = ProcessInfo.processInfo.environment["BITCOIN_REGTEST_RPC_PASSWORD"] ?? "polarpass" // "DONT_USE_THIS_YOU_WILL_GET_ROBBED" + let port = + NumberFormatter().number(from: ProcessInfo.processInfo.environment["BITCOIN_REGTEST_RPC_PORT"] ?? "") + ?? 18443 + let rpcInterface = try RegtestBlockchainManager( + rpcProtocol: .http, rpcDomain: "localhost", rpcPort: (port as! UInt), rpcUsername: username, + rpcPassword: password) + let chaintipHeight = try await rpcInterface.getChaintipHeight() + XCTAssertGreaterThanOrEqual(chaintipHeight, 0) + let chaintipHashImplicit = try await rpcInterface.getBlockHashHex(height: chaintipHeight) + let chaintipHashExplicit = try await rpcInterface.getChaintipHashHex() + XCTAssertEqual(chaintipHashImplicit, chaintipHashExplicit) + let blockDetails = try await rpcInterface.getBlock(hash: chaintipHashExplicit) + XCTAssertEqual(blockDetails.height, chaintipHeight) + + let chainInfo = try await rpcInterface.getChainInfo() + + let genesisHash = try await rpcInterface.getBlockHashHex(height: 1) + let genesisDetails = try await rpcInterface.getBlock(hash: genesisHash) + let genesisBinary = try await rpcInterface.getBlockBinary(hash: genesisHash) + let genesisHeader = try await rpcInterface.getBlockHeader(hash: genesisHash) + + let genesisCoinbaseTxHash = genesisDetails.tx.first! + let genesisCoinbaseTx = try await rpcInterface.getTransaction(hash: genesisCoinbaseTxHash) + + + // try? await rpcInterface.submitTransaction(transaction: modifiedCoinbaseTx) + + // let address = try await rpcInterface.generateAddress() + // let details = try await rpcInterface.mineBlocks(number: 1, coinbaseDestinationAddress: address) + let helpDetails = try await rpcInterface.getHelp() + // print(helpDetails) + + let exampleFundingTx: [UInt8] = [ + 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 1, 160, 134, 1, 0, 0, 0, 0, 0, 34, 0, 32, 15, 45, + 153, 123, 98, 37, 242, 142, 113, 51, 126, 45, 206, 175, 66, 247, 173, 2, 141, 2, 27, 84, 38, 188, 34, + 74, 82, 164, 28, 229, 39, 139, 1, 1, 1, 0, 0, 0, 0, + ] + + // try? await rpcInterface.submitTransaction(transaction: exampleFundingTx) + + + class Listener: BlockchainListener { + var initializationComplete = false + var newBlocksDetected = 0 + var blocksLost = 0 + + func blockConnected(block: [UInt8], height: UInt32) { + if self.initializationComplete { + self.newBlocksDetected += 1 + } + let blockHash = LDKSwiftTests.bytesToHexString(bytes: BTCHashing.doubleSha256(block)) + // print("block connected at height \(height): \(blockHash)") + } + + func blockDisconnected(header: [UInt8]?, height: UInt32) { + self.blocksLost += 1 + // print("block disconnected from height \(height): \(header)") + } + } + + var listener = Listener() + rpcInterface.registerListener(listener) + + try await rpcInterface.preloadMonitor() + // async let monitor = try rpcInterface.monitorBlockchain() + listener.initializationComplete = true - do { let testAddress = try await rpcInterface.generateAddress() - let chaintip = try await rpcInterface.getChaintipHeight() - let penultimateBlockHash = try await rpcInterface.getBlockHashHex(height: chaintip - 1) - try await rpcInterface.unmineBlock(hash: penultimateBlockHash) + let exampleOutputScript: [UInt8] = [ + 0, 32, 200, 194, 75, 55, 227, 33, 251, 71, 196, 33, 177, 196, 155, 145, 17, 78, 244, 226, 155, 141, 216, + 230, 180, 183, 149, 172, 116, 249, 56, 6, 118, 255, + ] + let scriptInfo = try await rpcInterface.decodeScript(script: exampleOutputScript) + let outputAddress = (scriptInfo["addresses"] as! [String]).first! try await rpcInterface.mineBlocks(number: 1, coinbaseDestinationAddress: testAddress) + try await rpcInterface.mineBlocks(number: 1, coinbaseDestinationAddress: outputAddress) + // sleep for six seconds + // try await Task.sleep(nanoseconds: 6_000_000_000) try await rpcInterface.reconcileChaintips() - XCTAssertEqual(listener.newBlocksDetected, 3) - XCTAssertEqual(listener.blocksLost, 2) - } + XCTAssertEqual(listener.newBlocksDetected, 2) + XCTAssertEqual(listener.blocksLost, 0) + + do { + let testAddress = try await rpcInterface.generateAddress() + let chaintip = try await rpcInterface.getChaintipHeight() + let penultimateBlockHash = try await rpcInterface.getBlockHashHex(height: chaintip - 1) + try await rpcInterface.unmineBlock(hash: penultimateBlockHash) + try await rpcInterface.mineBlocks(number: 1, coinbaseDestinationAddress: testAddress) + + try await rpcInterface.reconcileChaintips() + XCTAssertEqual(listener.newBlocksDetected, 3) + XCTAssertEqual(listener.blocksLost, 2) + } - do { - let testAddress = try await rpcInterface.generateAddress() - let chaintipHash = try await rpcInterface.getChaintipHashHex() - print("chaintip: \(chaintipHash)") - try await rpcInterface.unmineBlock(hash: chaintipHash) - try await rpcInterface.mineBlocks(number: 2, coinbaseDestinationAddress: testAddress) + do { + let testAddress = try await rpcInterface.generateAddress() + let chaintipHash = try await rpcInterface.getChaintipHashHex() + print("chaintip: \(chaintipHash)") + try await rpcInterface.unmineBlock(hash: chaintipHash) + try await rpcInterface.mineBlocks(number: 2, coinbaseDestinationAddress: testAddress) - try await rpcInterface.reconcileChaintips() - XCTAssertEqual(listener.newBlocksDetected, 5) - XCTAssertEqual(listener.blocksLost, 3) - } + try await rpcInterface.reconcileChaintips() + XCTAssertEqual(listener.newBlocksDetected, 5) + XCTAssertEqual(listener.blocksLost, 3) + } - do { - let chaintipHash = try await rpcInterface.getChaintipHashHex() - print("chaintip: \(chaintipHash)") - try await rpcInterface.unmineBlock(hash: chaintipHash) + do { + let chaintipHash = try await rpcInterface.getChaintipHashHex() + print("chaintip: \(chaintipHash)") + try await rpcInterface.unmineBlock(hash: chaintipHash) - try await rpcInterface.reconcileChaintips() - XCTAssertEqual(listener.newBlocksDetected, 5) - XCTAssertEqual(listener.blocksLost, 4) + try await rpcInterface.reconcileChaintips() + XCTAssertEqual(listener.newBlocksDetected, 5) + XCTAssertEqual(listener.blocksLost, 4) + } } - } - #endif + #endif } diff --git a/out/VersionDescriptor.swift b/out/VersionDescriptor.swift index fce01225..e41700d7 100644 --- a/out/VersionDescriptor.swift +++ b/out/VersionDescriptor.swift @@ -4,12 +4,12 @@ extension Bindings { public class func getLDKSwiftBindingsSerializationHash() -> String { - return "a73d8edaba590e126534bd9364129f5feb663463cffef700384e674737e624a2" + return "79076e354f009c6232a3c95bfacfc59f4b4ca4a3cc6b6133ee8f58ee856fac31" } public class func getLDKSwiftBindingsVersion() -> String { - return "0.0.116-36-g49968007-dirty" + return "0.0.124-2-g591c5860-dirty" } public class func getLDKSwiftBindingsCommitHash() -> String { - return "49968007a7246b169da452d6f25898dd7fb9cd14" + return "591c58608bdf1a3e1c22902c42b4f00530b1dd9d" } } diff --git a/xcode/LDKFramework/DirectlyLinkedBindingsApp/ContentView.swift b/xcode/LDKFramework/DirectlyLinkedBindingsApp/ContentView.swift index 5fd73235..b9b05a70 100644 --- a/xcode/LDKFramework/DirectlyLinkedBindingsApp/ContentView.swift +++ b/xcode/LDKFramework/DirectlyLinkedBindingsApp/ContentView.swift @@ -9,55 +9,59 @@ import SwiftUI struct ContentView: View { - @State private var isRunningTestFlow = false - - @State private var multiPeerSimulation: PolarIntegrationSample.MultiPeerSimulator? = nil + @State private var isRunningTestFlow = false - var body: some View { + @State private var multiPeerSimulation: PolarIntegrationSample.MultiPeerSimulator? = nil - Button(action: { - self.isRunningTestFlow = true - if #available(iOS 15.0, *) { - let sample = PolarIntegrationSample() - Task { - try? await sample.testPolarFlow() - self.isRunningTestFlow = false - } - } else { - // Fallback on earlier versions - } - - }, label: { - Text("Test Polar Invoice Invoice Payment Flow") - }).disabled(self.isRunningTestFlow) + var body: some View { - if let simulation = self.multiPeerSimulation { - Button { - // self.multiPeerSimulation = nil - Task { - await self.multiPeerSimulation?.interrupt() - - // freeing the simulation - // try! await Task.sleep(nanoseconds: 5_000_000_000) - // self.multiPeerSimulation = nil - } - } label: { - Text("Stop multi-peer simulation") - } - } else { - Button { - self.multiPeerSimulation = PolarIntegrationSample.MultiPeerSimulator() - Task { - try? await self.multiPeerSimulation!.simulateMultiplePeers() - } - } label: { - Text("Simulate multiple peers") - } - } - - } + Button( + action: { + self.isRunningTestFlow = true + if #available(iOS 15.0, *) { + let sample = PolarIntegrationSample() + Task { + try? await sample.testPolarFlow() + self.isRunningTestFlow = false + } + } else { + // Fallback on earlier versions + } + + }, + label: { + Text("Test Polar Invoice Invoice Payment Flow") + } + ) + .disabled(self.isRunningTestFlow) + + if let simulation = self.multiPeerSimulation { + Button { + // self.multiPeerSimulation = nil + Task { + await self.multiPeerSimulation?.interrupt() + + // freeing the simulation + // try! await Task.sleep(nanoseconds: 5_000_000_000) + // self.multiPeerSimulation = nil + } + } label: { + Text("Stop multi-peer simulation") + } + } else { + Button { + self.multiPeerSimulation = PolarIntegrationSample.MultiPeerSimulator() + Task { + try? await self.multiPeerSimulation!.simulateMultiplePeers() + } + } label: { + Text("Simulate multiple peers") + } + } - /*@StateObject private var experiment = PolarConnectionExperiment() + } + + /*@StateObject private var experiment = PolarConnectionExperiment() var body: some View { if !self.experiment.isMonitoring { Button("Monitor Chain") { @@ -73,16 +77,16 @@ struct ContentView: View { self.experiment.completeChainSync() }.padding() } else { - + Text("Sync Complete").padding() - + if !self.experiment.isConnectedToAlice { Button("Connect to Alice") { self.experiment.connectToAlice() }.padding() } else { Text("Connected to Alice").padding() - + if !self.experiment.isChannelWithAliceOpen { Button("Open Channel w/ Alice") { self.experiment.openChannelWithAlice() @@ -98,7 +102,7 @@ struct ContentView: View { } struct ContentView_Previews: PreviewProvider { - static var previews: some View { - ContentView() - } + static var previews: some View { + ContentView() + } } diff --git a/xcode/LDKFramework/DirectlyLinkedBindingsApp/DirectlyLinkedBindingsAppApp.swift b/xcode/LDKFramework/DirectlyLinkedBindingsApp/DirectlyLinkedBindingsAppApp.swift index c0aed17b..a3592b25 100644 --- a/xcode/LDKFramework/DirectlyLinkedBindingsApp/DirectlyLinkedBindingsAppApp.swift +++ b/xcode/LDKFramework/DirectlyLinkedBindingsApp/DirectlyLinkedBindingsAppApp.swift @@ -9,42 +9,42 @@ import SwiftUI @main struct DirectlyLinkedBindingsAppApp: App { - - init() { - print("Directly linked bindings app is initialized") - Bindings.setLogThreshold(severity: .DEBUG) - - func printCompiledVersion() { - let compiledVersion = Bindings.getLDKSwiftBindingsSerializationHash() - print("Compiled version: \(compiledVersion)\n\(#file)::\(#function):\(#line)") - } - printCompiledVersion() - - do { - let thing = Bindings.Fallback.initWithSegWitProgram(version: 255, program: []) - } - - Fallback.enableDeinitLogging = false - do { - let thing = Bindings.Fallback.initWithSegWitProgram(version: 255, program: []) - let otherThing = Bindings.UserConfig.initWithDefault() - } - - // let tx = BuiltCommitmentTransaction(transactionArg: [], txidArg: [UInt8](repeating: 0, count: 32)) - - let nativeNetwork = LDKNetwork_Regtest - let swiftNetwork = Bindings.Network.Bitcoin - - let restoredNetwork = swiftNetwork.getCValue() - let reconstructedNetwork = Bindings.Network(value: nativeNetwork) - - print("here we are") - - } - - var body: some Scene { - WindowGroup { - ContentView() - } - } + + init() { + print("Directly linked bindings app is initialized") + Bindings.setLogThreshold(severity: .DEBUG) + + func printCompiledVersion() { + let compiledVersion = Bindings.getLDKSwiftBindingsSerializationHash() + print("Compiled version: \(compiledVersion)\n\(#file)::\(#function):\(#line)") + } + printCompiledVersion() + + do { + let thing = Bindings.Fallback.initWithSegWitProgram(version: 255, program: []) + } + + Fallback.enableDeinitLogging = false + do { + let thing = Bindings.Fallback.initWithSegWitProgram(version: 255, program: []) + let otherThing = Bindings.UserConfig.initWithDefault() + } + + // let tx = BuiltCommitmentTransaction(transactionArg: [], txidArg: [UInt8](repeating: 0, count: 32)) + + let nativeNetwork = LDKNetwork_Regtest + let swiftNetwork = Bindings.Network.Bitcoin + + let restoredNetwork = swiftNetwork.getCValue() + let reconstructedNetwork = Bindings.Network(value: nativeNetwork) + + print("here we are") + + } + + var body: some Scene { + WindowGroup { + ContentView() + } + } } diff --git a/xcode/LDKFramework/DirectlyLinkedBindingsApp/PolarConnectionExperiment.swift b/xcode/LDKFramework/DirectlyLinkedBindingsApp/PolarConnectionExperiment.swift index fa2b3aa6..19c599c0 100644 --- a/xcode/LDKFramework/DirectlyLinkedBindingsApp/PolarConnectionExperiment.swift +++ b/xcode/LDKFramework/DirectlyLinkedBindingsApp/PolarConnectionExperiment.swift @@ -8,155 +8,166 @@ import Foundation class PolarConnectionExperiment: ObservableObject { - - @Published internal var isMonitoring = false - @Published internal var hasCaughtUpToChainTip = false - @Published internal var hasCompletedChainSync = false - @Published internal var isConnectedToAlice = false - @Published internal var isChannelWithAliceOpen = false - - // non-reliant on anything - var feeEstimator: FeeEstimator - var logger: Logger - var broadcaster: BroadcasterInterface - var persister: Persist - var filter: Filter - var chainMonitor: ChainMonitor - var keysManager: KeysManager - var keysInterface: KeysInterface - - var blockchainObserver: RegtestBlockchainObserverOld! - - // reliant on async response from blockchain observer - var channelManagerConstructor: ChannelManagerConstructor! - var channelManager: ChannelManager! - var cmPersister: ExtendedChannelManagerPersister! - var peerManager: PeerManager! - var peerNetworkHandler: TCPPeerHandler! - - init() { - - self.filter = TestFilter() - self.broadcaster = RegtestBroadcasterInterface() - self.logger = TestLogger() - self.feeEstimator = TestFeeEstimator() - self.persister = TestPersister() - - let filterOption = Option_FilterZ(value: self.filter) - self.chainMonitor = ChainMonitor(chain_source: filterOption.dangle(), broadcaster: self.broadcaster, logger: self.logger, feeest: self.feeEstimator, persister: self.persister) - - let seed: [UInt8] = [UInt8](Data(base64Encoded: "//////////////////////////////////////////8=")!) - let timestamp_seconds = UInt64(NSDate().timeIntervalSince1970) - let timestamp_nanos = UInt32(truncating: NSNumber(value: timestamp_seconds * 1000 * 1000)) - - self.keysManager = KeysManager(seed: seed, starting_time_secs: timestamp_seconds, starting_time_nanos: timestamp_nanos) - - self.keysInterface = self.keysManager.as_KeysInterface() - - self.blockchainObserver = RegtestBlockchainObserverOld(listeners: [], chainTip: nil) { - // sync complete - let config = UserConfig() - let lightningNetwork = LDKNetwork_Regtest - let genesis_hash: [UInt8] = [6, 34, 110, 70, 17, 26, 11, 89, 202, 175, 18, 96, 67, 235, 91, 191, 40, 195, 79, 58, 94, 51, 42, 31, 199, 178, 183, 60, 241, 136, 145, 15] - let chainTipHash = self.blockchainObserver.chainTipHash - let chainTipHeight = self.blockchainObserver.chainTipHeight - - let networkGraph = NetworkGraph(genesis_hash: genesis_hash, logger: self.logger) - - - - - - self.channelManagerConstructor = ChannelManagerConstructor(network: lightningNetwork, userConfig: config, current_blockchain_tip_hash: chainTipHash, current_blockchain_tip_height: chainTipHeight, keys_interface: self.keysInterface, fee_estimator: self.feeEstimator, chain_monitor: self.chainMonitor, net_graph: networkGraph, tx_broadcaster: self.broadcaster, logger: self.logger) - let userConfig = UserConfig() - let latestBlockHash = [UInt8](Data(base64Encoded: "AAAAAAAAAAAABe5Xh25D12zkQuLAJQbBeLoF1tEQqR8=")!) - let latestBlockHeight = 700123 - - - - self.channelManager = self.channelManagerConstructor.channelManager - self.peerManager = self.channelManagerConstructor.peerManager - self.peerNetworkHandler = self.channelManagerConstructor.getTCPPeerHandler() - - self.cmPersister = RegtestChannelManagerPersister(channelManager: self.channelManager) - self.hasCaughtUpToChainTip = true - self.objectWillChange.send() - - // Bindings.new_LDKTransactionWrapper(array: <#T##[UInt8]#>) - } - - } - - func startMonitoring() { - if self.isMonitoring { - return - } - self.isMonitoring = true - self.blockchainObserver.monitor() - self.objectWillChange.send() - } - - func completeChainSync() { - if !self.hasCaughtUpToChainTip || self.hasCompletedChainSync { - return - } - self.hasCompletedChainSync = true - self.channelManagerConstructor.chain_sync_completed(persister: self.cmPersister, scorer: nil) - self.objectWillChange.send() - } - - func connectToAlice() { - if !self.hasCompletedChainSync || self.isConnectedToAlice { - return - } - self.isConnectedToAlice = true - // let theirNodeId: [UInt8] = [3, 76, 1, 164, 167, 52, 78, 65, 176, 169, 137, 4, 159, 182, 49, 198, 72, 197, 210, 127, 107, 63, 166, 28, 124, 25, 59, 64, 220, 201, 106, 147, 65] - let theirNodeId = Block.hexStringToBytes(hexString: "034c01a4a7344e41b0a989049fb631c648c5d27f6b3fa61c7c193b40dcc96a9341")! - let connectionSucceeded = self.peerNetworkHandler.connect(address: "127.0.0.1", port: 9735, theirNodeId: theirNodeId) - if !connectionSucceeded { - self.isConnectedToAlice = false - return - } - self.objectWillChange.send() - - let peers = self.peerManager.get_peer_node_ids() - } - - func openChannelWithAlice() { - if !self.isConnectedToAlice || self.isChannelWithAliceOpen { - return - } - self.isChannelWithAliceOpen = true - - let config = UserConfig() - let theirNodeId = Block.hexStringToBytes(hexString: "034c01a4a7344e41b0a989049fb631c648c5d27f6b3fa61c7c193b40dcc96a9341")! - let channelOpenResult = self.channelManager.create_channel(their_network_key: theirNodeId, channel_value_satoshis: 4000000, push_msat: 2000000, user_channel_id: 42, override_config: config) - - self.objectWillChange.send() - - if channelOpenResult.isOk() { - print("Channel should have opened successfully!") - }else if let errorDetails = channelOpenResult.getError(){ - print("Channel open failed!") - if let error = errorDetails.getValueAsAPIMisuseError() { - print("Misuse: \(error.getErr())") - }else if let error = errorDetails.getValueAsRouteError() { - print("Route: \(error.getErr())") - }else if let error = errorDetails.getValueAsChannelUnavailable() { - print("Channel Unavailable: \(error.getErr())") - }else if let error = errorDetails.getValueAsFeeRateTooHigh() { - print("fee rate: \(error.getErr())") - }else if let error = errorDetails.getValueAsIncompatibleShutdownScript() { - print("incompatible shutdown script") - } - self.isChannelWithAliceOpen = false - self.objectWillChange.send() - - } - } - - deinit { - print("Polar Connection Experiment deinited") - } - + + @Published internal var isMonitoring = false + @Published internal var hasCaughtUpToChainTip = false + @Published internal var hasCompletedChainSync = false + @Published internal var isConnectedToAlice = false + @Published internal var isChannelWithAliceOpen = false + + // non-reliant on anything + var feeEstimator: FeeEstimator + var logger: Logger + var broadcaster: BroadcasterInterface + var persister: Persist + var filter: Filter + var chainMonitor: ChainMonitor + var keysManager: KeysManager + var keysInterface: KeysInterface + + var blockchainObserver: RegtestBlockchainObserverOld! + + // reliant on async response from blockchain observer + var channelManagerConstructor: ChannelManagerConstructor! + var channelManager: ChannelManager! + var cmPersister: ExtendedChannelManagerPersister! + var peerManager: PeerManager! + var peerNetworkHandler: TCPPeerHandler! + + init() { + + self.filter = TestFilter() + self.broadcaster = RegtestBroadcasterInterface() + self.logger = TestLogger() + self.feeEstimator = TestFeeEstimator() + self.persister = TestPersister() + + let filterOption = Option_FilterZ(value: self.filter) + self.chainMonitor = ChainMonitor( + chain_source: filterOption.dangle(), broadcaster: self.broadcaster, logger: self.logger, + feeest: self.feeEstimator, persister: self.persister) + + let seed: [UInt8] = [UInt8](Data(base64Encoded: "//////////////////////////////////////////8=")!) + let timestamp_seconds = UInt64(NSDate().timeIntervalSince1970) + let timestamp_nanos = UInt32(truncating: NSNumber(value: timestamp_seconds * 1000 * 1000)) + + self.keysManager = KeysManager( + seed: seed, starting_time_secs: timestamp_seconds, starting_time_nanos: timestamp_nanos) + + self.keysInterface = self.keysManager.as_KeysInterface() + + self.blockchainObserver = RegtestBlockchainObserverOld(listeners: [], chainTip: nil) { + // sync complete + let config = UserConfig() + let lightningNetwork = LDKNetwork_Regtest + let genesis_hash: [UInt8] = [ + 6, 34, 110, 70, 17, 26, 11, 89, 202, 175, 18, 96, 67, 235, 91, 191, 40, 195, 79, 58, 94, 51, 42, 31, + 199, 178, 183, 60, 241, 136, 145, 15, + ] + let chainTipHash = self.blockchainObserver.chainTipHash + let chainTipHeight = self.blockchainObserver.chainTipHeight + + let networkGraph = NetworkGraph(genesis_hash: genesis_hash, logger: self.logger) + + + self.channelManagerConstructor = ChannelManagerConstructor( + network: lightningNetwork, userConfig: config, current_blockchain_tip_hash: chainTipHash, + current_blockchain_tip_height: chainTipHeight, keys_interface: self.keysInterface, + fee_estimator: self.feeEstimator, chain_monitor: self.chainMonitor, net_graph: networkGraph, + tx_broadcaster: self.broadcaster, logger: self.logger) + let userConfig = UserConfig() + let latestBlockHash = [UInt8](Data(base64Encoded: "AAAAAAAAAAAABe5Xh25D12zkQuLAJQbBeLoF1tEQqR8=")!) + let latestBlockHeight = 700123 + + + self.channelManager = self.channelManagerConstructor.channelManager + self.peerManager = self.channelManagerConstructor.peerManager + self.peerNetworkHandler = self.channelManagerConstructor.getTCPPeerHandler() + + self.cmPersister = RegtestChannelManagerPersister(channelManager: self.channelManager) + self.hasCaughtUpToChainTip = true + self.objectWillChange.send() + + // Bindings.new_LDKTransactionWrapper(array: <#T##[UInt8]#>) + } + + } + + func startMonitoring() { + if self.isMonitoring { + return + } + self.isMonitoring = true + self.blockchainObserver.monitor() + self.objectWillChange.send() + } + + func completeChainSync() { + if !self.hasCaughtUpToChainTip || self.hasCompletedChainSync { + return + } + self.hasCompletedChainSync = true + self.channelManagerConstructor.chain_sync_completed(persister: self.cmPersister, scorer: nil) + self.objectWillChange.send() + } + + func connectToAlice() { + if !self.hasCompletedChainSync || self.isConnectedToAlice { + return + } + self.isConnectedToAlice = true + // let theirNodeId: [UInt8] = [3, 76, 1, 164, 167, 52, 78, 65, 176, 169, 137, 4, 159, 182, 49, 198, 72, 197, 210, 127, 107, 63, 166, 28, 124, 25, 59, 64, 220, 201, 106, 147, 65] + let theirNodeId = Block.hexStringToBytes( + hexString: "034c01a4a7344e41b0a989049fb631c648c5d27f6b3fa61c7c193b40dcc96a9341")! + let connectionSucceeded = self.peerNetworkHandler.connect( + address: "127.0.0.1", port: 9735, theirNodeId: theirNodeId) + if !connectionSucceeded { + self.isConnectedToAlice = false + return + } + self.objectWillChange.send() + + let peers = self.peerManager.get_peer_node_ids() + } + + func openChannelWithAlice() { + if !self.isConnectedToAlice || self.isChannelWithAliceOpen { + return + } + self.isChannelWithAliceOpen = true + + let config = UserConfig() + let theirNodeId = Block.hexStringToBytes( + hexString: "034c01a4a7344e41b0a989049fb631c648c5d27f6b3fa61c7c193b40dcc96a9341")! + let channelOpenResult = self.channelManager.create_channel( + their_network_key: theirNodeId, channel_value_satoshis: 4_000_000, push_msat: 2_000_000, + user_channel_id: 42, override_config: config) + + self.objectWillChange.send() + + if channelOpenResult.isOk() { + print("Channel should have opened successfully!") + } else if let errorDetails = channelOpenResult.getError() { + print("Channel open failed!") + if let error = errorDetails.getValueAsAPIMisuseError() { + print("Misuse: \(error.getErr())") + } else if let error = errorDetails.getValueAsRouteError() { + print("Route: \(error.getErr())") + } else if let error = errorDetails.getValueAsChannelUnavailable() { + print("Channel Unavailable: \(error.getErr())") + } else if let error = errorDetails.getValueAsFeeRateTooHigh() { + print("fee rate: \(error.getErr())") + } else if let error = errorDetails.getValueAsIncompatibleShutdownScript() { + print("incompatible shutdown script") + } + self.isChannelWithAliceOpen = false + self.objectWillChange.send() + + } + } + + deinit { + print("Polar Connection Experiment deinited") + } + } diff --git a/xcode/LDKFramework/DirectlyLinkedBindingsApp/app-batteries/BlockchainObserver.swift b/xcode/LDKFramework/DirectlyLinkedBindingsApp/app-batteries/BlockchainObserver.swift index 21836a05..40bea631 100644 --- a/xcode/LDKFramework/DirectlyLinkedBindingsApp/app-batteries/BlockchainObserver.swift +++ b/xcode/LDKFramework/DirectlyLinkedBindingsApp/app-batteries/BlockchainObserver.swift @@ -5,8 +5,8 @@ // Created by Arik Sosman on 4/10/22. // -import Foundation import Combine +import Foundation @available(iOS 15.0, *) class BlockchainObserver { @@ -50,19 +50,19 @@ class BlockchainObserver { func toString() -> String { switch self { - case .http: - return "http" - case .https: - return "https" + case .http: + return "http" + case .https: + return "https" } } func defaultPort() -> UInt { switch self { - case .http: - return 80 - case .https: - return 443 + case .http: + return 80 + case .https: + return 443 } } } @@ -122,7 +122,9 @@ class BlockchainObserver { private var chainListeners = [BlockchainListener]() - public init(rpcProtocol: RpcProtocol, rpcDomain: String, rpcPort: UInt?, rpcUsername: String, rpcPassword: String) throws { + public init(rpcProtocol: RpcProtocol, rpcDomain: String, rpcPort: UInt?, rpcUsername: String, rpcPassword: String) + throws + { self.rpcProtocol = rpcProtocol self.rpcDomain = rpcDomain self.rpcPort = rpcPort ?? rpcProtocol.defaultPort() @@ -146,12 +148,12 @@ class BlockchainObserver { var lastTrustedBlockHeight: Int64 let chaintipHeight = try await self.getChaintipHeight() switch anchorHeight { - case .genesis: - lastTrustedBlockHeight = 0 - case .block(let height): - lastTrustedBlockHeight = height - case .chaintip: - lastTrustedBlockHeight = chaintipHeight + case .genesis: + lastTrustedBlockHeight = 0 + case .block(let height): + lastTrustedBlockHeight = height + case .chaintip: + lastTrustedBlockHeight = chaintipHeight } let anchorBlockHash = try await self.getBlockHashHex(height: lastTrustedBlockHeight) @@ -275,7 +277,8 @@ class BlockchainObserver { } private func getRpcEndpoint() -> URL? { - let urlString = "\(self.rpcProtocol)://\(self.rpcUsername):\(self.rpcPassword)@\(self.rpcDomain):\(self.rpcPort)" + let urlString = + "\(self.rpcProtocol)://\(self.rpcUsername):\(self.rpcPassword)@\(self.rpcDomain):\(self.rpcPort)" return URL(string: urlString) } @@ -283,7 +286,7 @@ class BlockchainObserver { let url = self.getRpcEndpoint()! let body: [String: Any] = [ "method": method, - "params": params + "params": params, ] let jsonBody = try! JSONSerialization.data(withJSONObject: body) var request = URLRequest(url: url) @@ -294,7 +297,8 @@ class BlockchainObserver { // print("JSON-RPC response: \(response)") let responseDictionary = response as! [String: Any] if let responseError = responseDictionary["error"] as? [String: Any] { - let errorDetails = RPCErrorDetails(message: responseError["message"] as! String, code: responseError["code"] as! Int64) + let errorDetails = RPCErrorDetails( + message: responseError["message"] as! String, code: responseError["code"] as! Int64) print("error details: \(errorDetails)") throw RpcError.errorResponse(errorDetails) } @@ -341,7 +345,8 @@ class BlockchainObserver { public func getBlock(hash: String) async throws -> RPCBlockDetails { let response = try await self.callRpcMethod(method: "getblock", params: [hash]) let result = response["result"] as! [String: Any] - let blockDetails = try JSONDecoder().decode(RPCBlockDetails.self, from: JSONSerialization.data(withJSONObject: result)) + let blockDetails = try JSONDecoder() + .decode(RPCBlockDetails.self, from: JSONSerialization.data(withJSONObject: result)) return blockDetails } @@ -478,26 +483,26 @@ class BlockchainObserver { return result } - public func isMonitoring() async throws -> Bool { - await monitoringTracker.isTracking - } - -// public var blockchainMonitorPublisher: AnyPublisher { -// Timer.publish(every: 5.0, on: RunLoop.main, in: .default) -// .autoconnect() -// .asyncMap { [unowned self] _ in -// try await self.reconcileChaintips() -// } -// .eraseToAnyPublisher() -// } + public func isMonitoring() async throws -> Bool { + await monitoringTracker.isTracking + } + + // public var blockchainMonitorPublisher: AnyPublisher { + // Timer.publish(every: 5.0, on: RunLoop.main, in: .default) + // .autoconnect() + // .asyncMap { [unowned self] _ in + // try await self.reconcileChaintips() + // } + // .eraseToAnyPublisher() + // } } public protocol BlockchainListener { - func blockConnected(block: [UInt8], height: UInt32); - func blockDisconnected(header: [UInt8]?, height: UInt32); + func blockConnected(block: [UInt8], height: UInt32) + func blockDisconnected(header: [UInt8]?, height: UInt32) } -fileprivate func hexStringToBytes(hexString: String) -> [UInt8]? { +private func hexStringToBytes(hexString: String) -> [UInt8]? { let hexStr = hexString.dropFirst(hexString.hasPrefix("0x") ? 2 : 0) guard hexStr.count % 2 == 0 else { @@ -520,10 +525,11 @@ fileprivate func hexStringToBytes(hexString: String) -> [UInt8]? { return newData } -fileprivate func bytesToHexString(bytes: [UInt8]) -> String { - let format = "%02hhx" // "%02hhX" (uppercase) - return bytes.map { - String(format: format, $0) - } - .joined() +private func bytesToHexString(bytes: [UInt8]) -> String { + let format = "%02hhx" // "%02hhX" (uppercase) + return + bytes.map { + String(format: format, $0) + } + .joined() } diff --git a/xcode/LDKFramework/DirectlyLinkedBindingsApp/app-batteries/CombineUtils.swift b/xcode/LDKFramework/DirectlyLinkedBindingsApp/app-batteries/CombineUtils.swift index 91423614..d41100b5 100644 --- a/xcode/LDKFramework/DirectlyLinkedBindingsApp/app-batteries/CombineUtils.swift +++ b/xcode/LDKFramework/DirectlyLinkedBindingsApp/app-batteries/CombineUtils.swift @@ -7,8 +7,8 @@ // Created by Jurvis Tan on 4/18/22. // -import Foundation import Combine +import Foundation //extension Publisher { // public func asyncMap( diff --git a/xcode/LDKFramework/DirectlyLinkedBindingsApp/app-batteries/LNSyncHandler.swift b/xcode/LDKFramework/DirectlyLinkedBindingsApp/app-batteries/LNSyncHandler.swift index 2ba5f69c..cf100ce4 100644 --- a/xcode/LDKFramework/DirectlyLinkedBindingsApp/app-batteries/LNSyncHandler.swift +++ b/xcode/LDKFramework/DirectlyLinkedBindingsApp/app-batteries/LNSyncHandler.swift @@ -1,278 +1,297 @@ import Foundation - import PromiseKit class LNSyncHandler: ObservableObject { - - @Published internal var syncState = LNSyncState.idle - - var network: NetworkGraph! - - enum LNSyncError: Error { - case badHTTPResponseStatus - case badHTTPResponseType - case missingBinary - case prefixParseError - case versionParseError - case messageParseError(DecodeError) - case tlvParseError - } - - public enum LNSyncState: String { - case idle = "idle" - case downloading = "downloading" - case parsing = "parsing" - case updating = "updating" - } - - enum GossipMessage { - case channelAnnouncement(ChannelAnnouncement) - case siglessChannelAnnouncement(UnsignedChannelAnnouncement) - case nodeAnnouncement(NodeAnnouncement) - case siglessNodeAnnouncement(UnsignedNodeAnnouncement) - case channelUpdate(ChannelUpdate) - case siglessChannelUpdate(UnsignedChannelUpdate) - } - - init(networkGraph: NetworkGraph? = nil) { - if let network = networkGraph { - self.network = network - } else { - self.network = NetworkGraph(genesis_hash: [111, 226, 140, 10, 182, 241, 179, 114, 193, 166, 162, 70, 174, 99, 247, 79, 147, 30, 131, 101, 225, 90, 8, 156, 104, 214, 25, 0, 0, 0, 0, 0], logger: Logger()) - } - } - - func updateAndPrintNetworkGraph() { - -// let timeInterval = TimeInterval(-15 * 60) // negative fifteen minutes - let timeInterval = TimeInterval(-2 * 3600) // negative two hours -// let timeInterval = TimeInterval(-24 * 3600) // negative 24 hours - - firstly { - return self.downloadRawLNSyncData(timeInterval: timeInterval, ignoreCache: true, storeToCache: false) - }.done { syncData in - let networkSerialization = self.network.write() - let byteCountFormatter = ByteCountFormatter() - print("initial network graph serialization: \(byteCountFormatter.string(fromByteCount: Int64(networkSerialization.count)))") - print("gossip data: \(byteCountFormatter.string(fromByteCount: Int64(syncData.count)))") - - let gossipMessages = try! self.parseSyncData(data: syncData) - print("fast-forwarding through \(gossipMessages.count) gossip messages") - - DispatchQueue.main.async { - self.syncState = .updating - self.objectWillChange.send() - } - - var errorCount = 0 - - for currentMessage in gossipMessages { - - var updateResult: Result_NoneLightningErrorZ! - - switch currentMessage { - case let .siglessChannelAnnouncement(channelAnnouncement): - updateResult = self.network.update_channel_from_unsigned_announcement(msg: channelAnnouncement, chain_access: Option_AccessZ.none()) - // print("channel announcement") - case let .siglessNodeAnnouncement(nodeAnnouncement): - updateResult = self.network.update_node_from_unsigned_announcement(msg: nodeAnnouncement) - // print("node announcement") - case let .siglessChannelUpdate(channelUpdate): - updateResult = self.network.update_channel_unsigned(msg: channelUpdate) - // print("channel update") - default: - print("we ignore signatures") - continue - } - - if let lightningError = updateResult.getError() { - errorCount += 1 - print("update error: \(lightningError.get_err())") - }else{ - // print("success") - } - } - - print("completed fast-forward with \(errorCount) errors") - - let cachedNetworkGraph = self.network.write() - let defaults = UserDefaults.standard - defaults.setValue(cachedNetworkGraph, forKey: "cachedNetworkGraph") - - self.syncState = .idle - self.objectWillChange.send() - - print("updated network graph serialization: \(byteCountFormatter.string(fromByteCount: Int64(cachedNetworkGraph.count)))") - } - - print("sync complete") - - } - - func parseSyncData(data: Data) throws -> [GossipMessage] { - - DispatchQueue.main.async { - self.syncState = .parsing - self.objectWillChange.send() - } - - let syncBytes = [UInt8](data) - let referencePrefix: [UInt8] = Array("GSP".utf8) - let arikPrefix: [UInt8] = Array("LDK".utf8) - - var isPreStripped = false - - guard syncBytes.starts(with: referencePrefix) || syncBytes.starts(with: arikPrefix) else { - throw LNSyncError.prefixParseError - } - - let versionByte: UInt8 = syncBytes[3] - guard syncBytes[3] == 1 || versionByte == 2 else { throw LNSyncError.versionParseError } - - if syncBytes.starts(with: arikPrefix) && versionByte == 2 { - isPreStripped = true - } - - var relevantBytes = syncBytes - relevantBytes.removeSubrange(0...3) - - var messages = [GossipMessage]() - - var i = 0 - while i < relevantBytes.count { - - let currentByte = relevantBytes[i] - - var messageLength = UInt(currentByte) - var messageStartIndex = i+1 - - if currentByte > 252 { - var sizeLength = 0 - if currentByte == 253 { - sizeLength = 2 - } else if currentByte == 254 { - sizeLength = 4 - } else if currentByte == 255 { - sizeLength = 8 - } - let sizeBytes: [UInt8] = Array(relevantBytes[i+1 ... i+sizeLength]) - messageLength = 0 - messageStartIndex += sizeLength - for currentSizeByte in sizeBytes { - messageLength = messageLength << 8 - messageLength += UInt(currentSizeByte) - } - } - - let messageEndIndex = messageStartIndex + Int(messageLength) - let currentMessage: [UInt8] = Array(relevantBytes[messageStartIndex.. relevantBytes.count { - throw LNSyncError.tlvParseError - } - - let currentMessageType = UInt(currentMessage[0]) << 8 + UInt(currentMessage[1]) - - let extractedMessage: [UInt8] = Array(currentMessage[2...]) - - if currentMessageType == 256 { - var strippedChannelAnnouncementBytes = extractedMessage - if !isPreStripped { - strippedChannelAnnouncementBytes = Array(extractedMessage[(64*4)...]) - } - let channelAnnouncementParseResult = UnsignedChannelAnnouncement.read(ser: strippedChannelAnnouncementBytes) - if let channelAnnouncement = channelAnnouncementParseResult.getValue() { - messages.append(GossipMessage.siglessChannelAnnouncement(channelAnnouncement)) - } else { - print("parse fail: channel announcement, \(currentMessage.count) bytes") - let decodeError = channelAnnouncementParseResult.getError() - throw LNSyncError.messageParseError(decodeError!) - } - } else if currentMessageType == 257 { - var strippedNodeAnnouncementBytes = extractedMessage - if !isPreStripped{ - strippedNodeAnnouncementBytes = Array(extractedMessage[(64)...]) - } - let nodeAnnouncementParseResult = UnsignedNodeAnnouncement.read(ser: strippedNodeAnnouncementBytes) - if let nodeAnnouncement = nodeAnnouncementParseResult.getValue() { - messages.append(GossipMessage.siglessNodeAnnouncement(nodeAnnouncement)) - } else { - print("parse fail: node announcement, \(currentMessage.count) bytes") - let decodeError = nodeAnnouncementParseResult.getError() - throw LNSyncError.messageParseError(decodeError!) - } - } else if currentMessageType == 258 { - var strippedChannelUpdateBytes = extractedMessage - if !isPreStripped { - strippedChannelUpdateBytes = Array(extractedMessage[(64)...]) - } - let channelUpdateParseResult = UnsignedChannelUpdate.read(ser: strippedChannelUpdateBytes) - if let channelUpdate = channelUpdateParseResult.getValue() { - messages.append(GossipMessage.siglessChannelUpdate(channelUpdate)) - } else { - print("parse fail: channel update, \(currentMessage.count) bytes") - let decodeError = channelUpdateParseResult.getError() - throw LNSyncError.messageParseError(decodeError!) - } - } - - } - - // print("parsing sanity: \(parseResultIsSane)") - return messages - - } - - func downloadRawLNSyncData(timeInterval: TimeInterval, ignoreCache: Bool = false, storeToCache: Bool = true) -> Promise { - - DispatchQueue.main.async { - self.syncState = .downloading - self.objectWillChange.send() - } - - let currentTime = NSDate() - let referenceTime = currentTime.addingTimeInterval(timeInterval) - let referenceTimestamp = UInt(referenceTime.timeIntervalSince1970.rounded()) - print("currentTime (\(currentTime.timeIntervalSince1970)) - delta (\(timeInterval)) = reference (\(referenceTimestamp))") - - let userDefaults = UserDefaults.standard - - let absoluteInterval = UInt(abs(timeInterval.rounded())) - - let urlString = "https://lnsync.blockstream.com/gossip/delta/\(referenceTimestamp)" - let localUrlString = "http://localhost:3000/since/\(referenceTimestamp)" - let herokuUrlString = "https://gossip.arik.io/delta/\(absoluteInterval)" - let binaryPromise = Promise { (resolver: Resolver) in - - if !ignoreCache { - if let cachedData = userDefaults.data(forKey: "cachedSyncData"){ - resolver.fulfill(cachedData) - return - } - } - - // let url = URL(string: urlString)! - let url = URL(string: herokuUrlString)! - let session = URLSession.shared - let task = session.dataTask(with: url, completionHandler: { data, response, error in - guard let httpResponse = response as? HTTPURLResponse, - (200...299).contains(httpResponse.statusCode) else { - return resolver.reject(LNSyncError.badHTTPResponseStatus) - } - guard let mime = httpResponse.mimeType, (mime == "application/ln.gossip" || mime == "application/octet-stream") else { - return resolver.reject(LNSyncError.badHTTPResponseType) - } - guard let blockData = data else { - return resolver.reject(LNSyncError.missingBinary) - } - if storeToCache { - userDefaults.setValue(blockData, forKey: "cachedSyncData") - } - resolver.fulfill(blockData) - }) - task.resume() - } - return binaryPromise - } - + + @Published internal var syncState = LNSyncState.idle + + var network: NetworkGraph! + + enum LNSyncError: Error { + case badHTTPResponseStatus + case badHTTPResponseType + case missingBinary + case prefixParseError + case versionParseError + case messageParseError(DecodeError) + case tlvParseError + } + + public enum LNSyncState: String { + case idle = "idle" + case downloading = "downloading" + case parsing = "parsing" + case updating = "updating" + } + + enum GossipMessage { + case channelAnnouncement(ChannelAnnouncement) + case siglessChannelAnnouncement(UnsignedChannelAnnouncement) + case nodeAnnouncement(NodeAnnouncement) + case siglessNodeAnnouncement(UnsignedNodeAnnouncement) + case channelUpdate(ChannelUpdate) + case siglessChannelUpdate(UnsignedChannelUpdate) + } + + init(networkGraph: NetworkGraph? = nil) { + if let network = networkGraph { + self.network = network + } else { + self.network = NetworkGraph( + genesis_hash: [ + 111, 226, 140, 10, 182, 241, 179, 114, 193, 166, 162, 70, 174, 99, 247, 79, 147, 30, 131, 101, 225, + 90, 8, 156, 104, 214, 25, 0, 0, 0, 0, 0, + ], logger: Logger()) + } + } + + func updateAndPrintNetworkGraph() { + + // let timeInterval = TimeInterval(-15 * 60) // negative fifteen minutes + let timeInterval = TimeInterval(-2 * 3600) // negative two hours + // let timeInterval = TimeInterval(-24 * 3600) // negative 24 hours + + firstly { + return self.downloadRawLNSyncData(timeInterval: timeInterval, ignoreCache: true, storeToCache: false) + } + .done { syncData in + let networkSerialization = self.network.write() + let byteCountFormatter = ByteCountFormatter() + print( + "initial network graph serialization: \(byteCountFormatter.string(fromByteCount: Int64(networkSerialization.count)))" + ) + print("gossip data: \(byteCountFormatter.string(fromByteCount: Int64(syncData.count)))") + + let gossipMessages = try! self.parseSyncData(data: syncData) + print("fast-forwarding through \(gossipMessages.count) gossip messages") + + DispatchQueue.main.async { + self.syncState = .updating + self.objectWillChange.send() + } + + var errorCount = 0 + + for currentMessage in gossipMessages { + + var updateResult: Result_NoneLightningErrorZ! + + switch currentMessage { + case let .siglessChannelAnnouncement(channelAnnouncement): + updateResult = self.network.update_channel_from_unsigned_announcement( + msg: channelAnnouncement, chain_access: Option_AccessZ.none()) + // print("channel announcement") + case let .siglessNodeAnnouncement(nodeAnnouncement): + updateResult = self.network.update_node_from_unsigned_announcement(msg: nodeAnnouncement) + // print("node announcement") + case let .siglessChannelUpdate(channelUpdate): + updateResult = self.network.update_channel_unsigned(msg: channelUpdate) + // print("channel update") + default: + print("we ignore signatures") + continue + } + + if let lightningError = updateResult.getError() { + errorCount += 1 + print("update error: \(lightningError.get_err())") + } else { + // print("success") + } + } + + print("completed fast-forward with \(errorCount) errors") + + let cachedNetworkGraph = self.network.write() + let defaults = UserDefaults.standard + defaults.setValue(cachedNetworkGraph, forKey: "cachedNetworkGraph") + + self.syncState = .idle + self.objectWillChange.send() + + print( + "updated network graph serialization: \(byteCountFormatter.string(fromByteCount: Int64(cachedNetworkGraph.count)))" + ) + } + + print("sync complete") + + } + + func parseSyncData(data: Data) throws -> [GossipMessage] { + + DispatchQueue.main.async { + self.syncState = .parsing + self.objectWillChange.send() + } + + let syncBytes = [UInt8](data) + let referencePrefix: [UInt8] = Array("GSP".utf8) + let arikPrefix: [UInt8] = Array("LDK".utf8) + + var isPreStripped = false + + guard syncBytes.starts(with: referencePrefix) || syncBytes.starts(with: arikPrefix) else { + throw LNSyncError.prefixParseError + } + + let versionByte: UInt8 = syncBytes[3] + guard syncBytes[3] == 1 || versionByte == 2 else { throw LNSyncError.versionParseError } + + if syncBytes.starts(with: arikPrefix) && versionByte == 2 { + isPreStripped = true + } + + var relevantBytes = syncBytes + relevantBytes.removeSubrange(0...3) + + var messages = [GossipMessage]() + + var i = 0 + while i < relevantBytes.count { + + let currentByte = relevantBytes[i] + + var messageLength = UInt(currentByte) + var messageStartIndex = i + 1 + + if currentByte > 252 { + var sizeLength = 0 + if currentByte == 253 { + sizeLength = 2 + } else if currentByte == 254 { + sizeLength = 4 + } else if currentByte == 255 { + sizeLength = 8 + } + let sizeBytes: [UInt8] = Array(relevantBytes[i + 1...i + sizeLength]) + messageLength = 0 + messageStartIndex += sizeLength + for currentSizeByte in sizeBytes { + messageLength = messageLength << 8 + messageLength += UInt(currentSizeByte) + } + } + + let messageEndIndex = messageStartIndex + Int(messageLength) + let currentMessage: [UInt8] = Array(relevantBytes[messageStartIndex.. relevantBytes.count { + throw LNSyncError.tlvParseError + } + + let currentMessageType = UInt(currentMessage[0]) << 8 + UInt(currentMessage[1]) + + let extractedMessage: [UInt8] = Array(currentMessage[2...]) + + if currentMessageType == 256 { + var strippedChannelAnnouncementBytes = extractedMessage + if !isPreStripped { + strippedChannelAnnouncementBytes = Array(extractedMessage[(64 * 4)...]) + } + let channelAnnouncementParseResult = UnsignedChannelAnnouncement.read( + ser: strippedChannelAnnouncementBytes) + if let channelAnnouncement = channelAnnouncementParseResult.getValue() { + messages.append(GossipMessage.siglessChannelAnnouncement(channelAnnouncement)) + } else { + print("parse fail: channel announcement, \(currentMessage.count) bytes") + let decodeError = channelAnnouncementParseResult.getError() + throw LNSyncError.messageParseError(decodeError!) + } + } else if currentMessageType == 257 { + var strippedNodeAnnouncementBytes = extractedMessage + if !isPreStripped { + strippedNodeAnnouncementBytes = Array(extractedMessage[(64)...]) + } + let nodeAnnouncementParseResult = UnsignedNodeAnnouncement.read(ser: strippedNodeAnnouncementBytes) + if let nodeAnnouncement = nodeAnnouncementParseResult.getValue() { + messages.append(GossipMessage.siglessNodeAnnouncement(nodeAnnouncement)) + } else { + print("parse fail: node announcement, \(currentMessage.count) bytes") + let decodeError = nodeAnnouncementParseResult.getError() + throw LNSyncError.messageParseError(decodeError!) + } + } else if currentMessageType == 258 { + var strippedChannelUpdateBytes = extractedMessage + if !isPreStripped { + strippedChannelUpdateBytes = Array(extractedMessage[(64)...]) + } + let channelUpdateParseResult = UnsignedChannelUpdate.read(ser: strippedChannelUpdateBytes) + if let channelUpdate = channelUpdateParseResult.getValue() { + messages.append(GossipMessage.siglessChannelUpdate(channelUpdate)) + } else { + print("parse fail: channel update, \(currentMessage.count) bytes") + let decodeError = channelUpdateParseResult.getError() + throw LNSyncError.messageParseError(decodeError!) + } + } + + } + + // print("parsing sanity: \(parseResultIsSane)") + return messages + + } + + func downloadRawLNSyncData(timeInterval: TimeInterval, ignoreCache: Bool = false, storeToCache: Bool = true) + -> Promise + { + + DispatchQueue.main.async { + self.syncState = .downloading + self.objectWillChange.send() + } + + let currentTime = NSDate() + let referenceTime = currentTime.addingTimeInterval(timeInterval) + let referenceTimestamp = UInt(referenceTime.timeIntervalSince1970.rounded()) + print( + "currentTime (\(currentTime.timeIntervalSince1970)) - delta (\(timeInterval)) = reference (\(referenceTimestamp))" + ) + + let userDefaults = UserDefaults.standard + + let absoluteInterval = UInt(abs(timeInterval.rounded())) + + let urlString = "https://lnsync.blockstream.com/gossip/delta/\(referenceTimestamp)" + let localUrlString = "http://localhost:3000/since/\(referenceTimestamp)" + let herokuUrlString = "https://gossip.arik.io/delta/\(absoluteInterval)" + let binaryPromise = Promise { (resolver: Resolver) in + + if !ignoreCache { + if let cachedData = userDefaults.data(forKey: "cachedSyncData") { + resolver.fulfill(cachedData) + return + } + } + + // let url = URL(string: urlString)! + let url = URL(string: herokuUrlString)! + let session = URLSession.shared + let task = session.dataTask( + with: url, + completionHandler: { data, response, error in + guard let httpResponse = response as? HTTPURLResponse, + (200...299).contains(httpResponse.statusCode) + else { + return resolver.reject(LNSyncError.badHTTPResponseStatus) + } + guard let mime = httpResponse.mimeType, + mime == "application/ln.gossip" || mime == "application/octet-stream" + else { + return resolver.reject(LNSyncError.badHTTPResponseType) + } + guard let blockData = data else { + return resolver.reject(LNSyncError.missingBinary) + } + if storeToCache { + userDefaults.setValue(blockData, forKey: "cachedSyncData") + } + resolver.fulfill(blockData) + }) + task.resume() + } + return binaryPromise + } + } diff --git a/xcode/LDKFramework/DirectlyLinkedBindingsApp/app-batteries/PolarIntegrationSample.swift b/xcode/LDKFramework/DirectlyLinkedBindingsApp/app-batteries/PolarIntegrationSample.swift index 316dfea9..59dd9e38 100644 --- a/xcode/LDKFramework/DirectlyLinkedBindingsApp/app-batteries/PolarIntegrationSample.swift +++ b/xcode/LDKFramework/DirectlyLinkedBindingsApp/app-batteries/PolarIntegrationSample.swift @@ -4,567 +4,627 @@ import Foundation - @available(iOS 15.0, *) public class PolarIntegrationSample { - enum TestFlowExceptions: Error { - case unexpectedChannelManagerEventType - case missingInvoicePayer - case invoiceParsingError(ParseOrSemanticError) - case hexParsingError - case invalidOutputScript - case outputScriptMissingAddresses - case paymentPathUnsuccessful - } - - static let WALLET_NAME = "POLAR_LDK_INTEGRATION_TEST_WALLET" - static let BOGUS_OUTPUT_SCRIPT: [UInt8] = [0, 1, 0] - - // EDIT ME - static let POLAR_LND_PEER_PUBKEY_HEX = "022ebd21c61ab89eef5313e72666093e946d78bad8db5569c3dbedd7e9b10c2774" - static let POLAR_LND_PEER_INVOICE = "lnbcrt500u1p34cr64pp5w4k29c568d9ccaycm9pmestdfu5w3tn9w90rdyslxk0g5gtrfdrsdq62pshjmt9de6zqar0yp3kzun0dssp5j6mf3g5fuz6eadz7hj9sg9ndjn5zcgjtuxsd9wycjnwhlwh56zsqmqz9gxqrrsscqp79q2sqqqqqysgqqah206m7ajavcyag4jjzqacwltk5tsrjsf0fw2pmvzpkhs32cj3y483ktfqxcr787tcf8m0qlzatt6435dvf0gkvh30grhkl5du4u2spaf7anm" - - func testPolarFlow() async throws { - - Bindings.setLogThreshold(severity: .DEBUG) - - let rpcInterface = try RegtestBlockchainManager(rpcProtocol: .http, rpcDomain: "localhost", rpcPort: 18443, rpcUsername: "polaruser", rpcPassword: "polarpass") - // let help = try await rpcInterface.getHelp() - // print(help) - - try await self.ascertainSpareMoney(rpcInterface: rpcInterface) - try await rpcInterface.preloadMonitor(anchorHeight: .chaintip) - - // let seed: [UInt8] = [UInt8](Data(base64Encoded: "//////////////////////////////////////////8=")!) - var seed = [UInt8](repeating: 0, count: 32) - let status = SecRandomCopyBytes(kSecRandomDefault, seed.count, &seed) - - let timestamp_seconds = UInt64(NSDate().timeIntervalSince1970) - let timestamp_nanos = UInt32(truncating: NSNumber(value: timestamp_seconds * 1000 * 1000)) - let keysManager = KeysManager(seed: seed, startingTimeSecs: timestamp_seconds, startingTimeNanos: timestamp_nanos) - - let logger = LDKTraitImplementations.PolarLogger() - let config = UserConfig.initWithDefault() - let lightningNetwork = Bindings.Network.Regtest - let genesisHash = try await rpcInterface.getBlockHash(height: 0) - let reversedGenesisHash = [UInt8](genesisHash.reversed()) - let chaintipHash = try await rpcInterface.getChaintipHash() - let reversedChaintipHash = [UInt8](chaintipHash.reversed()) - let chaintipHeight = try await rpcInterface.getChaintipHeight() - let networkGraph = NetworkGraph(network: lightningNetwork, logger: logger) - - let decayParams = ProbabilisticScoringDecayParameters.initWithDefault(); - let probabalisticScorer = ProbabilisticScorer(decayParams: decayParams, networkGraph: networkGraph, logger: logger) - let score = probabalisticScorer.asScore() - let multiThreadedScorer = MultiThreadedLockableScore(score: score) - - print("Genesis hash: \(PolarIntegrationSample.bytesToHexString(bytes: genesisHash))") - print("Genesis hash reversed: \(PolarIntegrationSample.bytesToHexString(bytes: reversedGenesisHash))") - print("Block 1 hash: \(try await rpcInterface.getBlockHashHex(height: 1))") - print("Block 2 hash: \(try await rpcInterface.getBlockHashHex(height: 2))") - print("Chaintip hash: \(PolarIntegrationSample.bytesToHexString(bytes: chaintipHash))") - print("Chaintip hash reversed: \(PolarIntegrationSample.bytesToHexString(bytes: reversedChaintipHash))") - - let feeEstimator = LDKTraitImplementations.PolarFeeEstimator() - let broadcaster = LDKTraitImplementations.PolarBroadcaster(rpcInterface: rpcInterface) - let channelMonitorPersister = LDKTraitImplementations.PolarChannelMonitorPersister() - let channelManagerAndNetworkGraphPersisterAndEventHandler = LDKTraitImplementations.PolarChannelManagerAndNetworkGraphPersisterAndEventHandler() - let chainMonitor = ChainMonitor(chainSource: nil, broadcaster: broadcaster, logger: logger, feeest: feeEstimator, persister: channelMonitorPersister) - - let constructionParameters = ChannelManagerConstructionParameters( - config: config, - entropySource: keysManager.asEntropySource(), - nodeSigner: keysManager.asNodeSigner(), - signerProvider: keysManager.asSignerProvider(), - feeEstimator: feeEstimator, - chainMonitor: chainMonitor, - txBroadcaster: broadcaster, - logger: logger - ) - let channelManagerConstructor = ChannelManagerConstructor(network: lightningNetwork, currentBlockchainTipHash: reversedChaintipHash, currentBlockchainTipHeight: UInt32(chaintipHeight), netGraph: networkGraph, params: constructionParameters) - let channelManager = channelManagerConstructor.channelManager - let peerManager = channelManagerConstructor.peerManager - let tcpPeerHandler = channelManagerConstructor.getTCPPeerHandler() - if let netGraph = channelManagerConstructor.netGraph { - print("net graph available!") - } - - - - struct Listener: BlockchainListener { - private let channelManager: ChannelManager - private let chainMonitor: ChainMonitor - - init(channelManager: ChannelManager, chainMonitor: ChainMonitor) { - self.channelManager = channelManager - self.chainMonitor = chainMonitor - } - - func blockConnected(block: [UInt8], height: UInt32) { - self.channelManager.asListen().blockConnected(block: block, height: height) - self.chainMonitor.asListen().blockConnected(block: block, height: height) - } - - func blockDisconnected(header: [UInt8]?, height: UInt32) { - self.chainMonitor.asListen().blockDisconnected(header: header, height: height) - self.channelManager.asListen().blockDisconnected(header: header, height: height) - } - } - - - let listener = Listener(channelManager: channelManager, chainMonitor: chainMonitor) - rpcInterface.registerListener(listener); - async let monitor = try rpcInterface.monitorBlockchain() - channelManagerConstructor.chainSyncCompleted(persister: channelManagerAndNetworkGraphPersisterAndEventHandler) - - guard let lndPubkey = PolarIntegrationSample.hexStringToBytes(hexString: PolarIntegrationSample.POLAR_LND_PEER_PUBKEY_HEX) else { - throw TestFlowExceptions.hexParsingError - } - let connectionSuccess = tcpPeerHandler.connect(address: "127.0.0.1", port: 9937, theirNodeId: lndPubkey) - assert(connectionSuccess) - - // sleep for one second - try await Task.sleep(nanoseconds: 1_000_000_000) - let connectedPeers = peerManager.listPeers() - - let channelValue: UInt64 = 1_300_000 // 1.3 million satoshis, or 0.013 BTC - let channelValueBtcString = "0.013" - let reserveAmount: UInt64 = 1000 // a thousand satoshis rserve - let userChannelId: [UInt8] = [UInt8](repeating: 42, count: 16); - let channelOpenResult = channelManager.createChannel(theirNetworkKey: lndPubkey, channelValueSatoshis: channelValue, pushMsat: reserveAmount, userChannelId: userChannelId, temporaryChannelId: ChannelId.initWithZero(), overrideConfig: config) - - if let channelOpenError = channelOpenResult.getError() { - print("error type: \(channelOpenError.getValueType())") - if let error = channelOpenError.getValueAsApiMisuseError() { - print("API misuse error: \(error.getErr())") - } else if let error = channelOpenError.getValueAsChannelUnavailable() { - print("anchor count: \(error.anchors.count)") - print("channel unavailable error: \(error.getErr())") - } else if let error = channelOpenError.getValueAsFeeRateTooHigh() { - print("excessive fee rate error: \(error.getErr())") - } else if let error = channelOpenError.getValueAsIncompatibleShutdownScript() { - print("incompatible shutdown script: \(error.getScript())") - } else if let error = channelOpenError.getValueAsInvalidRoute() { - print("invalid route error: \(error.getErr())") - } - return - } - - let managerEvents = await channelManagerAndNetworkGraphPersisterAndEventHandler.getManagerEvents(expectedCount: 1) - - - let managerEvent = managerEvents[0] - print("value type: \(managerEvent.getValueType())") - - if let channelClosedEvent = managerEvent.getValueAsChannelClosed() { - let reason = channelClosedEvent.getReason() - print("closure reason: \(reason.getValueType()) \(reason)") - if let preciseReason = reason.getValueAsProcessingError() { - let errorString = preciseReason.getErr() - print("processing error: \(errorString)") - } else if let preciseReason = reason.getValueAsCounterpartyForceClosed() { - print("peer message: \(preciseReason.getPeerMsg())") - } - } - - guard let fundingReadyEvent = managerEvent.getValueAsFundingGenerationReady() else { - throw TestFlowExceptions.unexpectedChannelManagerEventType - } - let fundingOutputScript = fundingReadyEvent.getOutputScript(); - let temporaryChannelId = fundingReadyEvent.getTemporaryChannelId(); - - let outputScriptDetails = try await rpcInterface.decodeScript(script: fundingOutputScript) - guard let outputScriptAddresses = outputScriptDetails["addresses"] as? [String] else { - throw TestFlowExceptions.invalidOutputScript - } - guard let outputAddress = outputScriptAddresses.first else { - throw TestFlowExceptions.outputScriptMissingAddresses - } - let fundingTxid = try await rpcInterface.sendMoney(destinationAddress: outputAddress, amount: channelValueBtcString) - let fundingTransaction = try await rpcInterface.getTransaction(hash: fundingTxid) - channelManager.fundingTransactionGenerated(temporaryChannelId: temporaryChannelId, counterpartyNodeId: lndPubkey, fundingTransaction: fundingTransaction) - - // let's add a couple confirmations - let fakeAddress = try await self.getBogusAddress(rpcInterface: rpcInterface) - try await rpcInterface.mineBlocks(number: 6, coinbaseDestinationAddress: fakeAddress) - - var usableChannels = [ChannelDetails]() - while (usableChannels.isEmpty) { - usableChannels = channelManager.listUsableChannels() - // sleep for 100ms - try await Task.sleep(nanoseconds: 0_100_000_000) - } - let invoiceResult = Bolt11Invoice.fromStr(s: PolarIntegrationSample.POLAR_LND_PEER_INVOICE) - - guard let invoice = invoiceResult.getValue() else { - throw TestFlowExceptions.invoiceParsingError(invoiceResult.getError()!) - } - - // let's not pay any invoices - // channelManagerConstructor.interrupt(tcpPeerHandler: tcpPeerHandler) - // return - let (paymentHash, recipientOnion, routeParameters) = Bindings.paymentParametersFromInvoice(invoice: invoice).getValue()! - let paymentId = invoice.paymentHash()! - let invoicePaymentResult = channelManager.sendPayment(paymentHash: paymentHash, recipientOnion: recipientOnion, paymentId: paymentId, routeParams: routeParameters, retryStrategy: constructionParameters.payerRetries) - - do { - // process payment - let events = await channelManagerAndNetworkGraphPersisterAndEventHandler.getManagerEvents(expectedCount: 2) - let paymentSentEvent = events[0] - let paymentPathSuccessfulEvent = events[1] - guard let paymentSent = paymentSentEvent.getValueAsPaymentSent() else { - throw TestFlowExceptions.unexpectedChannelManagerEventType - } - guard let paymentPathSuccessful = paymentPathSuccessfulEvent.getValueAsPaymentPathSuccessful() else { - throw TestFlowExceptions.paymentPathUnsuccessful - } - print("sent payment \(paymentSent.getPaymentId()) with fee \(paymentSent.getFeePaidMsat()) via \(paymentPathSuccessful.getPath().getHops().map { h in h.getShortChannelId() })") - } - - for i in 0...600 { - // sleep for 100ms - try await Task.sleep(nanoseconds: 0_100_000_000) - } - - print(channelManagerConstructor.peerManager.listPeers()) - - } - - private func ascertainSpareMoney(rpcInterface: RegtestBlockchainManager) async throws { - let availableWallets = try await rpcInterface.listAvailableWallets() - let walletNames = (availableWallets["wallets"] as! [[String: Any]]).map { dictionary -> String in - dictionary["name"] as! String - } - - if !walletNames.contains(PolarIntegrationSample.WALLET_NAME) { - // if a wallet is already loaded, this will load it also - try await rpcInterface.createWallet(name: PolarIntegrationSample.WALLET_NAME) - } - - let loadedWallets = try await rpcInterface.listLoadedWallets() - let isPolarWalletLoaded = loadedWallets.contains(PolarIntegrationSample.WALLET_NAME) - for currentWalletName in loadedWallets { - if currentWalletName == PolarIntegrationSample.WALLET_NAME { - continue - } - try await rpcInterface.unloadWallet(name: currentWalletName) - } - - if !isPolarWalletLoaded { - try await rpcInterface.loadWallet(name: PolarIntegrationSample.WALLET_NAME) - } - - try await rpcInterface.getWalletInfo() - let walletBalance = try await rpcInterface.getWalletBalance() - - if walletBalance < 5 { - print("Wallet balance of \(walletBalance) too low, mining some blocks") - let address = try await rpcInterface.generateAddress() - try await rpcInterface.mineBlocks(number: 1, coinbaseDestinationAddress: address) - - let fakeAddress = try await self.getBogusAddress(rpcInterface: rpcInterface) - try await rpcInterface.mineBlocks(number: 100, coinbaseDestinationAddress: fakeAddress) - - let updatedWalletBalance = try await rpcInterface.getWalletBalance() - let balanceIncrease = updatedWalletBalance - walletBalance - print("New wallet balance: \(updatedWalletBalance) (increase of \(balanceIncrease))") - } - } - - private func getBogusAddress(rpcInterface: RegtestBlockchainManager) async throws -> String { - let scriptDetails = try await rpcInterface.decodeScript(script: PolarIntegrationSample.BOGUS_OUTPUT_SCRIPT) - let fakeAddress = ((scriptDetails["segwit"] as! [String: Any])["addresses"] as! [String]).first! - return fakeAddress - } - - class LDKTraitImplementations { - - class PolarFeeEstimator: FeeEstimator { - override func getEstSatPer1000Weight(confirmationTarget: Bindings.ConfirmationTarget) -> UInt32 { - return 253 - } - } - - class PolarBroadcaster: BroadcasterInterface { - - private let rpcInterface: BlockchainObserver - - init(rpcInterface: BlockchainObserver) { - self.rpcInterface = rpcInterface - super.init() - } - - override func broadcastTransactions(txs: [[UInt8]]) { - for tx in txs { - Task { - try? await self.rpcInterface.submitTransaction(transaction: tx) - } - } - } - } - - class MuteBroadcaster: BroadcasterInterface { - override func broadcastTransactions(txs: [[UInt8]]) { - // do nothing - } - } - - class PolarLogger: Logger { - override func log(record: Record) { - print("\nRLPolarLog (\(record.getLevel())): \(record.getFile()):\(record.getLine()):\n> \(record.getArgs())\n") - } - } - - class MuteLogger: Logger { - override func log(record: Record) { - // do nothing - } - } - - class PolarChannelMonitorPersister: Persist { - - override func persistNewChannel(channelFundingOutpoint: Bindings.OutPoint, monitor: Bindings.ChannelMonitor) -> Bindings.ChannelMonitorUpdateStatus { - return .Completed - } - - override func updatePersistedChannel(channelFundingOutpoint: Bindings.OutPoint, monitorUpdate: Bindings.ChannelMonitorUpdate, monitor: Bindings.ChannelMonitor) -> Bindings.ChannelMonitorUpdateStatus { - return .Completed - } - - - } - - class PolarChannelManagerAndNetworkGraphPersisterAndEventHandler: Persister, ExtendedChannelManagerPersister { - - private let eventTracker = PendingEventTracker() - - fileprivate func getManagerEvents(expectedCount: UInt) async -> [Event] { - while true { - if await self.eventTracker.getCount() >= expectedCount { - return await self.eventTracker.getAndClearEvents() - } - await self.eventTracker.awaitAddition() - } - } - - func handleEvent(event: Event) -> Result_NoneReplayEventZ { - Task { - await self.eventTracker.addEvent(event: event) - } - return .initWithOk() - } - - override func persistScorer(scorer: Bindings.WriteableScore) -> Bindings.Result_NoneIOErrorZ { - .initWithOk() - } - - override func persistGraph(networkGraph: Bindings.NetworkGraph) -> Bindings.Result_NoneIOErrorZ { - .initWithOk() - } - - override func persistManager(channelManager: Bindings.ChannelManager) -> Bindings.Result_NoneIOErrorZ { - .initWithOk() - } - - fileprivate actor PendingEventTracker { + enum TestFlowExceptions: Error { + case unexpectedChannelManagerEventType + case missingInvoicePayer + case invoiceParsingError(ParseOrSemanticError) + case hexParsingError + case invalidOutputScript + case outputScriptMissingAddresses + case paymentPathUnsuccessful + } + + static let WALLET_NAME = "POLAR_LDK_INTEGRATION_TEST_WALLET" + static let BOGUS_OUTPUT_SCRIPT: [UInt8] = [0, 1, 0] + + // EDIT ME + static let POLAR_LND_PEER_PUBKEY_HEX = "022ebd21c61ab89eef5313e72666093e946d78bad8db5569c3dbedd7e9b10c2774" + static let POLAR_LND_PEER_INVOICE = + "lnbcrt500u1p34cr64pp5w4k29c568d9ccaycm9pmestdfu5w3tn9w90rdyslxk0g5gtrfdrsdq62pshjmt9de6zqar0yp3kzun0dssp5j6mf3g5fuz6eadz7hj9sg9ndjn5zcgjtuxsd9wycjnwhlwh56zsqmqz9gxqrrsscqp79q2sqqqqqysgqqah206m7ajavcyag4jjzqacwltk5tsrjsf0fw2pmvzpkhs32cj3y483ktfqxcr787tcf8m0qlzatt6435dvf0gkvh30grhkl5du4u2spaf7anm" + + func testPolarFlow() async throws { + + Bindings.setLogThreshold(severity: .DEBUG) + + let rpcInterface = try RegtestBlockchainManager( + rpcProtocol: .http, rpcDomain: "localhost", rpcPort: 18443, rpcUsername: "polaruser", + rpcPassword: "polarpass") + // let help = try await rpcInterface.getHelp() + // print(help) + + try await self.ascertainSpareMoney(rpcInterface: rpcInterface) + try await rpcInterface.preloadMonitor(anchorHeight: .chaintip) + + // let seed: [UInt8] = [UInt8](Data(base64Encoded: "//////////////////////////////////////////8=")!) + var seed = [UInt8](repeating: 0, count: 32) + let status = SecRandomCopyBytes(kSecRandomDefault, seed.count, &seed) + + let timestamp_seconds = UInt64(NSDate().timeIntervalSince1970) + let timestamp_nanos = UInt32(truncating: NSNumber(value: timestamp_seconds * 1000 * 1000)) + let keysManager = KeysManager( + seed: seed, startingTimeSecs: timestamp_seconds, startingTimeNanos: timestamp_nanos) + + let logger = LDKTraitImplementations.PolarLogger() + let config = UserConfig.initWithDefault() + let lightningNetwork = Bindings.Network.Regtest + let genesisHash = try await rpcInterface.getBlockHash(height: 0) + let reversedGenesisHash = [UInt8](genesisHash.reversed()) + let chaintipHash = try await rpcInterface.getChaintipHash() + let reversedChaintipHash = [UInt8](chaintipHash.reversed()) + let chaintipHeight = try await rpcInterface.getChaintipHeight() + let networkGraph = NetworkGraph(network: lightningNetwork, logger: logger) + + let decayParams = ProbabilisticScoringDecayParameters.initWithDefault() + let probabalisticScorer = ProbabilisticScorer( + decayParams: decayParams, networkGraph: networkGraph, logger: logger) + let score = probabalisticScorer.asScore() + let multiThreadedScorer = MultiThreadedLockableScore(score: score) + + print("Genesis hash: \(PolarIntegrationSample.bytesToHexString(bytes: genesisHash))") + print("Genesis hash reversed: \(PolarIntegrationSample.bytesToHexString(bytes: reversedGenesisHash))") + print("Block 1 hash: \(try await rpcInterface.getBlockHashHex(height: 1))") + print("Block 2 hash: \(try await rpcInterface.getBlockHashHex(height: 2))") + print("Chaintip hash: \(PolarIntegrationSample.bytesToHexString(bytes: chaintipHash))") + print("Chaintip hash reversed: \(PolarIntegrationSample.bytesToHexString(bytes: reversedChaintipHash))") + + let feeEstimator = LDKTraitImplementations.PolarFeeEstimator() + let broadcaster = LDKTraitImplementations.PolarBroadcaster(rpcInterface: rpcInterface) + let channelMonitorPersister = LDKTraitImplementations.PolarChannelMonitorPersister() + let channelManagerAndNetworkGraphPersisterAndEventHandler = + LDKTraitImplementations.PolarChannelManagerAndNetworkGraphPersisterAndEventHandler() + let chainMonitor = ChainMonitor( + chainSource: nil, broadcaster: broadcaster, logger: logger, feeest: feeEstimator, + persister: channelMonitorPersister) + + let constructionParameters = ChannelManagerConstructionParameters( + config: config, + entropySource: keysManager.asEntropySource(), + nodeSigner: keysManager.asNodeSigner(), + signerProvider: keysManager.asSignerProvider(), + feeEstimator: feeEstimator, + chainMonitor: chainMonitor, + txBroadcaster: broadcaster, + logger: logger + ) + let channelManagerConstructor = ChannelManagerConstructor( + network: lightningNetwork, currentBlockchainTipHash: reversedChaintipHash, + currentBlockchainTipHeight: UInt32(chaintipHeight), netGraph: networkGraph, params: constructionParameters) + let channelManager = channelManagerConstructor.channelManager + let peerManager = channelManagerConstructor.peerManager + let tcpPeerHandler = channelManagerConstructor.getTCPPeerHandler() + if let netGraph = channelManagerConstructor.netGraph { + print("net graph available!") + } + + + struct Listener: BlockchainListener { + private let channelManager: ChannelManager + private let chainMonitor: ChainMonitor + + init(channelManager: ChannelManager, chainMonitor: ChainMonitor) { + self.channelManager = channelManager + self.chainMonitor = chainMonitor + } + + func blockConnected(block: [UInt8], height: UInt32) { + self.channelManager.asListen().blockConnected(block: block, height: height) + self.chainMonitor.asListen().blockConnected(block: block, height: height) + } + + func blockDisconnected(header: [UInt8]?, height: UInt32) { + self.chainMonitor.asListen().blockDisconnected(header: header, height: height) + self.channelManager.asListen().blockDisconnected(header: header, height: height) + } + } + + + let listener = Listener(channelManager: channelManager, chainMonitor: chainMonitor) + rpcInterface.registerListener(listener) + async let monitor = try rpcInterface.monitorBlockchain() + channelManagerConstructor.chainSyncCompleted(persister: channelManagerAndNetworkGraphPersisterAndEventHandler) + + guard + let lndPubkey = PolarIntegrationSample.hexStringToBytes( + hexString: PolarIntegrationSample.POLAR_LND_PEER_PUBKEY_HEX) + else { + throw TestFlowExceptions.hexParsingError + } + let connectionSuccess = tcpPeerHandler.connect(address: "127.0.0.1", port: 9937, theirNodeId: lndPubkey) + assert(connectionSuccess) + + // sleep for one second + try await Task.sleep(nanoseconds: 1_000_000_000) + let connectedPeers = peerManager.listPeers() + + let channelValue: UInt64 = 1_300_000 // 1.3 million satoshis, or 0.013 BTC + let channelValueBtcString = "0.013" + let reserveAmount: UInt64 = 1000 // a thousand satoshis rserve + let userChannelId: [UInt8] = [UInt8](repeating: 42, count: 16) + let channelOpenResult = channelManager.createChannel( + theirNetworkKey: lndPubkey, channelValueSatoshis: channelValue, pushMsat: reserveAmount, + userChannelId: userChannelId, temporaryChannelId: ChannelId.initWithZero(), overrideConfig: config) + + if let channelOpenError = channelOpenResult.getError() { + print("error type: \(channelOpenError.getValueType())") + if let error = channelOpenError.getValueAsApiMisuseError() { + print("API misuse error: \(error.getErr())") + } else if let error = channelOpenError.getValueAsChannelUnavailable() { + print("anchor count: \(error.anchors.count)") + print("channel unavailable error: \(error.getErr())") + } else if let error = channelOpenError.getValueAsFeeRateTooHigh() { + print("excessive fee rate error: \(error.getErr())") + } else if let error = channelOpenError.getValueAsIncompatibleShutdownScript() { + print("incompatible shutdown script: \(error.getScript())") + } else if let error = channelOpenError.getValueAsInvalidRoute() { + print("invalid route error: \(error.getErr())") + } + return + } + + let managerEvents = await channelManagerAndNetworkGraphPersisterAndEventHandler.getManagerEvents( + expectedCount: 1) + + + let managerEvent = managerEvents[0] + print("value type: \(managerEvent.getValueType())") + + if let channelClosedEvent = managerEvent.getValueAsChannelClosed() { + let reason = channelClosedEvent.getReason() + print("closure reason: \(reason.getValueType()) \(reason)") + if let preciseReason = reason.getValueAsProcessingError() { + let errorString = preciseReason.getErr() + print("processing error: \(errorString)") + } else if let preciseReason = reason.getValueAsCounterpartyForceClosed() { + print("peer message: \(preciseReason.getPeerMsg())") + } + } + + guard let fundingReadyEvent = managerEvent.getValueAsFundingGenerationReady() else { + throw TestFlowExceptions.unexpectedChannelManagerEventType + } + let fundingOutputScript = fundingReadyEvent.getOutputScript() + let temporaryChannelId = fundingReadyEvent.getTemporaryChannelId() + + let outputScriptDetails = try await rpcInterface.decodeScript(script: fundingOutputScript) + guard let outputScriptAddresses = outputScriptDetails["addresses"] as? [String] else { + throw TestFlowExceptions.invalidOutputScript + } + guard let outputAddress = outputScriptAddresses.first else { + throw TestFlowExceptions.outputScriptMissingAddresses + } + let fundingTxid = try await rpcInterface.sendMoney( + destinationAddress: outputAddress, amount: channelValueBtcString) + let fundingTransaction = try await rpcInterface.getTransaction(hash: fundingTxid) + channelManager.fundingTransactionGenerated( + temporaryChannelId: temporaryChannelId, counterpartyNodeId: lndPubkey, + fundingTransaction: fundingTransaction) + + // let's add a couple confirmations + let fakeAddress = try await self.getBogusAddress(rpcInterface: rpcInterface) + try await rpcInterface.mineBlocks(number: 6, coinbaseDestinationAddress: fakeAddress) + + var usableChannels = [ChannelDetails]() + while usableChannels.isEmpty { + usableChannels = channelManager.listUsableChannels() + // sleep for 100ms + try await Task.sleep(nanoseconds: 0_100_000_000) + } + let invoiceResult = Bolt11Invoice.fromStr(s: PolarIntegrationSample.POLAR_LND_PEER_INVOICE) + + guard let invoice = invoiceResult.getValue() else { + throw TestFlowExceptions.invoiceParsingError(invoiceResult.getError()!) + } + + // let's not pay any invoices + // channelManagerConstructor.interrupt(tcpPeerHandler: tcpPeerHandler) + // return + let (paymentHash, recipientOnion, routeParameters) = Bindings.paymentParametersFromInvoice(invoice: invoice) + .getValue()! + let paymentId = invoice.paymentHash()! + let invoicePaymentResult = channelManager.sendPayment( + paymentHash: paymentHash, recipientOnion: recipientOnion, paymentId: paymentId, + routeParams: routeParameters, retryStrategy: constructionParameters.payerRetries) + + do { + // process payment + let events = await channelManagerAndNetworkGraphPersisterAndEventHandler.getManagerEvents(expectedCount: 2) + let paymentSentEvent = events[0] + let paymentPathSuccessfulEvent = events[1] + guard let paymentSent = paymentSentEvent.getValueAsPaymentSent() else { + throw TestFlowExceptions.unexpectedChannelManagerEventType + } + guard let paymentPathSuccessful = paymentPathSuccessfulEvent.getValueAsPaymentPathSuccessful() else { + throw TestFlowExceptions.paymentPathUnsuccessful + } + print( + "sent payment \(paymentSent.getPaymentId()) with fee \(paymentSent.getFeePaidMsat()) via \(paymentPathSuccessful.getPath().getHops().map { h in h.getShortChannelId() })" + ) + } + + for i in 0...600 { + // sleep for 100ms + try await Task.sleep(nanoseconds: 0_100_000_000) + } + + print(channelManagerConstructor.peerManager.listPeers()) + + } + + private func ascertainSpareMoney(rpcInterface: RegtestBlockchainManager) async throws { + let availableWallets = try await rpcInterface.listAvailableWallets() + let walletNames = (availableWallets["wallets"] as! [[String: Any]]) + .map { dictionary -> String in + dictionary["name"] as! String + } + + if !walletNames.contains(PolarIntegrationSample.WALLET_NAME) { + // if a wallet is already loaded, this will load it also + try await rpcInterface.createWallet(name: PolarIntegrationSample.WALLET_NAME) + } + + let loadedWallets = try await rpcInterface.listLoadedWallets() + let isPolarWalletLoaded = loadedWallets.contains(PolarIntegrationSample.WALLET_NAME) + for currentWalletName in loadedWallets { + if currentWalletName == PolarIntegrationSample.WALLET_NAME { + continue + } + try await rpcInterface.unloadWallet(name: currentWalletName) + } + + if !isPolarWalletLoaded { + try await rpcInterface.loadWallet(name: PolarIntegrationSample.WALLET_NAME) + } + + try await rpcInterface.getWalletInfo() + let walletBalance = try await rpcInterface.getWalletBalance() + + if walletBalance < 5 { + print("Wallet balance of \(walletBalance) too low, mining some blocks") + let address = try await rpcInterface.generateAddress() + try await rpcInterface.mineBlocks(number: 1, coinbaseDestinationAddress: address) + + let fakeAddress = try await self.getBogusAddress(rpcInterface: rpcInterface) + try await rpcInterface.mineBlocks(number: 100, coinbaseDestinationAddress: fakeAddress) + + let updatedWalletBalance = try await rpcInterface.getWalletBalance() + let balanceIncrease = updatedWalletBalance - walletBalance + print("New wallet balance: \(updatedWalletBalance) (increase of \(balanceIncrease))") + } + } + + private func getBogusAddress(rpcInterface: RegtestBlockchainManager) async throws -> String { + let scriptDetails = try await rpcInterface.decodeScript(script: PolarIntegrationSample.BOGUS_OUTPUT_SCRIPT) + let fakeAddress = ((scriptDetails["segwit"] as! [String: Any])["addresses"] as! [String]).first! + return fakeAddress + } + + class LDKTraitImplementations { + + class PolarFeeEstimator: FeeEstimator { + override func getEstSatPer1000Weight(confirmationTarget: Bindings.ConfirmationTarget) -> UInt32 { + return 253 + } + } + + class PolarBroadcaster: BroadcasterInterface { + + private let rpcInterface: BlockchainObserver + + init(rpcInterface: BlockchainObserver) { + self.rpcInterface = rpcInterface + super.init() + } + + override func broadcastTransactions(txs: [[UInt8]]) { + for tx in txs { + Task { + try? await self.rpcInterface.submitTransaction(transaction: tx) + } + } + } + } + + class MuteBroadcaster: BroadcasterInterface { + override func broadcastTransactions(txs: [[UInt8]]) { + // do nothing + } + } + + class PolarLogger: Logger { + override func log(record: Record) { + print( + "\nRLPolarLog (\(record.getLevel())): \(record.getFile()):\(record.getLine()):\n> \(record.getArgs())\n" + ) + } + } + + class MuteLogger: Logger { + override func log(record: Record) { + // do nothing + } + } + + class PolarChannelMonitorPersister: Persist { + + override func persistNewChannel(channelFundingOutpoint: Bindings.OutPoint, monitor: Bindings.ChannelMonitor) + -> Bindings.ChannelMonitorUpdateStatus + { + return .Completed + } + + override func updatePersistedChannel( + channelFundingOutpoint: Bindings.OutPoint, monitorUpdate: Bindings.ChannelMonitorUpdate, + monitor: Bindings.ChannelMonitor + ) -> Bindings.ChannelMonitorUpdateStatus { + return .Completed + } + + + } + + class PolarChannelManagerAndNetworkGraphPersisterAndEventHandler: Persister, ExtendedChannelManagerPersister { + + private let eventTracker = PendingEventTracker() + + fileprivate func getManagerEvents(expectedCount: UInt) async -> [Event] { + while true { + if await self.eventTracker.getCount() >= expectedCount { + return await self.eventTracker.getAndClearEvents() + } + await self.eventTracker.awaitAddition() + } + } + + func handleEvent(event: Event) -> Result_NoneReplayEventZ { + Task { + await self.eventTracker.addEvent(event: event) + } + return .initWithOk() + } + + override func persistScorer(scorer: Bindings.WriteableScore) -> Bindings.Result_NoneIOErrorZ { + .initWithOk() + } + + override func persistGraph(networkGraph: Bindings.NetworkGraph) -> Bindings.Result_NoneIOErrorZ { + .initWithOk() + } + + override func persistManager(channelManager: Bindings.ChannelManager) -> Bindings.Result_NoneIOErrorZ { + .initWithOk() + } + + fileprivate actor PendingEventTracker { + + private(set) var pendingEvents: [Event] = [] + private(set) var continuations: [CheckedContinuation] = [] + + private func triggerContinuations() { + let continuations = self.continuations + self.continuations.removeAll() + for currentContinuation in continuations { + currentContinuation.resume() + } + } + + func addEvent(event: Event) { + self.pendingEvents.append(event) + self.triggerContinuations() + } + + func addEvents(events: [Event]) { + self.pendingEvents.append(contentsOf: events) + self.triggerContinuations() + } + + func getCount() -> Int { + return self.pendingEvents.count + } + + private func getEvents() -> [Event] { + return self.pendingEvents + } + + func getAndClearEvents() -> [Event] { + let events = self.pendingEvents + self.pendingEvents.removeAll() + return events + } + + func awaitAddition() async { + await withCheckedContinuation({ continuation in + self.continuations.append(continuation) + }) + } + } - private(set) var pendingEvents: [Event] = [] - private(set) var continuations: [CheckedContinuation] = [] + } - private func triggerContinuations() { - let continuations = self.continuations - self.continuations.removeAll() - for currentContinuation in continuations { - currentContinuation.resume() - } - } + } - func addEvent(event: Event) { - self.pendingEvents.append(event) - self.triggerContinuations() - } + public class MultiPeerSimulator { - func addEvents(events: [Event]) { - self.pendingEvents.append(contentsOf: events) - self.triggerContinuations() - } + private var rpcInterface: BlockchainObserver! - func getCount() -> Int { - return self.pendingEvents.count - } + private var keysManager: KeysManager! + private var config: UserConfig! + // private var networkGraph: NetworkGraph! + // private var multiThreadedScorer: MultiThreadedLockableScore! - private func getEvents() -> [Event] { - return self.pendingEvents - } + // private var feeEstimator: FeeEstimator! + // private var broadcaster: BroadcasterInterface! + // private var logger: Logger! - func getAndClearEvents() -> [Event] { - let events = self.pendingEvents - self.pendingEvents.removeAll() - return events - } + public var channelManagerConstructor: ChannelManagerConstructor! - func awaitAddition() async { - await withCheckedContinuation({ continuation in - self.continuations.append(continuation) - }) - } - } + public func simulateMultiplePeers() async throws { + print("bindings version: \(Bindings.getLDKSwiftBindingsSerializationHash())") + // print("\(Bindings.versio)") - } - - } - - public class MultiPeerSimulator { - - private var rpcInterface: BlockchainObserver! - - private var keysManager: KeysManager! - private var config: UserConfig! - // private var networkGraph: NetworkGraph! - // private var multiThreadedScorer: MultiThreadedLockableScore! - - // private var feeEstimator: FeeEstimator! - // private var broadcaster: BroadcasterInterface! - // private var logger: Logger! - - public var channelManagerConstructor: ChannelManagerConstructor! - - public func simulateMultiplePeers() async throws { - print("bindings version: \(Bindings.getLDKSwiftBindingsSerializationHash())") - // print("\(Bindings.versio)") - - /* + /* let username = ProcessInfo.processInfo.environment["BITCOIN_REGTEST_RPC_USERNAME"] ?? "polaruser" // "alice" let password = ProcessInfo.processInfo.environment["BITCOIN_REGTEST_RPC_PASSWORD"] ?? "polarpass" // "DONT_USE_THIS_YOU_WILL_GET_ROBBED" rpcInterface = try BlockchainObserver(rpcProtocol: .http, rpcDomain: "localhost", rpcPort: 8332, rpcUsername: username, rpcPassword: password) try await rpcInterface.preloadMonitor(anchorHeight: .chaintip) */ - - var seed = [UInt8](repeating: 0, count: 32) - let status = SecRandomCopyBytes(kSecRandomDefault, seed.count, &seed) - - let timestamp_seconds = UInt64(NSDate().timeIntervalSince1970) - let timestamp_nanos = UInt32(truncating: NSNumber(value: timestamp_seconds * 1000 * 1000)) - keysManager = KeysManager(seed: seed, startingTimeSecs: timestamp_seconds, startingTimeNanos: timestamp_nanos) - - config = UserConfig.initWithDefault() - let logger = LDKTraitImplementations.MuteLogger() - let lightningNetwork = Bindings.Network.Bitcoin - /* + + var seed = [UInt8](repeating: 0, count: 32) + let status = SecRandomCopyBytes(kSecRandomDefault, seed.count, &seed) + + let timestamp_seconds = UInt64(NSDate().timeIntervalSince1970) + let timestamp_nanos = UInt32(truncating: NSNumber(value: timestamp_seconds * 1000 * 1000)) + keysManager = KeysManager( + seed: seed, startingTimeSecs: timestamp_seconds, startingTimeNanos: timestamp_nanos) + + config = UserConfig.initWithDefault() + let logger = LDKTraitImplementations.MuteLogger() + let lightningNetwork = Bindings.Network.Bitcoin + /* let genesisHash = try await rpcInterface.getBlockHash(height: 0) let reversedGenesisHash = [UInt8](genesisHash.reversed()) let chaintipHash = try await rpcInterface.getChaintipHash() let reversedChaintipHash = [UInt8](chaintipHash.reversed()) let chaintipHeight = try await rpcInterface.getChaintipHeight() */ - let reversedGenesisHashHex = "6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000" - let reversedGenesisHash = PolarIntegrationSample.hexStringToBytes(hexString: reversedGenesisHashHex)! - let chaintipHeight = 0 - let reversedChaintipHash = reversedGenesisHash - let networkGraph = NetworkGraph(network: lightningNetwork, logger: logger) - - print("Genesis hash reversed: \(PolarIntegrationSample.bytesToHexString(bytes: reversedGenesisHash))") - - let decayParams = ProbabilisticScoringDecayParameters.initWithDefault(); - let probabalisticScorer = ProbabilisticScorer(decayParams: decayParams, networkGraph: networkGraph, logger: logger) - let score = probabalisticScorer.asScore() - let multiThreadedScorer = MultiThreadedLockableScore(score: score) - - let feeEstimator = LDKTraitImplementations.PolarFeeEstimator() - // broadcaster = LDKTraitImplementations.PolarBroadcaster(rpcInterface: rpcInterface) - let broadcaster = LDKTraitImplementations.MuteBroadcaster() - // logger = LDKTraitImplementations.PolarLogger() - - let channelMonitorPersister = LDKTraitImplementations.PolarChannelMonitorPersister() - let channelManagerAndNetworkGraphPersisterAndEventHandler = LDKTraitImplementations.PolarChannelManagerAndNetworkGraphPersisterAndEventHandler() - let chainMonitor = ChainMonitor(chainSource: nil, broadcaster: broadcaster, logger: logger, feeest: feeEstimator, persister: channelMonitorPersister) - - let constructionParameters = ChannelManagerConstructionParameters( - config: config, - entropySource: keysManager.asEntropySource(), - nodeSigner: keysManager.asNodeSigner(), - signerProvider: keysManager.asSignerProvider(), - feeEstimator: feeEstimator, - chainMonitor: chainMonitor, - txBroadcaster: broadcaster, - logger: logger - ) - self.channelManagerConstructor = ChannelManagerConstructor(network: lightningNetwork, currentBlockchainTipHash: reversedChaintipHash, currentBlockchainTipHeight: UInt32(chaintipHeight), netGraph: networkGraph, params: constructionParameters) - let channelManager = channelManagerConstructor.channelManager - let peerManager = channelManagerConstructor.peerManager - let tcpPeerHandler = channelManagerConstructor.getTCPPeerHandler() - - self.channelManagerConstructor.chainSyncCompleted(persister: channelManagerAndNetworkGraphPersisterAndEventHandler) - - let interPeerConnectionInterval = 0 - let pauseForNextPeer = { () async in - for _ in 0..<(interPeerConnectionInterval * 10) { - // sleep for 100ms - try! await Task.sleep(nanoseconds: 0_100_000_000) - } - } - - print("increasing log threshold") - Bindings.setLogThreshold(severity: .WARNING) - - do { - // bitrefill - print("connecting bitrefill") - let connectionResult = tcpPeerHandler.connect(address: "52.50.244.44", port: 9735, theirNodeId: PolarIntegrationSample.hexStringToBytes(hexString: "030c3f19d742ca294a55c00376b3b355c3c90d61c6b6b39554dbc7ac19b141c14f")!) - print("bitrefill connection success: \(connectionResult)") - await pauseForNextPeer() - } - - do { - // River - print("connecting river") - let connectionResult = tcpPeerHandler.connect(address: "104.196.249.140", port: 9735, theirNodeId: PolarIntegrationSample.hexStringToBytes(hexString: "03037dc08e9ac63b82581f79b662a4d0ceca8a8ca162b1af3551595b8f2d97b70a")!) - print("river connection success: \(connectionResult)") - await pauseForNextPeer() - } - - do { - // Acinq - print("connecting acinq") - let connectionResult = tcpPeerHandler.connect(address: "3.33.236.230", port: 9735, theirNodeId: PolarIntegrationSample.hexStringToBytes(hexString: "03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f")!) - print("acinq connection success: \(connectionResult)") - await pauseForNextPeer() - } - - do { - // Kraken - print("connecting kraken") - let connectionResult = tcpPeerHandler.connect(address: "52.13.118.208", port: 9735, theirNodeId: PolarIntegrationSample.hexStringToBytes(hexString: "02f1a8c87607f415c8f22c00593002775941dea48869ce23096af27b0cfdcc0b69")!) - print("kraken connection success: \(connectionResult)") - await pauseForNextPeer() - } - - do { - // Matt - print("connecting matt") - let connectionResult = tcpPeerHandler.connect(address: "69.59.18.80", port: 9735, theirNodeId: PolarIntegrationSample.hexStringToBytes(hexString: "03db10aa09ff04d3568b0621750794063df401e6853c79a21a83e1a3f3b5bfb0c8")!) - print("matt connection success: \(connectionResult)") - await pauseForNextPeer() - } - - do { - // Fold - print("connecting fold") - let connectionResult = tcpPeerHandler.connect(address: "35.238.153.25", port: 9735, theirNodeId: PolarIntegrationSample.hexStringToBytes(hexString: "02816caed43171d3c9854e3b0ab2cf0c42be086ff1bd4005acc2a5f7db70d83774")!) - print("fold connection success: \(connectionResult)") - await pauseForNextPeer() - } - - do { - // wallet of satoshi - print("connecting wallet of satoshi") - let connectionResult = tcpPeerHandler.connect(address: "170.75.163.209", port: 9735, theirNodeId: PolarIntegrationSample.hexStringToBytes(hexString: "035e4ff418fc8b5554c5d9eea66396c227bd429a3251c8cbc711002ba215bfc226")!) - print("wallet of satoshi connection success: \(connectionResult)") - await pauseForNextPeer() - } - - /* + let reversedGenesisHashHex = "6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000" + let reversedGenesisHash = PolarIntegrationSample.hexStringToBytes(hexString: reversedGenesisHashHex)! + let chaintipHeight = 0 + let reversedChaintipHash = reversedGenesisHash + let networkGraph = NetworkGraph(network: lightningNetwork, logger: logger) + + print("Genesis hash reversed: \(PolarIntegrationSample.bytesToHexString(bytes: reversedGenesisHash))") + + let decayParams = ProbabilisticScoringDecayParameters.initWithDefault() + let probabalisticScorer = ProbabilisticScorer( + decayParams: decayParams, networkGraph: networkGraph, logger: logger) + let score = probabalisticScorer.asScore() + let multiThreadedScorer = MultiThreadedLockableScore(score: score) + + let feeEstimator = LDKTraitImplementations.PolarFeeEstimator() + // broadcaster = LDKTraitImplementations.PolarBroadcaster(rpcInterface: rpcInterface) + let broadcaster = LDKTraitImplementations.MuteBroadcaster() + // logger = LDKTraitImplementations.PolarLogger() + + let channelMonitorPersister = LDKTraitImplementations.PolarChannelMonitorPersister() + let channelManagerAndNetworkGraphPersisterAndEventHandler = + LDKTraitImplementations.PolarChannelManagerAndNetworkGraphPersisterAndEventHandler() + let chainMonitor = ChainMonitor( + chainSource: nil, broadcaster: broadcaster, logger: logger, feeest: feeEstimator, + persister: channelMonitorPersister) + + let constructionParameters = ChannelManagerConstructionParameters( + config: config, + entropySource: keysManager.asEntropySource(), + nodeSigner: keysManager.asNodeSigner(), + signerProvider: keysManager.asSignerProvider(), + feeEstimator: feeEstimator, + chainMonitor: chainMonitor, + txBroadcaster: broadcaster, + logger: logger + ) + self.channelManagerConstructor = ChannelManagerConstructor( + network: lightningNetwork, currentBlockchainTipHash: reversedChaintipHash, + currentBlockchainTipHeight: UInt32(chaintipHeight), netGraph: networkGraph, + params: constructionParameters) + let channelManager = channelManagerConstructor.channelManager + let peerManager = channelManagerConstructor.peerManager + let tcpPeerHandler = channelManagerConstructor.getTCPPeerHandler() + + self.channelManagerConstructor.chainSyncCompleted( + persister: channelManagerAndNetworkGraphPersisterAndEventHandler) + + let interPeerConnectionInterval = 0 + let pauseForNextPeer = { () async in + for _ in 0..<(interPeerConnectionInterval * 10) { + // sleep for 100ms + try! await Task.sleep(nanoseconds: 0_100_000_000) + } + } + + print("increasing log threshold") + Bindings.setLogThreshold(severity: .WARNING) + + do { + // bitrefill + print("connecting bitrefill") + let connectionResult = tcpPeerHandler.connect( + address: "52.50.244.44", port: 9735, + theirNodeId: PolarIntegrationSample.hexStringToBytes( + hexString: "030c3f19d742ca294a55c00376b3b355c3c90d61c6b6b39554dbc7ac19b141c14f")!) + print("bitrefill connection success: \(connectionResult)") + await pauseForNextPeer() + } + + do { + // River + print("connecting river") + let connectionResult = tcpPeerHandler.connect( + address: "104.196.249.140", port: 9735, + theirNodeId: PolarIntegrationSample.hexStringToBytes( + hexString: "03037dc08e9ac63b82581f79b662a4d0ceca8a8ca162b1af3551595b8f2d97b70a")!) + print("river connection success: \(connectionResult)") + await pauseForNextPeer() + } + + do { + // Acinq + print("connecting acinq") + let connectionResult = tcpPeerHandler.connect( + address: "3.33.236.230", port: 9735, + theirNodeId: PolarIntegrationSample.hexStringToBytes( + hexString: "03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f")!) + print("acinq connection success: \(connectionResult)") + await pauseForNextPeer() + } + + do { + // Kraken + print("connecting kraken") + let connectionResult = tcpPeerHandler.connect( + address: "52.13.118.208", port: 9735, + theirNodeId: PolarIntegrationSample.hexStringToBytes( + hexString: "02f1a8c87607f415c8f22c00593002775941dea48869ce23096af27b0cfdcc0b69")!) + print("kraken connection success: \(connectionResult)") + await pauseForNextPeer() + } + + do { + // Matt + print("connecting matt") + let connectionResult = tcpPeerHandler.connect( + address: "69.59.18.80", port: 9735, + theirNodeId: PolarIntegrationSample.hexStringToBytes( + hexString: "03db10aa09ff04d3568b0621750794063df401e6853c79a21a83e1a3f3b5bfb0c8")!) + print("matt connection success: \(connectionResult)") + await pauseForNextPeer() + } + + do { + // Fold + print("connecting fold") + let connectionResult = tcpPeerHandler.connect( + address: "35.238.153.25", port: 9735, + theirNodeId: PolarIntegrationSample.hexStringToBytes( + hexString: "02816caed43171d3c9854e3b0ab2cf0c42be086ff1bd4005acc2a5f7db70d83774")!) + print("fold connection success: \(connectionResult)") + await pauseForNextPeer() + } + + do { + // wallet of satoshi + print("connecting wallet of satoshi") + let connectionResult = tcpPeerHandler.connect( + address: "170.75.163.209", port: 9735, + theirNodeId: PolarIntegrationSample.hexStringToBytes( + hexString: "035e4ff418fc8b5554c5d9eea66396c227bd429a3251c8cbc711002ba215bfc226")!) + print("wallet of satoshi connection success: \(connectionResult)") + await pauseForNextPeer() + } + + /* struct Listener: BlockchainListener { private let channelManager: ChannelManager private let chainMonitor: ChainMonitor @@ -590,88 +650,87 @@ public class PolarIntegrationSample { rpcInterface.registerListener(listener); async let monitor = try rpcInterface.monitorBlockchain() */ - - // channelManagerConstructor.chain_sync_completed(persister: channelManagerAndNetworkGraphPersisterAndEventHandler, scorer: multiThreadedScorer) - - // channelManagerConstructor.interrupt() - - } - - public func interrupt() async { - Bindings.setLogThreshold(severity: .DEBUG) - channelManagerConstructor.interrupt() - // channelManagerConstructor = nil - - let interFreeConnectionInterval = 5 - let pauseForNextFree = { () async in - for _ in 0..<(interFreeConnectionInterval * 10) { - // sleep for 100ms - try! await Task.sleep(nanoseconds: 0_100_000_000) - } - } - - return; - - await pauseForNextFree() - print("freeing channel manager constructor") - channelManagerConstructor = nil - - /* + + // channelManagerConstructor.chain_sync_completed(persister: channelManagerAndNetworkGraphPersisterAndEventHandler, scorer: multiThreadedScorer) + + // channelManagerConstructor.interrupt() + + } + + public func interrupt() async { + Bindings.setLogThreshold(severity: .DEBUG) + channelManagerConstructor.interrupt() + // channelManagerConstructor = nil + + let interFreeConnectionInterval = 5 + let pauseForNextFree = { () async in + for _ in 0..<(interFreeConnectionInterval * 10) { + // sleep for 100ms + try! await Task.sleep(nanoseconds: 0_100_000_000) + } + } + + return + + await pauseForNextFree() + print("freeing channel manager constructor") + channelManagerConstructor = nil + + /* await pauseForNextFree() print("freeing network graph") networkGraph = nil - + await pauseForNextFree() print("freeing logger") logger = nil - + await pauseForNextFree() print("freeing multi-threaded scorer") multiThreadedScorer = nil - + await pauseForNextFree() print("freeing broadcaster") broadcaster = nil */ - - print("here we are") - - } - - } - - private class func bytesToHexString(bytes: [UInt8]) -> String { - let format = "%02hhx" // "%02hhX" (uppercase) - return bytes.map { - String(format: format, $0) - } - .joined() - } - - private class func hexStringToBytes(hexString: String) -> [UInt8]? { - let hexStr = hexString.dropFirst(hexString.hasPrefix("0x") ? 2 : 0) - - guard hexStr.count % 2 == 0 else { - return nil - } - - var newData = [UInt8]() - - var indexIsEven = true - for i in hexStr.indices { - if indexIsEven { - let byteRange = i...hexStr.index(after: i) - guard let byte = UInt8(hexStr[byteRange], radix: 16) else { - return nil - } - newData.append(byte) - } - indexIsEven.toggle() - } - return newData - } - - -} + print("here we are") + + } + + } + + private class func bytesToHexString(bytes: [UInt8]) -> String { + let format = "%02hhx" // "%02hhX" (uppercase) + return + bytes.map { + String(format: format, $0) + } + .joined() + } + private class func hexStringToBytes(hexString: String) -> [UInt8]? { + let hexStr = hexString.dropFirst(hexString.hasPrefix("0x") ? 2 : 0) + + guard hexStr.count % 2 == 0 else { + return nil + } + + var newData = [UInt8]() + + var indexIsEven = true + for i in hexStr.indices { + if indexIsEven { + let byteRange = i...hexStr.index(after: i) + guard let byte = UInt8(hexStr[byteRange], radix: 16) else { + return nil + } + newData.append(byte) + } + indexIsEven.toggle() + } + return newData + } + + +} diff --git a/xcode/LDKFramework/DirectlyLinkedBindingsApp/app-batteries/RegtestBlockchainManager.swift b/xcode/LDKFramework/DirectlyLinkedBindingsApp/app-batteries/RegtestBlockchainManager.swift index 0d141452..93e6943a 100644 --- a/xcode/LDKFramework/DirectlyLinkedBindingsApp/app-batteries/RegtestBlockchainManager.swift +++ b/xcode/LDKFramework/DirectlyLinkedBindingsApp/app-batteries/RegtestBlockchainManager.swift @@ -12,7 +12,7 @@ import Foundation @available(iOS 15.0, *) class RegtestBlockchainManager: BlockchainObserver { - /** + /** Mine regtest blocks - Parameters: - number: The number of blocks to mine @@ -20,23 +20,25 @@ class RegtestBlockchainManager: BlockchainObserver { - Returns: Array of the mined blocks' hashes - Throws: If the RPC connection fails or the call results in an error */ - public func mineBlocks(number: Int64, coinbaseDestinationAddress: String) async throws -> [String] { - let response = try await self.callRpcMethod(method: "generatetoaddress", params: [ - "nblocks": number, - "address": coinbaseDestinationAddress - ]) - let result = response["result"] as! [String] - return result - } + public func mineBlocks(number: Int64, coinbaseDestinationAddress: String) async throws -> [String] { + let response = try await self.callRpcMethod( + method: "generatetoaddress", + params: [ + "nblocks": number, + "address": coinbaseDestinationAddress, + ]) + let result = response["result"] as! [String] + return result + } - /** + /** Invalidate or un-mine a block - Parameter hash: The block hash hex to invalidate - Returns: - Throws: */ - public func unmineBlock(hash: String) async throws { - let response = try await self.callRpcMethod(method: "invalidateblock", params: [hash]) - } + public func unmineBlock(hash: String) async throws { + let response = try await self.callRpcMethod(method: "invalidateblock", params: [hash]) + } } diff --git a/xcode/LDKFramework/DirectlyLinkedBindingsApp/app-batteries/RegtestBlockchainObserverOld.swift b/xcode/LDKFramework/DirectlyLinkedBindingsApp/app-batteries/RegtestBlockchainObserverOld.swift index 00df8f3a..a45ec226 100644 --- a/xcode/LDKFramework/DirectlyLinkedBindingsApp/app-batteries/RegtestBlockchainObserverOld.swift +++ b/xcode/LDKFramework/DirectlyLinkedBindingsApp/app-batteries/RegtestBlockchainObserverOld.swift @@ -7,415 +7,429 @@ import Foundation import PromiseKit struct BlockInfo { - var height: UInt - var hash: String - var previousHash: String? - var rawData: Data? - var header: Data? + var height: UInt + var hash: String + var previousHash: String? + var rawData: Data? + var header: Data? } struct ReorgPath { - var orphanChain: Block? // 1) disconnect from tail (typically empty) - var newChain: Block // 2) connect from head (typically just one block) + var orphanChain: Block? // 1) disconnect from tail (typically empty) + var newChain: Block // 2) connect from head (typically just one block) } class Block { - var info: BlockInfo - var previous: Block? - var next: Block? - - class func hexStringToBytes(hexString: String) -> [UInt8]? { - let hexStr = hexString.dropFirst(hexString.hasPrefix("0x") ? 2 : 0) - - guard hexStr.count % 2 == 0 else { return nil } - - var newData = [UInt8]() - - var indexIsEven = true - for i in hexStr.indices { - if indexIsEven { - let byteRange = i...hexStr.index(after: i) - guard let byte = UInt8(hexStr[byteRange], radix: 16) else { return nil } - newData.append(byte) - } - indexIsEven.toggle() - } - return newData - } - - class func bytesToHexString(bytes: [UInt8]) -> String { - let format = "%02hhx" // "%02hhX" (uppercase) - return bytes.map { String(format: format, $0) }.joined() - } - - init(info: BlockInfo, previous: Block?, next: Block?) { - self.info = info - self.previous = previous - self.next = next - } - - enum SequencingError: Error { - case appendixHasPrevious - case prependixHasNext - } - - func insertAfter(newNext: Block) throws -> Block { - if (newNext.previous != nil) { - throw SequencingError.appendixHasPrevious - } - if let oldNext = self.next { - newNext.next = oldNext - oldNext.previous = newNext - } - newNext.previous = self - self.next = newNext - return newNext - } - - func insertBefore(newPrevious: Block) throws -> Block { - if (newPrevious.next != nil) { - throw SequencingError.prependixHasNext - } - if let oldPrevious = self.previous { - newPrevious.previous = oldPrevious - oldPrevious.next = newPrevious - } - newPrevious.next = self - self.previous = newPrevious - return newPrevious - } - - func earliestBlock() -> Block { - guard let previous = self.previous else { - return self - } - return previous.earliestBlock() - } - - func latestBlock() -> Block { - guard let next = self.next else { - return self - } - return next.latestBlock() - } - - func seekBlockHashBackwards(hash: String) -> Bool { - if (self.info.hash == hash) { - return true - } - guard let previous = self.previous else { - return false - } - return previous.seekBlockHashBackwards(hash: hash) - } - - func getOrphanChain(lastKeptHash: String, trailingChain: Block?) -> Block? { - if (self.info.hash == lastKeptHash) { - if let chain = trailingChain { - chain.previous = self - } - return trailingChain - } - - let chain = Block(info: self.info, previous: nil, next: trailingChain) - trailingChain?.previous = chain // double-link it - - return self.previous!.getOrphanChain(lastKeptHash: lastKeptHash, trailingChain: chain) - } - - func toChainString(trailingInfo: String?) -> String { - var currentInfo = "\(self.info.height): \(self.info.hash)" - if let trailingInfo = trailingInfo { - currentInfo = "\(trailingInfo)\n↘️ \(currentInfo)" - } - if let previous = self.previous { - return previous.toChainString(trailingInfo: currentInfo) - } - return currentInfo - }; - - // Insert - func reconcile(newChain: Block) -> ReorgPath { - if (self.next != nil) { - return self.latestBlock().reconcile(newChain: newChain) - } - if (newChain.previous != nil) { - return self.reconcile(newChain: newChain.earliestBlock()) - } - // gotta make sure reconciliation is tip against head - let orphanChain = self.getOrphanChain(lastKeptHash: newChain.info.previousHash!, trailingChain: nil) - if let chain = orphanChain { - let anchor = chain.previous! - chain.previous = nil // cut off the orphan chain - anchor.next = nil // cut off the anchor - try! anchor.insertAfter(newNext: newChain) - } else { - try! self.insertAfter(newNext: newChain) - } - return ReorgPath(orphanChain: orphanChain, newChain: newChain) - } + var info: BlockInfo + var previous: Block? + var next: Block? + + class func hexStringToBytes(hexString: String) -> [UInt8]? { + let hexStr = hexString.dropFirst(hexString.hasPrefix("0x") ? 2 : 0) + + guard hexStr.count % 2 == 0 else { return nil } + + var newData = [UInt8]() + + var indexIsEven = true + for i in hexStr.indices { + if indexIsEven { + let byteRange = i...hexStr.index(after: i) + guard let byte = UInt8(hexStr[byteRange], radix: 16) else { return nil } + newData.append(byte) + } + indexIsEven.toggle() + } + return newData + } + + class func bytesToHexString(bytes: [UInt8]) -> String { + let format = "%02hhx" // "%02hhX" (uppercase) + return bytes.map { String(format: format, $0) }.joined() + } + + init(info: BlockInfo, previous: Block?, next: Block?) { + self.info = info + self.previous = previous + self.next = next + } + + enum SequencingError: Error { + case appendixHasPrevious + case prependixHasNext + } + + func insertAfter(newNext: Block) throws -> Block { + if newNext.previous != nil { + throw SequencingError.appendixHasPrevious + } + if let oldNext = self.next { + newNext.next = oldNext + oldNext.previous = newNext + } + newNext.previous = self + self.next = newNext + return newNext + } + + func insertBefore(newPrevious: Block) throws -> Block { + if newPrevious.next != nil { + throw SequencingError.prependixHasNext + } + if let oldPrevious = self.previous { + newPrevious.previous = oldPrevious + oldPrevious.next = newPrevious + } + newPrevious.next = self + self.previous = newPrevious + return newPrevious + } + + func earliestBlock() -> Block { + guard let previous = self.previous else { + return self + } + return previous.earliestBlock() + } + + func latestBlock() -> Block { + guard let next = self.next else { + return self + } + return next.latestBlock() + } + + func seekBlockHashBackwards(hash: String) -> Bool { + if self.info.hash == hash { + return true + } + guard let previous = self.previous else { + return false + } + return previous.seekBlockHashBackwards(hash: hash) + } + + func getOrphanChain(lastKeptHash: String, trailingChain: Block?) -> Block? { + if self.info.hash == lastKeptHash { + if let chain = trailingChain { + chain.previous = self + } + return trailingChain + } + + let chain = Block(info: self.info, previous: nil, next: trailingChain) + trailingChain?.previous = chain // double-link it + + return self.previous!.getOrphanChain(lastKeptHash: lastKeptHash, trailingChain: chain) + } + + func toChainString(trailingInfo: String?) -> String { + var currentInfo = "\(self.info.height): \(self.info.hash)" + if let trailingInfo = trailingInfo { + currentInfo = "\(trailingInfo)\n↘️ \(currentInfo)" + } + if let previous = self.previous { + return previous.toChainString(trailingInfo: currentInfo) + } + return currentInfo + } + + // Insert + func reconcile(newChain: Block) -> ReorgPath { + if self.next != nil { + return self.latestBlock().reconcile(newChain: newChain) + } + if newChain.previous != nil { + return self.reconcile(newChain: newChain.earliestBlock()) + } + // gotta make sure reconciliation is tip against head + let orphanChain = self.getOrphanChain(lastKeptHash: newChain.info.previousHash!, trailingChain: nil) + if let chain = orphanChain { + let anchor = chain.previous! + chain.previous = nil // cut off the orphan chain + anchor.next = nil // cut off the anchor + try! anchor.insertAfter(newNext: newChain) + } else { + try! self.insertAfter(newNext: newChain) + } + return ReorgPath(orphanChain: orphanChain, newChain: newChain) + } } class RegtestBlockchainObserverOld { - private var earliestBlockHeight: UInt? = nil - private var latestBlockHeight: UInt = 0 - private var chainTip: Block? - public var listeners: [Listen]; - private var syncComplete: (() -> Void)?; - - init(listeners: [Listen], chainTip: Block?, syncComplete: (() -> Void)?) { - - self.listeners = listeners - - - // let chainMonitor = LDKChainWatchInterface() - // let blockNotifier = BlockNotifier_new(T##chain_monitor: LDKChainWatchInterface##LDKChainWatchInterface) - // self.earliestBlockHeight = earliestKnownHeight - - - self.chainTip = chainTip - self.earliestBlockHeight = self.chainTip?.info.height - self.syncComplete = syncComplete - } - - var chainTipHash: [UInt8] { - get { - return Block.hexStringToBytes(hexString: self.chainTip!.info.hash)! - } - } - - var chainTipHeight: UInt32 { - get { - return UInt32(self.chainTip!.info.height) - } - } - - enum MonitoringError: Error { - case invalidJSON - case invalidHeight - case invalidHash - case startHashTooEarly - case badHTTPResponseStatus - case badHTTPResponseType - case missingBinary - } - - - - func getLatestBlockInfo() -> Promise { - firstly { - getChainInfo() - }.then { chainInfo in - when(fulfilled: self.getBlockInfo(blockHash: chainInfo.hash), self.getBlockBinary(blockHash: chainInfo.hash)) - }.map { (blockInfo: BlockInfo, blockData: Data) in - var richBlockInfo = blockInfo - richBlockInfo.rawData = blockData - richBlockInfo.header = blockData.subdata(in: 4..<84) - let headerHex = Block.bytesToHexString(bytes: [UInt8](richBlockInfo.header!)) - return richBlockInfo - } - } - - func getChainInfo() -> Promise { - // let url = "http://cloudflare.testnet.deanonymizingseed.com/rest/chaininfo.json" - let urlString = "http://localhost:3000/api/lab/chaininfo" - let infoPromise = Promise { (resolver: Resolver) in - let url = URL(string: urlString)! - let session = URLSession.shared - let task = session.dataTask(with: url, completionHandler: { data, response, error in - guard let httpResponse = response as? HTTPURLResponse, - (200...299).contains(httpResponse.statusCode) else { - return resolver.reject(MonitoringError.badHTTPResponseStatus) - } - guard let mime = httpResponse.mimeType, mime == "application/json" else { - return resolver.reject(MonitoringError.badHTTPResponseType) - } - guard let json = try? JSONSerialization.jsonObject(with: data!, options: []) else { - return resolver.reject(MonitoringError.invalidJSON) - } - guard let chainInfo = json as? [String: Any] else { - return resolver.reject(MonitoringError.invalidJSON) - } - guard let height = chainInfo["blocks"] as? UInt else { - return resolver.reject(MonitoringError.invalidHeight) - } - guard let hash = chainInfo["bestblockhash"] as? String else { - return resolver.reject(MonitoringError.invalidHash) - } - resolver.fulfill(BlockInfo(height: height, hash: hash)) - }) - task.resume() - } - return infoPromise - } - - func getBlockInfo(blockHash: String) -> Promise { - // let url = "http://cloudflare.testnet.deanonymizingseed.com/rest/block/\(blockHash).json" - let urlString = "http://localhost:3000/api/lab/block/\(blockHash)/info" - let infoPromise = Promise { (resolver: Resolver) in - let url = URL(string: urlString)! - let session = URLSession.shared - let task = session.dataTask(with: url, completionHandler: { data, response, error in - guard let httpResponse = response as? HTTPURLResponse, - (200...299).contains(httpResponse.statusCode) else { - return resolver.reject(MonitoringError.badHTTPResponseStatus) - } - guard let mime = httpResponse.mimeType, mime == "application/json" else { - return resolver.reject(MonitoringError.badHTTPResponseType) - } - guard let json = try? JSONSerialization.jsonObject(with: data!, options: []) else { - return resolver.reject(MonitoringError.invalidJSON) - } - guard let chainInfo = json as? [String: Any] else { - return resolver.reject(MonitoringError.invalidJSON) - } - guard let height = chainInfo["height"] as? UInt else { - return resolver.reject(MonitoringError.invalidHeight) - } - guard let previousHash = chainInfo["previousblockhash"] as? String else { - return resolver.reject(MonitoringError.invalidHash) - } - resolver.fulfill(BlockInfo(height: height, hash: blockHash, previousHash: previousHash)) - }) - task.resume() - } - return infoPromise - } - - // fetches a sequence of blocks starting at the given hash, working its way backwards until a known previous block hash - func getBlockSequenceUntilKnown(startHash: String, trailingChain: Block?) -> Promise { - firstly { - when(fulfilled: self.getBlockInfo(blockHash: startHash), self.getBlockBinary(blockHash: startHash)) - }.then { (blockInfo: BlockInfo, blockData: Data) -> Promise in - if (blockInfo.height < self.earliestBlockHeight! + 1) { - // we need to be able to keep the first block we started out with - // TODO: fix this such that the first block, too, can be reorged - throw MonitoringError.startHashTooEarly - } - - var richBlock = blockInfo - richBlock.rawData = blockData - richBlock.header = blockData.subdata(in: 4..<84) - - let chain = Block(info: richBlock, previous: nil, next: trailingChain) - let isPreviousKnown = self.chainTip!.seekBlockHashBackwards(hash: richBlock.previousHash!) - - if isPreviousKnown { - return Promise.value(chain) - } else { - return self.getBlockSequenceUntilKnown(startHash: richBlock.previousHash!, trailingChain: chain) - } - } - } - - func getBlockBinary(blockHash: String) -> Promise { - // let url = "http://cloudflare.testnet.deanonymizingseed.com/rest/block/\(blockHash).bin" - let urlString = "http://localhost:3000/api/lab/block/\(blockHash)/bin" - let binaryPromise = Promise { (resolver: Resolver) in - let url = URL(string: urlString)! - let session = URLSession.shared - let task = session.dataTask(with: url, completionHandler: { data, response, error in - guard let httpResponse = response as? HTTPURLResponse, - (200...299).contains(httpResponse.statusCode) else { - return resolver.reject(MonitoringError.badHTTPResponseStatus) - } - guard let mime = httpResponse.mimeType, mime == "application/octet-stream" else { - return resolver.reject(MonitoringError.badHTTPResponseType) - } - guard let blockData = data else { - return resolver.reject(MonitoringError.missingBinary) - } - resolver.fulfill(blockData) - }) - task.resume() - } - return binaryPromise - } - - func monitor() { - let backgroundQueue = DispatchQueue.global(qos: .background); - firstly { - getLatestBlockInfo() - }.then { blockInfo in - self.reconcileBlock(blockInfo: blockInfo) - }.then(on: backgroundQueue) { - after(seconds: 5) // wait half a minute - }.done { - self.monitor() - } - } - - func reconcileBlock(blockInfo: BlockInfo) -> Promise { - //print(blockInfo.rawData) - - guard let chainTip = self.chainTip else { - self.chainTip = Block(info: blockInfo, previous: nil, next: nil) - self.earliestBlockHeight = self.earliestBlockHeight ?? blockInfo.height - self.latestBlockHeight = blockInfo.height - if self.earliestBlockHeight == self.latestBlockHeight { - for listener in self.listeners { - // listener.connectBlock(block: blockInfo) - listener.block_connected(block: [UInt8](blockInfo.rawData!), height: UInt32(blockInfo.height)) - } - } - print("Fast-forwarded to chain tip: \(self.chainTip!.toChainString(trailingInfo: nil))\n") - if let completionHandler = self.syncComplete { - self.syncComplete = nil - completionHandler() - } - return Promise.value(()) - } - - if (blockInfo.hash == chainTip.info.hash) { - print("Already at chain tip: \(self.chainTip!.toChainString(trailingInfo: nil))\n") - if let completionHandler = self.syncComplete { - self.syncComplete = nil - completionHandler() - } - return Promise.value(()) // nothing to do here - } - - print("\nReconciling block! \nBlock height: \(blockInfo.height) \nHash: \(blockInfo.hash) \nPrevious hash: \(blockInfo.previousHash)") - return firstly { - self.getBlockSequenceUntilKnown(startHash: blockInfo.hash, trailingChain: nil) - }.map { newBlockSequence in - print("New block sequence: \(newBlockSequence.toChainString(trailingInfo: nil))\n") - let reorgPath = chainTip.reconcile(newChain: newBlockSequence) - if let orphans = reorgPath.orphanChain { - print("Orphaned: \(orphans.latestBlock().toChainString(trailingInfo: nil))\n") - } - if let orphans = reorgPath.orphanChain { - var currentOrphan: Block? = orphans.latestBlock(); - while (currentOrphan != nil) { - // listener.disconnectBlock(block: currentOrphan!.info) - // listener.block_disconnected(header: currentOrphan!.info.hash, height: <#T##UInt32#>) - currentOrphan = currentOrphan!.previous - } - } - var currentAddition: Block? = reorgPath.newChain; - while (currentAddition != nil) { - for listener in self.listeners { - // listener.connectBlock(block: currentAddition!.info) - listener.block_connected(block: [UInt8](currentAddition!.info.rawData!), height: UInt32(currentAddition!.info.height)) - } - currentAddition = currentAddition!.next - } - - self.chainTip = reorgPath.newChain.latestBlock() // set chain tip to the latest block in the new chain - - let defaults = UserDefaults.standard - defaults.set(self.chainTip!.info.hash, forKey: "chainTipHash") - defaults.set(self.chainTip!.info.height, forKey: "chainTipHeight") - - print("Updated chain path: \(self.chainTip!.toChainString(trailingInfo: nil))\n") - if let completionHandler = self.syncComplete { - self.syncComplete = nil - completionHandler() - } - } - } - - + private var earliestBlockHeight: UInt? = nil + private var latestBlockHeight: UInt = 0 + private var chainTip: Block? + public var listeners: [Listen] + private var syncComplete: (() -> Void)? + + init(listeners: [Listen], chainTip: Block?, syncComplete: (() -> Void)?) { + + self.listeners = listeners + + + // let chainMonitor = LDKChainWatchInterface() + // let blockNotifier = BlockNotifier_new(T##chain_monitor: LDKChainWatchInterface##LDKChainWatchInterface) + // self.earliestBlockHeight = earliestKnownHeight + + + self.chainTip = chainTip + self.earliestBlockHeight = self.chainTip?.info.height + self.syncComplete = syncComplete + } + + var chainTipHash: [UInt8] { + return Block.hexStringToBytes(hexString: self.chainTip!.info.hash)! + } + + var chainTipHeight: UInt32 { + return UInt32(self.chainTip!.info.height) + } + + enum MonitoringError: Error { + case invalidJSON + case invalidHeight + case invalidHash + case startHashTooEarly + case badHTTPResponseStatus + case badHTTPResponseType + case missingBinary + } + + + func getLatestBlockInfo() -> Promise { + firstly { + getChainInfo() + } + .then { chainInfo in + when( + fulfilled: self.getBlockInfo(blockHash: chainInfo.hash), self.getBlockBinary(blockHash: chainInfo.hash)) + } + .map { (blockInfo: BlockInfo, blockData: Data) in + var richBlockInfo = blockInfo + richBlockInfo.rawData = blockData + richBlockInfo.header = blockData.subdata(in: 4..<84) + let headerHex = Block.bytesToHexString(bytes: [UInt8](richBlockInfo.header!)) + return richBlockInfo + } + } + + func getChainInfo() -> Promise { + // let url = "http://cloudflare.testnet.deanonymizingseed.com/rest/chaininfo.json" + let urlString = "http://localhost:3000/api/lab/chaininfo" + let infoPromise = Promise { (resolver: Resolver) in + let url = URL(string: urlString)! + let session = URLSession.shared + let task = session.dataTask( + with: url, + completionHandler: { data, response, error in + guard let httpResponse = response as? HTTPURLResponse, + (200...299).contains(httpResponse.statusCode) + else { + return resolver.reject(MonitoringError.badHTTPResponseStatus) + } + guard let mime = httpResponse.mimeType, mime == "application/json" else { + return resolver.reject(MonitoringError.badHTTPResponseType) + } + guard let json = try? JSONSerialization.jsonObject(with: data!, options: []) else { + return resolver.reject(MonitoringError.invalidJSON) + } + guard let chainInfo = json as? [String: Any] else { + return resolver.reject(MonitoringError.invalidJSON) + } + guard let height = chainInfo["blocks"] as? UInt else { + return resolver.reject(MonitoringError.invalidHeight) + } + guard let hash = chainInfo["bestblockhash"] as? String else { + return resolver.reject(MonitoringError.invalidHash) + } + resolver.fulfill(BlockInfo(height: height, hash: hash)) + }) + task.resume() + } + return infoPromise + } + + func getBlockInfo(blockHash: String) -> Promise { + // let url = "http://cloudflare.testnet.deanonymizingseed.com/rest/block/\(blockHash).json" + let urlString = "http://localhost:3000/api/lab/block/\(blockHash)/info" + let infoPromise = Promise { (resolver: Resolver) in + let url = URL(string: urlString)! + let session = URLSession.shared + let task = session.dataTask( + with: url, + completionHandler: { data, response, error in + guard let httpResponse = response as? HTTPURLResponse, + (200...299).contains(httpResponse.statusCode) + else { + return resolver.reject(MonitoringError.badHTTPResponseStatus) + } + guard let mime = httpResponse.mimeType, mime == "application/json" else { + return resolver.reject(MonitoringError.badHTTPResponseType) + } + guard let json = try? JSONSerialization.jsonObject(with: data!, options: []) else { + return resolver.reject(MonitoringError.invalidJSON) + } + guard let chainInfo = json as? [String: Any] else { + return resolver.reject(MonitoringError.invalidJSON) + } + guard let height = chainInfo["height"] as? UInt else { + return resolver.reject(MonitoringError.invalidHeight) + } + guard let previousHash = chainInfo["previousblockhash"] as? String else { + return resolver.reject(MonitoringError.invalidHash) + } + resolver.fulfill(BlockInfo(height: height, hash: blockHash, previousHash: previousHash)) + }) + task.resume() + } + return infoPromise + } + + // fetches a sequence of blocks starting at the given hash, working its way backwards until a known previous block hash + func getBlockSequenceUntilKnown(startHash: String, trailingChain: Block?) -> Promise { + firstly { + when(fulfilled: self.getBlockInfo(blockHash: startHash), self.getBlockBinary(blockHash: startHash)) + } + .then { (blockInfo: BlockInfo, blockData: Data) -> Promise in + if blockInfo.height < self.earliestBlockHeight! + 1 { + // we need to be able to keep the first block we started out with + // TODO: fix this such that the first block, too, can be reorged + throw MonitoringError.startHashTooEarly + } + + var richBlock = blockInfo + richBlock.rawData = blockData + richBlock.header = blockData.subdata(in: 4..<84) + + let chain = Block(info: richBlock, previous: nil, next: trailingChain) + let isPreviousKnown = self.chainTip!.seekBlockHashBackwards(hash: richBlock.previousHash!) + + if isPreviousKnown { + return Promise.value(chain) + } else { + return self.getBlockSequenceUntilKnown(startHash: richBlock.previousHash!, trailingChain: chain) + } + } + } + + func getBlockBinary(blockHash: String) -> Promise { + // let url = "http://cloudflare.testnet.deanonymizingseed.com/rest/block/\(blockHash).bin" + let urlString = "http://localhost:3000/api/lab/block/\(blockHash)/bin" + let binaryPromise = Promise { (resolver: Resolver) in + let url = URL(string: urlString)! + let session = URLSession.shared + let task = session.dataTask( + with: url, + completionHandler: { data, response, error in + guard let httpResponse = response as? HTTPURLResponse, + (200...299).contains(httpResponse.statusCode) + else { + return resolver.reject(MonitoringError.badHTTPResponseStatus) + } + guard let mime = httpResponse.mimeType, mime == "application/octet-stream" else { + return resolver.reject(MonitoringError.badHTTPResponseType) + } + guard let blockData = data else { + return resolver.reject(MonitoringError.missingBinary) + } + resolver.fulfill(blockData) + }) + task.resume() + } + return binaryPromise + } + + func monitor() { + let backgroundQueue = DispatchQueue.global(qos: .background) + firstly { + getLatestBlockInfo() + } + .then { blockInfo in + self.reconcileBlock(blockInfo: blockInfo) + } + .then(on: backgroundQueue) { + after(seconds: 5) // wait half a minute + } + .done { + self.monitor() + } + } + + func reconcileBlock(blockInfo: BlockInfo) -> Promise { + //print(blockInfo.rawData) + + guard let chainTip = self.chainTip else { + self.chainTip = Block(info: blockInfo, previous: nil, next: nil) + self.earliestBlockHeight = self.earliestBlockHeight ?? blockInfo.height + self.latestBlockHeight = blockInfo.height + if self.earliestBlockHeight == self.latestBlockHeight { + for listener in self.listeners { + // listener.connectBlock(block: blockInfo) + listener.block_connected(block: [UInt8](blockInfo.rawData!), height: UInt32(blockInfo.height)) + } + } + print("Fast-forwarded to chain tip: \(self.chainTip!.toChainString(trailingInfo: nil))\n") + if let completionHandler = self.syncComplete { + self.syncComplete = nil + completionHandler() + } + return Promise.value(()) + } + + if blockInfo.hash == chainTip.info.hash { + print("Already at chain tip: \(self.chainTip!.toChainString(trailingInfo: nil))\n") + if let completionHandler = self.syncComplete { + self.syncComplete = nil + completionHandler() + } + return Promise.value(()) // nothing to do here + } + + print( + "\nReconciling block! \nBlock height: \(blockInfo.height) \nHash: \(blockInfo.hash) \nPrevious hash: \(blockInfo.previousHash)" + ) + return firstly { + self.getBlockSequenceUntilKnown(startHash: blockInfo.hash, trailingChain: nil) + } + .map { newBlockSequence in + print("New block sequence: \(newBlockSequence.toChainString(trailingInfo: nil))\n") + let reorgPath = chainTip.reconcile(newChain: newBlockSequence) + if let orphans = reorgPath.orphanChain { + print("Orphaned: \(orphans.latestBlock().toChainString(trailingInfo: nil))\n") + } + if let orphans = reorgPath.orphanChain { + var currentOrphan: Block? = orphans.latestBlock() + while currentOrphan != nil { + // listener.disconnectBlock(block: currentOrphan!.info) + // listener.block_disconnected(header: currentOrphan!.info.hash, height: <#T##UInt32#>) + currentOrphan = currentOrphan!.previous + } + } + var currentAddition: Block? = reorgPath.newChain + while currentAddition != nil { + for listener in self.listeners { + // listener.connectBlock(block: currentAddition!.info) + listener.block_connected( + block: [UInt8](currentAddition!.info.rawData!), height: UInt32(currentAddition!.info.height)) + } + currentAddition = currentAddition!.next + } + + self.chainTip = reorgPath.newChain.latestBlock() // set chain tip to the latest block in the new chain + + let defaults = UserDefaults.standard + defaults.set(self.chainTip!.info.hash, forKey: "chainTipHash") + defaults.set(self.chainTip!.info.height, forKey: "chainTipHeight") + + print("Updated chain path: \(self.chainTip!.toChainString(trailingInfo: nil))\n") + if let completionHandler = self.syncComplete { + self.syncComplete = nil + completionHandler() + } + } + } + } diff --git a/xcode/LDKFramework/DirectlyLinkedBindingsApp/app-batteries/RegtestBroadcasterInterface.swift b/xcode/LDKFramework/DirectlyLinkedBindingsApp/app-batteries/RegtestBroadcasterInterface.swift index ac6f2e71..fc4b4d13 100644 --- a/xcode/LDKFramework/DirectlyLinkedBindingsApp/app-batteries/RegtestBroadcasterInterface.swift +++ b/xcode/LDKFramework/DirectlyLinkedBindingsApp/app-batteries/RegtestBroadcasterInterface.swift @@ -8,32 +8,34 @@ import Foundation class RegtestBroadcasterInterface: BroadcasterInterface { - - override func broadcastTransactions(txs: [[UInt8]]) { - - - let url = URL(string: "http://localhost:3000/api/lab/broadcast")! - - var components = URLComponents(url: url, resolvingAgainstBaseURL: false)! - - for tx in txs { - print("TX TO BROADCAST: \(tx)") - components.queryItems = [ - URLQueryItem(name: "tx", value: "\(tx)") - ] - - let query = components.url!.query - - var request = URLRequest(url: url) - request.httpMethod = "POST" - request.httpBody = Data(query!.utf8) - - let session = URLSession(configuration: .default) - let task = session.dataTask(with: request, completionHandler: { data, response, error in - print("transaction broadcast result") - }) - task.resume() - } - } - + + override func broadcastTransactions(txs: [[UInt8]]) { + + + let url = URL(string: "http://localhost:3000/api/lab/broadcast")! + + var components = URLComponents(url: url, resolvingAgainstBaseURL: false)! + + for tx in txs { + print("TX TO BROADCAST: \(tx)") + components.queryItems = [ + URLQueryItem(name: "tx", value: "\(tx)") + ] + + let query = components.url!.query + + var request = URLRequest(url: url) + request.httpMethod = "POST" + request.httpBody = Data(query!.utf8) + + let session = URLSession(configuration: .default) + let task = session.dataTask( + with: request, + completionHandler: { data, response, error in + print("transaction broadcast result") + }) + task.resume() + } + } + } diff --git a/xcode/LDKFramework/DirectlyLinkedBindingsApp/app-batteries/RegtestChannelManagerPersister.swift b/xcode/LDKFramework/DirectlyLinkedBindingsApp/app-batteries/RegtestChannelManagerPersister.swift index 87964b29..c148f88e 100644 --- a/xcode/LDKFramework/DirectlyLinkedBindingsApp/app-batteries/RegtestChannelManagerPersister.swift +++ b/xcode/LDKFramework/DirectlyLinkedBindingsApp/app-batteries/RegtestChannelManagerPersister.swift @@ -7,85 +7,91 @@ import Foundation -class RegtestChannelManagerPersister : Persister, ExtendedChannelManagerPersister { - - - private let channelManager: ChannelManager? - private let keysManager: KeysManager? = nil - - init(channelManager: ChannelManager?) { - self.channelManager = channelManager - super.init() - } - - func handleEvent(event: Event) -> Result_NoneReplayEventZ { - privateHandleEvent(event: event) - } - - private func privateHandleEvent(event: Event) -> Result_NoneReplayEventZ { - if let spendableOutputEvent = event.getValueAsSpendableOutputs() { - - let outputs = spendableOutputEvent.getOutputs() - - let fastFeerate = 7500 - let destinationScriptHardcoded: [UInt8] = [118,169,20,25,18,157,83,230,49,155,175,25,219,160,89,190,173,22,109,249,10,184,245,136,172] - - guard let result = self.keysManager?.asOutputSpender().spendSpendableOutputs(descriptors: outputs, outputs: [], changeDestinationScript: destinationScriptHardcoded, feerateSatPer1000Weight: UInt32(fastFeerate), locktime: nil) else { - return .initWithErr(e: ReplayEvent()) - } - - if let transaction = result.getValue() { - // sendEvent(eventName: MARKER_BROADCAST, eventBody: ["txhex": bytesToHex(bytes: transaction)]) - } - - return .initWithOk() - - }else if let paymentSentEvent = event.getValueAsPaymentSent() { - // print what needs printing - }else if let pendingHTLCsForwardableEvent = event.getValueAsPendingHtlcsForwardable() { - channelManager?.processPendingHtlcForwards() - - - - - - - } else if let fundingReadyEvent = event.getValueAsFundingGenerationReady() { - let outputScript = fundingReadyEvent.getOutputScript() - let amount = fundingReadyEvent.getChannelValueSatoshis() - - print("ready stuff: \(amount) to \(outputScript)") - print("funding event ready!") - - let url = URL(string: "http://localhost:3000/api/lab/funding-generation-ready")! - var components = URLComponents(url: url, resolvingAgainstBaseURL: false)! - - let tcid = fundingReadyEvent.getTemporaryChannelId() - - components.queryItems = [ - URLQueryItem(name: "script", value: "\(outputScript)"), - URLQueryItem(name: "amount", value: "\(amount)"), - URLQueryItem(name: "tcid", value: "\(fundingReadyEvent.getTemporaryChannelId())"), - URLQueryItem(name: "ucid", value: "\(fundingReadyEvent.getUserChannelId())") - ] - - let query = components.url!.query - - var request = URLRequest(url: url) - request.httpMethod = "POST" - request.httpBody = Data(query!.utf8) - - let session = URLSession(configuration: .default) - let task = session.dataTask(with: request, completionHandler: { data, response, error in - print("received local funding generation ready response") - }) - task.resume() - } - - return .initWithOk() - } - - override func persistManager(channelManager: Bindings.ChannelManager) -> Bindings.Result_NoneIOErrorZ { - .initWithOk() - } +class RegtestChannelManagerPersister: Persister, ExtendedChannelManagerPersister { + + + private let channelManager: ChannelManager? + private let keysManager: KeysManager? = nil + + init(channelManager: ChannelManager?) { + self.channelManager = channelManager + super.init() + } + + func handleEvent(event: Event) -> Result_NoneReplayEventZ { + privateHandleEvent(event: event) + } + + private func privateHandleEvent(event: Event) -> Result_NoneReplayEventZ { + if let spendableOutputEvent = event.getValueAsSpendableOutputs() { + + let outputs = spendableOutputEvent.getOutputs() + + let fastFeerate = 7500 + let destinationScriptHardcoded: [UInt8] = [ + 118, 169, 20, 25, 18, 157, 83, 230, 49, 155, 175, 25, 219, 160, 89, 190, 173, 22, 109, 249, 10, 184, + 245, 136, 172, + ] + + guard + let result = self.keysManager?.asOutputSpender() + .spendSpendableOutputs( + descriptors: outputs, outputs: [], changeDestinationScript: destinationScriptHardcoded, + feerateSatPer1000Weight: UInt32(fastFeerate), locktime: nil) + else { + return .initWithErr(e: ReplayEvent()) + } + + if let transaction = result.getValue() { + // sendEvent(eventName: MARKER_BROADCAST, eventBody: ["txhex": bytesToHex(bytes: transaction)]) + } + + return .initWithOk() + + } else if let paymentSentEvent = event.getValueAsPaymentSent() { + // print what needs printing + } else if let pendingHTLCsForwardableEvent = event.getValueAsPendingHtlcsForwardable() { + channelManager?.processPendingHtlcForwards() + + + } else if let fundingReadyEvent = event.getValueAsFundingGenerationReady() { + let outputScript = fundingReadyEvent.getOutputScript() + let amount = fundingReadyEvent.getChannelValueSatoshis() + + print("ready stuff: \(amount) to \(outputScript)") + print("funding event ready!") + + let url = URL(string: "http://localhost:3000/api/lab/funding-generation-ready")! + var components = URLComponents(url: url, resolvingAgainstBaseURL: false)! + + let tcid = fundingReadyEvent.getTemporaryChannelId() + + components.queryItems = [ + URLQueryItem(name: "script", value: "\(outputScript)"), + URLQueryItem(name: "amount", value: "\(amount)"), + URLQueryItem(name: "tcid", value: "\(fundingReadyEvent.getTemporaryChannelId())"), + URLQueryItem(name: "ucid", value: "\(fundingReadyEvent.getUserChannelId())"), + ] + + let query = components.url!.query + + var request = URLRequest(url: url) + request.httpMethod = "POST" + request.httpBody = Data(query!.utf8) + + let session = URLSession(configuration: .default) + let task = session.dataTask( + with: request, + completionHandler: { data, response, error in + print("received local funding generation ready response") + }) + task.resume() + } + + return .initWithOk() + } + + override func persistManager(channelManager: Bindings.ChannelManager) -> Bindings.Result_NoneIOErrorZ { + .initWithOk() + } } diff --git a/xcode/LDKFramework/DirectlyLinkedBindingsApp/app-batteries/SwiftSocketEchoHandler.swift b/xcode/LDKFramework/DirectlyLinkedBindingsApp/app-batteries/SwiftSocketEchoHandler.swift index 30564c95..a9bbab4f 100644 --- a/xcode/LDKFramework/DirectlyLinkedBindingsApp/app-batteries/SwiftSocketEchoHandler.swift +++ b/xcode/LDKFramework/DirectlyLinkedBindingsApp/app-batteries/SwiftSocketEchoHandler.swift @@ -9,152 +9,153 @@ import Foundation import SwiftSocket class SwiftSocketEchoHandler: ObservableObject { - - private let server: TCPServer - private var ioWorkItem: DispatchWorkItem? - private var shutdown = false - fileprivate var peers = [UInt64: TcpPeer]() - @Published public var connectedPeers: [(UInt64, String)] = [] - - private static var runningCounter: Int64 = 0 - - init() { - self.server = TCPServer(address: "127.0.0.1", port: 9736) - - self.ioWorkItem = DispatchWorkItem { - if self.server.listen().isSuccess { - print("Echo Server listening") - var lastTimerTick = NSDate().timeIntervalSince1970 - while !self.shutdown { - if let client = self.server.accept() { - print("Got a client! \(client.address):\(client.port)") - let peer = self.setupSocket(client: client) - self.peers[peer.ourId] = peer - DispatchQueue.main.async { - self.connectedPeers.append((peer.ourId, "\(client.address):\(client.port)")) - peer.doRead() - } - } - } - } - } - - let backgroundQueue = DispatchQueue(label: "org.ldk.SwiftSocketEchoHandler.ioThread", qos: .background) - backgroundQueue.async(execute: self.ioWorkItem!) - } - - fileprivate func setupSocket(client: TCPClient) -> TcpPeer { - OSAtomicIncrement64(&SwiftSocketEchoHandler.runningCounter) - let socketDescriptor = TcpPeer(tcpClient: client, ourId: SwiftSocketEchoHandler.runningCounter) - return socketDescriptor - } - - public func connect(address: String, port: Int32, theirNodeId: [UInt8]) { - let client = TCPClient(address: address, port: port) - let peer = setupSocket(client: client) - self.peers[peer.ourId] = peer - } - - public func disconnect(connectionId: UInt64){ - if let peer = self.peers[connectionId] { - peer.disconnectPeer() - } - } - - public func interrupt() { - self.shutdown = true - if let workItem = self.ioWorkItem { - workItem.wait() - } - } - + + private let server: TCPServer + private var ioWorkItem: DispatchWorkItem? + private var shutdown = false + fileprivate var peers = [UInt64: TcpPeer]() + @Published public var connectedPeers: [(UInt64, String)] = [] + + private static var runningCounter: Int64 = 0 + + init() { + self.server = TCPServer(address: "127.0.0.1", port: 9736) + + self.ioWorkItem = DispatchWorkItem { + if self.server.listen().isSuccess { + print("Echo Server listening") + var lastTimerTick = NSDate().timeIntervalSince1970 + while !self.shutdown { + if let client = self.server.accept() { + print("Got a client! \(client.address):\(client.port)") + let peer = self.setupSocket(client: client) + self.peers[peer.ourId] = peer + DispatchQueue.main.async { + self.connectedPeers.append((peer.ourId, "\(client.address):\(client.port)")) + peer.doRead() + } + } + } + } + } + + let backgroundQueue = DispatchQueue(label: "org.ldk.SwiftSocketEchoHandler.ioThread", qos: .background) + backgroundQueue.async(execute: self.ioWorkItem!) + } + + fileprivate func setupSocket(client: TCPClient) -> TcpPeer { + OSAtomicIncrement64(&SwiftSocketEchoHandler.runningCounter) + let socketDescriptor = TcpPeer(tcpClient: client, ourId: SwiftSocketEchoHandler.runningCounter) + return socketDescriptor + } + + public func connect(address: String, port: Int32, theirNodeId: [UInt8]) { + let client = TCPClient(address: address, port: port) + let peer = setupSocket(client: client) + self.peers[peer.ourId] = peer + } + + public func disconnect(connectionId: UInt64) { + if let peer = self.peers[connectionId] { + peer.disconnectPeer() + } + } + + public func interrupt() { + self.shutdown = true + if let workItem = self.ioWorkItem { + workItem.wait() + } + } + } private class TcpPeer: SocketDescriptor { - /** + /** * When we are told by LDK to disconnect, we can't return to LDK until we are sure * won't call any more read/write PeerManager functions with the same connection. * This is set to true if we're in such a condition (with disconnect checked * before with the Peer monitor lock held) and false when we can return. */ - var blockDisconnectSocket = false - - /** + var blockDisconnectSocket = false + + /** * Indicates LDK told us to disconnect this peer, and thus we should not call socket_disconnected. */ - var disconnectRequested = false - - let client: TCPClient - let ourId: UInt64 - let dispatchQueue: DispatchQueue - var workItems: [DispatchWorkItem] = [] - - init(tcpClient: TCPClient, ourId: Int64) { - self.client = tcpClient - self.ourId = UInt64(ourId) - self.dispatchQueue = DispatchQueue(label: "org.ldk.SwiftSocketPeerHandler.peerThread:\(self.ourId)", qos: .background) - super.init() - } - - fileprivate func doRead() { - guard let bytesAvailable = self.client.bytesAvailable() else { - print("invalid bytes available from peer #\(self.ourId), disconnecting") - self.disconnect_socket() - return - } - print("\(bytesAvailable) bytes available from peer #\(self.ourId)") - let workItem = DispatchWorkItem { - if bytesAvailable == 0 { - Thread.sleep(forTimeInterval: 1) - self.doRead() - return - } - print("starting read from peer #\(self.ourId)") - guard let readData = self.client.read(Int(bytesAvailable)) else { - print("read failed from peer #\(self.ourId), disconnecting") - self.disconnect_socket() - return - } - let readBytes = [UInt8](readData) - print("Read from peer #\(self.ourId):\n\(readBytes)\n") - - // after read, always write - self.send_data(data: readData, resume_read: true) - - } - self.workItems.append(workItem) - self.dispatchQueue.async(execute: workItem) - } - - override func send_data(data: [UInt8], resume_read: Bool) -> UInt { - defer { - // do the read at the end - if resume_read { - self.doRead() - } - } - let result = self.client.send(data: Data(data)) - if result.isSuccess { - print("Write to peer #\(self.ourId):\n\(data)\n") - return UInt(data.count) - } - return 0 - } - - fileprivate func disconnectPeer() { - self.client.close() - - } - - override func disconnect_socket() { - self.client.close() - } - override func hash() -> UInt64 { - return self.ourId - } - override func eq(other_arg: SocketDescriptor) -> Bool { - let comparable = other_arg as! TcpPeer - return comparable.ourId == self.ourId - } + var disconnectRequested = false + + let client: TCPClient + let ourId: UInt64 + let dispatchQueue: DispatchQueue + var workItems: [DispatchWorkItem] = [] + + init(tcpClient: TCPClient, ourId: Int64) { + self.client = tcpClient + self.ourId = UInt64(ourId) + self.dispatchQueue = DispatchQueue( + label: "org.ldk.SwiftSocketPeerHandler.peerThread:\(self.ourId)", qos: .background) + super.init() + } + + fileprivate func doRead() { + guard let bytesAvailable = self.client.bytesAvailable() else { + print("invalid bytes available from peer #\(self.ourId), disconnecting") + self.disconnect_socket() + return + } + print("\(bytesAvailable) bytes available from peer #\(self.ourId)") + let workItem = DispatchWorkItem { + if bytesAvailable == 0 { + Thread.sleep(forTimeInterval: 1) + self.doRead() + return + } + print("starting read from peer #\(self.ourId)") + guard let readData = self.client.read(Int(bytesAvailable)) else { + print("read failed from peer #\(self.ourId), disconnecting") + self.disconnect_socket() + return + } + let readBytes = [UInt8](readData) + print("Read from peer #\(self.ourId):\n\(readBytes)\n") + + // after read, always write + self.send_data(data: readData, resume_read: true) + + } + self.workItems.append(workItem) + self.dispatchQueue.async(execute: workItem) + } + + override func send_data(data: [UInt8], resume_read: Bool) -> UInt { + defer { + // do the read at the end + if resume_read { + self.doRead() + } + } + let result = self.client.send(data: Data(data)) + if result.isSuccess { + print("Write to peer #\(self.ourId):\n\(data)\n") + return UInt(data.count) + } + return 0 + } + + fileprivate func disconnectPeer() { + self.client.close() + + } + + override func disconnect_socket() { + self.client.close() + } + override func hash() -> UInt64 { + return self.ourId + } + override func eq(other_arg: SocketDescriptor) -> Bool { + let comparable = other_arg as! TcpPeer + return comparable.ourId == self.ourId + } } diff --git a/xcode/LDKFramework/DirectlyLinkedBindingsApp/app-batteries/SwiftSocketPeerHandler.swift b/xcode/LDKFramework/DirectlyLinkedBindingsApp/app-batteries/SwiftSocketPeerHandler.swift index 9ac2eb5a..15d6e4b1 100644 --- a/xcode/LDKFramework/DirectlyLinkedBindingsApp/app-batteries/SwiftSocketPeerHandler.swift +++ b/xcode/LDKFramework/DirectlyLinkedBindingsApp/app-batteries/SwiftSocketPeerHandler.swift @@ -9,45 +9,45 @@ import Foundation import SwiftSocket class SwiftSocketPeerHandler: ObservableObject { - - private let peerManager: PeerManager - private let server: TCPServer - private var ioWorkItem: DispatchWorkItem? - private var eventProcessingItem: DispatchWorkItem? - public private(set) var shutdown = false - fileprivate var peersByConnectionId = [UInt64: TcpPeer]() - @Published public fileprivate(set) var peers: [(UInt64, TCPClient)] = [] - - private static var runningConnectionCounter: Int64 = 0 - - init(peerManager: PeerManager) { - self.peerManager = peerManager - self.server = TCPServer(address: "127.0.0.1", port: 9734) - - self.ioWorkItem = DispatchWorkItem { - if self.server.listen().isSuccess { - while !self.shutdown { - if let client = self.server.accept() { - let peer = self.setupSocket(client: client) - DispatchQueue.main.async { - let inboundResult = self.peerManager.new_inbound_connection(descriptor: peer) - // self.peerManager.socket_disconnected(descriptor: peer) - if inboundResult.cOpaqueStruct?.result_ok == true { - self.peersByConnectionId[peer.connectionId] = peer - self.peers.append((peer.connectionId, client)) - peer.doRead() - } - } - } - } - } - } - - - self.eventProcessingItem = DispatchWorkItem { - var lastTimerTick = NSDate().timeIntervalSince1970 - while !self.shutdown { - /* + + private let peerManager: PeerManager + private let server: TCPServer + private var ioWorkItem: DispatchWorkItem? + private var eventProcessingItem: DispatchWorkItem? + public private(set) var shutdown = false + fileprivate var peersByConnectionId = [UInt64: TcpPeer]() + @Published public fileprivate(set) var peers: [(UInt64, TCPClient)] = [] + + private static var runningConnectionCounter: Int64 = 0 + + init(peerManager: PeerManager) { + self.peerManager = peerManager + self.server = TCPServer(address: "127.0.0.1", port: 9734) + + self.ioWorkItem = DispatchWorkItem { + if self.server.listen().isSuccess { + while !self.shutdown { + if let client = self.server.accept() { + let peer = self.setupSocket(client: client) + DispatchQueue.main.async { + let inboundResult = self.peerManager.new_inbound_connection(descriptor: peer) + // self.peerManager.socket_disconnected(descriptor: peer) + if inboundResult.cOpaqueStruct?.result_ok == true { + self.peersByConnectionId[peer.connectionId] = peer + self.peers.append((peer.connectionId, client)) + peer.doRead() + } + } + } + } + } + } + + + self.eventProcessingItem = DispatchWorkItem { + var lastTimerTick = NSDate().timeIntervalSince1970 + while !self.shutdown { + /* let currentTimerTick = NSDate().timeIntervalSince1970 if lastTimerTick < (currentTimerTick-30) { // more than 30 seconds have passed since the last timer tick print("calling PeerManager::timer_tick_occurred()") @@ -55,184 +55,189 @@ class SwiftSocketPeerHandler: ObservableObject { lastTimerTick = currentTimerTick } */ - // print("calling PeerManager::process_events()") - self.peerManager.process_events() - Thread.sleep(forTimeInterval: 1) - } - } - - - let backgroundQueueA = DispatchQueue(label: "org.ldk.SwiftSocketPeerHandler.ioThread", qos: .background) - let backgroundQueueB = DispatchQueue(label: "org.ldk.SwiftSocketPeerHandler.eventProcessingThread", qos: .background) - backgroundQueueA.async(execute: self.ioWorkItem!) - backgroundQueueB.async(execute: self.eventProcessingItem!) - } - - fileprivate func setupSocket(client: TCPClient) -> TcpPeer { - OSAtomicIncrement64(&SwiftSocketPeerHandler.runningConnectionCounter) - let socketDescriptor = TcpPeer(peerManager: self.peerManager, tcpClient: client, connectionId: SwiftSocketPeerHandler.runningConnectionCounter) - return socketDescriptor - } - - public func connect(address: String, port: Int32, theirNodeId: [UInt8]) -> UInt64? { - if self.shutdown { - return nil - } - let client = TCPClient(address: address, port: port) - if client.connect(timeout: 5).isFailure { - return nil - } - let peer = setupSocket(client: client) - let outboundResult = self.peerManager.new_outbound_connection(their_node_id: theirNodeId, descriptor: peer) - if outboundResult.cOpaqueStruct?.result_ok == true { - self.peersByConnectionId[peer.connectionId] = peer - self.peers.append((peer.connectionId, client)) - let firstMessage = outboundResult.cOpaqueStruct!.contents.result - let firstMessageBytes = Bindings.LDKCVec_u8Z_to_array(nativeType: firstMessage!.pointee) - peer.client.send(data: Data(firstMessageBytes)) - peer.doRead() - return peer.connectionId - } - return nil - } - - public func disconnect(connectionId: UInt64){ - if let peer = self.peersByConnectionId[connectionId] { - peer.disconnect() - self.objectWillChange.send() - } - } - - public func interrupt() { - self.shutdown = true - self.server.close() - if let workItem = self.eventProcessingItem { - workItem.wait() - self.eventProcessingItem = nil - } - if let ioItem = self.ioWorkItem { - ioItem.cancel() - self.ioWorkItem = nil - } - self.objectWillChange.send() - for (_, peer) in self.peersByConnectionId { - peer.disconnect() - } - self.objectWillChange.send() - } - + // print("calling PeerManager::process_events()") + self.peerManager.process_events() + Thread.sleep(forTimeInterval: 1) + } + } + + + let backgroundQueueA = DispatchQueue(label: "org.ldk.SwiftSocketPeerHandler.ioThread", qos: .background) + let backgroundQueueB = DispatchQueue( + label: "org.ldk.SwiftSocketPeerHandler.eventProcessingThread", qos: .background) + backgroundQueueA.async(execute: self.ioWorkItem!) + backgroundQueueB.async(execute: self.eventProcessingItem!) + } + + fileprivate func setupSocket(client: TCPClient) -> TcpPeer { + OSAtomicIncrement64(&SwiftSocketPeerHandler.runningConnectionCounter) + let socketDescriptor = TcpPeer( + peerManager: self.peerManager, tcpClient: client, + connectionId: SwiftSocketPeerHandler.runningConnectionCounter) + return socketDescriptor + } + + public func connect(address: String, port: Int32, theirNodeId: [UInt8]) -> UInt64? { + if self.shutdown { + return nil + } + let client = TCPClient(address: address, port: port) + if client.connect(timeout: 5).isFailure { + return nil + } + let peer = setupSocket(client: client) + let outboundResult = self.peerManager.new_outbound_connection(their_node_id: theirNodeId, descriptor: peer) + if outboundResult.cOpaqueStruct?.result_ok == true { + self.peersByConnectionId[peer.connectionId] = peer + self.peers.append((peer.connectionId, client)) + let firstMessage = outboundResult.cOpaqueStruct!.contents.result + let firstMessageBytes = Bindings.LDKCVec_u8Z_to_array(nativeType: firstMessage!.pointee) + peer.client.send(data: Data(firstMessageBytes)) + peer.doRead() + return peer.connectionId + } + return nil + } + + public func disconnect(connectionId: UInt64) { + if let peer = self.peersByConnectionId[connectionId] { + peer.disconnect() + self.objectWillChange.send() + } + } + + public func interrupt() { + self.shutdown = true + self.server.close() + if let workItem = self.eventProcessingItem { + workItem.wait() + self.eventProcessingItem = nil + } + if let ioItem = self.ioWorkItem { + ioItem.cancel() + self.ioWorkItem = nil + } + self.objectWillChange.send() + for (_, peer) in self.peersByConnectionId { + peer.disconnect() + } + self.objectWillChange.send() + } + } -fileprivate class TcpPeer: SocketDescriptor { - /** +private class TcpPeer: SocketDescriptor { + /** * When we are told by LDK to disconnect, we can't return to LDK until we are sure * won't call any more read/write PeerManager functions with the same connection. * This is set to true if we're in such a condition (with disconnect checked * before with the Peer monitor lock held) and false when we can return. */ - var blockDisconnectSocket = false - - /** + var blockDisconnectSocket = false + + /** * Indicates LDK told us to disconnect this peer, and thus we should not call socket_disconnected. */ - var disconnectRequested = false - - /** + var disconnectRequested = false + + /** * Indicates that the user requested disconnect this peer */ - var disconnectInitiated = false - - let peerManager: PeerManager - let client: TCPClient - let connectionId: UInt64 - - let dispatchQueue: DispatchQueue - var workItems: [DispatchWorkItem] = [] - var consecutiveReadFails = 0 - - init(peerManager: PeerManager, tcpClient: TCPClient, connectionId: Int64) { - self.peerManager = peerManager - self.client = tcpClient - self.connectionId = UInt64(connectionId) - self.dispatchQueue = DispatchQueue(label: "org.ldk.SwiftSocketPeerHandler.peerThread:\(self.connectionId)", qos: .background) - super.init() - } - - fileprivate func doRead() { - if let bytesAvailable = self.client.bytesAvailable() { - // print("\(bytesAvailable) bytes available from peer #\(self.connectionId)") - let workItem = DispatchWorkItem { - if bytesAvailable == 0 { - Thread.sleep(forTimeInterval: 1) - self.doRead() - return - } - // print("starting read from peer #\(self.connectionId)") - if let readData = self.client.read(Int(bytesAvailable), timeout: 0){ - let readBytes = [UInt8](readData) - self.consecutiveReadFails = 0 - // print("Read from peer #\(self.connectionId):\n\(readBytes)\n") - - // after read, always write - self.peerManager.read_event(peer_descriptor: self, data: readBytes) - }else{ - // if(errno == ) - print("failed to read \(bytesAvailable) from peer #\(self.connectionId)") - self.consecutiveReadFails += 1 - if self.consecutiveReadFails >= 3 { - print("Consecutive read fails: \(self.consecutiveReadFails)") - } - } - } - self.workItems.append(workItem) - self.dispatchQueue.async(execute: workItem) - }else { - print("no bytes available from peer #\(self.connectionId)") - } - } - - override func send_data(data: [UInt8], resume_read: Bool) -> UInt { - defer { - // do the read at the end - if resume_read { - self.doRead() - } - } - let result = self.client.send(data: Data(data)) - if result.isSuccess { - // print("Write to peer #\(self.connectionId):\n\(data)\n") - return UInt(data.count) - } - return 0 - } - - fileprivate func disconnect() { - if(!self.disconnectInitiated && !self.disconnectRequested){ - self.disconnectInitiated = true - self.peerManager.socket_disconnected(descriptor: self) - self.client.close() - } - } - - override func disconnect_socket() { - print("LDK disconnected from Peer #\(self.connectionId)") - if(!self.disconnectInitiated && !self.disconnectRequested){ - self.disconnectRequested = true - self.client.close() - } - } - override func hash() -> UInt64 { - return self.connectionId - } - override func eq(other_arg: SocketDescriptor) -> Bool { - let comparable: TcpPeer = Bindings.pointerToInstance(pointer: other_arg.cOpaqueStruct!.this_arg, sourceMarker: "SwiftSocketPeerHandler.swift::eq") - return comparable.connectionId == self.connectionId - } - - func clone() -> UnsafeMutableRawPointer { - return self.cOpaqueStruct!.this_arg - - /* + var disconnectInitiated = false + + let peerManager: PeerManager + let client: TCPClient + let connectionId: UInt64 + + let dispatchQueue: DispatchQueue + var workItems: [DispatchWorkItem] = [] + var consecutiveReadFails = 0 + + init(peerManager: PeerManager, tcpClient: TCPClient, connectionId: Int64) { + self.peerManager = peerManager + self.client = tcpClient + self.connectionId = UInt64(connectionId) + self.dispatchQueue = DispatchQueue( + label: "org.ldk.SwiftSocketPeerHandler.peerThread:\(self.connectionId)", qos: .background) + super.init() + } + + fileprivate func doRead() { + if let bytesAvailable = self.client.bytesAvailable() { + // print("\(bytesAvailable) bytes available from peer #\(self.connectionId)") + let workItem = DispatchWorkItem { + if bytesAvailable == 0 { + Thread.sleep(forTimeInterval: 1) + self.doRead() + return + } + // print("starting read from peer #\(self.connectionId)") + if let readData = self.client.read(Int(bytesAvailable), timeout: 0) { + let readBytes = [UInt8](readData) + self.consecutiveReadFails = 0 + // print("Read from peer #\(self.connectionId):\n\(readBytes)\n") + + // after read, always write + self.peerManager.read_event(peer_descriptor: self, data: readBytes) + } else { + // if(errno == ) + print("failed to read \(bytesAvailable) from peer #\(self.connectionId)") + self.consecutiveReadFails += 1 + if self.consecutiveReadFails >= 3 { + print("Consecutive read fails: \(self.consecutiveReadFails)") + } + } + } + self.workItems.append(workItem) + self.dispatchQueue.async(execute: workItem) + } else { + print("no bytes available from peer #\(self.connectionId)") + } + } + + override func send_data(data: [UInt8], resume_read: Bool) -> UInt { + defer { + // do the read at the end + if resume_read { + self.doRead() + } + } + let result = self.client.send(data: Data(data)) + if result.isSuccess { + // print("Write to peer #\(self.connectionId):\n\(data)\n") + return UInt(data.count) + } + return 0 + } + + fileprivate func disconnect() { + if !self.disconnectInitiated && !self.disconnectRequested { + self.disconnectInitiated = true + self.peerManager.socket_disconnected(descriptor: self) + self.client.close() + } + } + + override func disconnect_socket() { + print("LDK disconnected from Peer #\(self.connectionId)") + if !self.disconnectInitiated && !self.disconnectRequested { + self.disconnectRequested = true + self.client.close() + } + } + override func hash() -> UInt64 { + return self.connectionId + } + override func eq(other_arg: SocketDescriptor) -> Bool { + let comparable: TcpPeer = Bindings.pointerToInstance( + pointer: other_arg.cOpaqueStruct!.this_arg, sourceMarker: "SwiftSocketPeerHandler.swift::eq") + return comparable.connectionId == self.connectionId + } + + func clone() -> UnsafeMutableRawPointer { + return self.cOpaqueStruct!.this_arg + + /* let clone = TcpPeer(peerManager: self.peerManager, tcpClient: self.client, connectionId: Int64(self.connectionId)) clone.cOpaqueStruct!.this_arg = self.cOpaqueStruct!.this_arg // the Swift clone might disappear soon, only clone C struct let pointer = UnsafeMutablePointer.allocate(capacity: 1) @@ -240,6 +245,6 @@ fileprivate class TcpPeer: SocketDescriptor { let rawPointer = UnsafeMutableRawPointer(pointer) return rawPointer */ - } + } } diff --git a/xcode/LDKFramework/DirectlyLinkedBindingsAppTests/DirectlyLinkedBindingsAppTests.swift b/xcode/LDKFramework/DirectlyLinkedBindingsAppTests/DirectlyLinkedBindingsAppTests.swift index 8c8a203f..fcaebc26 100644 --- a/xcode/LDKFramework/DirectlyLinkedBindingsAppTests/DirectlyLinkedBindingsAppTests.swift +++ b/xcode/LDKFramework/DirectlyLinkedBindingsAppTests/DirectlyLinkedBindingsAppTests.swift @@ -6,31 +6,32 @@ // import XCTest + @testable import DirectlyLinkedBindingsApp class DirectlyLinkedBindingsAppTests: XCTestCase { - override func setUpWithError() throws { - // Put setup code here. This method is called before the invocation of each test method in the class. - } - - override func tearDownWithError() throws { - // Put teardown code here. This method is called after the invocation of each test method in the class. - } - - func testExample() throws { - // This is an example of a functional test case. - // Use XCTAssert and related functions to verify your tests produce the correct results. - // Any test you write for XCTest can be annotated as throws and async. - // Mark your test throws to produce an unexpected failure when your test encounters an uncaught error. - // Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards. - } - - func testPerformanceExample() throws { - // This is an example of a performance test case. - self.measure { - // Put the code you want to measure the time of here. - } - } + override func setUpWithError() throws { + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testExample() throws { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct results. + // Any test you write for XCTest can be annotated as throws and async. + // Mark your test throws to produce an unexpected failure when your test encounters an uncaught error. + // Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards. + } + + func testPerformanceExample() throws { + // This is an example of a performance test case. + self.measure { + // Put the code you want to measure the time of here. + } + } } diff --git a/xcode/LDKFramework/DirectlyLinkedBindingsAppTests/polar/BlockchainObserver.swift b/xcode/LDKFramework/DirectlyLinkedBindingsAppTests/polar/BlockchainObserver.swift index 64590ee7..d0688c2b 100644 --- a/xcode/LDKFramework/DirectlyLinkedBindingsAppTests/polar/BlockchainObserver.swift +++ b/xcode/LDKFramework/DirectlyLinkedBindingsAppTests/polar/BlockchainObserver.swift @@ -5,8 +5,8 @@ // Created by Arik Sosman on 4/10/22. // -import Foundation import Combine +import Foundation @available(iOS 15.0, *) class BlockchainObserver { @@ -50,19 +50,19 @@ class BlockchainObserver { func toString() -> String { switch self { - case .http: - return "http" - case .https: - return "https" + case .http: + return "http" + case .https: + return "https" } } func defaultPort() -> UInt { switch self { - case .http: - return 80 - case .https: - return 443 + case .http: + return 80 + case .https: + return 443 } } } @@ -122,7 +122,9 @@ class BlockchainObserver { private var chainListeners = [BlockchainListener]() - public init(rpcProtocol: RpcProtocol, rpcDomain: String, rpcPort: UInt?, rpcUsername: String, rpcPassword: String) throws { + public init(rpcProtocol: RpcProtocol, rpcDomain: String, rpcPort: UInt?, rpcUsername: String, rpcPassword: String) + throws + { self.rpcProtocol = rpcProtocol self.rpcDomain = rpcDomain self.rpcPort = rpcPort ?? rpcProtocol.defaultPort() @@ -146,12 +148,12 @@ class BlockchainObserver { var lastTrustedBlockHeight: Int64 let chaintipHeight = try await self.getChaintipHeight() switch anchorHeight { - case .genesis: - lastTrustedBlockHeight = 0 - case .block(let height): - lastTrustedBlockHeight = height - case .chaintip: - lastTrustedBlockHeight = chaintipHeight + case .genesis: + lastTrustedBlockHeight = 0 + case .block(let height): + lastTrustedBlockHeight = height + case .chaintip: + lastTrustedBlockHeight = chaintipHeight } let anchorBlockHash = try await self.getBlockHashHex(height: lastTrustedBlockHeight) @@ -275,7 +277,8 @@ class BlockchainObserver { } private func getRpcEndpoint() -> URL? { - let urlString = "\(self.rpcProtocol)://\(self.rpcUsername):\(self.rpcPassword)@\(self.rpcDomain):\(self.rpcPort)" + let urlString = + "\(self.rpcProtocol)://\(self.rpcUsername):\(self.rpcPassword)@\(self.rpcDomain):\(self.rpcPort)" return URL(string: urlString) } @@ -283,7 +286,7 @@ class BlockchainObserver { let url = self.getRpcEndpoint()! let body: [String: Any] = [ "method": method, - "params": params + "params": params, ] let jsonBody = try! JSONSerialization.data(withJSONObject: body) var request = URLRequest(url: url) @@ -294,7 +297,8 @@ class BlockchainObserver { // print("JSON-RPC response: \(response)") let responseDictionary = response as! [String: Any] if let responseError = responseDictionary["error"] as? [String: Any] { - let errorDetails = RPCErrorDetails(message: responseError["message"] as! String, code: responseError["code"] as! Int64) + let errorDetails = RPCErrorDetails( + message: responseError["message"] as! String, code: responseError["code"] as! Int64) print("error details: \(errorDetails)") throw RpcError.errorResponse(errorDetails) } @@ -341,7 +345,8 @@ class BlockchainObserver { public func getBlock(hash: String) async throws -> RPCBlockDetails { let response = try await self.callRpcMethod(method: "getblock", params: [hash]) let result = response["result"] as! [String: Any] - let blockDetails = try JSONDecoder().decode(RPCBlockDetails.self, from: JSONSerialization.data(withJSONObject: result)) + let blockDetails = try JSONDecoder() + .decode(RPCBlockDetails.self, from: JSONSerialization.data(withJSONObject: result)) return blockDetails } @@ -478,26 +483,26 @@ class BlockchainObserver { return result } - public func isMonitoring() async throws -> Bool { - await monitoringTracker.isTracking - } - - public var blockchainMonitorPublisher: AnyPublisher { - Timer.publish(every: 5.0, on: RunLoop.main, in: .default) - .autoconnect() - .asyncMap { [unowned self] _ in - try await self.reconcileChaintips() - } - .eraseToAnyPublisher() - } + public func isMonitoring() async throws -> Bool { + await monitoringTracker.isTracking + } + + public var blockchainMonitorPublisher: AnyPublisher { + Timer.publish(every: 5.0, on: RunLoop.main, in: .default) + .autoconnect() + .asyncMap { [unowned self] _ in + try await self.reconcileChaintips() + } + .eraseToAnyPublisher() + } } public protocol BlockchainListener { - func blockConnected(block: [UInt8], height: UInt32); - func blockDisconnected(header: [UInt8]?, height: UInt32); + func blockConnected(block: [UInt8], height: UInt32) + func blockDisconnected(header: [UInt8]?, height: UInt32) } -fileprivate func hexStringToBytes(hexString: String) -> [UInt8]? { +private func hexStringToBytes(hexString: String) -> [UInt8]? { let hexStr = hexString.dropFirst(hexString.hasPrefix("0x") ? 2 : 0) guard hexStr.count % 2 == 0 else { @@ -520,10 +525,11 @@ fileprivate func hexStringToBytes(hexString: String) -> [UInt8]? { return newData } -fileprivate func bytesToHexString(bytes: [UInt8]) -> String { - let format = "%02hhx" // "%02hhX" (uppercase) - return bytes.map { - String(format: format, $0) - } - .joined() +private func bytesToHexString(bytes: [UInt8]) -> String { + let format = "%02hhx" // "%02hhX" (uppercase) + return + bytes.map { + String(format: format, $0) + } + .joined() } diff --git a/xcode/LDKFramework/DirectlyLinkedBindingsAppTests/polar/CombineUtils.swift b/xcode/LDKFramework/DirectlyLinkedBindingsAppTests/polar/CombineUtils.swift index 27938f4b..b676e00f 100644 --- a/xcode/LDKFramework/DirectlyLinkedBindingsAppTests/polar/CombineUtils.swift +++ b/xcode/LDKFramework/DirectlyLinkedBindingsAppTests/polar/CombineUtils.swift @@ -7,25 +7,29 @@ // Created by Jurvis Tan on 4/18/22. // -import Foundation import Combine +import Foundation extension Publisher { - func asyncMap( - _ transform: @escaping (Output) async throws -> T - ) -> Publishers.FlatMap, - Publishers.SetFailureType> { - flatMap { value in - Future { promise in - Task { - do { - let output = try await transform(value) - promise(.success(output)) - } catch { - promise(.failure(error)) - } - } - } - } - } + func asyncMap( + _ transform: @escaping (Output) async throws -> T + ) + -> Publishers.FlatMap< + Future, + Publishers.SetFailureType + > + { + flatMap { value in + Future { promise in + Task { + do { + let output = try await transform(value) + promise(.success(output)) + } catch { + promise(.failure(error)) + } + } + } + } + } } diff --git a/xcode/LDKFramework/DirectlyLinkedBindingsAppTests/polar/PolarIntegrationSample.swift b/xcode/LDKFramework/DirectlyLinkedBindingsAppTests/polar/PolarIntegrationSample.swift index fb108505..56d92732 100644 --- a/xcode/LDKFramework/DirectlyLinkedBindingsAppTests/polar/PolarIntegrationSample.swift +++ b/xcode/LDKFramework/DirectlyLinkedBindingsAppTests/polar/PolarIntegrationSample.swift @@ -5,7 +5,6 @@ import Foundation import LightningDevKit - @available(iOS 15.0, *) public class PolarIntegrationSample { @@ -24,25 +23,29 @@ public class PolarIntegrationSample { // EDIT ME static let POLAR_LND_PEER_PUBKEY_HEX = "02e62868ab834e7c062a929ca2f22ee8707827a5821e3a8eec343f106cbee24e7c" - static let POLAR_LND_PEER_INVOICE = "lnbcrt420690n1p3xrgslpp50n0wgusa2lzv56u6cqqlcdllmgwtpyvtz9czpc88ggvwwp76gk8sdqqcqzpgxqyz5vqsp5j6futhfxhn38rkmuepz8hsjj4zh66z4xqspemyg20l4r7kwnhh8s9qyyssqad3n9lzkp4pl358en022q5qm3ru57crmy4c70kq2s60apupdmm4xfn85m85dv2eu54m4dv2v7ldugwankz03ezk7k5l76rz6m70f05gqre4g8p" + static let POLAR_LND_PEER_INVOICE = + "lnbcrt420690n1p3xrgslpp50n0wgusa2lzv56u6cqqlcdllmgwtpyvtz9czpc88ggvwwp76gk8sdqqcqzpgxqyz5vqsp5j6futhfxhn38rkmuepz8hsjj4zh66z4xqspemyg20l4r7kwnhh8s9qyyssqad3n9lzkp4pl358en022q5qm3ru57crmy4c70kq2s60apupdmm4xfn85m85dv2eu54m4dv2v7ldugwankz03ezk7k5l76rz6m70f05gqre4g8p" func testPolarFlow() async throws { - let rpcInterface = try RegtestBlockchainManager(rpcProtocol: .http, rpcDomain: "localhost", rpcPort: 18443, rpcUsername: "polaruser", rpcPassword: "polarpass") + let rpcInterface = try RegtestBlockchainManager( + rpcProtocol: .http, rpcDomain: "localhost", rpcPort: 18443, rpcUsername: "polaruser", + rpcPassword: "polarpass") // let help = try await rpcInterface.getHelp() // print(help) try await self.ascertainSpareMoney(rpcInterface: rpcInterface) try await rpcInterface.preloadMonitor(anchorHeight: .chaintip) - + // let seed: [UInt8] = [UInt8](Data(base64Encoded: "//////////////////////////////////////////8=")!) var seed = [UInt8](repeating: 0, count: 32) let status = SecRandomCopyBytes(kSecRandomDefault, seed.count, &seed) let timestamp_seconds = UInt64(NSDate().timeIntervalSince1970) let timestamp_nanos = UInt32(truncating: NSNumber(value: timestamp_seconds * 1000 * 1000)) - let keysManager = KeysManager(seed: seed, starting_time_secs: timestamp_seconds, starting_time_nanos: timestamp_nanos) + let keysManager = KeysManager( + seed: seed, starting_time_secs: timestamp_seconds, starting_time_nanos: timestamp_nanos) let keysInterface = keysManager.as_KeysInterface() - let logger = LDKTraitImplementations.PolarLogger() + let logger = LDKTraitImplementations.PolarLogger() let config = UserConfig() let lightningNetwork = LDKNetwork_Regtest @@ -54,7 +57,8 @@ public class PolarIntegrationSample { let networkGraph = NetworkGraph(genesis_hash: reversedGenesisHash, logger: logger) let scoringParams = ProbabilisticScoringParameters() - let probabalisticScorer = ProbabilisticScorer(params: scoringParams, network_graph: networkGraph, logger: logger) + let probabalisticScorer = ProbabilisticScorer( + params: scoringParams, network_graph: networkGraph, logger: logger) let score = probabalisticScorer.as_Score() let multiThreadedScorer = MultiThreadedLockableScore(score: score) @@ -67,18 +71,25 @@ public class PolarIntegrationSample { let feeEstimator = LDKTraitImplementations.PolarFeeEstimator() let broadcaster = LDKTraitImplementations.PolarBroadcaster(rpcInterface: rpcInterface) - - let channelMonitorPersister = LDKTraitImplementations.PolarChannelMonitorPersister() - let channelManagerAndNetworkGraphPersisterAndEventHandler = LDKTraitImplementations.PolarChannelManagerAndNetworkGraphPersisterAndEventHandler() - let chainMonitor = ChainMonitor(chain_source: Option_FilterZ(value: nil), broadcaster: broadcaster, logger: logger, feeest: feeEstimator, persister: channelMonitorPersister) - let channelManagerConstructor = ChannelManagerConstructor(network: lightningNetwork, userConfig: config, current_blockchain_tip_hash: reversedChaintipHash, current_blockchain_tip_height: UInt32(chaintipHeight), keys_interface: keysInterface, fee_estimator: feeEstimator, chain_monitor: chainMonitor, net_graph: networkGraph, tx_broadcaster: broadcaster, logger: logger) + let channelMonitorPersister = LDKTraitImplementations.PolarChannelMonitorPersister() + let channelManagerAndNetworkGraphPersisterAndEventHandler = + LDKTraitImplementations.PolarChannelManagerAndNetworkGraphPersisterAndEventHandler() + let chainMonitor = ChainMonitor( + chain_source: Option_FilterZ(value: nil), broadcaster: broadcaster, logger: logger, feeest: feeEstimator, + persister: channelMonitorPersister) + + let channelManagerConstructor = ChannelManagerConstructor( + network: lightningNetwork, userConfig: config, current_blockchain_tip_hash: reversedChaintipHash, + current_blockchain_tip_height: UInt32(chaintipHeight), keys_interface: keysInterface, + fee_estimator: feeEstimator, chain_monitor: chainMonitor, net_graph: networkGraph, + tx_broadcaster: broadcaster, logger: logger) let channelManager = channelManagerConstructor.channelManager let peerManager = channelManagerConstructor.peerManager - let tcpPeerHandler = channelManagerConstructor.getTCPPeerHandler() - if let netGraph = channelManagerConstructor.net_graph { - print("net graph available!") - } + let tcpPeerHandler = channelManagerConstructor.getTCPPeerHandler() + if let netGraph = channelManagerConstructor.net_graph { + print("net graph available!") + } struct Listener: BlockchainListener { private let channelManager: ChannelManager @@ -102,11 +113,15 @@ public class PolarIntegrationSample { let listener = Listener(channelManager: channelManager, chainMonitor: chainMonitor) - rpcInterface.registerListener(listener); + rpcInterface.registerListener(listener) async let monitor = try rpcInterface.monitorBlockchain() - channelManagerConstructor.chain_sync_completed(persister: channelManagerAndNetworkGraphPersisterAndEventHandler, scorer: multiThreadedScorer) + channelManagerConstructor.chain_sync_completed( + persister: channelManagerAndNetworkGraphPersisterAndEventHandler, scorer: multiThreadedScorer) - guard let lndPubkey = PolarIntegrationSample.hexStringToBytes(hexString: PolarIntegrationSample.POLAR_LND_PEER_PUBKEY_HEX) else { + guard + let lndPubkey = PolarIntegrationSample.hexStringToBytes( + hexString: PolarIntegrationSample.POLAR_LND_PEER_PUBKEY_HEX) + else { throw TestFlowExceptions.hexParsingError } let connectionSuccess = tcpPeerHandler.connect(address: "127.0.0.1", port: 9735, theirNodeId: lndPubkey) @@ -115,10 +130,12 @@ public class PolarIntegrationSample { try await Task.sleep(nanoseconds: 1_000_000_000) let connectedPeers = peerManager.get_peer_node_ids() - let channelValue: UInt64 = 1_300_000 // 1.3 million satoshis, or 0.013 BTC + let channelValue: UInt64 = 1_300_000 // 1.3 million satoshis, or 0.013 BTC let channelValueBtcString = "0.013" - let reserveAmount: UInt64 = 1000 // a thousand satoshis rserve - let channelOpenResult = channelManager.create_channel(their_network_key: lndPubkey, channel_value_satoshis: channelValue, push_msat: reserveAmount, user_channel_id: 42, override_config: config) + let reserveAmount: UInt64 = 1000 // a thousand satoshis rserve + let channelOpenResult = channelManager.create_channel( + their_network_key: lndPubkey, channel_value_satoshis: channelValue, push_msat: reserveAmount, + user_channel_id: 42, override_config: config) if let channelOpenError = channelOpenResult.getError() { print("error type: \(channelOpenError.getValueType())") @@ -136,7 +153,8 @@ public class PolarIntegrationSample { return } - let managerEvents = await channelManagerAndNetworkGraphPersisterAndEventHandler.getManagerEvents(expectedCount: 1) + let managerEvents = await channelManagerAndNetworkGraphPersisterAndEventHandler.getManagerEvents( + expectedCount: 1) let managerEvent = managerEvents[0] @@ -156,8 +174,8 @@ public class PolarIntegrationSample { guard let fundingReadyEvent = managerEvent.getValueAsFundingGenerationReady() else { throw TestFlowExceptions.unexpectedChannelManagerEventType } - let fundingOutputScript = fundingReadyEvent.getOutput_script(); - let temporaryChannelId = fundingReadyEvent.getTemporary_channel_id(); + let fundingOutputScript = fundingReadyEvent.getOutput_script() + let temporaryChannelId = fundingReadyEvent.getTemporary_channel_id() let outputScriptDetails = try await rpcInterface.decodeScript(script: fundingOutputScript) guard let outputScriptAddresses = outputScriptDetails["addresses"] as? [String] else { @@ -166,16 +184,19 @@ public class PolarIntegrationSample { guard let outputAddress = outputScriptAddresses.first else { throw TestFlowExceptions.outputScriptMissingAddresses } - let fundingTxid = try await rpcInterface.sendMoney(destinationAddress: outputAddress, amount: channelValueBtcString) + let fundingTxid = try await rpcInterface.sendMoney( + destinationAddress: outputAddress, amount: channelValueBtcString) let fundingTransaction = try await rpcInterface.getTransaction(hash: fundingTxid) - channelManager.funding_transaction_generated(temporary_channel_id: temporaryChannelId, counterparty_node_id: lndPubkey, funding_transaction: fundingTransaction) + channelManager.funding_transaction_generated( + temporary_channel_id: temporaryChannelId, counterparty_node_id: lndPubkey, + funding_transaction: fundingTransaction) // let's add a couple confirmations let fakeAddress = try await self.getBogusAddress(rpcInterface: rpcInterface) try await rpcInterface.mineBlocks(number: 6, coinbaseDestinationAddress: fakeAddress) var usableChannels = [ChannelDetails]() - while (usableChannels.isEmpty) { + while usableChannels.isEmpty { usableChannels = channelManager.list_usable_channels() // sleep for 100ms try await Task.sleep(nanoseconds: 0_100_000_000) @@ -191,10 +212,10 @@ public class PolarIntegrationSample { throw TestFlowExceptions.invoiceParsingError(invoiceResult.getError()!) } - // let's not pay any invoices - // channelManagerConstructor.interrupt(tcpPeerHandler: tcpPeerHandler) - // return - + // let's not pay any invoices + // channelManagerConstructor.interrupt(tcpPeerHandler: tcpPeerHandler) + // return + let invoicePaymentResult = invoicePayer.pay_invoice(invoice: invoice) do { @@ -208,23 +229,26 @@ public class PolarIntegrationSample { guard let paymentPathSuccessful = paymentPathSuccessfulEvent.getValueAsPaymentPathSuccessful() else { throw TestFlowExceptions.paymentPathUnsuccessful } - print("sent payment \(paymentSent.getPayment_id()) with fee \(paymentSent.getFee_paid_msat().getValue()) via \(paymentPathSuccessful.getPath().map { h in h.get_short_channel_id() })") + print( + "sent payment \(paymentSent.getPayment_id()) with fee \(paymentSent.getFee_paid_msat().getValue()) via \(paymentPathSuccessful.getPath().map { h in h.get_short_channel_id() })" + ) } - - for i in 0...600 { - // sleep for 100ms - try await Task.sleep(nanoseconds: 0_100_000_000) - } - - print(channelManagerConstructor.peerManager.get_peer_node_ids()) + + for i in 0...600 { + // sleep for 100ms + try await Task.sleep(nanoseconds: 0_100_000_000) + } + + print(channelManagerConstructor.peerManager.get_peer_node_ids()) } private func ascertainSpareMoney(rpcInterface: RegtestBlockchainManager) async throws { let availableWallets = try await rpcInterface.listAvailableWallets() - let walletNames = (availableWallets["wallets"] as! [[String: Any]]).map { dictionary -> String in - dictionary["name"] as! String - } + let walletNames = (availableWallets["wallets"] as! [[String: Any]]) + .map { dictionary -> String in + dictionary["name"] as! String + } if !walletNames.contains(PolarIntegrationSample.WALLET_NAME) { // if a wallet is already loaded, this will load it also @@ -290,34 +314,40 @@ public class PolarIntegrationSample { } } } - - class MuteBroadcaster: BroadcasterInterface { - override func broadcast_transaction(tx: [UInt8]) { - // do nothing - } - } + + class MuteBroadcaster: BroadcasterInterface { + override func broadcast_transaction(tx: [UInt8]) { + // do nothing + } + } class PolarLogger: Logger { override func log(record: Record) { - print("\nRLPolarLog (\(record.get_level())): \(record.get_file()):\(record.get_line()):\n> \(record.get_args())\n") + print( + "\nRLPolarLog (\(record.get_level())): \(record.get_file()):\(record.get_line()):\n> \(record.get_args())\n" + ) + } + } + + class MuteLogger: Logger { + override func log(record: Record) { + // do nothing } } - - class MuteLogger: Logger { - override func log(record: Record) { - // do nothing - } - } class PolarChannelMonitorPersister: Persist { - override func persist_new_channel(channel_id: OutPoint, data: ChannelMonitor, update_id: MonitorUpdateId) -> Result_NoneChannelMonitorUpdateErrZ { + override func persist_new_channel(channel_id: OutPoint, data: ChannelMonitor, update_id: MonitorUpdateId) + -> Result_NoneChannelMonitorUpdateErrZ + { let idBytes: [UInt8] = channel_id.write() let monitorBytes: [UInt8] = data.write() return Result_NoneChannelMonitorUpdateErrZ.ok() } - override func update_persisted_channel(channel_id: OutPoint, update: ChannelMonitorUpdate, data: ChannelMonitor, update_id: MonitorUpdateId) -> Result_NoneChannelMonitorUpdateErrZ { + override func update_persisted_channel( + channel_id: OutPoint, update: ChannelMonitorUpdate, data: ChannelMonitor, update_id: MonitorUpdateId + ) -> Result_NoneChannelMonitorUpdateErrZ { let idBytes: [UInt8] = channel_id.write() let monitorBytes: [UInt8] = data.write() return Result_NoneChannelMonitorUpdateErrZ.ok() @@ -399,148 +429,179 @@ public class PolarIntegrationSample { } } - - public class MultiPeerSimulator { - - private var rpcInterface: BlockchainObserver! - - private var keysManager: KeysManager! - private var keysInterface: KeysInterface! - private var config: UserConfig! - private var networkGraph: NetworkGraph! - private var multiThreadedScorer: MultiThreadedLockableScore! - - private var feeEstimator: FeeEstimator! - private var broadcaster: BroadcasterInterface! - private var logger: Logger! - - public var channelManagerConstructor: ChannelManagerConstructor! - - public func simulateMultiplePeers() async throws { - print("bindings version: \(Bindings.get_ldk_swift_bindings_version())") - - /* + + public class MultiPeerSimulator { + + private var rpcInterface: BlockchainObserver! + + private var keysManager: KeysManager! + private var keysInterface: KeysInterface! + private var config: UserConfig! + private var networkGraph: NetworkGraph! + private var multiThreadedScorer: MultiThreadedLockableScore! + + private var feeEstimator: FeeEstimator! + private var broadcaster: BroadcasterInterface! + private var logger: Logger! + + public var channelManagerConstructor: ChannelManagerConstructor! + + public func simulateMultiplePeers() async throws { + print("bindings version: \(Bindings.get_ldk_swift_bindings_version())") + + /* let username = ProcessInfo.processInfo.environment["BITCOIN_REGTEST_RPC_USERNAME"] ?? "polaruser" // "alice" let password = ProcessInfo.processInfo.environment["BITCOIN_REGTEST_RPC_PASSWORD"] ?? "polarpass" // "DONT_USE_THIS_YOU_WILL_GET_ROBBED" rpcInterface = try BlockchainObserver(rpcProtocol: .http, rpcDomain: "localhost", rpcPort: 8332, rpcUsername: username, rpcPassword: password) try await rpcInterface.preloadMonitor(anchorHeight: .chaintip) */ - - var seed = [UInt8](repeating: 0, count: 32) - let status = SecRandomCopyBytes(kSecRandomDefault, seed.count, &seed) - - let timestamp_seconds = UInt64(NSDate().timeIntervalSince1970) - let timestamp_nanos = UInt32(truncating: NSNumber(value: timestamp_seconds * 1000 * 1000)) - keysManager = KeysManager(seed: seed, starting_time_secs: timestamp_seconds, starting_time_nanos: timestamp_nanos) - keysInterface = keysManager.as_KeysInterface() - logger = LDKTraitImplementations.MuteLogger() - - config = UserConfig() - let lightningNetwork = LDKNetwork_Bitcoin - /* + + var seed = [UInt8](repeating: 0, count: 32) + let status = SecRandomCopyBytes(kSecRandomDefault, seed.count, &seed) + + let timestamp_seconds = UInt64(NSDate().timeIntervalSince1970) + let timestamp_nanos = UInt32(truncating: NSNumber(value: timestamp_seconds * 1000 * 1000)) + keysManager = KeysManager( + seed: seed, starting_time_secs: timestamp_seconds, starting_time_nanos: timestamp_nanos) + keysInterface = keysManager.as_KeysInterface() + logger = LDKTraitImplementations.MuteLogger() + + config = UserConfig() + let lightningNetwork = LDKNetwork_Bitcoin + /* let genesisHash = try await rpcInterface.getBlockHash(height: 0) let reversedGenesisHash = [UInt8](genesisHash.reversed()) let chaintipHash = try await rpcInterface.getChaintipHash() let reversedChaintipHash = [UInt8](chaintipHash.reversed()) let chaintipHeight = try await rpcInterface.getChaintipHeight() */ - let reversedGenesisHashHex = "6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000" - let reversedGenesisHash = PolarIntegrationSample.hexStringToBytes(hexString: reversedGenesisHashHex)! - let chaintipHeight = 0 - let reversedChaintipHash = reversedGenesisHash - networkGraph = NetworkGraph(genesis_hash: reversedGenesisHash, logger: logger) - - print("Genesis hash reversed: \(PolarIntegrationSample.bytesToHexString(bytes: reversedGenesisHash))") - - let scoringParams = ProbabilisticScoringParameters() - let probabalisticScorer = ProbabilisticScorer(params: scoringParams, network_graph: networkGraph, logger: logger) - let score = probabalisticScorer.as_Score() - multiThreadedScorer = MultiThreadedLockableScore(score: score) - - feeEstimator = LDKTraitImplementations.PolarFeeEstimator() - // broadcaster = LDKTraitImplementations.PolarBroadcaster(rpcInterface: rpcInterface) - broadcaster = LDKTraitImplementations.MuteBroadcaster() - // logger = LDKTraitImplementations.PolarLogger() - - let channelMonitorPersister = LDKTraitImplementations.PolarChannelMonitorPersister() - let channelManagerAndNetworkGraphPersisterAndEventHandler = LDKTraitImplementations.PolarChannelManagerAndNetworkGraphPersisterAndEventHandler() - let chainMonitor = ChainMonitor(chain_source: Option_FilterZ(value: nil), broadcaster: broadcaster, logger: logger, feeest: feeEstimator, persister: channelMonitorPersister) - - channelManagerConstructor = ChannelManagerConstructor(network: lightningNetwork, userConfig: config, current_blockchain_tip_hash: reversedChaintipHash, current_blockchain_tip_height: UInt32(chaintipHeight), keys_interface: keysInterface, fee_estimator: feeEstimator, chain_monitor: chainMonitor, net_graph: networkGraph, tx_broadcaster: broadcaster, logger: logger) - let channelManager = channelManagerConstructor.channelManager - let peerManager = channelManagerConstructor.peerManager - let tcpPeerHandler = channelManagerConstructor.getTCPPeerHandler() - - channelManagerConstructor.chain_sync_completed(persister: channelManagerAndNetworkGraphPersisterAndEventHandler, scorer: multiThreadedScorer) - - let interPeerConnectionInterval = 0 - let pauseForNextPeer = { () async in - for _ in 0..<(interPeerConnectionInterval * 10) { - // sleep for 100ms - try! await Task.sleep(nanoseconds: 0_100_000_000) - } - } - - print("increasing log threshold") - Bindings.setLogThreshold(severity: .WARNING) - - do { - // bitrefill - print("connecting bitrefill") - let connectionResult = tcpPeerHandler.connect(address: "52.50.244.44", port: 9735, theirNodeId: PolarIntegrationSample.hexStringToBytes(hexString: "030c3f19d742ca294a55c00376b3b355c3c90d61c6b6b39554dbc7ac19b141c14f")!) - print("bitrefill connection success: \(connectionResult)") - await pauseForNextPeer() - } - - do { - // River - print("connecting river") - let connectionResult = tcpPeerHandler.connect(address: "104.196.249.140", port: 9735, theirNodeId: PolarIntegrationSample.hexStringToBytes(hexString: "03037dc08e9ac63b82581f79b662a4d0ceca8a8ca162b1af3551595b8f2d97b70a")!) - print("river connection success: \(connectionResult)") - await pauseForNextPeer() - } - - do { - // Acinq - print("connecting acinq") - let connectionResult = tcpPeerHandler.connect(address: "3.33.236.230", port: 9735, theirNodeId: PolarIntegrationSample.hexStringToBytes(hexString: "03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f")!) - print("acinq connection success: \(connectionResult)") - await pauseForNextPeer() - } - - do { - // Kraken - print("connecting kraken") - let connectionResult = tcpPeerHandler.connect(address: "52.13.118.208", port: 9735, theirNodeId: PolarIntegrationSample.hexStringToBytes(hexString: "02f1a8c87607f415c8f22c00593002775941dea48869ce23096af27b0cfdcc0b69")!) - print("kraken connection success: \(connectionResult)") - await pauseForNextPeer() - } - - do { - // Matt - print("connecting matt") - let connectionResult = tcpPeerHandler.connect(address: "69.59.18.80", port: 9735, theirNodeId: PolarIntegrationSample.hexStringToBytes(hexString: "03db10aa09ff04d3568b0621750794063df401e6853c79a21a83e1a3f3b5bfb0c8")!) - print("matt connection success: \(connectionResult)") - await pauseForNextPeer() - } - - do { - // Fold - print("connecting fold") - let connectionResult = tcpPeerHandler.connect(address: "35.238.153.25", port: 9735, theirNodeId: PolarIntegrationSample.hexStringToBytes(hexString: "02816caed43171d3c9854e3b0ab2cf0c42be086ff1bd4005acc2a5f7db70d83774")!) - print("fold connection success: \(connectionResult)") - await pauseForNextPeer() - } - - do { - // wallet of satoshi - print("connecting wallet of satoshi") - let connectionResult = tcpPeerHandler.connect(address: "170.75.163.209", port: 9735, theirNodeId: PolarIntegrationSample.hexStringToBytes(hexString: "035e4ff418fc8b5554c5d9eea66396c227bd429a3251c8cbc711002ba215bfc226")!) - print("wallet of satoshi connection success: \(connectionResult)") - await pauseForNextPeer() - } - - /* + let reversedGenesisHashHex = "6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000" + let reversedGenesisHash = PolarIntegrationSample.hexStringToBytes(hexString: reversedGenesisHashHex)! + let chaintipHeight = 0 + let reversedChaintipHash = reversedGenesisHash + networkGraph = NetworkGraph(genesis_hash: reversedGenesisHash, logger: logger) + + print("Genesis hash reversed: \(PolarIntegrationSample.bytesToHexString(bytes: reversedGenesisHash))") + + let scoringParams = ProbabilisticScoringParameters() + let probabalisticScorer = ProbabilisticScorer( + params: scoringParams, network_graph: networkGraph, logger: logger) + let score = probabalisticScorer.as_Score() + multiThreadedScorer = MultiThreadedLockableScore(score: score) + + feeEstimator = LDKTraitImplementations.PolarFeeEstimator() + // broadcaster = LDKTraitImplementations.PolarBroadcaster(rpcInterface: rpcInterface) + broadcaster = LDKTraitImplementations.MuteBroadcaster() + // logger = LDKTraitImplementations.PolarLogger() + + let channelMonitorPersister = LDKTraitImplementations.PolarChannelMonitorPersister() + let channelManagerAndNetworkGraphPersisterAndEventHandler = + LDKTraitImplementations.PolarChannelManagerAndNetworkGraphPersisterAndEventHandler() + let chainMonitor = ChainMonitor( + chain_source: Option_FilterZ(value: nil), broadcaster: broadcaster, logger: logger, + feeest: feeEstimator, persister: channelMonitorPersister) + + channelManagerConstructor = ChannelManagerConstructor( + network: lightningNetwork, userConfig: config, current_blockchain_tip_hash: reversedChaintipHash, + current_blockchain_tip_height: UInt32(chaintipHeight), keys_interface: keysInterface, + fee_estimator: feeEstimator, chain_monitor: chainMonitor, net_graph: networkGraph, + tx_broadcaster: broadcaster, logger: logger) + let channelManager = channelManagerConstructor.channelManager + let peerManager = channelManagerConstructor.peerManager + let tcpPeerHandler = channelManagerConstructor.getTCPPeerHandler() + + channelManagerConstructor.chain_sync_completed( + persister: channelManagerAndNetworkGraphPersisterAndEventHandler, scorer: multiThreadedScorer) + + let interPeerConnectionInterval = 0 + let pauseForNextPeer = { () async in + for _ in 0..<(interPeerConnectionInterval * 10) { + // sleep for 100ms + try! await Task.sleep(nanoseconds: 0_100_000_000) + } + } + + print("increasing log threshold") + Bindings.setLogThreshold(severity: .WARNING) + + do { + // bitrefill + print("connecting bitrefill") + let connectionResult = tcpPeerHandler.connect( + address: "52.50.244.44", port: 9735, + theirNodeId: PolarIntegrationSample.hexStringToBytes( + hexString: "030c3f19d742ca294a55c00376b3b355c3c90d61c6b6b39554dbc7ac19b141c14f")!) + print("bitrefill connection success: \(connectionResult)") + await pauseForNextPeer() + } + + do { + // River + print("connecting river") + let connectionResult = tcpPeerHandler.connect( + address: "104.196.249.140", port: 9735, + theirNodeId: PolarIntegrationSample.hexStringToBytes( + hexString: "03037dc08e9ac63b82581f79b662a4d0ceca8a8ca162b1af3551595b8f2d97b70a")!) + print("river connection success: \(connectionResult)") + await pauseForNextPeer() + } + + do { + // Acinq + print("connecting acinq") + let connectionResult = tcpPeerHandler.connect( + address: "3.33.236.230", port: 9735, + theirNodeId: PolarIntegrationSample.hexStringToBytes( + hexString: "03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f")!) + print("acinq connection success: \(connectionResult)") + await pauseForNextPeer() + } + + do { + // Kraken + print("connecting kraken") + let connectionResult = tcpPeerHandler.connect( + address: "52.13.118.208", port: 9735, + theirNodeId: PolarIntegrationSample.hexStringToBytes( + hexString: "02f1a8c87607f415c8f22c00593002775941dea48869ce23096af27b0cfdcc0b69")!) + print("kraken connection success: \(connectionResult)") + await pauseForNextPeer() + } + + do { + // Matt + print("connecting matt") + let connectionResult = tcpPeerHandler.connect( + address: "69.59.18.80", port: 9735, + theirNodeId: PolarIntegrationSample.hexStringToBytes( + hexString: "03db10aa09ff04d3568b0621750794063df401e6853c79a21a83e1a3f3b5bfb0c8")!) + print("matt connection success: \(connectionResult)") + await pauseForNextPeer() + } + + do { + // Fold + print("connecting fold") + let connectionResult = tcpPeerHandler.connect( + address: "35.238.153.25", port: 9735, + theirNodeId: PolarIntegrationSample.hexStringToBytes( + hexString: "02816caed43171d3c9854e3b0ab2cf0c42be086ff1bd4005acc2a5f7db70d83774")!) + print("fold connection success: \(connectionResult)") + await pauseForNextPeer() + } + + do { + // wallet of satoshi + print("connecting wallet of satoshi") + let connectionResult = tcpPeerHandler.connect( + address: "170.75.163.209", port: 9735, + theirNodeId: PolarIntegrationSample.hexStringToBytes( + hexString: "035e4ff418fc8b5554c5d9eea66396c227bd429a3251c8cbc711002ba215bfc226")!) + print("wallet of satoshi connection success: \(connectionResult)") + await pauseForNextPeer() + } + + /* struct Listener: BlockchainListener { private let channelManager: ChannelManager private let chainMonitor: ChainMonitor @@ -566,92 +627,94 @@ public class PolarIntegrationSample { rpcInterface.registerListener(listener); async let monitor = try rpcInterface.monitorBlockchain() */ - - // channelManagerConstructor.chain_sync_completed(persister: channelManagerAndNetworkGraphPersisterAndEventHandler, scorer: multiThreadedScorer) - } - - } - - public class RapidGossipSyncTester { - - public func testRapidGossipSync() async throws { - // first, download the gossip data - print("Sending rapid gossip sync request…"); - var request = URLRequest(url: URL(string: "https://rapidsync.lightningdevkit.org/snapshot/0")!) - request.httpMethod = "GET" - - let startA = DispatchTime.now() - - // DOWNLOAD DATA - let (data, _) = try await URLSession.shared.data(for: request) - - let finishA = DispatchTime.now() - let elapsedA = Double(finishA.uptimeNanoseconds-startA.uptimeNanoseconds)/1_000_000_000 - print("Received rapid gossip sync response: \(data.count) bytes! Time: \(elapsedA)s"); - - let reversedGenesisHashHex = "6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000" - let reversedGenesisHash = PolarIntegrationSample.hexStringToBytes(hexString: reversedGenesisHashHex)! - - let logger = LDKTraitImplementations.PolarLogger() - let networkGraph = NetworkGraph(genesis_hash: reversedGenesisHash, logger: logger) - let rapidSync = RapidGossipSync(network_graph: networkGraph) - - let gossipDataRaw = [UInt8](data) - print("Applying rapid sync data…") - let startB = DispatchTime.now() - - // APPLY DATA - let timestamp = rapidSync.update_network_graph(update_data: gossipDataRaw) - - if let error = timestamp.getError() { - print("error! type: \(error.getValueType())") - let specificError = error.getValueAsLightningError() - print("details: \(specificError?.get_err())") - } - let finishB = DispatchTime.now() - let elapsedB = Double(finishB.uptimeNanoseconds-startB.uptimeNanoseconds)/1_000_000_000 - print("Applied rapid sync data: \(timestamp.getValue())! Time: \(elapsedB)s") - - print("Measuring graph size…") - let startC = DispatchTime.now() - let graphBytes = networkGraph.write() - let finishC = DispatchTime.now() - let elapsedC = Double(finishC.uptimeNanoseconds-startC.uptimeNanoseconds)/1_000_000_000 - print("Network graph size: \(graphBytes.count)! Time: \(elapsedC)s") - - - // networkGraph.read_only().get_addresses(pubkey: <#T##[UInt8]#>) - - - - - let scoringParams = ProbabilisticScoringParameters() - let scorer = ProbabilisticScorer(params: scoringParams, network_graph: networkGraph, logger: logger) - let score = scorer.as_Score() - // let multiThreadedScorer = MultiThreadedLockableScore(score: score) - - - let payerPubkey = hexStringToBytes(hexString: "0242a4ae0c5bef18048fbecf995094b74bfb0f7391418d71ed394784373f41e4f3")! - let recipientPubkey = hexStringToBytes(hexString: "03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f")! - - let paymentParameters = PaymentParameters(payee_pubkey: recipientPubkey) - let routeParameters = RouteParameters(payment_params_arg: paymentParameters, final_value_msat_arg: 500, final_cltv_expiry_delta_arg: 3) - - print("STEP A") - -// let firstHops: [ChannelDetails]? = nil -// print("STEP B") -// let foundRoute = router.find_route(payer: payerPubkey, route_params: routeParameters, payment_hash: nil, first_hops: firstHops, scorer: score) -// print("found route: \(foundRoute)") - } - } - private class func bytesToHexString(bytes: [UInt8]) -> String { - let format = "%02hhx" // "%02hhX" (uppercase) - return bytes.map { - String(format: format, $0) + // channelManagerConstructor.chain_sync_completed(persister: channelManagerAndNetworkGraphPersisterAndEventHandler, scorer: multiThreadedScorer) } - .joined() + + } + + public class RapidGossipSyncTester { + + public func testRapidGossipSync() async throws { + // first, download the gossip data + print("Sending rapid gossip sync request…") + var request = URLRequest(url: URL(string: "https://rapidsync.lightningdevkit.org/snapshot/0")!) + request.httpMethod = "GET" + + let startA = DispatchTime.now() + + // DOWNLOAD DATA + let (data, _) = try await URLSession.shared.data(for: request) + + let finishA = DispatchTime.now() + let elapsedA = Double(finishA.uptimeNanoseconds - startA.uptimeNanoseconds) / 1_000_000_000 + print("Received rapid gossip sync response: \(data.count) bytes! Time: \(elapsedA)s") + + let reversedGenesisHashHex = "6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000" + let reversedGenesisHash = PolarIntegrationSample.hexStringToBytes(hexString: reversedGenesisHashHex)! + + let logger = LDKTraitImplementations.PolarLogger() + let networkGraph = NetworkGraph(genesis_hash: reversedGenesisHash, logger: logger) + let rapidSync = RapidGossipSync(network_graph: networkGraph) + + let gossipDataRaw = [UInt8](data) + print("Applying rapid sync data…") + let startB = DispatchTime.now() + + // APPLY DATA + let timestamp = rapidSync.update_network_graph(update_data: gossipDataRaw) + + if let error = timestamp.getError() { + print("error! type: \(error.getValueType())") + let specificError = error.getValueAsLightningError() + print("details: \(specificError?.get_err())") + } + let finishB = DispatchTime.now() + let elapsedB = Double(finishB.uptimeNanoseconds - startB.uptimeNanoseconds) / 1_000_000_000 + print("Applied rapid sync data: \(timestamp.getValue())! Time: \(elapsedB)s") + + print("Measuring graph size…") + let startC = DispatchTime.now() + let graphBytes = networkGraph.write() + let finishC = DispatchTime.now() + let elapsedC = Double(finishC.uptimeNanoseconds - startC.uptimeNanoseconds) / 1_000_000_000 + print("Network graph size: \(graphBytes.count)! Time: \(elapsedC)s") + + + // networkGraph.read_only().get_addresses(pubkey: <#T##[UInt8]#>) + + + let scoringParams = ProbabilisticScoringParameters() + let scorer = ProbabilisticScorer(params: scoringParams, network_graph: networkGraph, logger: logger) + let score = scorer.as_Score() + // let multiThreadedScorer = MultiThreadedLockableScore(score: score) + + + let payerPubkey = hexStringToBytes( + hexString: "0242a4ae0c5bef18048fbecf995094b74bfb0f7391418d71ed394784373f41e4f3")! + let recipientPubkey = hexStringToBytes( + hexString: "03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f")! + + let paymentParameters = PaymentParameters(payee_pubkey: recipientPubkey) + let routeParameters = RouteParameters( + payment_params_arg: paymentParameters, final_value_msat_arg: 500, final_cltv_expiry_delta_arg: 3) + + print("STEP A") + + // let firstHops: [ChannelDetails]? = nil + // print("STEP B") + // let foundRoute = router.find_route(payer: payerPubkey, route_params: routeParameters, payment_hash: nil, first_hops: firstHops, scorer: score) + // print("found route: \(foundRoute)") + } + } + + private class func bytesToHexString(bytes: [UInt8]) -> String { + let format = "%02hhx" // "%02hhX" (uppercase) + return + bytes.map { + String(format: format, $0) + } + .joined() } private class func hexStringToBytes(hexString: String) -> [UInt8]? { @@ -676,8 +739,6 @@ public class PolarIntegrationSample { } return newData } - - -} +} diff --git a/xcode/LDKFramework/DirectlyLinkedBindingsAppTests/polar/RegtestBlockchainManager.swift b/xcode/LDKFramework/DirectlyLinkedBindingsAppTests/polar/RegtestBlockchainManager.swift index 0d141452..93e6943a 100644 --- a/xcode/LDKFramework/DirectlyLinkedBindingsAppTests/polar/RegtestBlockchainManager.swift +++ b/xcode/LDKFramework/DirectlyLinkedBindingsAppTests/polar/RegtestBlockchainManager.swift @@ -12,7 +12,7 @@ import Foundation @available(iOS 15.0, *) class RegtestBlockchainManager: BlockchainObserver { - /** + /** Mine regtest blocks - Parameters: - number: The number of blocks to mine @@ -20,23 +20,25 @@ class RegtestBlockchainManager: BlockchainObserver { - Returns: Array of the mined blocks' hashes - Throws: If the RPC connection fails or the call results in an error */ - public func mineBlocks(number: Int64, coinbaseDestinationAddress: String) async throws -> [String] { - let response = try await self.callRpcMethod(method: "generatetoaddress", params: [ - "nblocks": number, - "address": coinbaseDestinationAddress - ]) - let result = response["result"] as! [String] - return result - } + public func mineBlocks(number: Int64, coinbaseDestinationAddress: String) async throws -> [String] { + let response = try await self.callRpcMethod( + method: "generatetoaddress", + params: [ + "nblocks": number, + "address": coinbaseDestinationAddress, + ]) + let result = response["result"] as! [String] + return result + } - /** + /** Invalidate or un-mine a block - Parameter hash: The block hash hex to invalidate - Returns: - Throws: */ - public func unmineBlock(hash: String) async throws { - let response = try await self.callRpcMethod(method: "invalidateblock", params: [hash]) - } + public func unmineBlock(hash: String) async throws { + let response = try await self.callRpcMethod(method: "invalidateblock", params: [hash]) + } }