Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 14 additions & 8 deletions LightningKit.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
/* Begin PBXBuildFile section */
2FA5DC8A66918200D42EA1EE /* MessageResponseStream.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FA5D6FACBD18084B3922C1B /* MessageResponseStream.swift */; };
D059940C2403C9DE0096AC17 /* Lndmobile.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D059940B2403C9DE0096AC17 /* Lndmobile.framework */; };
D09B6F4F240CD100009A883F /* GRPC in Frameworks */ = {isa = PBXBuildFile; productRef = D3A733C923FA80A2005DAC30 /* GRPC */; };
D09B6F56240D3332009A883F /* RpcReadyCallback.swift in Sources */ = {isa = PBXBuildFile; fileRef = D09B6F55240D3332009A883F /* RpcReadyCallback.swift */; };
D3A7339423FA7D94005DAC30 /* LightningKit.h in Headers */ = {isa = PBXBuildFile; fileRef = D3A7339223FA7D94005DAC30 /* LightningKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
D3A733B123FA7E98005DAC30 /* RemoteLnd.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3A7339B23FA7E98005DAC30 /* RemoteLnd.swift */; };
D3A733B223FA7E98005DAC30 /* WalletUnlocker.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3A7339C23FA7E98005DAC30 /* WalletUnlocker.swift */; };
Expand Down Expand Up @@ -36,6 +38,8 @@
2FA5D6FACBD18084B3922C1B /* MessageResponseStream.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageResponseStream.swift; sourceTree = "<group>"; };
D059940B2403C9DE0096AC17 /* Lndmobile.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Lndmobile.framework; path = Carthage/Build/iOS/Lndmobile.framework; sourceTree = SOURCE_ROOT; };
D05C723F70B6969736B4E735 /* Pods_LightningKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_LightningKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
D09B6F50240CD306009A883F /* RxBlocking.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = RxBlocking.framework; sourceTree = BUILT_PRODUCTS_DIR; };
D09B6F55240D3332009A883F /* RpcReadyCallback.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RpcReadyCallback.swift; sourceTree = "<group>"; };
D3A7338F23FA7D94005DAC30 /* LightningKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = LightningKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
D3A7339223FA7D94005DAC30 /* LightningKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LightningKit.h; sourceTree = "<group>"; };
D3A7339323FA7D94005DAC30 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
Expand Down Expand Up @@ -67,6 +71,7 @@
files = (
D059940C2403C9DE0096AC17 /* Lndmobile.framework in Frameworks */,
E989B9E19F21A2518A31A210 /* Pods_LightningKit.framework in Frameworks */,
D09B6F4F240CD100009A883F /* GRPC in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -161,17 +166,19 @@
D3A733AC23FA7E98005DAC30 /* LndMobileCallbacks */ = {
isa = PBXGroup;
children = (
D3A733AE23FA7E98005DAC30 /* LndMobileCallbackError.swift */,
D3A733AF23FA7E98005DAC30 /* VoidResponseCallback.swift */,
D3A733AD23FA7E98005DAC30 /* MessageResponseCallback.swift */,
2FA5D6FACBD18084B3922C1B /* MessageResponseStream.swift */,
D3A733AE23FA7E98005DAC30 /* LndMobileCallbackError.swift */,
D09B6F55240D3332009A883F /* RpcReadyCallback.swift */,
);
path = LndMobileCallbacks;
sourceTree = "<group>";
};
D3A733C823FA80A2005DAC30 /* Frameworks */ = {
isa = PBXGroup;
children = (
D09B6F50240CD306009A883F /* RxBlocking.framework */,
D05C723F70B6969736B4E735 /* Pods_LightningKit.framework */,
);
name = Frameworks;
Expand Down Expand Up @@ -207,7 +214,7 @@
);
name = LightningKit;
packageProductDependencies = (
D3A733C923FA80A2005DAC30 /* XCSwiftPackageProductDependency */,
D3A733C923FA80A2005DAC30 /* GRPC */,
);
productName = LightningKit;
productReference = D3A7338F23FA7D94005DAC30 /* LightningKit.framework */;
Expand Down Expand Up @@ -238,7 +245,7 @@
);
mainGroup = D3A7338523FA7D94005DAC30;
packageReferences = (
D3A733C523FA7FD1005DAC30 /* XCRemoteSwiftPackageReference */,
D3A733C523FA7FD1005DAC30 /* XCRemoteSwiftPackageReference "grpc-swift" */,
);
productRefGroup = D3A7339023FA7D94005DAC30 /* Products */;
projectDirPath = "";
Expand Down Expand Up @@ -293,6 +300,7 @@
D3A733B723FA7E98005DAC30 /* rpc.pb.swift in Sources */,
D3A733B423FA7E98005DAC30 /* ConnectivityManager.swift in Sources */,
D3A733BC23FA7E98005DAC30 /* FileManager.swift in Sources */,
D09B6F56240D3332009A883F /* RpcReadyCallback.swift in Sources */,
D3A733C023FA7E98005DAC30 /* LndMobileCallbackError.swift in Sources */,
D3A733B923FA7E98005DAC30 /* RpcCredentials.swift in Sources */,
D3A733BB23FA7E98005DAC30 /* Data.swift in Sources */,
Expand Down Expand Up @@ -451,7 +459,6 @@
"$(PROJECT_DIR)/Carthage/Build/iOS",
);
INFOPLIST_FILE = LightningKit/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
Expand Down Expand Up @@ -484,7 +491,6 @@
"$(PROJECT_DIR)/Carthage/Build/iOS",
);
INFOPLIST_FILE = LightningKit/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
Expand Down Expand Up @@ -523,7 +529,7 @@
/* End XCConfigurationList section */

