migrate to ffi plugin

This commit is contained in:
julian
2023-11-28 15:52:26 -06:00
parent 16854f96de
commit 8d9c2cbe96
58 changed files with 1219 additions and 1408 deletions

8
.gitignore vendored
View File

@@ -28,10 +28,6 @@ doc/api/
.idea/
lib/git_versions.dart
# Ignore build artifacts.
scripts/*/cache/
scripts/*/sparkmobile
scripts/*/CMakeLists/sparkmobile/CMakeLists.txt
scripts/*/CMakeLists/secp256k1/CMakeLists.txt
src/build
src/deps/sparkmobile

3
.gitmodules vendored
View File

@@ -1,3 +0,0 @@
[submodule "sparkmobile"]
path = sparkmobile
url = https://github.com/firoorg/sparkmobile

View File

@@ -1,20 +1,21 @@
// The Android Gradle Plugin builds the native code with the Android NDK.
group 'com.cypherstack.flutter_libsparkmobile'
version '1.0-SNAPSHOT'
version '1.0'
buildscript {
ext.kotlin_version = '1.7.10'
repositories {
google()
mavenCentral()
}
dependencies {
// The Android Gradle Plugin knows how to build native code with the NDK.
classpath 'com.android.tools.build:gradle:7.3.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
allprojects {
rootProject.allprojects {
repositories {
google()
mavenCentral()
@@ -22,43 +23,37 @@ allprojects {
}
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
android {
// Bumping the plugin compileSdkVersion requires all clients of this plugin
// to bump the version in their app.
compileSdkVersion 31
// Bumping the plugin ndkVersion requires all clients of this plugin to bump
// the version in their app and to download a newer version of the NDK.
ndkVersion "23.1.7779620"
// Invoke the shared CMake build with the Android Gradle Plugin.
externalNativeBuild {
cmake {
path "../src/CMakeLists.txt"
// The default CMake version for the Android Gradle Plugin is 3.10.2.
// https://developer.android.com/studio/projects/install-ndk#vanilla_cmake
//
// The Flutter tooling requires that developers have CMake 3.10 or later
// installed. You should not increase this version, as doing so will cause
// the plugin to fail to compile for some customers of the plugin.
// version "3.10.2"
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
test.java.srcDirs += 'src/test/kotlin'
}
defaultConfig {
minSdkVersion 16
}
dependencies {
testImplementation 'org.jetbrains.kotlin:kotlin-test'
testImplementation 'org.mockito:mockito-core:5.0.0'
}
testOptions {
unitTests.all {
useJUnitPlatform()
testLogging {
events "passed", "skipped", "failed", "standardOut", "standardError"
outputs.upToDateWhen {false}
showStandardStreams = true
}
}
}
}

View File

@@ -1,35 +0,0 @@
package com.cypherstack.flutter_libsparkmobile
import androidx.annotation.NonNull
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.MethodChannel.Result
/** FlutterLibsparkmobilePlugin */
class FlutterLibsparkmobilePlugin: FlutterPlugin, MethodCallHandler {
/// The MethodChannel that will the communication between Flutter and native Android
///
/// This local reference serves to register the plugin with the Flutter Engine and unregister it
/// when the Flutter Engine is detached from the Activity
private lateinit var channel : MethodChannel
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
channel = MethodChannel(flutterPluginBinding.binaryMessenger, "flutter_libsparkmobile")
channel.setMethodCallHandler(this)
}
override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
if (call.method == "getPlatformVersion") {
result.success("Android ${android.os.Build.VERSION.RELEASE}")
} else {
result.notImplemented()
}
}
override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
channel.setMethodCallHandler(null)
}
}

View File

@@ -1,27 +0,0 @@
package com.cypherstack.flutter_libsparkmobile
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import kotlin.test.Test
import org.mockito.Mockito
/*
* This demonstrates a simple unit test of the Kotlin portion of this plugin's implementation.
*
* Once you have built the plugin's example app, you can run these tests from the command
* line by running `./gradlew testDebugUnitTest` in the `example/android/` directory, or
* you can run them directly from IDEs that support JUnit such as Android Studio.
*/
internal class FlutterLibsparkmobilePluginTest {
@Test
fun onMethodCall_getPlatformVersion_returnsExpectedValue() {
val plugin = FlutterLibsparkmobilePlugin()
val call = MethodCall("getPlatformVersion", null)
val mockResult: MethodChannel.Result = Mockito.mock(MethodChannel.Result::class.java)
plugin.onMethodCall(call, mockResult)
Mockito.verify(mockResult).success("Android " + android.os.Build.VERSION.RELEASE)
}
}

View File

@@ -2,25 +2,11 @@ import Flutter
import UIKit
import XCTest
@testable import flutter_libsparkmobile
// This demonstrates a simple unit test of the Swift portion of this plugin's implementation.
//
// See https://developer.apple.com/documentation/xctest for more information about using XCTest.
class RunnerTests: XCTestCase {
func testGetPlatformVersion() {
let plugin = FlutterLibsparkmobilePlugin()
let call = FlutterMethodCall(methodName: "getPlatformVersion", arguments: [])
let resultExpectation = expectation(description: "result block must be called.")
plugin.handle(call) { result in
XCTAssertEqual(result as! String, "iOS " + UIDevice.current.systemVersion)
resultExpectation.fulfill()
}
waitForExpectations(timeout: 1)
func testExample() {
// If you add code to the Runner application, consider adding tests here.
// See https://developer.apple.com/documentation/xctest for more information about using XCTest.
}
}

View File

@@ -86,8 +86,6 @@ set_target_properties(${BINARY_NAME}
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run"
)
# Enable the test target.
set(include_flutter_libsparkmobile_tests TRUE)
# Generated plugin build rules, which manage building the plugins and adding
# them to the application.
@@ -119,10 +117,6 @@ install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}
install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../../scripts/linux/build/libsparkmobile.so" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
# Must have ran build script(s) in order for this library to exist.
foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES})
install(FILES "${bundled_library}"
DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"

View File

@@ -6,10 +6,6 @@
#include "generated_plugin_registrant.h"
#include <flutter_libsparkmobile/flutter_libsparkmobile_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) flutter_libsparkmobile_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterLibsparkmobilePlugin");
flutter_libsparkmobile_plugin_register_with_registrar(flutter_libsparkmobile_registrar);
}

View File

@@ -3,11 +3,11 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
flutter_libsparkmobile
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
coinlib_flutter
flutter_libsparkmobile
)
set(PLUGIN_BUNDLED_LIBRARIES)

View File

@@ -5,8 +5,6 @@
import FlutterMacOS
import Foundation
import flutter_libsparkmobile
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
FlutterLibsparkmobilePlugin.register(with: registry.registrar(forPlugin: "FlutterLibsparkmobilePlugin"))
}

View File

@@ -2,26 +2,11 @@ import FlutterMacOS
import Cocoa
import XCTest
@testable import flutter_libsparkmobile
// This demonstrates a simple unit test of the Swift portion of this plugin's implementation.
//
// See https://developer.apple.com/documentation/xctest for more information about using XCTest.
class RunnerTests: XCTestCase {
func testGetPlatformVersion() {
let plugin = FlutterLibsparkmobilePlugin()
let call = FlutterMethodCall(methodName: "getPlatformVersion", arguments: [])
let resultExpectation = expectation(description: "result block must be called.")
plugin.handle(call) { result in
XCTAssertEqual(result as! String,
"macOS " + ProcessInfo.processInfo.operatingSystemVersionString)
resultExpectation.fulfill()
}
waitForExpectations(timeout: 1)
func testExample() {
// If you add code to the Runner application, consider adding tests here.
// See https://developer.apple.com/documentation/xctest for more information about using XCTest.
}
}

View File

@@ -4,6 +4,20 @@ description: Demonstrates how to use the flutter_libsparkmobile plugin.
# pub.dev using `flutter pub publish`. This is preferred for private packages.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# The following defines the version and build number for your application.
# A version number is three numbers separated by dots, like 1.2.43
# followed by an optional build number separated by a +.
# Both the version and the builder number may be overridden in flutter
# build by specifying --build-name and --build-number, respectively.
# In Android, build-name is used as versionName while build-number used as versionCode.
# Read more about Android versioning at https://developer.android.com/studio/publish/versioning
# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix.
version: 1.0.0+1
environment:
sdk: '>=3.0.6 <4.0.0'

View File

@@ -52,8 +52,6 @@ add_subdirectory(${FLUTTER_MANAGED_DIR})
# Application build; see runner/CMakeLists.txt.
add_subdirectory("runner")
# Enable the test target.
set(include_flutter_libsparkmobile_tests TRUE)
# Generated plugin build rules, which manage building the plugins and adding
# them to the application.

View File

@@ -6,9 +6,6 @@
#include "generated_plugin_registrant.h"
#include <flutter_libsparkmobile/flutter_libsparkmobile_plugin_c_api.h>
void RegisterPlugins(flutter::PluginRegistry* registry) {
FlutterLibsparkmobilePluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FlutterLibsparkmobilePluginCApi"));
}

View File

@@ -3,10 +3,10 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
flutter_libsparkmobile
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
flutter_libsparkmobile
)
set(PLUGIN_BUNDLED_LIBRARIES)

19
ffigen.yaml Normal file
View File

@@ -0,0 +1,19 @@
# Run with `flutter pub run ffigen --config ffigen.yaml`.
name: FlutterLibsparkmobileBindings
description: |
Bindings for `src/flutter_libsparkmobile.h`.
Regenerate bindings with `flutter pub run ffigen --config ffigen.yaml`.
output: 'lib/flutter_libsparkmobile_bindings_generated.dart'
headers:
entry-points:
- 'src/flutter_libsparkmobile.h'
include-directives:
- 'src/flutter_libsparkmobile.h'
preamble: |
// ignore_for_file: always_specify_types
// ignore_for_file: camel_case_types
// ignore_for_file: non_constant_identifier_names
comments:
style: any
length: full

View File

@@ -3,7 +3,7 @@
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/lib" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/example/.idea" />
<excludeFolder url="file://$MODULE_DIR$/.idea" />
<excludeFolder url="file://$MODULE_DIR$/example/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/example/.pub" />

38
ios/.gitignore vendored
View File

@@ -1,38 +0,0 @@
.idea/
.vagrant/
.sconsign.dblite
.svn/
.DS_Store
*.swp
profile
DerivedData/
build/
GeneratedPluginRegistrant.h
GeneratedPluginRegistrant.m
.generated/
*.pbxuser
*.mode1v3
*.mode2v3
*.perspectivev3
!default.pbxuser
!default.mode1v3
!default.mode2v3
!default.perspectivev3
xcuserdata
*.moved-aside
*.pyc
*sync/
Icon?
.tags*
/Flutter/Generated.xcconfig
/Flutter/ephemeral/
/Flutter/flutter_export_environment.sh

View File

View File

@@ -1,19 +0,0 @@
import Flutter
import UIKit
public class FlutterLibsparkmobilePlugin: NSObject, FlutterPlugin {
public static func register(with registrar: FlutterPluginRegistrar) {
let channel = FlutterMethodChannel(name: "flutter_libsparkmobile", binaryMessenger: registrar.messenger())
let instance = FlutterLibsparkmobilePlugin()
registrar.addMethodCallDelegate(instance, channel: channel)
}
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
switch call.method {
case "getPlatformVersion":
result("iOS " + UIDevice.current.systemVersion)
default:
result(FlutterMethodNotImplemented)
}
}
}

View File

@@ -0,0 +1,3 @@
// Relative import to be able to reuse the C sources.
// See the comment in ../{projectName}}.podspec for more information.
#include flutter_libsparkmobile.cpp

View File

@@ -5,13 +5,18 @@
Pod::Spec.new do |s|
s.name = 'flutter_libsparkmobile'
s.version = '0.0.1'
s.summary = 'A new Flutter plugin project.'
s.summary = 'A new Flutter project.'
s.description = <<-DESC
A new Flutter plugin project.
A new Flutter project.
DESC
s.homepage = 'http://example.com'
s.license = { :file => '../LICENSE' }
s.author = { 'Your Company' => 'email@example.com' }
# This will ensure the source files in Classes/ are included in the native
# builds of apps using this FFI plugin. Podspec does not support relative
# paths, so Classes contains a forwarder C file that relatively imports
# `../src/*` so that the C sources can be shared among all target platforms.
s.source = { :path => '.' }
s.source_files = 'Classes/**/*'
s.dependency 'Flutter'

