From 78642261f6889276ffc6a1a0cabf6b687cc7e68e Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 8 May 2019 10:47:12 -0400 Subject: [PATCH] Set up a Swift shim to the Rust FFI --- ZcashLightClientKit.xcodeproj/project.pbxproj | 25 ++++ ZcashLightClientKit/ZcashRustBackend.swift | 126 ++++++++++++++++++ ZcashLightClientKit/zcashlc/module.modulemap | 4 + .../ZcashLightClientKitTests.swift | 1 + rust/build.rs | 2 +- 5 files changed, 157 insertions(+), 1 deletion(-) create mode 100644 ZcashLightClientKit/ZcashRustBackend.swift create mode 100644 ZcashLightClientKit/zcashlc/module.modulemap diff --git a/ZcashLightClientKit.xcodeproj/project.pbxproj b/ZcashLightClientKit.xcodeproj/project.pbxproj index 1012b218..4c062788 100644 --- a/ZcashLightClientKit.xcodeproj/project.pbxproj +++ b/ZcashLightClientKit.xcodeproj/project.pbxproj @@ -11,6 +11,8 @@ 103AFE94228312A30074BC98 /* ZcashLightClientKitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 103AFE93228312A30074BC98 /* ZcashLightClientKitTests.swift */; }; 103AFE96228312A30074BC98 /* ZcashLightClientKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 103AFE88228312A30074BC98 /* ZcashLightClientKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; 103AFEA22283166B0074BC98 /* libzcashlc.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 103AFEA12283166B0074BC98 /* libzcashlc.a */; }; + 103AFEA422831BB00074BC98 /* ZcashRustBackend.swift in Sources */ = {isa = PBXBuildFile; fileRef = 103AFEA322831BB00074BC98 /* ZcashRustBackend.swift */; }; + 103AFEAA228320F00074BC98 /* zcashlc.h in Headers */ = {isa = PBXBuildFile; fileRef = 103AFEA9228320F00074BC98 /* zcashlc.h */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -31,6 +33,9 @@ 103AFE93228312A30074BC98 /* ZcashLightClientKitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZcashLightClientKitTests.swift; sourceTree = ""; }; 103AFE95228312A30074BC98 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 103AFEA12283166B0074BC98 /* libzcashlc.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libzcashlc.a; path = target/universal/debug/libzcashlc.a; sourceTree = ""; }; + 103AFEA322831BB00074BC98 /* ZcashRustBackend.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZcashRustBackend.swift; sourceTree = ""; }; + 103AFEA8228320F00074BC98 /* module.modulemap */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = "sourcecode.module-map"; path = module.modulemap; sourceTree = ""; }; + 103AFEA9228320F00074BC98 /* zcashlc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = zcashlc.h; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -75,7 +80,9 @@ 103AFE87228312A30074BC98 /* ZcashLightClientKit */ = { isa = PBXGroup; children = ( + 103AFEA7228320F00074BC98 /* zcashlc */, 103AFE88228312A30074BC98 /* ZcashLightClientKit.h */, + 103AFEA322831BB00074BC98 /* ZcashRustBackend.swift */, 103AFE89228312A30074BC98 /* Info.plist */, ); path = ZcashLightClientKit; @@ -98,6 +105,15 @@ name = Frameworks; sourceTree = ""; }; + 103AFEA7228320F00074BC98 /* zcashlc */ = { + isa = PBXGroup; + children = ( + 103AFEA8228320F00074BC98 /* module.modulemap */, + 103AFEA9228320F00074BC98 /* zcashlc.h */, + ); + path = zcashlc; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -106,6 +122,7 @@ buildActionMask = 2147483647; files = ( 103AFE96228312A30074BC98 /* ZcashLightClientKit.h in Headers */, + 103AFEAA228320F00074BC98 /* zcashlc.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -229,6 +246,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 103AFEA422831BB00074BC98 /* ZcashRustBackend.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -376,6 +394,7 @@ 103AFE9A228312A30074BC98 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; DEFINES_MODULE = YES; @@ -397,6 +416,8 @@ PRODUCT_BUNDLE_IDENTIFIER = co.electriccoin.ZcashLightClientKit; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; + SWIFT_INCLUDE_PATHS = "${SRCROOT}/ZcashLightClientKit/zcashlc"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; @@ -405,6 +426,7 @@ 103AFE9B228312A30074BC98 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; DEFINES_MODULE = YES; @@ -426,6 +448,7 @@ PRODUCT_BUNDLE_IDENTIFIER = co.electriccoin.ZcashLightClientKit; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; + SWIFT_INCLUDE_PATHS = "${SRCROOT}/ZcashLightClientKit/zcashlc"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; @@ -434,6 +457,7 @@ 103AFE9D228312A30074BC98 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CODE_SIGN_STYLE = Automatic; INFOPLIST_FILE = ZcashLightClientKitTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -451,6 +475,7 @@ 103AFE9E228312A30074BC98 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CODE_SIGN_STYLE = Automatic; INFOPLIST_FILE = ZcashLightClientKitTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( diff --git a/ZcashLightClientKit/ZcashRustBackend.swift b/ZcashLightClientKit/ZcashRustBackend.swift new file mode 100644 index 00000000..418c06b6 --- /dev/null +++ b/ZcashLightClientKit/ZcashRustBackend.swift @@ -0,0 +1,126 @@ +// +// ZcashRustBackend.swift +// ZcashLightClientKit +// +// Created by Jack Grigg on 5/8/19. +// Copyright © 2019 Electric Coin Company. All rights reserved. +// + +import Foundation +import Zcashlc + +public class ZcashRustBackend { + static func osStrFromURL(_ url: URL) -> (String, UInt) { + let path = url.absoluteString + return (path, UInt(path.lengthOfBytes(using: .utf8))) + } + + public static func getLastError() -> String? { + let errorLen = zcashlc_last_error_length() + if errorLen > 0 { + let error = UnsafeMutablePointer.allocate(capacity: Int(errorLen)) + zcashlc_error_message_utf8(error, errorLen) + zcashlc_clear_last_error() + return String(validatingUTF8: error)! + } else { + return nil + } + } + + public static func initDataDb(dbData: URL) -> Bool { + let dbData = osStrFromURL(dbData) + return zcashlc_init_data_database(dbData.0, dbData.1) != 0 + } + + public static func initAccountsTable(dbData: URL, seed: [UInt8], accounts: Int32) -> [String]? { + let dbData = osStrFromURL(dbData) + let extsksCStr = zcashlc_init_accounts_table(dbData.0, dbData.1, seed, UInt(seed.count), accounts) + if extsksCStr == nil { + return nil + } + + let extsks = UnsafeBufferPointer(start: extsksCStr, count: Int(accounts)).map { + String(cString: $0!) + } + zcashlc_vec_string_free(extsksCStr, UInt(accounts)) + return extsks + } + + public static func initBlocksTable(dbData: URL, height: Int32, hash: String, time: UInt32, saplingTree: String) -> Bool { + let dbData = osStrFromURL(dbData) + return zcashlc_init_blocks_table(dbData.0, dbData.1, height, [CChar](hash.utf8CString), time, [CChar](saplingTree.utf8CString)) != 0 + } + + public static func getAddress(dbData: URL, account: Int32) -> String? { + let dbData = osStrFromURL(dbData) + + let addressCStr = zcashlc_get_address(dbData.0, dbData.1, account) + if addressCStr == nil { + return nil + } + + let address = String(validatingUTF8: addressCStr!) + zcashlc_string_free(addressCStr) + return address + } + + public static func getBalance(dbData: URL, account: Int32) -> Int64 { + let dbData = osStrFromURL(dbData) + return zcashlc_get_balance(dbData.0, dbData.1, account) + } + + public static func getVerifiedBalance(dbData: URL, account: Int32) -> Int64 { + let dbData = osStrFromURL(dbData) + return zcashlc_get_verified_balance(dbData.0, dbData.1, account) + } + + public static func getReceivedMemoAsUTF8(dbData: URL, idNote: Int64) -> String? { + let dbData = osStrFromURL(dbData) + + let memoCStr = zcashlc_get_received_memo_as_utf8(dbData.0, dbData.1, idNote) + if memoCStr == nil { + return nil + } + + let memo = String(validatingUTF8: memoCStr!) + zcashlc_string_free(memoCStr) + return memo + } + + public static func getSentMemoAsUTF8(dbData: URL, idNote: Int64) -> String? { + let dbData = osStrFromURL(dbData) + + let memoCStr = zcashlc_get_sent_memo_as_utf8(dbData.0, dbData.1, idNote) + if memoCStr == nil { + return nil + } + + let memo = String(validatingUTF8: memoCStr!) + zcashlc_string_free(memoCStr) + return memo + } + + public static func validateCombinedChain(dbCache: URL, dbData: URL) -> Int32 { + let dbCache = osStrFromURL(dbCache) + let dbData = osStrFromURL(dbData) + return zcashlc_validate_combined_chain(dbCache.0, dbCache.1, dbData.0, dbData.1) + } + + public static func rewindToHeight(dbData: URL, height: Int32) -> Bool { + let dbData = osStrFromURL(dbData) + return zcashlc_rewind_to_height(dbData.0, dbData.1, height) != 0 + } + + public static func scanBlocks(dbCache: URL, dbData: URL) -> Bool { + let dbCache = osStrFromURL(dbCache) + let dbData = osStrFromURL(dbData) + return zcashlc_scan_blocks(dbCache.0, dbCache.1, dbData.0, dbData.1) != 0 + } + + public static func sendToAddress(dbData: URL, account: Int32, extsk: String, to: String, value: Int64, memo: String?, spendParams: URL, outputParams: URL) -> Int64 { + let dbData = osStrFromURL(dbData) + let spendParams = osStrFromURL(spendParams) + let outputParams = osStrFromURL(outputParams) + return zcashlc_send_to_address(dbData.0, dbData.1, account, extsk, to, value, memo, spendParams.0, spendParams.1, outputParams.0, outputParams.1) + } +} diff --git a/ZcashLightClientKit/zcashlc/module.modulemap b/ZcashLightClientKit/zcashlc/module.modulemap new file mode 100644 index 00000000..50579740 --- /dev/null +++ b/ZcashLightClientKit/zcashlc/module.modulemap @@ -0,0 +1,4 @@ +module Zcashlc [system][extern_c] { + header "zcashlc.h" + export * +} diff --git a/ZcashLightClientKitTests/ZcashLightClientKitTests.swift b/ZcashLightClientKitTests/ZcashLightClientKitTests.swift index 427db2ec..d247a7a1 100644 --- a/ZcashLightClientKitTests/ZcashLightClientKitTests.swift +++ b/ZcashLightClientKitTests/ZcashLightClientKitTests.swift @@ -22,6 +22,7 @@ class ZcashLightClientKitTests: XCTestCase { func testExample() { // This is an example of a functional test case. // Use XCTAssert and related functions to verify your tests produce the correct results. + XCTAssert(ZcashRustBackend.getLastError() == nil) } func testPerformanceExample() { diff --git a/rust/build.rs b/rust/build.rs index b7aa51ed..ad6146ec 100644 --- a/rust/build.rs +++ b/rust/build.rs @@ -10,5 +10,5 @@ fn main() { .with_language(cbindgen::Language::C) .generate() .expect("Unable to generate bindings") - .write_to_file("zcashlc.h"); + .write_to_file("ZcashLightClientKit/zcashlc/zcashlc.h"); }