/* Begin XCRemoteSwiftPackageReference section */
D3A733C523FA7FD1005DAC30 /* XCRemoteSwiftPackageReference */ = {
D3A733C523FA7FD1005DAC30 /* XCRemoteSwiftPackageReference "grpc-swift" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "git@github.com:grpc/grpc-swift.git";
requirement = {
Expand All @@ -534,9 +540,9 @@
/* End XCRemoteSwiftPackageReference section */

/* Begin XCSwiftPackageProductDependency section */
D3A733C923FA80A2005DAC30 /* XCSwiftPackageProductDependency */ = {
D3A733C923FA80A2005DAC30 /* GRPC */ = {
isa = XCSwiftPackageProductDependency;
package = D3A733C523FA7FD1005DAC30 /* XCRemoteSwiftPackageReference */;
package = D3A733C523FA7FD1005DAC30 /* XCRemoteSwiftPackageReference "grpc-swift" */;
productName = GRPC;
};
/* End XCSwiftPackageProductDependency section */
Expand Down
3 changes: 3 additions & 0 deletions LightningKit/ILndNode.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ protocol ILndNode {
var pendingChannelsSingle: Single<Lnrpc_PendingChannelsResponse> { get }
var paymentsSingle: Single<Lnrpc_ListPaymentsResponse> { get }
var transactionsSingle: Single<Lnrpc_TransactionDetails> { get }
var newSeedSingle: Single<Lnrpc_GenSeedResponse> { get }

func invoicesSingle(request: Lnrpc_ListInvoiceRequest) -> Single<Lnrpc_ListInvoiceResponse>
func paySingle(request: Lnrpc_SendRequest) -> Single<Lnrpc_SendResponse>
Expand All @@ -27,4 +28,6 @@ protocol ILndNode {
func openChannelSingle(request: Lnrpc_OpenChannelRequest) -> Observable<Lnrpc_OpenStatusUpdate>
func closeChannelSingle(request: Lnrpc_CloseChannelRequest) throws -> Observable<Lnrpc_CloseStatusUpdate>
func connectSingle(request: Lnrpc_ConnectPeerRequest) -> Single<Lnrpc_ConnectPeerResponse>

func initWalletSingle(request: Lnrpc_InitWalletRequest) -> Single<Void>
}
61 changes: 29 additions & 32 deletions LightningKit/Kit.swift
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,10 @@ public class Kit {
lndNode.transactionsSingle
}

public var newSeedSingle: Single<Lnrpc_GenSeedResponse> {
lndNode.newSeedSingle
}

public func invoicesSingle(pendingOnly: Bool = false, offset: UInt64 = 0, limit: UInt64 = 1000, reversed: Bool = false) -> Single<Lnrpc_ListInvoiceResponse> {
var request = Lnrpc_ListInvoiceRequest()
request.pendingOnly = pendingOnly
Expand Down Expand Up @@ -112,13 +116,20 @@ public class Kit {
return lndNode.addInvoiceSingle(invoice: invoice)
}

public func unlockWalletSingle(password: Data) -> Single<Void> {
public func unlockWalletSingle(password: String) -> Single<Void> {
var request = Lnrpc_UnlockWalletRequest()
request.walletPassword = password

request.walletPassword = Data(Array(password.utf8))
return lndNode.unlockWalletSingle(request: request)
}


public func unlockWallet(password: String) throws {
var request = Lnrpc_UnlockWalletRequest()
request.walletPassword = Data(Array(password.utf8))

_ = try lndNode.unlockWalletSingle(request: request).toBlocking().first()
}

public func decodeSingle(paymentRequest: String) -> Single<Lnrpc_PayReq> {
var request = Lnrpc_PayReqString()
request.payReq = paymentRequest
Expand Down Expand Up @@ -180,34 +191,13 @@ public class Kit {
return try lndNode.closeChannelSingle(request: request)
}

// LocalLnd methods

public func start(password: String) {
guard let localNode = lndNode as? LocalLnd else {
return
}

localNode.startAndUnlock(password: password)
}

public func create(password: String) -> Single<[String]> {
guard let localNode = lndNode as? LocalLnd else {
return Single.error(KitErrors.cannotInitRemoteNode)
}
public func initWalletSingle(words: [String], password: String, recoveryWindow: Int32 = 100) -> Single<Void> {
var request = Lnrpc_InitWalletRequest()
request.cipherSeedMnemonic = words
request.walletPassword = Data(Array(password.utf8))
request.recoveryWindow = recoveryWindow

return localNode.start().flatMap {
localNode.createWalletSingle(password: password)
}
}

public func restore(words: [String], password: String) -> Single<Void> {
guard let localNode = lndNode as? LocalLnd else {
return Single.error(KitErrors.cannotInitRemoteNode)
}

return localNode.start().flatMap {
localNode.restoreWalletSingle(words: words, password: password)
}
return lndNode.initWalletSingle(request: request)
}

// Private methods
Expand Down Expand Up @@ -244,8 +234,15 @@ public extension Kit {
}

static func local() throws -> Kit {
let localLnd = LocalLnd(filesDir: try FileManager.default.walletDirectory().path)
let localLnd = try LocalLnd(filesDir: try FileManager.default.walletDirectory().path)

return Kit(lndNode: localLnd)
}

static func clearLocalNodeData() throws {
let fileManager = FileManager.default
let lndDirectoryPath = try fileManager.walletDirectory().path

try fileManager.removeItem(atPath: lndDirectoryPath)
}
}
22 changes: 22 additions & 0 deletions LightningKit/Local/LndMobileCallbacks/RpcReadyCallback.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import Lndmobile

class RpcReadyCallback: NSObject, LndmobileCallbackProtocol {
private weak var delegate: RpcReadyCallbackDelegate?

init(_ delegate: RpcReadyCallbackDelegate?) {
self.delegate = delegate
}

func onError(_ error: Error?) {
delegate?.RpcServerStartFailed(error: error ?? LndMobileCallbackError.unknownError)
}

func onResponse(_ response: Data?) {
delegate?.rpcReady()
}
}

protocol RpcReadyCallbackDelegate: AnyObject {
func RpcServerStartFailed(error: Error)
func rpcReady()
}
94 changes: 29 additions & 65 deletions LightningKit/Local/LocalLnd.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Lndmobile
import RxSwift
import RxBlocking
import SwiftProtobuf

class LocalLnd: ILndNode {
Expand Down Expand Up @@ -116,23 +117,32 @@ class LocalLnd: ILndNode {
var transactionsSingle: Single<Lnrpc_TransactionDetails> {
Single.create { emitter in
LndmobileGetTransactions(try! Lnrpc_GetTransactionsRequest().serializedData(), MessageResponseCallback(emitter: emitter))

return Disposables.create()
}
}

init(filesDir: String) {
self.filesDir = filesDir
}

private func genSeedSingle() -> Single<Lnrpc_GenSeedResponse> {
var newSeedSingle: Single<Lnrpc_GenSeedResponse> {
Single.create { emitter in
LndmobileGenSeed(try! Lnrpc_GenSeedRequest().serializedData(), MessageResponseCallback(emitter: emitter))

return Disposables.create()
}
}

init(filesDir: String) throws {
self.filesDir = filesDir

// start Lndmobile daemon
let args = "--lnddir=\(filesDir) --bitcoin.active --bitcoin.mainnet --debuglevel=warn --no-macaroons --nolisten --norest --bitcoin.node=neutrino --routing.assumechanvalid --debuglevel=info"

_ = try Single<Void>.create { [weak self] emitter in
LndmobileStart(args, VoidResponseCallback(emitter: emitter), RpcReadyCallback(self))

return Disposables.create()
}.toBlocking().first()
}

func scheduleStatusUpdates() {
Observable<Int>.interval(.seconds(3), scheduler: SerialDispatchQueueScheduler(qos: .background))
.flatMap { [weak self] _ -> Observable<NodeStatus> in
Expand Down Expand Up @@ -197,7 +207,7 @@ class LocalLnd: ILndNode {
func closeChannelSingle(request: Lnrpc_CloseChannelRequest) throws -> Observable<Lnrpc_CloseStatusUpdate> {
Observable.create { emitter in
LndmobileCloseChannel(try! request.serializedData(), MessageResponseStream(emitter: emitter))

return Disposables.create()
}
}
Expand All @@ -210,65 +220,9 @@ class LocalLnd: ILndNode {
}
}

func start() -> Single<Void> {
let args = "--lnddir=\(filesDir) --bitcoin.active --bitcoin.mainnet --debuglevel=warn --no-macaroons --nolisten --norest --bitcoin.node=neutrino --routing.assumechanvalid --debuglevel=info"

return Single<Void>.create { emitter in
LndmobileStart(args, VoidResponseCallback(emitter: emitter), VoidResponseCallback(emitter: nil))

return Disposables.create()
}
}

func startAndUnlock(password: String) {
start()
.flatMap { [weak self] _ in
var request = Lnrpc_UnlockWalletRequest()
request.walletPassword = Data(Array(password.utf8))

return self?.unlockWalletSingle(request: request) ?? Single.error(NodeNotRetained())
}
.subscribe(
onSuccess: { [weak self] in self?.scheduleStatusUpdates() },
onError: { [weak self] in self?.status = .error($0) }
)
.disposed(by: disposeBag)
}

func createWalletSingle(password: String) -> Single<[String]> {
genSeedSingle()
.flatMap { [weak self] genSeedResponse in
guard let node = self else {
return Single<[String]>.error(NodeNotRetained())
}

return node.initWalletSingle(mnemonicWords: genSeedResponse.cipherSeedMnemonic, password: password, recoveryWindow: 0)
.do(
onSuccess: { [weak self] _ in self?.scheduleStatusUpdates() },
onError: { [weak self] in self?.status = .error($0) }
)
.map { _ in
genSeedResponse.cipherSeedMnemonic
}
}
}

func restoreWalletSingle(words: [String], password: String) -> Single<Void> {
initWalletSingle(mnemonicWords: words, password: password)
.do(
onSuccess: { [weak self] _ in self?.scheduleStatusUpdates() },
onError: { [weak self] in self?.status = .error($0) }
)
}

func initWalletSingle(mnemonicWords: [String], password: String, recoveryWindow: Int32 = 100) -> Single<Void> {
func initWalletSingle(request: Lnrpc_InitWalletRequest) -> Single<Void> {
Single.create { emitter in
var msg = Lnrpc_InitWalletRequest()
msg.cipherSeedMnemonic = mnemonicWords
msg.walletPassword = Data(Array(password.utf8))
msg.recoveryWindow = recoveryWindow

LndmobileInitWallet(try! msg.serializedData(), VoidResponseCallback(emitter: emitter))
LndmobileInitWallet(try! request.serializedData(), VoidResponseCallback(emitter: emitter))

return Disposables.create()
}
Expand All @@ -284,3 +238,13 @@ class LocalLnd: ILndNode {
}
}
}

extension LocalLnd: RpcReadyCallbackDelegate {
func RpcServerStartFailed(error: Error) {
status = .error(error)
}

func rpcReady() {
scheduleStatusUpdates()
}
}
Loading