View File

@@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:ffi';
import 'dart:io';
import 'dart:typed_data';
@@ -5,46 +6,67 @@ import 'dart:typed_data';
import 'package:ffi/ffi.dart';
import 'package:flutter_libsparkmobile/extensions.dart';
import 'flutter_libsparkmobile_bindings.dart';
import 'flutter_libsparkmobile_bindings_generated.dart';
const kSparkChain = 6;
const kSparkBaseDerivationPath = "m/44'/136'/0'/$kSparkChain/";
const String _kLibName = 'flutter_libsparkmobile';
/// The dynamic library in which the symbols for [FlutterLibsparkmobileBindings] can be found.
final DynamicLibrary _dylib = () {
if (Platform.isMacOS || Platform.isIOS) {
return DynamicLibrary.open('$_kLibName.framework/$_kLibName');
}
if (Platform.isAndroid || Platform.isLinux) {
return DynamicLibrary.open('lib$_kLibName.so');
}
if (Platform.isWindows) {
return DynamicLibrary.open('$_kLibName.dll');
}
throw UnsupportedError('Unknown platform: ${Platform.operatingSystem}');
}();
/// The bindings to the native functions in [_dylib].
final FlutterLibsparkmobileBindings _bindings =
FlutterLibsparkmobileBindings(_dylib);
abstract final class LibSpark {
static SparkMobileBindings? _bindings;
static void _checkLoaded() {
_bindings ??= SparkMobileBindings(_loadLibrary());
}
static DynamicLibrary _loadLibrary() {
// hack in prefix for test env
String testPrefix = "";
if (Platform.environment.containsKey('FLUTTER_TEST')) {
if (Platform.isLinux) {
testPrefix = 'scripts/linux/build/';
} else if (Platform.isMacOS) {
testPrefix = 'scripts/macos/build/';
} else if (Platform.isWindows) {
testPrefix = 'scripts/windows/build/';
} else {
throw UnsupportedError('This platform is not supported');
}
}
if (Platform.isLinux) {
return DynamicLibrary.open('${testPrefix}libsparkmobile.so');
} else if (Platform.isAndroid) {
// return DynamicLibrary.open('${testPrefix}libsparkmobile.so');
} else if (Platform.isIOS) {
// return DynamicLibrary.open('${testPrefix}libsparkmobile.dylib');
} else if (Platform.isMacOS) {
// return DynamicLibrary.open('${testPrefix}libsparkmobile.dylib');
} else if (Platform.isWindows) {
// return DynamicLibrary.open('${testPrefix}sparkmobile.dll');
}
throw UnsupportedError('This platform is not supported');
}
static final FlutterLibsparkmobileBindings _bindings =
FlutterLibsparkmobileBindings(_dylib);
//
// static void _checkLoaded() {
// _bindings ??= SparkMobileBindings(_loadLibrary());
// }
//
// static DynamicLibrary _loadLibrary() {
// // hack in prefix for test env
// String testPrefix = "";
// if (Platform.environment.containsKey('FLUTTER_TEST')) {
// if (Platform.isLinux) {
// testPrefix = 'scripts/linux/build/';
// } else if (Platform.isMacOS) {
// testPrefix = 'scripts/macos/build/';
// } else if (Platform.isWindows) {
// testPrefix = 'scripts/windows/build/';
// } else {
// throw UnsupportedError('This platform is not supported');
// }
// }
//
// if (Platform.isLinux) {
// return DynamicLibrary.open('${testPrefix}libsparkmobile.so');
// } else if (Platform.isAndroid) {
// // return DynamicLibrary.open('${testPrefix}libsparkmobile.so');
// } else if (Platform.isIOS) {
// // return DynamicLibrary.open('${testPrefix}libsparkmobile.dylib');
// } else if (Platform.isMacOS) {
// // return DynamicLibrary.open('${testPrefix}libsparkmobile.dylib');
// } else if (Platform.isWindows) {
// // return DynamicLibrary.open('${testPrefix}sparkmobile.dll');
// }
// throw UnsupportedError('This platform is not supported');
// }
// SparkMobileBindings methods:
@@ -55,7 +77,7 @@ abstract final class LibSpark {
required int diversifier,
bool isTestNet = false,
}) async {
_checkLoaded();
// _checkLoaded();
if (index < 0) {
throw Exception("Index must not be negative.");
@@ -75,7 +97,7 @@ abstract final class LibSpark {
final keyDataPointer = privateKey.toHexString().toNativeUtf8().cast<Char>();
// Call the native method with the pointer.
final addressPointer = _bindings!.getAddress(
final addressPointer = _bindings.getAddress(
keyDataPointer,
index,
diversifier,
@@ -92,3 +114,128 @@ abstract final class LibSpark {
return addressString;
}
}
//
// /// A very short-lived native function.
// ///
// /// For very short-lived functions, it is fine to call them on the main isolate.
// /// They will block the Dart execution while running the native function, so
// /// only do this for native functions which are guaranteed to be short-lived.
// int sum(int a, int b) => _bindings.sum(a, b);
//
// /// A longer lived native function, which occupies the thread calling it.
// ///
// /// Do not call these kind of native functions in the main isolate. They will
// /// block Dart execution. This will cause dropped frames in Flutter applications.
// /// Instead, call these native functions on a separate isolate.
// ///
// /// Modify this to suit your own use case. Example use cases:
// ///
// /// 1. Reuse a single isolate for various different kinds of requests.
// /// 2. Use multiple helper isolates for parallel execution.
// Future<int> sumAsync(int a, int b) async {
// final SendPort helperIsolateSendPort = await _helperIsolateSendPort;
// final int requestId = _nextSumRequestId++;
// final _SumRequest request = _SumRequest(requestId, a, b);
// final Completer<int> completer = Completer<int>();
// _sumRequests[requestId] = completer;
// helperIsolateSendPort.send(request);
// return completer.future;
// }
//
// const String _libName = 'flutter_libsparkmobile';
//
// /// The dynamic library in which the symbols for [FlutterLibsparkmobileBindings] can be found.
// final DynamicLibrary _dylib = () {
// if (Platform.isMacOS || Platform.isIOS) {
// return DynamicLibrary.open('$_libName.framework/$_libName');
// }
// if (Platform.isAndroid || Platform.isLinux) {
// return DynamicLibrary.open('lib$_libName.so');
// }
// if (Platform.isWindows) {
// return DynamicLibrary.open('$_libName.dll');
// }
// throw UnsupportedError('Unknown platform: ${Platform.operatingSystem}');
// }();
//
// /// The bindings to the native functions in [_dylib].
// final FlutterLibsparkmobileBindings _bindings = FlutterLibsparkmobileBindings(_dylib);
//
// /// A request to compute `sum`.
// ///
// /// Typically sent from one isolate to another.
// class _SumRequest {
// final int id;
// final int a;
// final int b;
//
// const _SumRequest(this.id, this.a, this.b);
// }
//
// /// A response with the result of `sum`.
// ///
// /// Typically sent from one isolate to another.
// class _SumResponse {
// final int id;
// final int result;
//
// const _SumResponse(this.id, this.result);
// }
//
// /// Counter to identify [_SumRequest]s and [_SumResponse]s.
// int _nextSumRequestId = 0;
//
// /// Mapping from [_SumRequest] `id`s to the completers corresponding to the correct future of the pending request.
// final Map<int, Completer<int>> _sumRequests = <int, Completer<int>>{};
//
// /// The SendPort belonging to the helper isolate.
// Future<SendPort> _helperIsolateSendPort = () async {
// // The helper isolate is going to send us back a SendPort, which we want to
// // wait for.
// final Completer<SendPort> completer = Completer<SendPort>();
//
// // Receive port on the main isolate to receive messages from the helper.
// // We receive two types of messages:
// // 1. A port to send messages on.
// // 2. Responses to requests we sent.
// final ReceivePort receivePort = ReceivePort()
// ..listen((dynamic data) {
// if (data is SendPort) {
// // The helper isolate sent us the port on which we can sent it requests.
// completer.complete(data);
// return;
// }
// if (data is _SumResponse) {
// // The helper isolate sent us a response to a request we sent.
// final Completer<int> completer = _sumRequests[data.id]!;
// _sumRequests.remove(data.id);
// completer.complete(data.result);
// return;
// }
// throw UnsupportedError('Unsupported message type: ${data.runtimeType}');
// });
//
// // Start the helper isolate.
// await Isolate.spawn((SendPort sendPort) async {
// final ReceivePort helperReceivePort = ReceivePort()
// ..listen((dynamic data) {
// // On the helper isolate listen to requests and respond to them.
// if (data is _SumRequest) {
// final int result = _bindings.sum_long_running(data.a, data.b);
// final _SumResponse response = _SumResponse(data.id, result);
// sendPort.send(response);
// return;
// }
// throw UnsupportedError('Unsupported message type: ${data.runtimeType}');
// });
//
// // Send the port to the main isolate on which we can receive requests.
// sendPort.send(helperReceivePort.sendPort);
// }, receivePort.sendPort);
//
// // Wait until the helper isolate has sent us back the SendPort on which we
// // can start sending requests.
// return completer.future;
// }();

View File

@@ -1,439 +0,0 @@
// ignore_for_file: camel_case_types, non_constant_identifier_names, unused_element, unused_field, return_of_invalid_type, void_checks, annotate_overrides, no_leading_underscores_for_local_identifiers, library_private_types_in_public_api
// AUTO GENERATED FILE, DO NOT EDIT.
//
// Generated by `package:ffigen`.
// ignore_for_file: type=lint
import 'dart:ffi' as ffi;
/// Bindings for sparkmobile.
class SparkMobileBindings {
/// Holds the symbol lookup function.
final ffi.Pointer<T> Function<T extends ffi.NativeType>(String symbolName)
_lookup;
/// The symbols are looked up in [dynamicLibrary].
SparkMobileBindings(ffi.DynamicLibrary dynamicLibrary)
: _lookup = dynamicLibrary.lookup;
/// The symbols are looked up with [lookup].
SparkMobileBindings.fromLookup(
ffi.Pointer<T> Function<T extends ffi.NativeType>(String symbolName)
lookup)
: _lookup = lookup;
ffi.Pointer<ffi.Char> getAddress(
ffi.Pointer<ffi.Char> keyDataHex,
int index,
int diversifier,
int isTestNet,
) {
return _getAddress(
keyDataHex,
index,
diversifier,
isTestNet,
);
}
late final _getAddressPtr = _lookup<
ffi.NativeFunction<
ffi.Pointer<ffi.Char> Function(
ffi.Pointer<ffi.Char>, ffi.Int, ffi.Int, ffi.Int)>>('getAddress');
late final _getAddress = _getAddressPtr.asFunction<
ffi.Pointer<ffi.Char> Function(ffi.Pointer<ffi.Char>, int, int, int)>();
CIdentifiedCoinData identifyCoin(
CCoin c_struct,
ffi.Pointer<ffi.Char> keyDataHex,
int index,
) {
return _identifyCoin(
c_struct,
keyDataHex,
index,
);
}
late final _identifyCoinPtr = _lookup<
ffi.NativeFunction<
CIdentifiedCoinData Function(
CCoin, ffi.Pointer<ffi.Char>, ffi.Int)>>('identifyCoin');
late final _identifyCoin = _identifyCoinPtr.asFunction<
CIdentifiedCoinData Function(CCoin, ffi.Pointer<ffi.Char>, int)>();
ffi.Pointer<CCRecipient> createSparkMintRecipients(
int numRecipients,
ffi.Pointer<PubKeyScript> pubKeyScripts,
ffi.Pointer<ffi.Uint64> amounts,
ffi.Pointer<ffi.Char> memo,
int subtractFee,
) {
return _createSparkMintRecipients(
numRecipients,
pubKeyScripts,
amounts,
memo,
subtractFee,
);
}
late final _createSparkMintRecipientsPtr = _lookup<
ffi.NativeFunction<
ffi.Pointer<CCRecipient> Function(
ffi.Int,
ffi.Pointer<PubKeyScript>,
ffi.Pointer<ffi.Uint64>,
ffi.Pointer<ffi.Char>,
ffi.Int)>>('createSparkMintRecipients');
late final _createSparkMintRecipients =
_createSparkMintRecipientsPtr.asFunction<
ffi.Pointer<CCRecipient> Function(int, ffi.Pointer<PubKeyScript>,
ffi.Pointer<ffi.Uint64>, ffi.Pointer<ffi.Char>, int)>();
}
final class __fsid_t extends ffi.Struct {
@ffi.Array.multi([2])
external ffi.Array<ffi.Int> __val;
}
final class CCoin extends ffi.Struct {
@ffi.Char()
external int type;
external ffi.Pointer<ffi.UnsignedChar> k;
@ffi.Int()
external int kLength;
external ffi.Pointer<ffi.Char> address;
@ffi.Uint64()
external int v;
external ffi.Pointer<ffi.UnsignedChar> memo;
@ffi.Int()
external int memoLength;
external ffi.Pointer<ffi.UnsignedChar> serial_context;
@ffi.Int()
external int serial_contextLength;
}
final class CIdentifiedCoinData extends ffi.Struct {
@ffi.Uint64()
external int i;
external ffi.Pointer<ffi.UnsignedChar> d;
@ffi.Int()
external int dLength;
@ffi.Uint64()
external int v;
external ffi.Pointer<ffi.UnsignedChar> k;
@ffi.Int()
external int kLength;
external ffi.Pointer<ffi.Char> memo;
@ffi.Int()
external int memoLength;
}
final class CCRecipient extends ffi.Struct {
external ffi.Pointer<ffi.UnsignedChar> pubKey;
@ffi.Int()
external int pubKeyLength;
@ffi.Uint64()
external int cAmount;
@ffi.Int()
external int subtractFee;
}
final class CMintedCoinData extends ffi.Struct {
external ffi.Pointer<ffi.Char> address;
@ffi.Uint64()
external int value;
external ffi.Pointer<ffi.Char> memo;
}
final class PubKeyScript extends ffi.Struct {
external ffi.Pointer<ffi.UnsignedChar> bytes;
@ffi.Int()
external int length;
}
final class COutputCoinData extends ffi.Struct {
external ffi.Pointer<ffi.Char> address;
@ffi.Uint64()
external int value;
external ffi.Pointer<ffi.Char> memo;
}
final class CCSparkMintMeta extends ffi.Struct {
@ffi.Uint64()
external int height;
external ffi.Pointer<ffi.Char> id;
@ffi.Int()
external int isUsed;
external ffi.Pointer<ffi.Char> txid;
@ffi.Uint64()
external int i;
external ffi.Pointer<ffi.UnsignedChar> d;
@ffi.Int()
external int dLength;
@ffi.Uint64()
external int v;
external ffi.Pointer<ffi.UnsignedChar> k;
@ffi.Int()
external int kLength;
external ffi.Pointer<ffi.Char> memo;
@ffi.Int()
external int memoLength;
external ffi.Pointer<ffi.UnsignedChar> serial_context;
@ffi.Int()
external int serial_contextLength;
@ffi.Char()
external int type;
external CCoin coin;
}
const int _STDINT_H = 1;
const int _FEATURES_H = 1;
const int _DEFAULT_SOURCE = 1;
const int __GLIBC_USE_ISOC2X = 1;
const int __USE_ISOC11 = 1;
const int __USE_ISOC99 = 1;
const int __USE_ISOC95 = 1;
const int _POSIX_SOURCE = 1;
const int _POSIX_C_SOURCE = 200809;
const int __USE_POSIX = 1;
const int __USE_POSIX2 = 1;
const int __USE_POSIX199309 = 1;
const int __USE_POSIX199506 = 1;
const int __USE_XOPEN2K = 1;
const int __USE_XOPEN2K8 = 1;
const int _ATFILE_SOURCE = 1;
const int __USE_MISC = 1;
const int __USE_ATFILE = 1;
const int __USE_FORTIFY_LEVEL = 0;
const int __GLIBC_USE_DEPRECATED_GETS = 0;
const int __GLIBC_USE_DEPRECATED_SCANF = 0;
const int _STDC_PREDEF_H = 1;
const int __STDC_IEC_559__ = 1;
const int __STDC_IEC_559_COMPLEX__ = 1;
const int __STDC_ISO_10646__ = 201706;
const int __GNU_LIBRARY__ = 6;
const int __GLIBC__ = 2;
const int __GLIBC_MINOR__ = 31;
const int _SYS_CDEFS_H = 1;
const int __glibc_c99_flexarr_available = 1;
const int __WORDSIZE = 64;
const int __WORDSIZE_TIME64_COMPAT32 = 1;
const int __SYSCALL_WORDSIZE = 64;
const int __LONG_DOUBLE_USES_FLOAT128 = 0;
const int __HAVE_GENERIC_SELECTION = 0;
const int __GLIBC_USE_LIB_EXT2 = 1;
const int __GLIBC_USE_IEC_60559_BFP_EXT = 1;
const int __GLIBC_USE_IEC_60559_BFP_EXT_C2X = 1;
const int __GLIBC_USE_IEC_60559_FUNCS_EXT = 1;
const int __GLIBC_USE_IEC_60559_FUNCS_EXT_C2X = 1;
const int __GLIBC_USE_IEC_60559_TYPES_EXT = 1;
const int _BITS_TYPES_H = 1;
const int __TIMESIZE = 64;
const int _BITS_TYPESIZES_H = 1;
const int __OFF_T_MATCHES_OFF64_T = 1;
const int __INO_T_MATCHES_INO64_T = 1;
const int __RLIM_T_MATCHES_RLIM64_T = 1;
const int __STATFS_MATCHES_STATFS64 = 1;
const int __FD_SETSIZE = 1024;
const int _BITS_TIME64_H = 1;
const int _BITS_WCHAR_H = 1;
const int __WCHAR_MAX = 2147483647;
const int __WCHAR_MIN = -2147483648;
const int _BITS_STDINT_INTN_H = 1;
const int _BITS_STDINT_UINTN_H = 1;
const int INT8_MIN = -128;
const int INT16_MIN = -32768;
const int INT32_MIN = -2147483648;
const int INT64_MIN = -9223372036854775808;
const int INT8_MAX = 127;
const int INT16_MAX = 32767;
const int INT32_MAX = 2147483647;
const int INT64_MAX = 9223372036854775807;
const int UINT8_MAX = 255;
const int UINT16_MAX = 65535;
const int UINT32_MAX = 4294967295;
const int UINT64_MAX = -1;
const int INT_LEAST8_MIN = -128;
const int INT_LEAST16_MIN = -32768;
const int INT_LEAST32_MIN = -2147483648;
const int INT_LEAST64_MIN = -9223372036854775808;
const int INT_LEAST8_MAX = 127;
const int INT_LEAST16_MAX = 32767;
const int INT_LEAST32_MAX = 2147483647;
const int INT_LEAST64_MAX = 9223372036854775807;
const int UINT_LEAST8_MAX = 255;
const int UINT_LEAST16_MAX = 65535;
const int UINT_LEAST32_MAX = 4294967295;
const int UINT_LEAST64_MAX = -1;
const int INT_FAST8_MIN = -128;
const int INT_FAST16_MIN = -9223372036854775808;
const int INT_FAST32_MIN = -9223372036854775808;
const int INT_FAST64_MIN = -9223372036854775808;
const int INT_FAST8_MAX = 127;
const int INT_FAST16_MAX = 9223372036854775807;
const int INT_FAST32_MAX = 9223372036854775807;
const int INT_FAST64_MAX = 9223372036854775807;
const int UINT_FAST8_MAX = 255;
const int UINT_FAST16_MAX = -1;
const int UINT_FAST32_MAX = -1;
const int UINT_FAST64_MAX = -1;
const int INTPTR_MIN = -9223372036854775808;
const int INTPTR_MAX = 9223372036854775807;
const int UINTPTR_MAX = -1;
const int INTMAX_MIN = -9223372036854775808;
const int INTMAX_MAX = 9223372036854775807;
const int UINTMAX_MAX = -1;
const int PTRDIFF_MIN = -9223372036854775808;
const int PTRDIFF_MAX = 9223372036854775807;
const int SIG_ATOMIC_MIN = -2147483648;
const int SIG_ATOMIC_MAX = 2147483647;
const int SIZE_MAX = -1;
const int WCHAR_MIN = -2147483648;
const int WCHAR_MAX = 2147483647;
const int WINT_MIN = 0;
const int WINT_MAX = 4294967295;

View File

@@ -0,0 +1,198 @@
// ignore_for_file: always_specify_types
// ignore_for_file: camel_case_types
// ignore_for_file: non_constant_identifier_names
// AUTO GENERATED FILE, DO NOT EDIT.
//
// Generated by `package:ffigen`.
import 'dart:ffi' as ffi;
/// Bindings for `src/flutter_libsparkmobile.h`.
///
/// Regenerate bindings with `flutter pub run ffigen --config ffigen.yaml`.
///
class FlutterLibsparkmobileBindings {
/// Holds the symbol lookup function.
final ffi.Pointer<T> Function<T extends ffi.NativeType>(String symbolName)
_lookup;
/// The symbols are looked up in [dynamicLibrary].
FlutterLibsparkmobileBindings(ffi.DynamicLibrary dynamicLibrary)
: _lookup = dynamicLibrary.lookup;
/// The symbols are looked up with [lookup].
FlutterLibsparkmobileBindings.fromLookup(
ffi.Pointer<T> Function<T extends ffi.NativeType>(String symbolName)
lookup)
: _lookup = lookup;
/// FFI-friendly wrapper for spark::getAddress.
ffi.Pointer<ffi.Char> getAddress(
ffi.Pointer<ffi.Char> keyDataHex,
int index,
int diversifier,
int isTestNet,
) {
return _getAddress(
keyDataHex,
index,
diversifier,
isTestNet,
);
}
late final _getAddressPtr = _lookup<
ffi.NativeFunction<
ffi.Pointer<ffi.Char> Function(
ffi.Pointer<ffi.Char>, ffi.Int, ffi.Int, ffi.Int)>>('getAddress');
late final _getAddress = _getAddressPtr.asFunction<
ffi.Pointer<ffi.Char> Function(ffi.Pointer<ffi.Char>, int, int, int)>();
/// FFI-friendly wrapper for spark::identifyCoin.
CIdentifiedCoinData identifyCoin(
CCoin c_struct,
ffi.Pointer<ffi.Char> keyDataHex,
int index,
) {
return _identifyCoin(
c_struct,
keyDataHex,
index,
);
}
late final _identifyCoinPtr = _lookup<
ffi.NativeFunction<
CIdentifiedCoinData Function(
CCoin, ffi.Pointer<ffi.Char>, ffi.Int)>>('identifyCoin');
late final _identifyCoin = _identifyCoinPtr.asFunction<
CIdentifiedCoinData Function(CCoin, ffi.Pointer<ffi.Char>, int)>();
/// FFI-friendly wrapper for spark::createSparkMintRecipients.
ffi.Pointer<CCRecipient> createSparkMintRecipients(
int numRecipients,
ffi.Pointer<PubKeyScript> pubKeyScripts,
ffi.Pointer<ffi.Uint64> amounts,
ffi.Pointer<ffi.Char> memo,
int subtractFee,
) {
return _createSparkMintRecipients(
numRecipients,
pubKeyScripts,
amounts,
memo,
subtractFee,
);
}
late final _createSparkMintRecipientsPtr = _lookup<
ffi.NativeFunction<
ffi.Pointer<CCRecipient> Function(
ffi.Int,
ffi.Pointer<PubKeyScript>,
ffi.Pointer<ffi.Uint64>,
ffi.Pointer<ffi.Char>,
ffi.Int)>>('createSparkMintRecipients');
late final _createSparkMintRecipients =
_createSparkMintRecipientsPtr.asFunction<
ffi.Pointer<CCRecipient> Function(int, ffi.Pointer<PubKeyScript>,
ffi.Pointer<ffi.Uint64>, ffi.Pointer<ffi.Char>, int)>();
}
/// FFI-friendly wrapper for a spark::Coin.
///
/// A Coin is a type, a key, an index, a value, a memo, and a serial context. We accept these params
/// as a C struct, deriving the key from the keyData and index.
final class CCoin extends ffi.Struct {
@ffi.Char()
external int type;
external ffi.Pointer<ffi.UnsignedChar> k;
@ffi.Int()
external int kLength;
external ffi.Pointer<ffi.Char> keyData;
@ffi.Int()
external int index;
@ffi.Uint64()
external int v;
external ffi.Pointer<ffi.UnsignedChar> memo;
@ffi.Int()
external int memoLength;
external ffi.Pointer<ffi.UnsignedChar> serial_context;
@ffi.Int()
external int serial_contextLength;
}
/// FFI-friendly wrapper for a spark::IdentifiedCoinData.
///
/// An IdentifiedCoinData is a diversifier, encrypted diversifier, value, nonce, and memo. We accept
/// these params as a C struct.
final class CIdentifiedCoinData extends ffi.Struct {
@ffi.Uint64()
external int i;
external ffi.Pointer<ffi.UnsignedChar> d;
@ffi.Int()
external int dLength;
@ffi.Uint64()
external int v;
external ffi.Pointer<ffi.UnsignedChar> k;
@ffi.Int()
external int kLength;
external ffi.Pointer<ffi.Char> memo;
@ffi.Int()
external int memoLength;
}
/// FFI-friendly wrapper for a spark::CRecipient.
///
/// A CRecipient is a CScript, CAmount, and a bool. We accept a C-style, FFI-friendly CCRecipient
/// struct in order to construct a C++ CRecipient. A CScript is constructed from a hex string, a
/// CAmount is just a uint64_t, and the bool will be an int.
final class CCRecipient extends ffi.Struct {
external ffi.Pointer<ffi.UnsignedChar> pubKey;
@ffi.Int()
external int pubKeyLength;
@ffi.Uint64()
external int cAmount;
@ffi.Int()
external int subtractFee;
}
/// FFI-friendly wrapper for a spark::MintedCoinData.
///
/// A MintedCoinData is a struct that contains an Address, a uint64_t value, and a string memo. We
/// accept these as a CMintedCoinData from the Dart interface, and convert them to a MintedCoinData
/// struct.
final class CMintedCoinData extends ffi.Struct {
external ffi.Pointer<ffi.Char> address;
@ffi.Uint64()
external int value;
external ffi.Pointer<ffi.Char> memo;
}
final class PubKeyScript extends ffi.Struct {
external ffi.Pointer<ffi.UnsignedChar> bytes;
@ffi.Int()
external int length;
}

View File

@@ -1,17 +0,0 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'flutter_libsparkmobile_platform_interface.dart';
/// An implementation of [FlutterLibsparkmobilePlatform] that uses method channels.
class MethodChannelFlutterLibsparkmobile extends FlutterLibsparkmobilePlatform {
/// The method channel used to interact with the native platform.
@visibleForTesting
final methodChannel = const MethodChannel('flutter_libsparkmobile');
@override
Future<String?> getPlatformVersion() async {
final version = await methodChannel.invokeMethod<String>('getPlatformVersion');
return version;
}
}

View File

@@ -1,29 +0,0 @@
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
import 'flutter_libsparkmobile_method_channel.dart';
abstract class FlutterLibsparkmobilePlatform extends PlatformInterface {
/// Constructs a FlutterLibsparkmobilePlatform.
FlutterLibsparkmobilePlatform() : super(token: _token);
static final Object _token = Object();
static FlutterLibsparkmobilePlatform _instance = MethodChannelFlutterLibsparkmobile();
/// The default instance of [FlutterLibsparkmobilePlatform] to use.
///
/// Defaults to [MethodChannelFlutterLibsparkmobile].
static FlutterLibsparkmobilePlatform get instance => _instance;
/// Platform-specific implementations should set this with their own
/// platform-specific class that extends [FlutterLibsparkmobilePlatform] when
/// they register themselves.
static set instance(FlutterLibsparkmobilePlatform instance) {
PlatformInterface.verifyToken(instance, _token);
_instance = instance;
}
Future<String?> getPlatformVersion() {
throw UnimplementedError('platformVersion() has not been implemented.');
}
}

View File

@@ -1,23 +0,0 @@
import 'dart:io';
/*ANDROID_VERSION*/ const ANDROID_VERSION = "";
/*IOS_VERSION*/ const IOS_VERSION = "";
/*MACOS_VERSION*/ const MACOS_VERSION = "";
/*LINUX_VERSION*/ const LINUX_VERSION = "";
/*WINDOWS_VERSION*/ const WINDOWS_VERSION = "";
String getPluginVersion() {
if (Platform.isAndroid) {
return ANDROID_VERSION;
} else if (Platform.isIOS) {
return IOS_VERSION;
} else if (Platform.isMacOS) {
return MACOS_VERSION;
} else if (Platform.isLinux) {
return LINUX_VERSION;
} else if (Platform.isWindows) {
return WINDOWS_VERSION;
} else {
return "Unknown version";
}
}

View File

@@ -5,90 +5,20 @@ cmake_minimum_required(VERSION 3.10)
# Project-level configuration.
set(PROJECT_NAME "flutter_libsparkmobile")
set(PROJECT_SYSTEM_NAME "linux")
project(${PROJECT_NAME} LANGUAGES CXX)
# This value is used when generating builds using this plugin, so it must
# not be changed.
set(PLUGIN_NAME "flutter_libsparkmobile_plugin")
# Any new source files that you add to the plugin should be added here.
list(APPEND PLUGIN_SOURCES
"flutter_libsparkmobile_plugin.cc"
)
# Define the plugin library target. Its name must not be changed (see comment
# on PLUGIN_NAME above).
add_library(${PLUGIN_NAME} SHARED
${PLUGIN_SOURCES}
)
# Apply a standard set of build settings that are configured in the
# application-level CMakeLists.txt. This can be removed for plugins that want
# full control over build settings.
apply_standard_settings(${PLUGIN_NAME})
# Symbols are hidden by default to reduce the chance of accidental conflicts
# between plugins. This should not be removed; any symbols that should be
# exported should be explicitly exported with the FLUTTER_PLUGIN_EXPORT macro.
set_target_properties(${PLUGIN_NAME} PROPERTIES
CXX_VISIBILITY_PRESET hidden)
target_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL)
# Source include directories and library dependencies. Add any plugin-specific
# dependencies here.
target_include_directories(${PLUGIN_NAME} INTERFACE
"${CMAKE_CURRENT_SOURCE_DIR}/include")
target_link_libraries(${PLUGIN_NAME} PRIVATE flutter)
target_link_libraries(${PLUGIN_NAME} PRIVATE PkgConfig::GTK)
# Invoke the build for native code shared with the other target platforms.
# This can be changed to accommodate different builds.
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/../src" "${CMAKE_CURRENT_BINARY_DIR}/shared")
# List of absolute paths to libraries that should be bundled with the plugin.
# This list could contain prebuilt libraries, or libraries created by an
# external build triggered from this build file.
set(flutter_libsparkmobile_bundled_libraries
""
# Defined in ../src/CMakeLists.txt.
# This can be changed to accommodate different builds.
$<TARGET_FILE:flutter_libsparkmobile>
PARENT_SCOPE
)
# === Tests ===
# These unit tests can be run from a terminal after building the example.
# Only enable test builds when building the example (which sets this variable)
# so that plugin clients aren't building the tests.
if (${include_${PROJECT_NAME}_tests})
if(${CMAKE_VERSION} VERSION_LESS "3.11.0")
message("Unit tests require CMake 3.11.0 or later")
else()
set(TEST_RUNNER "${PROJECT_NAME}_test")
enable_testing()
# Add the Google Test dependency.
include(FetchContent)
FetchContent_Declare(
googletest
URL https://github.com/google/googletest/archive/release-1.11.0.zip
)
# Prevent overriding the parent project's compiler/linker settings
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
# Disable install commands for gtest so it doesn't end up in the bundle.
set(INSTALL_GTEST OFF CACHE BOOL "Disable installation of googletest" FORCE)
FetchContent_MakeAvailable(googletest)
# The plugin's exported API is not very useful for unit testing, so build the
# sources directly into the test binary rather than using the shared library.
add_executable(${TEST_RUNNER}
test/flutter_libsparkmobile_plugin_test.cc
${PLUGIN_SOURCES}
)
apply_standard_settings(${TEST_RUNNER})
target_include_directories(${TEST_RUNNER} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}")
target_link_libraries(${TEST_RUNNER} PRIVATE flutter)
target_link_libraries(${TEST_RUNNER} PRIVATE PkgConfig::GTK)
target_link_libraries(${TEST_RUNNER} PRIVATE gtest_main gmock)
# Enable automatic test discovery.
include(GoogleTest)
gtest_discover_tests(${TEST_RUNNER})
endif() # CMake version check
endif() # include_${PROJECT_NAME}_tests

View File

@@ -1,76 +0,0 @@
#include "include/flutter_libsparkmobile/flutter_libsparkmobile_plugin.h"
#include <flutter_linux/flutter_linux.h>
#include <gtk/gtk.h>
#include <sys/utsname.h>
#include <cstring>
#include "flutter_libsparkmobile_plugin_private.h"
#define FLUTTER_LIBSPARKMOBILE_PLUGIN(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj), flutter_libsparkmobile_plugin_get_type(), \
FlutterLibsparkmobilePlugin))
struct _FlutterLibsparkmobilePlugin {
GObject parent_instance;
};
G_DEFINE_TYPE(FlutterLibsparkmobilePlugin, flutter_libsparkmobile_plugin, g_object_get_type())
// Called when a method call is received from Flutter.
static void flutter_libsparkmobile_plugin_handle_method_call(
FlutterLibsparkmobilePlugin* self,
FlMethodCall* method_call) {
g_autoptr(FlMethodResponse) response = nullptr;
const gchar* method = fl_method_call_get_name(method_call);
if (strcmp(method, "getPlatformVersion") == 0) {
response = get_platform_version();
} else {
response = FL_METHOD_RESPONSE(fl_method_not_implemented_response_new());
}
fl_method_call_respond(method_call, response, nullptr);
}
FlMethodResponse* get_platform_version() {
struct utsname uname_data = {};
uname(&uname_data);
g_autofree gchar *version = g_strdup_printf("Linux %s", uname_data.version);
g_autoptr(FlValue) result = fl_value_new_string(version);
return FL_METHOD_RESPONSE(fl_method_success_response_new(result));
}
static void flutter_libsparkmobile_plugin_dispose(GObject* object) {
G_OBJECT_CLASS(flutter_libsparkmobile_plugin_parent_class)->dispose(object);
}
static void flutter_libsparkmobile_plugin_class_init(FlutterLibsparkmobilePluginClass* klass) {
G_OBJECT_CLASS(klass)->dispose = flutter_libsparkmobile_plugin_dispose;
}
static void flutter_libsparkmobile_plugin_init(FlutterLibsparkmobilePlugin* self) {}
static void method_call_cb(FlMethodChannel* channel, FlMethodCall* method_call,
gpointer user_data) {
FlutterLibsparkmobilePlugin* plugin = FLUTTER_LIBSPARKMOBILE_PLUGIN(user_data);
flutter_libsparkmobile_plugin_handle_method_call(plugin, method_call);
}
void flutter_libsparkmobile_plugin_register_with_registrar(FlPluginRegistrar* registrar) {
FlutterLibsparkmobilePlugin* plugin = FLUTTER_LIBSPARKMOBILE_PLUGIN(
g_object_new(flutter_libsparkmobile_plugin_get_type(), nullptr));
g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
g_autoptr(FlMethodChannel) channel =
fl_method_channel_new(fl_plugin_registrar_get_messenger(registrar),
"flutter_libsparkmobile",
FL_METHOD_CODEC(codec));
fl_method_channel_set_method_call_handler(channel, method_call_cb,
g_object_ref(plugin),
g_object_unref);
g_object_unref(plugin);
}

View File

@@ -1,10 +0,0 @@
#include <flutter_linux/flutter_linux.h>
#include "include/flutter_libsparkmobile/flutter_libsparkmobile_plugin.h"
// This file exposes some plugin internals for unit testing. See
// https://github.com/flutter/flutter/issues/88724 for current limitations
// in the unit-testable API.
// Handles the getPlatformVersion method call.
FlMethodResponse *get_platform_version();

View File

@@ -1,26 +0,0 @@
#ifndef FLUTTER_PLUGIN_FLUTTER_LIBSPARKMOBILE_PLUGIN_H_
#define FLUTTER_PLUGIN_FLUTTER_LIBSPARKMOBILE_PLUGIN_H_
#include <flutter_linux/flutter_linux.h>
G_BEGIN_DECLS
#ifdef FLUTTER_PLUGIN_IMPL
#define FLUTTER_PLUGIN_EXPORT __attribute__((visibility("default")))
#else
#define FLUTTER_PLUGIN_EXPORT
#endif
typedef struct _FlutterLibsparkmobilePlugin FlutterLibsparkmobilePlugin;
typedef struct {
GObjectClass parent_class;
} FlutterLibsparkmobilePluginClass;
FLUTTER_PLUGIN_EXPORT GType flutter_libsparkmobile_plugin_get_type();
FLUTTER_PLUGIN_EXPORT void flutter_libsparkmobile_plugin_register_with_registrar(
FlPluginRegistrar* registrar);
G_END_DECLS
#endif // FLUTTER_PLUGIN_FLUTTER_LIBSPARKMOBILE_PLUGIN_H_

View File

@@ -1,31 +0,0 @@
#include <flutter_linux/flutter_linux.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "include/flutter_libsparkmobile/flutter_libsparkmobile_plugin.h"
#include "flutter_libsparkmobile_plugin_private.h"
// This demonstrates a simple unit test of the C portion of this plugin's
// implementation.
//
// Once you have built the plugin's example app, you can run these tests
// from the command line. For instance, for a plugin called my_plugin
// built for x64 debug, run:
// $ build/linux/x64/debug/plugins/my_plugin/my_plugin_test
namespace flutter_libsparkmobile {
namespace test {
TEST(FlutterLibsparkmobilePlugin, GetPlatformVersion) {
g_autoptr(FlMethodResponse) response = get_platform_version();
ASSERT_NE(response, nullptr);
ASSERT_TRUE(FL_IS_METHOD_SUCCESS_RESPONSE(response));
FlValue* result = fl_method_success_response_get_result(
FL_METHOD_SUCCESS_RESPONSE(response));
ASSERT_EQ(fl_value_get_type(result), FL_VALUE_TYPE_STRING);
// The full string varies, so just validate that it has the right format.
EXPECT_THAT(fl_value_get_string(result), testing::StartsWith("Linux "));
}
} // namespace test
} // namespace flutter_libsparkmobile

View File

@@ -1,19 +0,0 @@
import Cocoa
import FlutterMacOS
public class FlutterLibsparkmobilePlugin: NSObject, FlutterPlugin {
public static func register(with registrar: FlutterPluginRegistrar) {
let channel = FlutterMethodChannel(name: "flutter_libsparkmobile", binaryMessenger: registrar.messenger)
let instance = FlutterLibsparkmobilePlugin()
registrar.addMethodCallDelegate(instance, channel: channel)
}
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
switch call.method {
case "getPlatformVersion":
result("macOS " + ProcessInfo.processInfo.operatingSystemVersionString)
default:
result(FlutterMethodNotImplemented)
}
}
}

View File

@@ -0,0 +1,3 @@
// Relative import to be able to reuse the C sources.
// See the comment in ../{projectName}}.podspec for more information.
#include flutter_libsparkmobile.cpp

View File

@@ -5,14 +5,18 @@
Pod::Spec.new do |s|
s.name = 'flutter_libsparkmobile'
s.version = '0.0.1'
s.summary = 'A new Flutter plugin project.'
s.summary = 'A new Flutter project.'
s.description = <<-DESC
A new Flutter plugin project.
A new Flutter project.
DESC
s.homepage = 'http://example.com'
s.license = { :file => '../LICENSE' }
s.author = { 'Your Company' => 'email@example.com' }
# This will ensure the source files in Classes/ are included in the native
# builds of apps using this FFI plugin. Podspec does not support relative
# paths, so Classes contains a forwarder C file that relatively imports
# `../src/*` so that the C sources can be shared among all target platforms.
s.source = { :path => '.' }
s.source_files = 'Classes/**/*'
s.dependency 'FlutterMacOS'

View File

@@ -1,5 +1,5 @@
name: flutter_libsparkmobile
description: A new Flutter plugin project.
description: Flutter Sparkmobile ffi wrapper
version: 0.0.1
homepage:
@@ -12,14 +12,12 @@ dependencies:
flutter:
sdk: flutter
plugin_platform_interface: ^2.0.2
test: ^1.24.1
typed_data: ^1.3.2
dev_dependencies:
ffigen: ^6.1.2
flutter_test:
sdk: flutter
flutter_lints: ^2.0.0
ffigen: ^9.0.1
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
@@ -36,19 +34,20 @@ flutter:
# This is required for using `dart:ffi`.
# All these are used by the tooling to maintain consistency when
# adding or updating assets for this project.
#
# Please refer to README.md for a detailed explanation.
plugin:
platforms:
android:
package: com.cypherstack.flutter_libsparkmobile
pluginClass: FlutterLibsparkmobilePlugin
ffiPlugin: true
ios:
pluginClass: FlutterLibsparkmobilePlugin
ffiPlugin: true
linux:
pluginClass: FlutterLibsparkmobilePlugin
ffiPlugin: true
macos:
pluginClass: FlutterLibsparkmobilePlugin
ffiPlugin: true
windows:
pluginClass: FlutterLibsparkmobilePluginCApi
ffiPlugin: true
# To add assets to your plugin package, add an assets section, like this:
# assets:
@@ -80,15 +79,3 @@ flutter:
#
# For details regarding fonts in packages, see
# https://flutter.dev/custom-fonts/#from-packages
ffigen:
name: SparkMobileBindings
description: Bindings for sparkmobile.
language: c
output: 'lib/flutter_libsparkmobile_bindings.dart'
headers:
entry-points:
- 'sparkmobile/src/dart_interface.h'
# copyCMakeLists.sh creates this header, see build_all.sh.
preamble: |
// ignore_for_file: camel_case_types, non_constant_identifier_names, unused_element, unused_field, return_of_invalid_type, void_checks, annotate_overrides, no_leading_underscores_for_local_identifiers, library_private_types_in_public_api

View File

@@ -1,5 +1,7 @@
#!/bin/bash
mkdir -p build
set -e
./build_openssl.sh
./build_sharedfile.sh

View File

@@ -2,67 +2,66 @@
set -e
export WORKDIR="$(pwd)/../../src/build/linux"
export ORIGINAL_PATH=$PATH
export THREADS=16
export TYPES_OF_BUILD="x86_64"
if [ -z "$IS_ARM" ]; then
TYPES_OF_BUILD="x86_64"
export TYPES_OF_BUILD="x86_64"
else
TYPES_OF_BUILD="aarch64"
export TYPES_OF_BUILD="aarch64"
fi
THREADS=16
WORKDIR="$(pwd)/"build
CACHEDIR="$(pwd)/"cache
mkdir -p $CACHEDIR
if [ ! -d "$WORKDIR" ] ; then
mkdir -p "$WORKDIR"
fi
OPENSSL_FILENAME=openssl-1.1.1k.tar.gz
OPENSSL_FILE_PATH=$CACHEDIR/$OPENSSL_FILENAME
OPENSSL_SRC_DIR=$WORKDIR/openssl-1.1.1k
OPENSSL_FILENAME="openssl-1.1.1k.tar.gz"
OPENSSL_FILE_PATH="$WORKDIR/$OPENSSL_FILENAME"
OPENSSL_SRC_DIR="$WORKDIR/openssl-1.1.1k"
OPENSSL_SHA256="892a0875b9872acd04a9fde79b1f943075d5ea162415de3047c327df33fbaee5"
ZLIB_DIR=$WORKDIR/zlib
ZLIB_TAG=v1.2.11
ZLIB_DIR="$WORKDIR/zlib"
ZLIB_TAG="v1.2.11"
ZLIB_COMMIT_HASH="cacf7f1d4e3d44d871b605da3b647f07d718623f"
# If a zlib dir exists, we assume that it's already been built.
# TODO make a better zlib-validity check.
if [ ! -d "$ZLIB_DIR" ] ; then
git clone -b $ZLIB_TAG --depth 1 https://github.com/madler/zlib $ZLIB_DIR
cd $ZLIB_DIR
git reset --hard $ZLIB_COMMIT_HASH
./configure --static
make
fi
cd $ZLIB_DIR
git reset --hard $ZLIB_COMMIT_HASH
./configure --static
make
# Download openssl if it doesn't exist.
curl https://www.openssl.org/source/$OPENSSL_FILENAME -o $OPENSSL_FILE_PATH
# Validate checksum.
echo $OPENSSL_SHA256 $OPENSSL_FILE_PATH | sha256sum -c - || exit 1
# Build openssl for each arch.
for arch in $TYPES_OF_BUILD
do
echo "Building $TYPES_OF_BUILD"
PREFIX=$WORKDIR/prefix_${arch}
echo "Building $TYPES_OF_BUILD"
PREFIX=$WORKDIR/prefix_${arch}
case $arch in
"x86_64") X_ARCH="linux-x86_64";;
"aarch64") X_ARCH="linux-aarch64";;
*) X_ARCH="linux-x86_64";;
esac
case $arch in
"x86_64") X_ARCH="linux-x86_64";;
"aarch64") X_ARCH="linux-aarch64";;
*) X_ARCH="linux-x86_64";;
esac
cd $WORKDIR
# Don't delete the openssl source dir. We don't need clean builds every time.
# rm -rf $OPENSSL_SRC_DIR
tar -xzf $OPENSSL_FILE_PATH -C $WORKDIR
cd $OPENSSL_SRC_DIR
cd $WORKDIR
rm -rf $OPENSSL_SRC_DIR
tar -xzf $OPENSSL_FILE_PATH -C $WORKDIR
cd $OPENSSL_SRC_DIR
./Configure ${X_ARCH} \
no-asm no-shared \
--with-zlib-include=${PREFIX}/include \
--with-zlib-lib=${PREFIX}/lib \
--prefix=${PREFIX} \
--openssldir=${PREFIX}
make -j$THREADS
make -j$THREADS install_sw
#sed -i -e "s/mandroid/target\ ${TARGET}\-linux\-android/" Configure
./Configure ${X_ARCH} \
no-asm no-shared \
--with-zlib-include=${PREFIX}/include \
--with-zlib-lib=${PREFIX}/lib \
--prefix=${PREFIX} \
--openssldir=${PREFIX}
make -j$THREADS
make -j$THREADS install_sw
done

View File

@@ -1,39 +0,0 @@
#!/bin/bash
# Copy the sparkmobile submodule to the build directory for interfacing to Dart.
#
# We copy the fresh and unmodified sparkmobile submodule to the build directory in order to graft
# an additional Dart interface onto the sparkmobile library.
cp -r ../../sparkmobile .
WORKDIR="$(pwd)/build"
SEDWORKDIR=$(echo $WORKDIR | sed 's/\//\\\//g')
# Create new CMakelists.txt files for the sparkmobile and secp256k1 directories.
#
# Copy the template CMakelists.txt files to the working sparkmobile directory and replace the
# distribution_DIR variable with the working directory.
sed "s/SET(distribution_DIR \/opt\/android)/SET(distribution_DIR $SEDWORKDIR)/g" ./CMakeLists/sparkmobile/template_CMakeLists.txt > ./CMakeLists/sparkmobile/CMakeLists.txt
sed "s/SET(distribution_DIR \/opt\/android)/SET(distribution_DIR $SEDWORKDIR)/g" ./CMakeLists/secp256k1/template_CMakeLists.txt > ./CMakeLists/secp256k1/CMakeLists.txt
# Copy the sparkmobile and secp256k1 CMakeLists.txts.
cp CMakeLists/sparkmobile/CMakeLists.txt sparkmobile/
cp CMakeLists/secp256k1/CMakeLists.txt sparkmobile/secp256k1/
# Git versioning.
echo ''$(git log -1 --pretty=format:"%H")' '$(date) >> build/git_commit_version.txt
VERSIONS_FILE=../../lib/git_versions.dart
EXAMPLE_VERSIONS_FILE=../../lib/git_versions_example.dart
if [ ! -f "$VERSIONS_FILE" ]; then
cp $EXAMPLE_VERSIONS_FILE $VERSIONS_FILE
fi
COMMIT=$(git log -1 --pretty=format:"%H")
OS="LINUX"
# Write the commit hash to the versions file.
sed -i "/\/\*${OS}_VERSION/c\\/\*${OS}_VERSION\*\/ const ${OS}_VERSION = \"$COMMIT\";" $VERSIONS_FILE
# Build the shared library.
cd build
cmake ../sparkmobile
make -j$(nproc)

17
scripts/prebuild.sh Executable file
View File

@@ -0,0 +1,17 @@
#!/usr/bin/env bash
set -e
cd ../src/deps
if [ -d sparkmobile ]; then
rm -rf sparkmobile
fi
git clone https://github.com/firoorg/sparkmobile.git
cd sparkmobile
git checkout ef2e39aae18ecc49e0ddc63a3183e9764b96012e
cd ..
cp CMakeLists/sparkmobile/CMakeLists.txt sparkmobile/
cp CMakeLists/secp256k1/CMakeLists.txt sparkmobile/secp256k1/

Submodule sparkmobile deleted from fe2148f01b

26
src/CMakeLists.txt Normal file
View File

@@ -0,0 +1,26 @@
# The Flutter tooling requires that developers have CMake 3.10 or later
# installed. You should not increase this version, as doing so will cause
# the plugin to fail to compile for some customers of the plugin.
cmake_minimum_required(VERSION 3.10)
project(flutter_libsparkmobile_library VERSION 0.0.1)
SET(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake;${CMAKE_MODULE_PATH}")
add_library(flutter_libsparkmobile SHARED
"flutter_libsparkmobile.cpp"
"utils.cpp"
)
add_subdirectory("deps/sparkmobile")
set_target_properties(flutter_libsparkmobile PROPERTIES
PUBLIC_HEADER flutter_libsparkmobile.h
OUTPUT_NAME "flutter_libsparkmobile"
)
target_link_libraries(flutter_libsparkmobile sparkmobile)
target_compile_definitions(flutter_libsparkmobile PUBLIC DART_SHARED_LIB)

7
src/cmake/FindGMP.cmake Normal file
View File

@@ -0,0 +1,7 @@
find_path(GMP_INCLUDE_DIRS NAMES gmp.h)
find_library(GMP_LIBRARY NAMES gmp libgmp)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(GMP DEFAULT_MSG
GMP_INCLUDE_DIRS
GMP_LIBRARY)
mark_as_advanced(GMP_INCLUDE_DIRS GMP_LIBRARY)

View File

@@ -1,9 +1,9 @@
# Copyright (c) 2017 The Bitcoin developers
cmake_minimum_required(VERSION 3.1)
project(secp256k1)
project(secp256k1_spark)
SET(distribution_DIR /opt/android)
SET(distribution_DIR "../../../build/${BUILD_FOR_SYSTEM_NAME}")
SET(OPENSSL_ROOT_DIR ${distribution_DIR}/prefix_${ANDROID_ABI})
SET(OPENSSL_LIBRARIES_DIR "${OPENSSL_ROOT_DIR}/lib")
@@ -22,14 +22,14 @@ LINK_DIRECTORIES(${OPENSSL_LIBRARIES_DIR})
# TODO: change this to include when possible
include_directories(. include src/modules/ecdh src)
add_library(secp256k1 src/secp256k1.c src/cpp/GroupElement.cpp src/cpp/MultiExponent.cpp src/cpp/Scalar.cpp src/modules/ecdh/main_impl.h include/secp256k1_ecdh.h src/scratch_impl.h src/scratch.h)
add_library(secp256k1_spark src/secp256k1.c src/cpp/GroupElement.cpp src/cpp/MultiExponent.cpp src/cpp/Scalar.cpp src/modules/ecdh/main_impl.h include/secp256k1_ecdh.h src/scratch_impl.h src/scratch.h)
# We need to link in GMP
find_package(GMP)
if(GMP_INCLUDE_DIR AND GMP_LIBRARIES)
target_include_directories(secp256k1 PUBLIC ${GMP_INCLUDE_DIR})
target_link_libraries(secp256k1 ${GMP_LIBRARIES})
target_compile_definitions(secp256k1
target_link_libraries(secp256k1_spark ${GMP_LIBRARIES})
target_compile_definitions(secp256k1_spark
PUBLIC
HAVE_LIBGMP
USE_NUM_GMP
@@ -37,7 +37,7 @@ if(GMP_INCLUDE_DIR AND GMP_LIBRARIES)
USE_SCALAR_INV_BUILTIN
)
else()
target_compile_definitions(secp256k1
target_compile_definitions(secp256k1_spark
PUBLIC
USE_NUM_NONE
USE_FIELD_INV_BUILTIN
@@ -49,7 +49,7 @@ endif()
include(CheckTypeSize)
check_type_size(__int128 SIZEOF___INT128)
if(SIZEOF___INT128 EQUAL 16)
target_compile_definitions(secp256k1 PUBLIC HAVE___INT128)
target_compile_definitions(secp256k1_spark PUBLIC HAVE___INT128)
else()
# If we do not support __int128, we should be falling back
# on 32bits implementations for field and scalar.
@@ -64,17 +64,17 @@ if(CMAKE_SIZEOF_VOID_P EQUAL 8)
message(SEND_ERROR "Compiler does not support __int128")
endif()
target_compile_definitions(secp256k1 PUBLIC USE_SCALAR_4X64)
target_compile_definitions(secp256k1 PUBLIC USE_FIELD_5X52)
target_compile_definitions(secp256k1_spark PUBLIC USE_SCALAR_4X64)
target_compile_definitions(secp256k1_spark PUBLIC USE_FIELD_5X52)
else()
target_compile_definitions(secp256k1 PUBLIC USE_SCALAR_8X32)
target_compile_definitions(secp256k1 PUBLIC USE_FIELD_10X26)
target_compile_definitions(secp256k1_spark PUBLIC USE_SCALAR_8X32)
target_compile_definitions(secp256k1_spark PUBLIC USE_FIELD_10X26)
endif()
# TODO: emult static precomputation
# TODO: ecdh module
# TODO: RECOVERY module
TARGET_INCLUDE_DIRECTORIES(secp256k1 PUBLIC ${OPENSSL_INCLUDE_DIR})
TARGET_LINK_LIBRARIES(secp256k1 ${OPENSSL_LIBRARIES})
#target_link_libraries(secp256k1 ${OPENSSL_LIBRARIES})
TARGET_INCLUDE_DIRECTORIES(secp256k1_spark PUBLIC ${OPENSSL_INCLUDE_DIR})
TARGET_LINK_LIBRARIES(secp256k1_spark ${OPENSSL_LIBRARIES})
#target_link_libraries(secp256k1_spark ${OPENSSL_LIBRARIES})

View File

@@ -4,7 +4,7 @@ project(sparkmobile)
set(OPENSSL_USE_STATIC_LIBS OFF CACHE BOOL "" FORCE)
SET(distribution_DIR /opt/android)
SET(distribution_DIR "../../build/${BUILD_FOR_SYSTEM_NAME}")
SET(OPENSSL_ROOT_DIR ${distribution_DIR}/prefix_${ANDROID_ABI})
SET(OPENSSL_LIBRARIES_DIR "${OPENSSL_ROOT_DIR}/lib")
@@ -42,9 +42,7 @@ set_property(SOURCE src/spark.cpp
src/schnorr.cpp
src/spend_transaction.cpp
src/transcript.cpp
src/util.cpp
src/utils.cpp
src/dart_interface.cpp
src/util.cpp
PROPERTY COMPILE_FLAGS "-std=c++17" )
add_library(sparkmobile SHARED
@@ -75,12 +73,10 @@ add_library(sparkmobile SHARED
src/schnorr.cpp
src/spend_transaction.cpp
src/transcript.cpp
src/util.cpp
src/utils.cpp
src/dart_interface.cpp)
src/util.cpp)
target_link_libraries(sparkmobile ${OPENSSL_LIBRARIES} secp256k1)
target_link_libraries(sparkmobile ${OPENSSL_LIBRARIES} secp256k1_spark)
add_dependencies(sparkmobile secp256k1)
add_dependencies(sparkmobile secp256k1_spark)
target_include_directories(sparkmobile PUBLIC secp256k1 ${OPENSSL_INCLUDE_DIR})
target_include_directories(sparkmobile PUBLIC secp256k1_spark ${OPENSSL_INCLUDE_DIR})

View File

@@ -0,0 +1,111 @@
#include "flutter_libsparkmobile.h"
#include "utils.h"
#include "deps/sparkmobile/include/spark.h"
#include <cstring>
#include <iostream> // Just for printing.
using namespace spark;
/*
* FFI-friendly wrapper for spark::getAddress.
*/
FFI_PLUGIN_EXPORT
const char* getAddress(const char* keyDataHex, int index, int diversifier, int isTestNet) {
try {
// Use the hex string directly to create the SpendKey.
spark::SpendKey spendKey = createSpendKeyFromData(keyDataHex, index);
spark::FullViewKey fullViewKey(spendKey);
spark::IncomingViewKey incomingViewKey(fullViewKey);
spark::Address address(incomingViewKey, static_cast<uint64_t>(diversifier));
// Encode the Address object into a string using the appropriate network.
std::string encodedAddress = address.encode(isTestNet ? spark::ADDRESS_NETWORK_TESTNET : spark::ADDRESS_NETWORK_MAINNET);
// Allocate memory for the C-style string.
char* cstr = new char[encodedAddress.length() + 1];
std::strcpy(cstr, encodedAddress.c_str());
return cstr;
} catch (const std::exception& e) {
std::cerr << "Exception: " << e.what() << std::endl;
return nullptr;
}
}
/*
* FFI-friendly wrapper for spark:identifyCoin.
*
* Uses the utility functions spark::Coin fromFFI(const CCoin& c_struct) to pass parameters to the
* C++ function spark::identifyCoin(const Coin& coin), then uses the utility function
* CIdentifiedCoinData toFFI(const spark::IdentifiedCoinData& cpp_struct) to convert the result back
* to a C struct.
*
* We also need the incoming view key or we need to derive it, so accept keyDataHex and index.
*/
FFI_PLUGIN_EXPORT
struct CIdentifiedCoinData identifyCoin(struct CCoin c_struct, const char* keyDataHex, int index) {
try {
spark::Coin coin = fromFFI(c_struct);
// Derive the incoming view key from the key data and index.
spark::SpendKey spendKey = createSpendKeyFromData(keyDataHex, index);
spark::FullViewKey fullViewKey(spendKey);
spark::IncomingViewKey incomingViewKey(fullViewKey);
spark::IdentifiedCoinData identifiedCoinData = coin.identify(incomingViewKey);
return toFFI(identifiedCoinData);
} catch (const std::exception& e) {
std::cerr << "Exception: " << e.what() << std::endl;
return CIdentifiedCoinData();
}
}
/*
* FFI-friendly wrapper for spark::createSparkMintRecipients.
*/
FFI_PLUGIN_EXPORT
struct CCRecipient* createSparkMintRecipients(
int numRecipients,
struct PubKeyScript* pubKeyScripts,
uint64_t* amounts,
const char* memo,
int subtractFee)
{
try {
std::vector<CRecipient> recipients;
for (int i = 0; i < numRecipients; i++) {
CScript scriptPubKey = createCScriptFromBytes(
pubKeyScripts[i].bytes,
pubKeyScripts[i].length
);
CRecipient recipient;
recipient.pubKey = scriptPubKey;
recipient.amount = amounts[i];
recipient.subtractFeeFromAmount = (bool)subtractFee;
recipients.push_back(recipient);
}
std::vector<CCRecipient> ccRecipients;
for (const CRecipient& recipient : recipients) {
CCRecipient ccRecipient = toFFI(recipient);
ccRecipients.push_back(ccRecipient);
}
CCRecipient* result = new CCRecipient[numRecipients];
std::copy(ccRecipients.begin(), ccRecipients.end(), result);
return result;
} catch (const std::exception& e) {
std::cerr << "Exception: " << e.what() << std::endl;
return nullptr;
}
}

View File

@@ -0,0 +1,118 @@
#ifndef ORG_FIRO_SPARK_DART_INTERFACE_H
#define ORG_FIRO_SPARK_DART_INTERFACE_H
#include <stdint.h>
#ifndef FFI_PLUGIN_EXPORT
#ifdef __cplusplus
#define FFI_PLUGIN_EXPORT extern "C" __attribute__((visibility("default"))) __attribute__((used))
#else
#define FFI_PLUGIN_EXPORT __attribute__((visibility("default"))) __attribute__((used))
#endif
#ifdef _WIN32
#define FFI_PLUGIN_EXPORT __declspec(dllexport)
#endif
#endif
/*
* FFI-friendly wrapper for spark::getAddress.
*/
FFI_PLUGIN_EXPORT
const char* getAddress(const char* keyDataHex, int index, int diversifier, int isTestNet);
/*
FFI_PLUGIN_EXPORT
const char *createFullViewKey(const char* keyData, int index);
FFI_PLUGIN_EXPORT
const char* createIncomingViewKey(const char* keyData, int index);
*/
/*
* FFI-friendly wrapper for a spark::Coin.
*
* A Coin is a type, a key, an index, a value, a memo, and a serial context. We accept these params
* as a C struct, deriving the key from the keyData and index.
*/
struct CCoin {
char type;
const unsigned char* k;
int kLength;
const char* keyData;
int index;
uint64_t v;
const unsigned char* memo;
int memoLength;
const unsigned char* serial_context;
int serial_contextLength;
};
/*
* FFI-friendly wrapper for a spark::IdentifiedCoinData.
*
* An IdentifiedCoinData is a diversifier, encrypted diversifier, value, nonce, and memo. We accept
* these params as a C struct.
*/
struct CIdentifiedCoinData {
uint64_t i;
const unsigned char* d;
int dLength;
uint64_t v;
const unsigned char* k;
int kLength;
const char* memo;
int memoLength;
};
/*
* FFI-friendly wrapper for spark::identifyCoin.
*/
FFI_PLUGIN_EXPORT
struct CIdentifiedCoinData identifyCoin(struct CCoin c_struct, const char* keyDataHex, int index);
/*
* FFI-friendly wrapper for a spark::CRecipient.
*
* A CRecipient is a CScript, CAmount, and a bool. We accept a C-style, FFI-friendly CCRecipient
* struct in order to construct a C++ CRecipient. A CScript is constructed from a hex string, a
* CAmount is just a uint64_t, and the bool will be an int.
*/
struct CCRecipient {
const unsigned char* pubKey;
int pubKeyLength;
uint64_t cAmount;
int subtractFee;
};
/*
* FFI-friendly wrapper for a spark::MintedCoinData.
*
* A MintedCoinData is a struct that contains an Address, a uint64_t value, and a string memo. We
* accept these as a CMintedCoinData from the Dart interface, and convert them to a MintedCoinData
* struct.
*/
struct CMintedCoinData {
const char* address;
uint64_t value;
const char* memo;
};
struct PubKeyScript {
unsigned char* bytes;
int length;
};
/*
* FFI-friendly wrapper for spark::createSparkMintRecipients.
*/
FFI_PLUGIN_EXPORT
struct CCRecipient* createSparkMintRecipients(
int numRecipients,
struct PubKeyScript* pubKeyScripts,
uint64_t* amounts,
const char* memo,
int subtractFee);
#endif //ORG_FIRO_SPARK_DART_INTERFACE_H

318
src/utils.cpp Normal file
View File

@@ -0,0 +1,318 @@
#include "utils.h"
#include <iomanip>
#include <iostream>
#include <sstream>
#include <utility>
#include <vector>
#include <string>
#include "flutter_libsparkmobile.h"
#include "deps/sparkmobile/src/coin.h"
#include "deps/sparkmobile/src/keys.h"
#include "deps/sparkmobile//bitcoin/script.h" // For CScript.
/*
* Utility function to generate an address from keyData, index, and a diversifier.
*/
const char* getAddressFromData(const char* keyData, int index, const uint64_t diversifier) {
try {
spark::SpendKey spendKey = createSpendKeyFromData(keyData, index);
spark::FullViewKey fullViewKey(spendKey);
spark::IncomingViewKey incomingViewKey(fullViewKey);
spark::Address address(incomingViewKey, diversifier);
// Encode the Address object into a string.
std::string encodedAddress = address.encode(spark::ADDRESS_NETWORK_TESTNET);
// Allocate memory for the C-style string and return it.
char* result = new char[encodedAddress.size() + 1];
std::copy(encodedAddress.begin(), encodedAddress.end(), result);
result[encodedAddress.size()] = '\0'; // Null-terminate the C string.
return result;
} catch (const std::exception& e) {
return nullptr;
}
}
/*
* Utility function to generate SpendKey from keyData and index.
*/
spark::SpendKey createSpendKeyFromData(const char *keyData, int index) {
try {
// Convert the keyData from hex string to binary
unsigned char* key_data_bin = hexToBytes(keyData);
const SpendKeyData *data = new SpendKeyData(key_data_bin, index);
return createSpendKey(*data);
} catch (const std::exception& e) {
// We can't return here, so just throw the exception again.
throw e;
}
}
/*
* CCoin factory.
*
* TODO manage the memory allocated by this function.
*/
struct CCoin createCCoin(char type, const unsigned char* k, int kLength, const char* keyData, int index, uint64_t v, const unsigned char* memo, int memoLength, const unsigned char* serial_context, int serial_contextLength) {
CCoin coin;
coin.type = type;
coin.k = copyBytes(k, kLength);
coin.kLength = kLength;
coin.keyData = strdup(keyData);
coin.index = index;
coin.v = v;
coin.memo = copyBytes(memo, memoLength);
coin.memoLength = memoLength;
coin.serial_context = copyBytes(serial_context, serial_contextLength);
coin.serial_contextLength = serial_contextLength;
return coin;
}
/*
* Utility function to convert an FFI-friendly C CCoin struct to a C++ Coin struct.
*/
spark::Coin fromFFI(const CCoin& c_struct) {
spark::Coin cpp_struct(
// The test params are only used for unit tests.
spark::Params::get_default(),
c_struct.type,
spark::Scalar(c_struct.k),
spark::Address(spark::IncomingViewKey(spark::FullViewKey(createSpendKeyFromData(c_struct.keyData, c_struct.index))), c_struct.index),
c_struct.v,
std::string(reinterpret_cast<const char*>(c_struct.memo), c_struct.memoLength),
std::vector<unsigned char>(c_struct.serial_context, c_struct.serial_context + c_struct.serial_contextLength)
);
return cpp_struct;
}
/*
* Utility function to convert a C++ IdentifiedCoinData struct to an FFI-friendly struct.
*/
CIdentifiedCoinData toFFI(const spark::IdentifiedCoinData& cpp_struct) {
CIdentifiedCoinData c_struct;
c_struct.i = cpp_struct.i;
c_struct.d = copyBytes(cpp_struct.d.data(), cpp_struct.d.size());
c_struct.dLength = cpp_struct.d.size();
c_struct.v = cpp_struct.v;
// Serialize and copy the Scalar k.
std::vector<unsigned char> scalarBytes(32);
cpp_struct.k.serialize(scalarBytes.data());
c_struct.k = copyBytes(scalarBytes.data(), scalarBytes.size());
c_struct.kLength = scalarBytes.size();
// Copy the memo.
c_struct.memo = strdup(cpp_struct.memo.c_str());
c_struct.memoLength = cpp_struct.memo.size();
return c_struct;
}
/*
* Factory function to create a CScript from a byte array.
*/
CScript createCScriptFromBytes(const unsigned char* bytes, int length) {
// Construct a CScript object
CScript script;
// Check if bytes is not nullptr and length is positive
if (bytes != nullptr && length > 0) {
// Append each byte to the script
for (int i = 0; i < length; ++i) {
script << bytes[i];
}
}
return script;
}
/*
* Utility function to convert a C++ CScript to a byte array.
*/
std::vector<unsigned char> serializeCScript(const CScript& script) {
return std::vector<unsigned char>(script.begin(), script.end());
}
/*
* Utility function to convert an FFI-friendly C CCRecipient struct to a C++ CRecipient struct.
*/
CRecipient fromFFI(const CCRecipient& c_struct) {
// Use the factory function to create a CScript object.
CScript script = createCScriptFromBytes(c_struct.pubKey, c_struct.pubKeyLength);
CRecipient cpp_struct = createCRecipient(
script,
c_struct.cAmount,
static_cast<bool>(c_struct.subtractFee)
);
return cpp_struct;
}
/*
* CCRecipient factory.
*
* TODO manage the memory allocated by this function.
*/
struct CCRecipient createCCRecipient(const unsigned char* pubKey, uint64_t amount, int subtractFee) {
CCRecipient recipient;
recipient.pubKey = copyBytes(pubKey, 32);
recipient.cAmount = amount;
recipient.subtractFee = subtractFee;
return recipient;
}
/*
* Utility function to convert a C++ CRecipient struct to an FFI-friendly struct.
*/
CCRecipient toFFI(const CRecipient& cpp_struct) {
CCRecipient c_struct;
// Serialize CScript and copy.
std::vector<unsigned char> scriptBytes = serializeCScript(cpp_struct.pubKey);
if (!scriptBytes.empty()) {
c_struct.pubKey = copyBytes(scriptBytes.data(), scriptBytes.size());
c_struct.pubKeyLength = static_cast<int>(scriptBytes.size());
} else {
c_struct.pubKey = nullptr;
c_struct.pubKeyLength = 0;
}
c_struct.cAmount = cpp_struct.amount;
c_struct.subtractFee = static_cast<int>(cpp_struct.subtractFeeFromAmount);
return c_struct;
}
/*
* CRecipient factory.
*
* TODO manage the memory allocated by this function.
*/
CRecipient createCRecipient(const CScript& script, CAmount amount, bool subtractFee) {
CRecipient recipient;
recipient.pubKey = script;
recipient.amount = amount;
recipient.subtractFeeFromAmount = subtractFee;
return recipient;
}
/*
* Utility function to decode an Address from a string.
*/
spark::Address decodeAddress(const std::string& str) {
spark::Address address;
address.decode(str);
return address;
}
/*
* MintedCoinData factory.
*/
spark::MintedCoinData createMintedCoinData(const char* address, uint64_t v, const char* memo) {
return {
decodeAddress(address),
v,
memo
};
}
/*
* Utility function to convert an FFI-friendly C CMintedCoinData struct to a C++ MintedCoinData.
*/
spark::MintedCoinData fromFFI(const CMintedCoinData& c_struct) {
return createMintedCoinData(
c_struct.address,
c_struct.value,
c_struct.memo
);
}
/*
* CMintedCoinData factory.
*/
CMintedCoinData createCMintedCoinData(const char* address, uint64_t value, const char* memo) {
CMintedCoinData c_struct;
c_struct.address = strdup(address);
c_struct.value = value;
c_struct.memo = strdup(memo);
return c_struct;
}
/*
* Utility function to convert a C++ MintedCoinData struct to an FFI-friendly CMintedCoinData.
*/
CMintedCoinData toFFI(const spark::MintedCoinData& cpp_struct) {
return createCMintedCoinData(
cpp_struct.address.encode(true).c_str(),
cpp_struct.v,
cpp_struct.memo.c_str()
);
}
/*
* Utility function for deep copying byte arrays.
*
* Used by createCCoin.
*/
unsigned char* copyBytes(const unsigned char* source, int length) {
if (source == nullptr || length <= 0) return nullptr;
unsigned char* dest = new unsigned char[length];
std::memcpy(dest, source, length);
return dest;
}
unsigned char *hexToBytes(const char *hexstr) {
size_t length = strlen(hexstr) / 2;
auto *chrs = (unsigned char *) malloc((length + 1) * sizeof(unsigned char));
for (size_t i = 0, j = 0; j < length; i += 2, j++) {
chrs[j] = (hexstr[i] % 32 + 9) % 25 * 16 + (hexstr[i + 1] % 32 + 9) % 25;
}
chrs[length] = '\0';
return chrs;
}
const char *bytesToHex(const unsigned char *bytes, int size) {
std::string str;
for (int i = 0; i < size; ++i) {
const unsigned char ch = bytes[i];
str.append(&hexArray[(ch & 0xF0) >> 4], 1);
str.append(&hexArray[ch & 0xF], 1);
}
char *new_str = new char[std::strlen(str.c_str()) + 1];
std::strcpy(new_str, str.c_str());
return new_str;
}
const char *bytesToHex(const char *bytes, int size) {
std::string str;
for (int i = 0; i < size; ++i) {
const unsigned char ch = (const unsigned char) bytes[i];
str.append(&hexArray[(ch & 0xF0) >> 4], 1);
str.append(&hexArray[ch & 0xF], 1);
}
char *new_str = new char[std::strlen(str.c_str()) + 1];
std::strcpy(new_str, str.c_str());
return new_str;
}
const char *bytesToHex(std::vector<unsigned char> bytes, int size) {
std::string str;
for (int i = 0; i < size; ++i) {
const unsigned char ch = bytes[i];
str.append(&hexArray[(ch & 0xF0) >> 4], 1);
str.append(&hexArray[ch & 0xF], 1);
}
char *new_str = new char[std::strlen(str.c_str()) + 1];
std::strcpy(new_str, str.c_str());
return new_str;
}

62
src/utils.h Normal file
View File

@@ -0,0 +1,62 @@
#ifndef ORG_FIRO_SPARK_UTILS_H
#define ORG_FIRO_SPARK_UTILS_H
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "flutter_libsparkmobile.h"
#include "deps/sparkmobile/include/spark.h"
const char* getAddressFromData(const char* keyData, int index, const uint64_t diversifier);
spark::SpendKey createSpendKeyFromData(const char *keyData, int index);
spark::Coin fromFFI(const CCoin& c_struct);
CCoin toFFI(const spark::Coin& cpp_struct);
struct CCoin createCCoin(char type, const unsigned char* k, int kLength, const char* keyData, int index, uint64_t v, const unsigned char* memo, int memoLength, const unsigned char* serial_context, int serial_contextLength);
spark::IdentifiedCoinData fromFFI(const CIdentifiedCoinData& c_struct);
CIdentifiedCoinData toFFI(const spark::IdentifiedCoinData& cpp_struct);
CScript createCScriptFromBytes(const unsigned char* bytes, int length);
std::vector<unsigned char> serializeCScript(const CScript& script);
CRecipient createCRecipient(const CScript& script, CAmount amount, bool subtractFee);
CRecipient fromFFI(const CCRecipient& c_struct);
struct CCRecipient createCCRecipient(const unsigned char* pubKey, uint64_t amount, int subtractFee);
CCRecipient toFFI(const CRecipient& cpp_struct);
spark::Address decodeAddress(const std::string& str);
spark::MintedCoinData createMintedCoinData(const char* address, uint64_t v, const char* memo);
spark::MintedCoinData fromFFI(const CMintedCoinData& c_struct);
CMintedCoinData createCMintedCoinData(const char* address, uint64_t value, const char* memo);
CMintedCoinData toFFI(const spark::MintedCoinData& cpp_struct);
char const hexArray[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd',
'e', 'f'};
unsigned char* copyBytes(const unsigned char* source, int length);
unsigned char *hexToBytes(const char *str);
const char *bytesToHex(const unsigned char *bytes, int size);
const char *bytesToHex(const char *bytes, int size);
const char *bytesToHex(std::vector<unsigned char> bytes, int size);
#endif //ORG_FIRO_SPARK_UTILS_H

View File

@@ -1,27 +0,0 @@
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_libsparkmobile/flutter_libsparkmobile_method_channel.dart';
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
MethodChannelFlutterLibsparkmobile platform = MethodChannelFlutterLibsparkmobile();
const MethodChannel channel = MethodChannel('flutter_libsparkmobile');
setUp(() {
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(
channel,
(MethodCall methodCall) async {
return '42';
},
);
});
tearDown(() {
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null);
});
test('getPlatformVersion', () async {
expect(await platform.getPlatformVersion(), '42');
});
}

View File

@@ -8,89 +8,16 @@ cmake_minimum_required(VERSION 3.14)
set(PROJECT_NAME "flutter_libsparkmobile")
project(${PROJECT_NAME} LANGUAGES CXX)
# This value is used when generating builds using this plugin, so it must
# not be changed
set(PLUGIN_NAME "flutter_libsparkmobile_plugin")
# Any new source files that you add to the plugin should be added here.
list(APPEND PLUGIN_SOURCES
"flutter_libsparkmobile_plugin.cpp"
"flutter_libsparkmobile_plugin.h"
)
# Define the plugin library target. Its name must not be changed (see comment
# on PLUGIN_NAME above).
add_library(${PLUGIN_NAME} SHARED
"include/flutter_libsparkmobile/flutter_libsparkmobile_plugin_c_api.h"
"flutter_libsparkmobile_plugin_c_api.cpp"
${PLUGIN_SOURCES}
)
# Apply a standard set of build settings that are configured in the
# application-level CMakeLists.txt. This can be removed for plugins that want
# full control over build settings.
apply_standard_settings(${PLUGIN_NAME})
# Symbols are hidden by default to reduce the chance of accidental conflicts
# between plugins. This should not be removed; any symbols that should be
# exported should be explicitly exported with the FLUTTER_PLUGIN_EXPORT macro.
set_target_properties(${PLUGIN_NAME} PROPERTIES
CXX_VISIBILITY_PRESET hidden)
target_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL)
# Source include directories and library dependencies. Add any plugin-specific
# dependencies here.
target_include_directories(${PLUGIN_NAME} INTERFACE
"${CMAKE_CURRENT_SOURCE_DIR}/include")
target_link_libraries(${PLUGIN_NAME} PRIVATE flutter flutter_wrapper_plugin)
# Invoke the build for native code shared with the other target platforms.
# This can be changed to accommodate different builds.
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/../src" "${CMAKE_CURRENT_BINARY_DIR}/shared")
# List of absolute paths to libraries that should be bundled with the plugin.
# This list could contain prebuilt libraries, or libraries created by an
# external build triggered from this build file.
set(flutter_libsparkmobile_bundled_libraries
""
# Defined in ../src/CMakeLists.txt.
# This can be changed to accommodate different builds.
$<TARGET_FILE:flutter_libsparkmobile>
PARENT_SCOPE
)
# === Tests ===
# These unit tests can be run from a terminal after building the example, or
# from Visual Studio after opening the generated solution file.
# Only enable test builds when building the example (which sets this variable)
# so that plugin clients aren't building the tests.
if (${include_${PROJECT_NAME}_tests})
set(TEST_RUNNER "${PROJECT_NAME}_test")
enable_testing()
# Add the Google Test dependency.
include(FetchContent)
FetchContent_Declare(
googletest
URL https://github.com/google/googletest/archive/release-1.11.0.zip
)
# Prevent overriding the parent project's compiler/linker settings
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
# Disable install commands for gtest so it doesn't end up in the bundle.
set(INSTALL_GTEST OFF CACHE BOOL "Disable installation of googletest" FORCE)
FetchContent_MakeAvailable(googletest)
# The plugin's C API is not very useful for unit testing, so build the sources
# directly into the test binary rather than using the DLL.
add_executable(${TEST_RUNNER}
test/flutter_libsparkmobile_plugin_test.cpp
${PLUGIN_SOURCES}
)
apply_standard_settings(${TEST_RUNNER})
target_include_directories(${TEST_RUNNER} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}")
target_link_libraries(${TEST_RUNNER} PRIVATE flutter_wrapper_plugin)
target_link_libraries(${TEST_RUNNER} PRIVATE gtest_main gmock)
# flutter_wrapper_plugin has link dependencies on the Flutter DLL.
add_custom_command(TARGET ${TEST_RUNNER} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${FLUTTER_LIBRARY}" $<TARGET_FILE_DIR:${TEST_RUNNER}>
)
# Enable automatic test discovery.
include(GoogleTest)
gtest_discover_tests(${TEST_RUNNER})
endif()

View File

@@ -1,59 +0,0 @@
#include "flutter_libsparkmobile_plugin.h"
// This must be included before many other Windows headers.
#include <windows.h>
// For getPlatformVersion; remove unless needed for your plugin implementation.
#include <VersionHelpers.h>
#include <flutter/method_channel.h>
#include <flutter/plugin_registrar_windows.h>
#include <flutter/standard_method_codec.h>
#include <memory>
#include <sstream>
namespace flutter_libsparkmobile {
// static
void FlutterLibsparkmobilePlugin::RegisterWithRegistrar(
flutter::PluginRegistrarWindows *registrar) {
auto channel =
std::make_unique<flutter::MethodChannel<flutter::EncodableValue>>(
registrar->messenger(), "flutter_libsparkmobile",
&flutter::StandardMethodCodec::GetInstance());
auto plugin = std::make_unique<FlutterLibsparkmobilePlugin>();
channel->SetMethodCallHandler(
[plugin_pointer = plugin.get()](const auto &call, auto result) {
plugin_pointer->HandleMethodCall(call, std::move(result));
});
registrar->AddPlugin(std::move(plugin));
}
FlutterLibsparkmobilePlugin::FlutterLibsparkmobilePlugin() {}
FlutterLibsparkmobilePlugin::~FlutterLibsparkmobilePlugin() {}
void FlutterLibsparkmobilePlugin::HandleMethodCall(
const flutter::MethodCall<flutter::EncodableValue> &method_call,
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result) {
if (method_call.method_name().compare("getPlatformVersion") == 0) {
std::ostringstream version_stream;
version_stream << "Windows ";
if (IsWindows10OrGreater()) {
version_stream << "10+";
} else if (IsWindows8OrGreater()) {
version_stream << "8";
} else if (IsWindows7OrGreater()) {
version_stream << "7";
}
result->Success(flutter::EncodableValue(version_stream.str()));
} else {
result->NotImplemented();
}
}
} // namespace flutter_libsparkmobile

View File

@@ -1,31 +0,0 @@
#ifndef FLUTTER_PLUGIN_FLUTTER_LIBSPARKMOBILE_PLUGIN_H_
#define FLUTTER_PLUGIN_FLUTTER_LIBSPARKMOBILE_PLUGIN_H_
#include <flutter/method_channel.h>
#include <flutter/plugin_registrar_windows.h>
#include <memory>
namespace flutter_libsparkmobile {
class FlutterLibsparkmobilePlugin : public flutter::Plugin {
public:
static void RegisterWithRegistrar(flutter::PluginRegistrarWindows *registrar);
FlutterLibsparkmobilePlugin();
virtual ~FlutterLibsparkmobilePlugin();
// Disallow copy and assign.
FlutterLibsparkmobilePlugin(const FlutterLibsparkmobilePlugin&) = delete;
FlutterLibsparkmobilePlugin& operator=(const FlutterLibsparkmobilePlugin&) = delete;
// Called when a method is called on this plugin's channel from Dart.
void HandleMethodCall(
const flutter::MethodCall<flutter::EncodableValue> &method_call,
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result);
};
} // namespace flutter_libsparkmobile
#endif // FLUTTER_PLUGIN_FLUTTER_LIBSPARKMOBILE_PLUGIN_H_

View File

@@ -1,12 +0,0 @@
#include "include/flutter_libsparkmobile/flutter_libsparkmobile_plugin_c_api.h"
#include <flutter/plugin_registrar_windows.h>
#include "flutter_libsparkmobile_plugin.h"
void FlutterLibsparkmobilePluginCApiRegisterWithRegistrar(
FlutterDesktopPluginRegistrarRef registrar) {
flutter_libsparkmobile::FlutterLibsparkmobilePlugin::RegisterWithRegistrar(
flutter::PluginRegistrarManager::GetInstance()
->GetRegistrar<flutter::PluginRegistrarWindows>(registrar));
}

View File

@@ -1,23 +0,0 @@
#ifndef FLUTTER_PLUGIN_FLUTTER_LIBSPARKMOBILE_PLUGIN_C_API_H_
#define FLUTTER_PLUGIN_FLUTTER_LIBSPARKMOBILE_PLUGIN_C_API_H_
#include <flutter_plugin_registrar.h>
#ifdef FLUTTER_PLUGIN_IMPL
#define FLUTTER_PLUGIN_EXPORT __declspec(dllexport)
#else
#define FLUTTER_PLUGIN_EXPORT __declspec(dllimport)
#endif
#if defined(__cplusplus)
extern "C" {
#endif
FLUTTER_PLUGIN_EXPORT void FlutterLibsparkmobilePluginCApiRegisterWithRegistrar(
FlutterDesktopPluginRegistrarRef registrar);
#if defined(__cplusplus)
} // extern "C"
#endif
#endif // FLUTTER_PLUGIN_FLUTTER_LIBSPARKMOBILE_PLUGIN_C_API_H_

View File

@@ -1,43 +0,0 @@
#include <flutter/method_call.h>
#include <flutter/method_result_functions.h>
#include <flutter/standard_method_codec.h>
#include <gtest/gtest.h>
#include <windows.h>
#include <memory>
#include <string>
#include <variant>
#include "flutter_libsparkmobile_plugin.h"
namespace flutter_libsparkmobile {
namespace test {
namespace {
using flutter::EncodableMap;
using flutter::EncodableValue;
using flutter::MethodCall;
using flutter::MethodResultFunctions;
} // namespace
TEST(FlutterLibsparkmobilePlugin, GetPlatformVersion) {
FlutterLibsparkmobilePlugin plugin;
// Save the reply value from the success callback.
std::string result_string;
plugin.HandleMethodCall(
MethodCall("getPlatformVersion", std::make_unique<EncodableValue>()),
std::make_unique<MethodResultFunctions<>>(
[&result_string](const EncodableValue* result) {
result_string = std::get<std::string>(*result);
},
nullptr, nullptr));
// Since the exact string varies by host, just ensure that it's a string
// with the expected format.
EXPECT_TRUE(result_string.rfind("Windows ", 0) == 0);
}
} // namespace test
} // namespace flutter_libsparkmobile