diff --git a/.gitmodules b/.gitmodules index 841321f8..d75d11c3 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,9 @@ +[submodule "rust/epic"] + path = rust/epic + url = https://github.com/cypherstack/epic [submodule "rust/epic-wallet"] path = rust/epic-wallet url = https://github.com/cypherstack/epic-wallet +[submodule "rust/epicbox"] + path = rust/epicbox + url = https://github.com/cypherstack/epicbox diff --git a/README.md b/README.md index daae1de8..4d822b53 100644 --- a/README.md +++ b/README.md @@ -80,3 +80,11 @@ Libraries will be output to `scripts/windows/build` ## Building on Windows `build_all.ps1` is not confirmed working and may need work eg. may need some missing dependencies added but has been included as a starting point or example for Windows users + +# Notes +## Cargokit +Cargokit may be updated using: +```sh +git subtree pull --prefix cargokit https://github.com/irondash/cargokit.git main --squash +``` +in the plugin root. diff --git a/cargokit/.github/workflows/check_and_lint.yml b/cargokit/.github/workflows/check_and_lint.yml new file mode 100644 index 00000000..d8979f0e --- /dev/null +++ b/cargokit/.github/workflows/check_and_lint.yml @@ -0,0 +1,26 @@ +on: + pull_request: + push: + branches: + - main + +name: Check and Lint + +jobs: + Flutter: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # 4.1.0 + - uses: subosito/flutter-action@44ac965b96f18d999802d4b807e3256d5a3f9fa1 # 2.16.0 + - name: Pub Get + run: dart pub get --no-precompile + working-directory: build_tool + - name: Dart Format + run: dart format . --output=none --set-exit-if-changed + working-directory: build_tool + - name: Analyze + run: dart analyze + working-directory: build_tool + - name: Test + run: flutter test + working-directory: build_tool diff --git a/cargokit/.github/workflows/test_example_plugin_build.yml b/cargokit/.github/workflows/test_example_plugin_build.yml new file mode 100644 index 00000000..698ea7e1 --- /dev/null +++ b/cargokit/.github/workflows/test_example_plugin_build.yml @@ -0,0 +1,86 @@ +on: + pull_request: + push: + branches: + - main + +name: Test Example Plugin + +jobs: + Build: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: + - ubuntu-latest + - macOS-latest + - windows-latest + build_mode: + - debug + - profile + - release + env: + EXAMPLE_DIR: "a b/hello_rust_ffi_plugin/example" + CARGOKIT_VERBOSE: 1 + steps: + - name: Extract branch name + shell: bash + run: echo "branch=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" >> $GITHUB_OUTPUT + id: extract_branch + - name: Setup Repository + shell: bash + run: | + mkdir "a b" # Space is intentional + cd "a b" + git config --global user.email "you@example.com" + git config --global user.name "Your Name" + # "advanced" branch has extra iOS flavor and uses rust nightly for release builds + git clone -b advanced https://github.com/irondash/hello_rust_ffi_plugin + cd hello_rust_ffi_plugin + git subtree pull --prefix cargokit https://github.com/${{ github.event.pull_request.head.repo.full_name || github.repository }} ${{ steps.extract_branch.outputs.branch }} --squash + - uses: subosito/flutter-action@44ac965b96f18d999802d4b807e3256d5a3f9fa1 # 2.16.0 + with: + channel: "stable" + - name: Install GTK + if: (matrix.os == 'ubuntu-latest') + run: sudo apt-get update && sudo apt-get install libgtk-3-dev + - name: Install ninja-build + if: (matrix.os == 'ubuntu-latest') + run: sudo apt-get update && sudo apt-get install ninja-build + - name: Build Linux (${{ matrix.build_mode }}) + if: matrix.os == 'ubuntu-latest' + shell: bash + working-directory: ${{ env.EXAMPLE_DIR }} + run: flutter build linux --${{ matrix.build_mode }} -v + - name: Build macOS (${{ matrix.build_mode }}) + if: matrix.os == 'macos-latest' + shell: bash + working-directory: ${{ env.EXAMPLE_DIR }} + run: flutter build macos --${{ matrix.build_mode }} -v + - name: Build iOS (${{ matrix.build_mode }}) + if: matrix.os == 'macos-latest' + shell: bash + working-directory: ${{ env.EXAMPLE_DIR }} + run: flutter build ios --${{ matrix.build_mode }} --no-codesign -v + - name: Build iOS (${{ matrix.build_mode }}) - flavor1 + if: matrix.os == 'macos-latest' + shell: bash + working-directory: ${{ env.EXAMPLE_DIR }} + run: flutter build ios --flavor flavor1 --${{ matrix.build_mode }} --no-codesign -v + - name: Build Windows (${{ matrix.build_mode }}) + if: matrix.os == 'windows-latest' + shell: bash + working-directory: ${{ env.EXAMPLE_DIR }} + run: flutter build windows --${{ matrix.build_mode }} -v + - name: Build Android (${{ matrix.build_mode }}) + shell: bash + working-directory: ${{ env.EXAMPLE_DIR }} + run: | + if [[ $(sysctl hw.optional.arm64) == *"hw.optional.arm64: 1"* ]]; then + export JAVA_HOME=$JAVA_HOME_17_arm64 + else + export JAVA_HOME=$JAVA_HOME_11_X64 + fi + flutter build apk --${{ matrix.build_mode }} -v + diff --git a/cargokit/.gitignore b/cargokit/.gitignore new file mode 100644 index 00000000..cf7bb868 --- /dev/null +++ b/cargokit/.gitignore @@ -0,0 +1,4 @@ +target +.dart_tool +*.iml +!pubspec.lock diff --git a/cargokit/LICENSE b/cargokit/LICENSE new file mode 100644 index 00000000..54a7d589 --- /dev/null +++ b/cargokit/LICENSE @@ -0,0 +1,39 @@ +Copyright 2022 Matej Knopp + +================================================================================ + +MIT LICENSE + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +================================================================================ + +APACHE LICENSE, VERSION 2.0 + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + diff --git a/cargokit/README b/cargokit/README new file mode 100644 index 00000000..8ae4a073 --- /dev/null +++ b/cargokit/README @@ -0,0 +1,8 @@ +Experimental repository to provide glue for seamlessly integrating cargo build +with flutter plugins and packages. + +See https://matejknopp.com/post/flutter_plugin_in_rust_with_no_prebuilt_binaries/ +for a tutorial on how to use Cargokit. + +Example plugin available at https://github.com/irondash/hello_rust_ffi_plugin. + diff --git a/cargokit/build_pod.sh b/cargokit/build_pod.sh new file mode 100755 index 00000000..ed0e0d98 --- /dev/null +++ b/cargokit/build_pod.sh @@ -0,0 +1,58 @@ +#!/bin/sh +set -e + +BASEDIR=$(dirname "$0") + +# Workaround for https://github.com/dart-lang/pub/issues/4010 +BASEDIR=$(cd "$BASEDIR" ; pwd -P) + +# Remove XCode SDK from path. Otherwise this breaks tool compilation when building iOS project +NEW_PATH=`echo $PATH | tr ":" "\n" | grep -v "Contents/Developer/" | tr "\n" ":"` + +export PATH=${NEW_PATH%?} # remove trailing : + +env + +# Platform name (macosx, iphoneos, iphonesimulator) +export CARGOKIT_DARWIN_PLATFORM_NAME=$PLATFORM_NAME + +# Arctive architectures (arm64, armv7, x86_64), space separated. +export CARGOKIT_DARWIN_ARCHS=$ARCHS + +# Current build configuration (Debug, Release) +export CARGOKIT_CONFIGURATION=$CONFIGURATION + +# Path to directory containing Cargo.toml. +export CARGOKIT_MANIFEST_DIR=$PODS_TARGET_SRCROOT/$1 + +# Temporary directory for build artifacts. +export CARGOKIT_TARGET_TEMP_DIR=$TARGET_TEMP_DIR + +# Output directory for final artifacts. +export CARGOKIT_OUTPUT_DIR=$PODS_CONFIGURATION_BUILD_DIR/$PRODUCT_NAME + +# Directory to store built tool artifacts. +export CARGOKIT_TOOL_TEMP_DIR=$TARGET_TEMP_DIR/build_tool + +# Directory inside root project. Not necessarily the top level directory of root project. +export CARGOKIT_ROOT_PROJECT_DIR=$SRCROOT + +FLUTTER_EXPORT_BUILD_ENVIRONMENT=( + "$PODS_ROOT/../Flutter/ephemeral/flutter_export_environment.sh" # macOS + "$PODS_ROOT/../Flutter/flutter_export_environment.sh" # iOS +) + +for path in "${FLUTTER_EXPORT_BUILD_ENVIRONMENT[@]}" +do + if [[ -f "$path" ]]; then + source "$path" + fi +done + +sh "$BASEDIR/run_build_tool.sh" build-pod "$@" + +# Make a symlink from built framework to phony file, which will be used as input to +# build script. This should force rebuild (podspec currently doesn't support alwaysOutOfDate +# attribute on custom build phase) +ln -fs "$OBJROOT/XCBuildData/build.db" "${BUILT_PRODUCTS_DIR}/cargokit_phony" +ln -fs "${BUILT_PRODUCTS_DIR}/${EXECUTABLE_PATH}" "${BUILT_PRODUCTS_DIR}/cargokit_phony_out" diff --git a/cargokit/build_tool/README.md b/cargokit/build_tool/README.md new file mode 100644 index 00000000..3816eca3 --- /dev/null +++ b/cargokit/build_tool/README.md @@ -0,0 +1,2 @@ +A sample command-line application with an entrypoint in `bin/`, library code +in `lib/`, and example unit test in `test/`. diff --git a/cargokit/build_tool/analysis_options.yaml b/cargokit/build_tool/analysis_options.yaml new file mode 100644 index 00000000..a1aad5b3 --- /dev/null +++ b/cargokit/build_tool/analysis_options.yaml @@ -0,0 +1,31 @@ +# This file configures the static analysis results for your project (errors, +# warnings, and lints). +# +# This enables the 'recommended' set of lints from `package:lints`. +# This set helps identify many issues that may lead to problems when running +# or consuming Dart code, and enforces writing Dart using a single, idiomatic +# style and format. +# +# If you want a smaller set of lints you can change this to specify +# 'package:lints/core.yaml'. These are just the most critical lints +# (the recommended set includes the core lints). +# The core lints are also what is used by pub.dev for scoring packages. + +include: package:lints/recommended.yaml + +# Uncomment the following section to specify additional rules. + +linter: + rules: + - prefer_relative_imports + - directives_ordering + +# analyzer: +# exclude: +# - path/to/excluded/files/** + +# For more information about the core and recommended set of lints, see +# https://dart.dev/go/core-lints + +# For additional information about configuring this file, see +# https://dart.dev/guides/language/analysis-options diff --git a/cargokit/build_tool/bin/build_tool.dart b/cargokit/build_tool/bin/build_tool.dart new file mode 100644 index 00000000..f27ec75c --- /dev/null +++ b/cargokit/build_tool/bin/build_tool.dart @@ -0,0 +1,5 @@ +import 'package:build_tool/build_tool.dart' as build_tool; + +void main(List arguments) { + build_tool.runMain(arguments); +} diff --git a/cargokit/build_tool/lib/build_tool.dart b/cargokit/build_tool/lib/build_tool.dart new file mode 100644 index 00000000..b329c01a --- /dev/null +++ b/cargokit/build_tool/lib/build_tool.dart @@ -0,0 +1,5 @@ +import 'src/build_tool.dart' as build_tool; + +Future runMain(List args) async { + return build_tool.runMain(args); +} diff --git a/cargokit/build_tool/lib/src/android_environment.dart b/cargokit/build_tool/lib/src/android_environment.dart new file mode 100644 index 00000000..9342964b --- /dev/null +++ b/cargokit/build_tool/lib/src/android_environment.dart @@ -0,0 +1,192 @@ +import 'dart:io'; +import 'dart:isolate'; +import 'dart:math' as math; + +import 'package:collection/collection.dart'; +import 'package:path/path.dart' as path; +import 'package:version/version.dart'; + +import 'target.dart'; +import 'util.dart'; + +class AndroidEnvironment { + AndroidEnvironment({ + required this.sdkPath, + required this.ndkVersion, + required this.minSdkVersion, + required this.targetTempDir, + required this.target, + }); + + static void clangLinkerWrapper(List args) { + final clang = Platform.environment['_CARGOKIT_NDK_LINK_CLANG']; + if (clang == null) { + throw Exception( + "cargo-ndk rustc linker: didn't find _CARGOKIT_NDK_LINK_CLANG env var"); + } + final target = Platform.environment['_CARGOKIT_NDK_LINK_TARGET']; + if (target == null) { + throw Exception( + "cargo-ndk rustc linker: didn't find _CARGOKIT_NDK_LINK_TARGET env var"); + } + + runCommand(clang, [ + target, + ...args, + ]); + } + + /// Full path to Android SDK. + final String sdkPath; + + /// Full version of Android NDK. + final String ndkVersion; + + /// Minimum supported SDK version. + final int minSdkVersion; + + /// Target directory for build artifacts. + final String targetTempDir; + + /// Target being built. + final Target target; + + bool ndkIsInstalled() { + final ndkPath = path.join(sdkPath, 'ndk', ndkVersion); + final ndkPackageXml = File(path.join(ndkPath, 'package.xml')); + return ndkPackageXml.existsSync(); + } + + void installNdk({ + required String javaHome, + }) { + final sdkManagerExtension = Platform.isWindows ? '.bat' : ''; + final sdkManager = path.join( + sdkPath, + 'cmdline-tools', + 'latest', + 'bin', + 'sdkmanager$sdkManagerExtension', + ); + + log.info('Installing NDK $ndkVersion'); + runCommand(sdkManager, [ + '--install', + 'ndk;$ndkVersion', + ], environment: { + 'JAVA_HOME': javaHome, + }); + } + + Future> buildEnvironment() async { + final hostArch = Platform.isMacOS + ? "darwin-x86_64" + : (Platform.isLinux ? "linux-x86_64" : "windows-x86_64"); + + final ndkPath = path.join(sdkPath, 'ndk', ndkVersion); + final toolchainPath = path.join( + ndkPath, + 'toolchains', + 'llvm', + 'prebuilt', + hostArch, + 'bin', + ); + + final minSdkVersion = + math.max(target.androidMinSdkVersion!, this.minSdkVersion); + + final exe = Platform.isWindows ? '.exe' : ''; + + final arKey = 'AR_${target.rust}'; + final arValue = ['${target.rust}-ar', 'llvm-ar', 'llvm-ar.exe'] + .map((e) => path.join(toolchainPath, e)) + .firstWhereOrNull((element) => File(element).existsSync()); + if (arValue == null) { + throw Exception('Failed to find ar for $target in $toolchainPath'); + } + + final targetArg = '--target=${target.rust}$minSdkVersion'; + + final ccKey = 'CC_${target.rust}'; + final ccValue = path.join(toolchainPath, 'clang$exe'); + final cfFlagsKey = 'CFLAGS_${target.rust}'; + final cFlagsValue = targetArg; + + final cxxKey = 'CXX_${target.rust}'; + final cxxValue = path.join(toolchainPath, 'clang++$exe'); + final cxxFlagsKey = 'CXXFLAGS_${target.rust}'; + final cxxFlagsValue = targetArg; + + final linkerKey = + 'cargo_target_${target.rust.replaceAll('-', '_')}_linker'.toUpperCase(); + + final ranlibKey = 'RANLIB_${target.rust}'; + final ranlibValue = path.join(toolchainPath, 'llvm-ranlib$exe'); + + final ndkVersionParsed = Version.parse(ndkVersion); + final rustFlagsKey = 'CARGO_ENCODED_RUSTFLAGS'; + final rustFlagsValue = _libGccWorkaround(targetTempDir, ndkVersionParsed); + + final runRustTool = + Platform.isWindows ? 'run_build_tool.cmd' : 'run_build_tool.sh'; + + final packagePath = (await Isolate.resolvePackageUri( + Uri.parse('package:build_tool/buildtool.dart')))! + .toFilePath(); + final selfPath = path.canonicalize(path.join( + packagePath, + '..', + '..', + '..', + runRustTool, + )); + + // Make sure that run_build_tool is working properly even initially launched directly + // through dart run. + final toolTempDir = + Platform.environment['CARGOKIT_TOOL_TEMP_DIR'] ?? targetTempDir; + + return { + arKey: arValue, + ccKey: ccValue, + cfFlagsKey: cFlagsValue, + cxxKey: cxxValue, + cxxFlagsKey: cxxFlagsValue, + ranlibKey: ranlibValue, + rustFlagsKey: rustFlagsValue, + linkerKey: selfPath, + // Recognized by main() so we know when we're acting as a wrapper + '_CARGOKIT_NDK_LINK_TARGET': targetArg, + '_CARGOKIT_NDK_LINK_CLANG': ccValue, + 'CARGOKIT_TOOL_TEMP_DIR': toolTempDir, + }; + } + + // Workaround for libgcc missing in NDK23, inspired by cargo-ndk + String _libGccWorkaround(String buildDir, Version ndkVersion) { + final workaroundDir = path.join( + buildDir, + 'cargokit', + 'libgcc_workaround', + '${ndkVersion.major}', + ); + Directory(workaroundDir).createSync(recursive: true); + if (ndkVersion.major >= 23) { + File(path.join(workaroundDir, 'libgcc.a')) + .writeAsStringSync('INPUT(-lunwind)'); + } else { + // Other way around, untested, forward libgcc.a from libunwind once Rust + // gets updated for NDK23+. + File(path.join(workaroundDir, 'libunwind.a')) + .writeAsStringSync('INPUT(-lgcc)'); + } + + var rustFlags = Platform.environment['CARGO_ENCODED_RUSTFLAGS'] ?? ''; + if (rustFlags.isNotEmpty) { + rustFlags = '$rustFlags\x1f'; + } + rustFlags = '$rustFlags-L\x1f$workaroundDir'; + return rustFlags; + } +} diff --git a/cargokit/build_tool/lib/src/artifacts_provider.dart b/cargokit/build_tool/lib/src/artifacts_provider.dart new file mode 100644 index 00000000..ef655a9e --- /dev/null +++ b/cargokit/build_tool/lib/src/artifacts_provider.dart @@ -0,0 +1,263 @@ +import 'dart:io'; + +import 'package:ed25519_edwards/ed25519_edwards.dart'; +import 'package:http/http.dart'; +import 'package:logging/logging.dart'; +import 'package:path/path.dart' as path; + +import 'builder.dart'; +import 'crate_hash.dart'; +import 'options.dart'; +import 'precompile_binaries.dart'; +import 'rustup.dart'; +import 'target.dart'; + +class Artifact { + /// File system location of the artifact. + final String path; + + /// Actual file name that the artifact should have in destination folder. + final String finalFileName; + + AritifactType get type { + if (finalFileName.endsWith('.dll') || + finalFileName.endsWith('.dll.lib') || + finalFileName.endsWith('.pdb') || + finalFileName.endsWith('.so') || + finalFileName.endsWith('.dylib')) { + return AritifactType.dylib; + } else if (finalFileName.endsWith('.lib') || finalFileName.endsWith('.a')) { + return AritifactType.staticlib; + } else { + throw Exception('Unknown artifact type for $finalFileName'); + } + } + + Artifact({ + required this.path, + required this.finalFileName, + }); +} + +final _log = Logger('artifacts_provider'); + +class ArtifactProvider { + ArtifactProvider({ + required this.environment, + required this.userOptions, + }); + + final BuildEnvironment environment; + final CargokitUserOptions userOptions; + + Future>> getArtifacts(List targets) async { + final result = await _getPrecompiledArtifacts(targets); + + final pendingTargets = List.of(targets); + pendingTargets.removeWhere((element) => result.containsKey(element)); + + if (pendingTargets.isEmpty) { + return result; + } + + final rustup = Rustup(); + for (final target in targets) { + final builder = RustBuilder(target: target, environment: environment); + builder.prepare(rustup); + _log.info('Building ${environment.crateInfo.packageName} for $target'); + final targetDir = await builder.build(); + // For local build accept both static and dynamic libraries. + final artifactNames = { + ...getArtifactNames( + target: target, + libraryName: environment.crateInfo.packageName, + aritifactType: AritifactType.dylib, + remote: false, + ), + ...getArtifactNames( + target: target, + libraryName: environment.crateInfo.packageName, + aritifactType: AritifactType.staticlib, + remote: false, + ) + }; + final artifacts = artifactNames + .map((artifactName) => Artifact( + path: path.join(targetDir, artifactName), + finalFileName: artifactName, + )) + .where((element) => File(element.path).existsSync()) + .toList(); + result[target] = artifacts; + } + return result; + } + + Future>> _getPrecompiledArtifacts( + List targets) async { + if (userOptions.usePrecompiledBinaries == false) { + _log.info('Precompiled binaries are disabled'); + return {}; + } + if (environment.crateOptions.precompiledBinaries == null) { + _log.fine('Precompiled binaries not enabled for this crate'); + return {}; + } + + final start = Stopwatch()..start(); + final crateHash = CrateHash.compute(environment.manifestDir, + tempStorage: environment.targetTempDir); + _log.fine( + 'Computed crate hash $crateHash in ${start.elapsedMilliseconds}ms'); + + final downloadedArtifactsDir = + path.join(environment.targetTempDir, 'precompiled', crateHash); + Directory(downloadedArtifactsDir).createSync(recursive: true); + + final res = >{}; + + for (final target in targets) { + final requiredArtifacts = getArtifactNames( + target: target, + libraryName: environment.crateInfo.packageName, + remote: true, + ); + final artifactsForTarget = []; + + for (final artifact in requiredArtifacts) { + final fileName = PrecompileBinaries.fileName(target, artifact); + final downloadedPath = path.join(downloadedArtifactsDir, fileName); + if (!File(downloadedPath).existsSync()) { + final signatureFileName = + PrecompileBinaries.signatureFileName(target, artifact); + await _tryDownloadArtifacts( + crateHash: crateHash, + fileName: fileName, + signatureFileName: signatureFileName, + finalPath: downloadedPath, + ); + } + if (File(downloadedPath).existsSync()) { + artifactsForTarget.add(Artifact( + path: downloadedPath, + finalFileName: artifact, + )); + } else { + break; + } + } + + // Only provide complete set of artifacts. + if (artifactsForTarget.length == requiredArtifacts.length) { + _log.fine('Found precompiled artifacts for $target'); + res[target] = artifactsForTarget; + } + } + + return res; + } + + static Future _get(Uri url, {Map? headers}) async { + int attempt = 0; + const maxAttempts = 10; + while (true) { + try { + return await get(url, headers: headers); + } on SocketException catch (e) { + // Try to detect reset by peer error and retry. + if (attempt++ < maxAttempts && + (e.osError?.errorCode == 54 || e.osError?.errorCode == 10054)) { + _log.severe( + 'Failed to download $url: $e, attempt $attempt of $maxAttempts, will retry...'); + await Future.delayed(Duration(seconds: 1)); + continue; + } else { + rethrow; + } + } + } + } + + Future _tryDownloadArtifacts({ + required String crateHash, + required String fileName, + required String signatureFileName, + required String finalPath, + }) async { + final precompiledBinaries = environment.crateOptions.precompiledBinaries!; + final prefix = precompiledBinaries.uriPrefix; + final url = Uri.parse('$prefix$crateHash/$fileName'); + final signatureUrl = Uri.parse('$prefix$crateHash/$signatureFileName'); + _log.fine('Downloading signature from $signatureUrl'); + final signature = await _get(signatureUrl); + if (signature.statusCode == 404) { + _log.warning( + 'Precompiled binaries not available for crate hash $crateHash ($fileName)'); + return; + } + if (signature.statusCode != 200) { + _log.severe( + 'Failed to download signature $signatureUrl: status ${signature.statusCode}'); + return; + } + _log.fine('Downloading binary from $url'); + final res = await _get(url); + if (res.statusCode != 200) { + _log.severe('Failed to download binary $url: status ${res.statusCode}'); + return; + } + if (verify( + precompiledBinaries.publicKey, res.bodyBytes, signature.bodyBytes)) { + File(finalPath).writeAsBytesSync(res.bodyBytes); + } else { + _log.shout('Signature verification failed! Ignoring binary.'); + } + } +} + +enum AritifactType { + staticlib, + dylib, +} + +AritifactType artifactTypeForTarget(Target target) { + if (target.darwinPlatform != null) { + return AritifactType.staticlib; + } else { + return AritifactType.dylib; + } +} + +List getArtifactNames({ + required Target target, + required String libraryName, + required bool remote, + AritifactType? aritifactType, +}) { + aritifactType ??= artifactTypeForTarget(target); + if (target.darwinArch != null) { + if (aritifactType == AritifactType.staticlib) { + return ['lib$libraryName.a']; + } else { + return ['lib$libraryName.dylib']; + } + } else if (target.rust.contains('-windows-')) { + if (aritifactType == AritifactType.staticlib) { + return ['$libraryName.lib']; + } else { + return [ + '$libraryName.dll', + '$libraryName.dll.lib', + if (!remote) '$libraryName.pdb' + ]; + } + } else if (target.rust.contains('-linux-')) { + if (aritifactType == AritifactType.staticlib) { + return ['lib$libraryName.a']; + } else { + return ['lib$libraryName.so']; + } + } else { + throw Exception("Unsupported target: ${target.rust}"); + } +} diff --git a/cargokit/build_tool/lib/src/build_cmake.dart b/cargokit/build_tool/lib/src/build_cmake.dart new file mode 100644 index 00000000..9154371e --- /dev/null +++ b/cargokit/build_tool/lib/src/build_cmake.dart @@ -0,0 +1,37 @@ +import 'dart:io'; + +import 'package:path/path.dart' as path; + +import 'artifacts_provider.dart'; +import 'builder.dart'; +import 'environment.dart'; +import 'options.dart'; +import 'target.dart'; + +class BuildCMake { + final CargokitUserOptions userOptions; + + BuildCMake({required this.userOptions}); + + Future build() async { + final targetPlatform = Environment.targetPlatform; + final target = Target.forFlutterName(Environment.targetPlatform); + if (target == null) { + throw Exception("Unknown target platform: $targetPlatform"); + } + + final environment = BuildEnvironment.fromEnvironment(isAndroid: false); + final provider = + ArtifactProvider(environment: environment, userOptions: userOptions); + final artifacts = await provider.getArtifacts([target]); + + final libs = artifacts[target]!; + + for (final lib in libs) { + if (lib.type == AritifactType.dylib) { + File(lib.path) + .copySync(path.join(Environment.outputDir, lib.finalFileName)); + } + } + } +} diff --git a/cargokit/build_tool/lib/src/build_gradle.dart b/cargokit/build_tool/lib/src/build_gradle.dart new file mode 100644 index 00000000..469c8b2d --- /dev/null +++ b/cargokit/build_tool/lib/src/build_gradle.dart @@ -0,0 +1,46 @@ +import 'dart:io'; + +import 'package:logging/logging.dart'; +import 'package:path/path.dart' as path; + +import 'artifacts_provider.dart'; +import 'builder.dart'; +import 'environment.dart'; +import 'options.dart'; +import 'target.dart'; + +final log = Logger('build_gradle'); + +class BuildGradle { + BuildGradle({required this.userOptions}); + + final CargokitUserOptions userOptions; + + Future build() async { + final targets = Environment.targetPlatforms.map((arch) { + final target = Target.forFlutterName(arch); + if (target == null) { + throw Exception( + "Unknown darwin target or platform: $arch, ${Environment.darwinPlatformName}"); + } + return target; + }).toList(); + + final environment = BuildEnvironment.fromEnvironment(isAndroid: true); + final provider = + ArtifactProvider(environment: environment, userOptions: userOptions); + final artifacts = await provider.getArtifacts(targets); + + for (final target in targets) { + final libs = artifacts[target]!; + final outputDir = path.join(Environment.outputDir, target.android!); + Directory(outputDir).createSync(recursive: true); + + for (final lib in libs) { + if (lib.type == AritifactType.dylib) { + File(lib.path).copySync(path.join(outputDir, lib.finalFileName)); + } + } + } + } +} diff --git a/cargokit/build_tool/lib/src/build_pod.dart b/cargokit/build_tool/lib/src/build_pod.dart new file mode 100644 index 00000000..f01401e1 --- /dev/null +++ b/cargokit/build_tool/lib/src/build_pod.dart @@ -0,0 +1,86 @@ +import 'dart:io'; + +import 'package:path/path.dart' as path; + +import 'artifacts_provider.dart'; +import 'builder.dart'; +import 'environment.dart'; +import 'options.dart'; +import 'target.dart'; +import 'util.dart'; + +class BuildPod { + BuildPod({required this.userOptions}); + + final CargokitUserOptions userOptions; + + Future build() async { + final targets = Environment.darwinArchs.map((arch) { + final target = Target.forDarwin( + platformName: Environment.darwinPlatformName, darwinAarch: arch); + if (target == null) { + throw Exception( + "Unknown darwin target or platform: $arch, ${Environment.darwinPlatformName}"); + } + return target; + }).toList(); + + final environment = BuildEnvironment.fromEnvironment(isAndroid: false); + final provider = + ArtifactProvider(environment: environment, userOptions: userOptions); + final artifacts = await provider.getArtifacts(targets); + + void performLipo(String targetFile, Iterable sourceFiles) { + runCommand("lipo", [ + '-create', + ...sourceFiles, + '-output', + targetFile, + ]); + } + + final outputDir = Environment.outputDir; + + Directory(outputDir).createSync(recursive: true); + + final staticLibs = artifacts.values + .expand((element) => element) + .where((element) => element.type == AritifactType.staticlib) + .toList(); + final dynamicLibs = artifacts.values + .expand((element) => element) + .where((element) => element.type == AritifactType.dylib) + .toList(); + + final libName = environment.crateInfo.packageName; + + // If there is static lib, use it and link it with pod + if (staticLibs.isNotEmpty) { + final finalTargetFile = path.join(outputDir, "lib$libName.a"); + performLipo(finalTargetFile, staticLibs.map((e) => e.path)); + } else { + // Otherwise try to replace bundle dylib with our dylib + final bundlePaths = [ + '$libName.framework/Versions/A/$libName', + '$libName.framework/$libName', + ]; + + for (final bundlePath in bundlePaths) { + final targetFile = path.join(outputDir, bundlePath); + if (File(targetFile).existsSync()) { + performLipo(targetFile, dynamicLibs.map((e) => e.path)); + + // Replace absolute id with @rpath one so that it works properly + // when moved to Frameworks. + runCommand("install_name_tool", [ + '-id', + '@rpath/$bundlePath', + targetFile, + ]); + return; + } + } + throw Exception('Unable to find bundle for dynamic library'); + } + } +} diff --git a/cargokit/build_tool/lib/src/build_tool.dart b/cargokit/build_tool/lib/src/build_tool.dart new file mode 100644 index 00000000..1d9462af --- /dev/null +++ b/cargokit/build_tool/lib/src/build_tool.dart @@ -0,0 +1,268 @@ +import 'dart:io'; + +import 'package:args/command_runner.dart'; +import 'package:ed25519_edwards/ed25519_edwards.dart'; +import 'package:github/github.dart'; +import 'package:hex/hex.dart'; +import 'package:logging/logging.dart'; + +import 'android_environment.dart'; +import 'build_cmake.dart'; +import 'build_gradle.dart'; +import 'build_pod.dart'; +import 'logging.dart'; +import 'options.dart'; +import 'precompile_binaries.dart'; +import 'target.dart'; +import 'util.dart'; +import 'verify_binaries.dart'; + +final log = Logger('build_tool'); + +abstract class BuildCommand extends Command { + Future runBuildCommand(CargokitUserOptions options); + + @override + Future run() async { + final options = CargokitUserOptions.load(); + + if (options.verboseLogging || + Platform.environment['CARGOKIT_VERBOSE'] == '1') { + enableVerboseLogging(); + } + + await runBuildCommand(options); + } +} + +class BuildPodCommand extends BuildCommand { + @override + final name = 'build-pod'; + + @override + final description = 'Build cocoa pod library'; + + @override + Future runBuildCommand(CargokitUserOptions options) async { + final build = BuildPod(userOptions: options); + await build.build(); + } +} + +class BuildGradleCommand extends BuildCommand { + @override + final name = 'build-gradle'; + + @override + final description = 'Build android library'; + + @override + Future runBuildCommand(CargokitUserOptions options) async { + final build = BuildGradle(userOptions: options); + await build.build(); + } +} + +class BuildCMakeCommand extends BuildCommand { + @override + final name = 'build-cmake'; + + @override + final description = 'Build CMake library'; + + @override + Future runBuildCommand(CargokitUserOptions options) async { + final build = BuildCMake(userOptions: options); + await build.build(); + } +} + +class GenKeyCommand extends Command { + @override + final name = 'gen-key'; + + @override + final description = 'Generate key pair for signing precompiled binaries'; + + @override + void run() { + final kp = generateKey(); + final private = HEX.encode(kp.privateKey.bytes); + final public = HEX.encode(kp.publicKey.bytes); + print("Private Key: $private"); + print("Public Key: $public"); + } +} + +class PrecompileBinariesCommand extends Command { + PrecompileBinariesCommand() { + argParser + ..addOption( + 'repository', + mandatory: true, + help: 'Github repository slug in format owner/name', + ) + ..addOption( + 'manifest-dir', + mandatory: true, + help: 'Directory containing Cargo.toml', + ) + ..addMultiOption('target', + help: 'Rust target triple of artifact to build.\n' + 'Can be specified multiple times or omitted in which case\n' + 'all targets for current platform will be built.') + ..addOption( + 'android-sdk-location', + help: 'Location of Android SDK (if available)', + ) + ..addOption( + 'android-ndk-version', + help: 'Android NDK version (if available)', + ) + ..addOption( + 'android-min-sdk-version', + help: 'Android minimum rquired version (if available)', + ) + ..addOption( + 'temp-dir', + help: 'Directory to store temporary build artifacts', + ) + ..addFlag( + "verbose", + abbr: "v", + defaultsTo: false, + help: "Enable verbose logging", + ); + } + + @override + final name = 'precompile-binaries'; + + @override + final description = 'Prebuild and upload binaries\n' + 'Private key must be passed through PRIVATE_KEY environment variable. ' + 'Use gen_key through generate priave key.\n' + 'Github token must be passed as GITHUB_TOKEN environment variable.\n'; + + @override + Future run() async { + final verbose = argResults!['verbose'] as bool; + if (verbose) { + enableVerboseLogging(); + } + + final privateKeyString = Platform.environment['PRIVATE_KEY']; + if (privateKeyString == null) { + throw ArgumentError('Missing PRIVATE_KEY environment variable'); + } + final githubToken = Platform.environment['GITHUB_TOKEN']; + if (githubToken == null) { + throw ArgumentError('Missing GITHUB_TOKEN environment variable'); + } + final privateKey = HEX.decode(privateKeyString); + if (privateKey.length != 64) { + throw ArgumentError('Private key must be 64 bytes long'); + } + final manifestDir = argResults!['manifest-dir'] as String; + if (!Directory(manifestDir).existsSync()) { + throw ArgumentError('Manifest directory does not exist: $manifestDir'); + } + String? androidMinSdkVersionString = + argResults!['android-min-sdk-version'] as String?; + int? androidMinSdkVersion; + if (androidMinSdkVersionString != null) { + androidMinSdkVersion = int.tryParse(androidMinSdkVersionString); + if (androidMinSdkVersion == null) { + throw ArgumentError( + 'Invalid android-min-sdk-version: $androidMinSdkVersionString'); + } + } + final targetStrigns = argResults!['target'] as List; + final targets = targetStrigns.map((target) { + final res = Target.forRustTriple(target); + if (res == null) { + throw ArgumentError('Invalid target: $target'); + } + return res; + }).toList(growable: false); + final precompileBinaries = PrecompileBinaries( + privateKey: PrivateKey(privateKey), + githubToken: githubToken, + manifestDir: manifestDir, + repositorySlug: RepositorySlug.full(argResults!['repository'] as String), + targets: targets, + androidSdkLocation: argResults!['android-sdk-location'] as String?, + androidNdkVersion: argResults!['android-ndk-version'] as String?, + androidMinSdkVersion: androidMinSdkVersion, + tempDir: argResults!['temp-dir'] as String?, + ); + + await precompileBinaries.run(); + } +} + +class VerifyBinariesCommand extends Command { + VerifyBinariesCommand() { + argParser.addOption( + 'manifest-dir', + mandatory: true, + help: 'Directory containing Cargo.toml', + ); + } + + @override + final name = "verify-binaries"; + + @override + final description = 'Verifies published binaries\n' + 'Checks whether there is a binary published for each targets\n' + 'and checks the signature.'; + + @override + Future run() async { + final manifestDir = argResults!['manifest-dir'] as String; + final verifyBinaries = VerifyBinaries( + manifestDir: manifestDir, + ); + await verifyBinaries.run(); + } +} + +Future runMain(List args) async { + try { + // Init logging before options are loaded + initLogging(); + + if (Platform.environment['_CARGOKIT_NDK_LINK_TARGET'] != null) { + return AndroidEnvironment.clangLinkerWrapper(args); + } + + final runner = CommandRunner('build_tool', 'Cargokit built_tool') + ..addCommand(BuildPodCommand()) + ..addCommand(BuildGradleCommand()) + ..addCommand(BuildCMakeCommand()) + ..addCommand(GenKeyCommand()) + ..addCommand(PrecompileBinariesCommand()) + ..addCommand(VerifyBinariesCommand()); + + await runner.run(args); + } on ArgumentError catch (e) { + stderr.writeln(e.toString()); + exit(1); + } catch (e, s) { + log.severe(kDoubleSeparator); + log.severe('Cargokit BuildTool failed with error:'); + log.severe(kSeparator); + log.severe(e); + // This tells user to install Rust, there's no need to pollute the log with + // stack trace. + if (e is! RustupNotFoundException) { + log.severe(kSeparator); + log.severe(s); + log.severe(kSeparator); + log.severe('BuildTool arguments: $args'); + } + log.severe(kDoubleSeparator); + exit(1); + } +} diff --git a/cargokit/build_tool/lib/src/builder.dart b/cargokit/build_tool/lib/src/builder.dart new file mode 100644 index 00000000..570a5375 --- /dev/null +++ b/cargokit/build_tool/lib/src/builder.dart @@ -0,0 +1,195 @@ +import 'package:collection/collection.dart'; +import 'package:logging/logging.dart'; +import 'package:path/path.dart' as path; + +import 'android_environment.dart'; +import 'cargo.dart'; +import 'environment.dart'; +import 'options.dart'; +import 'rustup.dart'; +import 'target.dart'; +import 'util.dart'; + +final _log = Logger('builder'); + +enum BuildConfiguration { + debug, + release, + profile, +} + +extension on BuildConfiguration { + bool get isDebug => this == BuildConfiguration.debug; + String get rustName => switch (this) { + BuildConfiguration.debug => 'debug', + BuildConfiguration.release => 'release', + BuildConfiguration.profile => 'release', + }; +} + +class BuildException implements Exception { + final String message; + + BuildException(this.message); + + @override + String toString() { + return 'BuildException: $message'; + } +} + +class BuildEnvironment { + final BuildConfiguration configuration; + final CargokitCrateOptions crateOptions; + final String targetTempDir; + final String manifestDir; + final CrateInfo crateInfo; + + final bool isAndroid; + final String? androidSdkPath; + final String? androidNdkVersion; + final int? androidMinSdkVersion; + final String? javaHome; + + BuildEnvironment({ + required this.configuration, + required this.crateOptions, + required this.targetTempDir, + required this.manifestDir, + required this.crateInfo, + required this.isAndroid, + this.androidSdkPath, + this.androidNdkVersion, + this.androidMinSdkVersion, + this.javaHome, + }); + + static BuildConfiguration parseBuildConfiguration(String value) { + // XCode configuration adds the flavor to configuration name. + final firstSegment = value.split('-').first; + final buildConfiguration = BuildConfiguration.values.firstWhereOrNull( + (e) => e.name == firstSegment, + ); + if (buildConfiguration == null) { + _log.warning('Unknown build configuraiton $value, will assume release'); + return BuildConfiguration.release; + } + return buildConfiguration; + } + + static BuildEnvironment fromEnvironment({ + required bool isAndroid, + }) { + final buildConfiguration = + parseBuildConfiguration(Environment.configuration); + final manifestDir = Environment.manifestDir; + final crateOptions = CargokitCrateOptions.load( + manifestDir: manifestDir, + ); + final crateInfo = CrateInfo.load(manifestDir); + return BuildEnvironment( + configuration: buildConfiguration, + crateOptions: crateOptions, + targetTempDir: Environment.targetTempDir, + manifestDir: manifestDir, + crateInfo: crateInfo, + isAndroid: isAndroid, + androidSdkPath: isAndroid ? Environment.sdkPath : null, + androidNdkVersion: isAndroid ? Environment.ndkVersion : null, + androidMinSdkVersion: + isAndroid ? int.parse(Environment.minSdkVersion) : null, + javaHome: isAndroid ? Environment.javaHome : null, + ); + } +} + +class RustBuilder { + final Target target; + final BuildEnvironment environment; + + RustBuilder({ + required this.target, + required this.environment, + }); + + void prepare( + Rustup rustup, + ) { + final toolchain = _toolchain; + if (rustup.installedTargets(toolchain) == null) { + rustup.installToolchain(toolchain); + } + if (toolchain == 'nightly') { + rustup.installRustSrcForNightly(); + } + if (!rustup.installedTargets(toolchain)!.contains(target.rust)) { + rustup.installTarget(target.rust, toolchain: toolchain); + } + } + + CargoBuildOptions? get _buildOptions => + environment.crateOptions.cargo[environment.configuration]; + + String get _toolchain => _buildOptions?.toolchain.name ?? 'stable'; + + /// Returns the path of directory containing build artifacts. + Future build() async { + final extraArgs = _buildOptions?.flags ?? []; + final manifestPath = path.join(environment.manifestDir, 'Cargo.toml'); + runCommand( + 'rustup', + [ + 'run', + _toolchain, + 'cargo', + 'build', + ...extraArgs, + '--manifest-path', + manifestPath, + '-p', + environment.crateInfo.packageName, + if (!environment.configuration.isDebug) '--release', + '--target', + target.rust, + '--target-dir', + environment.targetTempDir, + ], + environment: await _buildEnvironment(), + ); + return path.join( + environment.targetTempDir, + target.rust, + environment.configuration.rustName, + ); + } + + Future> _buildEnvironment() async { + if (target.android == null) { + return {}; + } else { + final sdkPath = environment.androidSdkPath; + final ndkVersion = environment.androidNdkVersion; + final minSdkVersion = environment.androidMinSdkVersion; + if (sdkPath == null) { + throw BuildException('androidSdkPath is not set'); + } + if (ndkVersion == null) { + throw BuildException('androidNdkVersion is not set'); + } + if (minSdkVersion == null) { + throw BuildException('androidMinSdkVersion is not set'); + } + final env = AndroidEnvironment( + sdkPath: sdkPath, + ndkVersion: ndkVersion, + minSdkVersion: minSdkVersion, + targetTempDir: environment.targetTempDir, + target: target, + ); + if (!env.ndkIsInstalled() && environment.javaHome != null) { + env.installNdk(javaHome: environment.javaHome!); + } + return env.buildEnvironment(); + } + } +} diff --git a/cargokit/build_tool/lib/src/cargo.dart b/cargokit/build_tool/lib/src/cargo.dart new file mode 100644 index 00000000..0d4483ff --- /dev/null +++ b/cargokit/build_tool/lib/src/cargo.dart @@ -0,0 +1,45 @@ +import 'dart:io'; + +import 'package:path/path.dart' as path; +import 'package:toml/toml.dart'; + +class ManifestException { + ManifestException(this.message, {required this.fileName}); + + final String? fileName; + final String message; + + @override + String toString() { + if (fileName != null) { + return 'Failed to parse package manifest at $fileName: $message'; + } else { + return 'Failed to parse package manifest: $message'; + } + } +} + +class CrateInfo { + CrateInfo({required this.packageName}); + + final String packageName; + + static CrateInfo parseManifest(String manifest, {final String? fileName}) { + final toml = TomlDocument.parse(manifest); + final package = toml.toMap()['package']; + if (package == null) { + throw ManifestException('Missing package section', fileName: fileName); + } + final name = package['name']; + if (name == null) { + throw ManifestException('Missing package name', fileName: fileName); + } + return CrateInfo(packageName: name); + } + + static CrateInfo load(String manifestDir) { + final manifestFile = File(path.join(manifestDir, 'Cargo.toml')); + final manifest = manifestFile.readAsStringSync(); + return parseManifest(manifest, fileName: manifestFile.path); + } +} diff --git a/cargokit/build_tool/lib/src/crate_hash.dart b/cargokit/build_tool/lib/src/crate_hash.dart new file mode 100644 index 00000000..e58c37ff --- /dev/null +++ b/cargokit/build_tool/lib/src/crate_hash.dart @@ -0,0 +1,121 @@ +import 'dart:convert'; +import 'dart:io'; +import 'dart:typed_data'; + +import 'package:collection/collection.dart'; +import 'package:convert/convert.dart'; +import 'package:crypto/crypto.dart'; +import 'package:path/path.dart' as path; + +class CrateHash { + /// Computes a hash uniquely identifying crate content. This takes into account + /// content all all .rs files inside the src directory, as well as Cargo.toml, + /// Cargo.lock, build.rs and cargokit.yaml. + /// + /// If [tempStorage] is provided, computed hash is stored in a file in that directory + /// and reused on subsequent calls if the crate content hasn't changed. + static String compute(String manifestDir, {String? tempStorage}) { + return CrateHash._( + manifestDir: manifestDir, + tempStorage: tempStorage, + )._compute(); + } + + CrateHash._({ + required this.manifestDir, + required this.tempStorage, + }); + + String _compute() { + final files = getFiles(); + final tempStorage = this.tempStorage; + if (tempStorage != null) { + final quickHash = _computeQuickHash(files); + final quickHashFolder = Directory(path.join(tempStorage, 'crate_hash')); + quickHashFolder.createSync(recursive: true); + final quickHashFile = File(path.join(quickHashFolder.path, quickHash)); + if (quickHashFile.existsSync()) { + return quickHashFile.readAsStringSync(); + } + final hash = _computeHash(files); + quickHashFile.writeAsStringSync(hash); + return hash; + } else { + return _computeHash(files); + } + } + + /// Computes a quick hash based on files stat (without reading contents). This + /// is used to cache the real hash, which is slower to compute since it involves + /// reading every single file. + String _computeQuickHash(List files) { + final output = AccumulatorSink(); + final input = sha256.startChunkedConversion(output); + + final data = ByteData(8); + for (final file in files) { + input.add(utf8.encode(file.path)); + final stat = file.statSync(); + data.setUint64(0, stat.size); + input.add(data.buffer.asUint8List()); + data.setUint64(0, stat.modified.millisecondsSinceEpoch); + input.add(data.buffer.asUint8List()); + } + + input.close(); + return base64Url.encode(output.events.single.bytes); + } + + String _computeHash(List files) { + final output = AccumulatorSink(); + final input = sha256.startChunkedConversion(output); + + void addTextFile(File file) { + // text Files are hashed by lines in case we're dealing with github checkout + // that auto-converts line endings. + final splitter = LineSplitter(); + if (file.existsSync()) { + final data = file.readAsStringSync(); + final lines = splitter.convert(data); + for (final line in lines) { + input.add(utf8.encode(line)); + } + } + } + + for (final file in files) { + addTextFile(file); + } + + input.close(); + final res = output.events.single; + + // Truncate to 128bits. + final hash = res.bytes.sublist(0, 16); + return hex.encode(hash); + } + + List getFiles() { + final src = Directory(path.join(manifestDir, 'src')); + final files = src + .listSync(recursive: true, followLinks: false) + .whereType() + .toList(); + files.sortBy((element) => element.path); + void addFile(String relative) { + final file = File(path.join(manifestDir, relative)); + if (file.existsSync()) { + files.add(file); + } + } + + addFile('Cargo.toml'); + addFile('Cargo.lock'); + addFile('build.rs'); + addFile('cargokit.yaml'); + return files; + } + + final String manifestDir; + final String? tempStorage; +} diff --git a/cargokit/build_tool/lib/src/environment.dart b/cargokit/build_tool/lib/src/environment.dart new file mode 100644 index 00000000..1d267edb --- /dev/null +++ b/cargokit/build_tool/lib/src/environment.dart @@ -0,0 +1,65 @@ +import 'dart:io'; + +extension on String { + String resolveSymlink() => File(this).resolveSymbolicLinksSync(); +} + +class Environment { + /// Current build configuration (debug or release). + static String get configuration => + _getEnv("CARGOKIT_CONFIGURATION").toLowerCase(); + + static bool get isDebug => configuration == 'debug'; + static bool get isRelease => configuration == 'release'; + + /// Temporary directory where Rust build artifacts are placed. + static String get targetTempDir => _getEnv("CARGOKIT_TARGET_TEMP_DIR"); + + /// Final output directory where the build artifacts are placed. + static String get outputDir => _getEnvPath('CARGOKIT_OUTPUT_DIR'); + + /// Path to the crate manifest (containing Cargo.toml). + static String get manifestDir => _getEnvPath('CARGOKIT_MANIFEST_DIR'); + + /// Directory inside root project. Not necessarily root folder. Symlinks are + /// not resolved on purpose. + static String get rootProjectDir => _getEnv('CARGOKIT_ROOT_PROJECT_DIR'); + + // Pod + + /// Platform name (macosx, iphoneos, iphonesimulator). + static String get darwinPlatformName => + _getEnv("CARGOKIT_DARWIN_PLATFORM_NAME"); + + /// List of architectures to build for (arm64, armv7, x86_64). + static List get darwinArchs => + _getEnv("CARGOKIT_DARWIN_ARCHS").split(' '); + + // Gradle + static String get minSdkVersion => _getEnv("CARGOKIT_MIN_SDK_VERSION"); + static String get ndkVersion => _getEnv("CARGOKIT_NDK_VERSION"); + static String get sdkPath => _getEnvPath("CARGOKIT_SDK_DIR"); + static String get javaHome => _getEnvPath("CARGOKIT_JAVA_HOME"); + static List get targetPlatforms => + _getEnv("CARGOKIT_TARGET_PLATFORMS").split(','); + + // CMAKE + static String get targetPlatform => _getEnv("CARGOKIT_TARGET_PLATFORM"); + + static String _getEnv(String key) { + final res = Platform.environment[key]; + if (res == null) { + throw Exception("Missing environment variable $key"); + } + return res; + } + + static String _getEnvPath(String key) { + final res = _getEnv(key); + if (Directory(res).existsSync()) { + return res.resolveSymlink(); + } else { + return res; + } + } +} diff --git a/cargokit/build_tool/lib/src/logging.dart b/cargokit/build_tool/lib/src/logging.dart new file mode 100644 index 00000000..06392b99 --- /dev/null +++ b/cargokit/build_tool/lib/src/logging.dart @@ -0,0 +1,49 @@ +import 'dart:io'; + +import 'package:logging/logging.dart'; + +const String kSeparator = "--"; +const String kDoubleSeparator = "=="; + +bool _lastMessageWasSeparator = false; + +void _log(LogRecord rec) { + final prefix = '${rec.level.name}: '; + final out = rec.level == Level.SEVERE ? stderr : stdout; + if (rec.message == kSeparator) { + if (!_lastMessageWasSeparator) { + out.write(prefix); + out.writeln('-' * 80); + _lastMessageWasSeparator = true; + } + return; + } else if (rec.message == kDoubleSeparator) { + out.write(prefix); + out.writeln('=' * 80); + _lastMessageWasSeparator = true; + return; + } + out.write(prefix); + out.writeln(rec.message); + _lastMessageWasSeparator = false; +} + +void initLogging() { + Logger.root.level = Level.INFO; + Logger.root.onRecord.listen((LogRecord rec) { + final lines = rec.message.split('\n'); + for (final line in lines) { + if (line.isNotEmpty || lines.length == 1 || line != lines.last) { + _log(LogRecord( + rec.level, + line, + rec.loggerName, + )); + } + } + }); +} + +void enableVerboseLogging() { + Logger.root.level = Level.ALL; +} diff --git a/cargokit/build_tool/lib/src/options.dart b/cargokit/build_tool/lib/src/options.dart new file mode 100644 index 00000000..7937dcac --- /dev/null +++ b/cargokit/build_tool/lib/src/options.dart @@ -0,0 +1,306 @@ +import 'dart:io'; + +import 'package:collection/collection.dart'; +import 'package:ed25519_edwards/ed25519_edwards.dart'; +import 'package:hex/hex.dart'; +import 'package:logging/logging.dart'; +import 'package:path/path.dart' as path; +import 'package:source_span/source_span.dart'; +import 'package:yaml/yaml.dart'; + +import 'builder.dart'; +import 'environment.dart'; +import 'rustup.dart'; + +final _log = Logger('options'); + +/// A class for exceptions that have source span information attached. +class SourceSpanException implements Exception { + // This is a getter so that subclasses can override it. + /// A message describing the exception. + String get message => _message; + final String _message; + + // This is a getter so that subclasses can override it. + /// The span associated with this exception. + /// + /// This may be `null` if the source location can't be determined. + SourceSpan? get span => _span; + final SourceSpan? _span; + + SourceSpanException(this._message, this._span); + + /// Returns a string representation of `this`. + /// + /// [color] may either be a [String], a [bool], or `null`. If it's a string, + /// it indicates an ANSI terminal color escape that should be used to + /// highlight the span's text. If it's `true`, it indicates that the text + /// should be highlighted using the default color. If it's `false` or `null`, + /// it indicates that the text shouldn't be highlighted. + @override + String toString({Object? color}) { + if (span == null) return message; + return 'Error on ${span!.message(message, color: color)}'; + } +} + +enum Toolchain { + stable, + beta, + nightly, +} + +class CargoBuildOptions { + final Toolchain toolchain; + final List flags; + + CargoBuildOptions({ + required this.toolchain, + required this.flags, + }); + + static Toolchain _toolchainFromNode(YamlNode node) { + if (node case YamlScalar(value: String name)) { + final toolchain = + Toolchain.values.firstWhereOrNull((element) => element.name == name); + if (toolchain != null) { + return toolchain; + } + } + throw SourceSpanException( + 'Unknown toolchain. Must be one of ${Toolchain.values.map((e) => e.name)}.', + node.span); + } + + static CargoBuildOptions parse(YamlNode node) { + if (node is! YamlMap) { + throw SourceSpanException('Cargo options must be a map', node.span); + } + Toolchain toolchain = Toolchain.stable; + List flags = []; + for (final MapEntry(:key, :value) in node.nodes.entries) { + if (key case YamlScalar(value: 'toolchain')) { + toolchain = _toolchainFromNode(value); + } else if (key case YamlScalar(value: 'extra_flags')) { + if (value case YamlList(nodes: List list)) { + if (list.every((element) { + if (element case YamlScalar(value: String _)) { + return true; + } + return false; + })) { + flags = list.map((e) => e.value as String).toList(); + continue; + } + } + throw SourceSpanException( + 'Extra flags must be a list of strings', value.span); + } else { + throw SourceSpanException( + 'Unknown cargo option type. Must be "toolchain" or "extra_flags".', + key.span); + } + } + return CargoBuildOptions(toolchain: toolchain, flags: flags); + } +} + +extension on YamlMap { + /// Map that extracts keys so that we can do map case check on them. + Map get valueMap => + nodes.map((key, value) => MapEntry(key.value, value)); +} + +class PrecompiledBinaries { + final String uriPrefix; + final PublicKey publicKey; + + PrecompiledBinaries({ + required this.uriPrefix, + required this.publicKey, + }); + + static PublicKey _publicKeyFromHex(String key, SourceSpan? span) { + final bytes = HEX.decode(key); + if (bytes.length != 32) { + throw SourceSpanException( + 'Invalid public key. Must be 32 bytes long.', span); + } + return PublicKey(bytes); + } + + static PrecompiledBinaries parse(YamlNode node) { + if (node case YamlMap(valueMap: Map map)) { + if (map + case { + 'url_prefix': YamlNode urlPrefixNode, + 'public_key': YamlNode publicKeyNode, + }) { + final urlPrefix = switch (urlPrefixNode) { + YamlScalar(value: String urlPrefix) => urlPrefix, + _ => throw SourceSpanException( + 'Invalid URL prefix value.', urlPrefixNode.span), + }; + final publicKey = switch (publicKeyNode) { + YamlScalar(value: String publicKey) => + _publicKeyFromHex(publicKey, publicKeyNode.span), + _ => throw SourceSpanException( + 'Invalid public key value.', publicKeyNode.span), + }; + return PrecompiledBinaries( + uriPrefix: urlPrefix, + publicKey: publicKey, + ); + } + } + throw SourceSpanException( + 'Invalid precompiled binaries value. ' + 'Expected Map with "url_prefix" and "public_key".', + node.span); + } +} + +/// Cargokit options specified for Rust crate. +class CargokitCrateOptions { + CargokitCrateOptions({ + this.cargo = const {}, + this.precompiledBinaries, + }); + + final Map cargo; + final PrecompiledBinaries? precompiledBinaries; + + static CargokitCrateOptions parse(YamlNode node) { + if (node is! YamlMap) { + throw SourceSpanException('Cargokit options must be a map', node.span); + } + final options = {}; + PrecompiledBinaries? precompiledBinaries; + + for (final entry in node.nodes.entries) { + if (entry + case MapEntry( + key: YamlScalar(value: 'cargo'), + value: YamlNode node, + )) { + if (node is! YamlMap) { + throw SourceSpanException('Cargo options must be a map', node.span); + } + for (final MapEntry(:YamlNode key, :value) in node.nodes.entries) { + if (key case YamlScalar(value: String name)) { + final configuration = BuildConfiguration.values + .firstWhereOrNull((element) => element.name == name); + if (configuration != null) { + options[configuration] = CargoBuildOptions.parse(value); + continue; + } + } + throw SourceSpanException( + 'Unknown build configuration. Must be one of ${BuildConfiguration.values.map((e) => e.name)}.', + key.span); + } + } else if (entry.key case YamlScalar(value: 'precompiled_binaries')) { + precompiledBinaries = PrecompiledBinaries.parse(entry.value); + } else { + throw SourceSpanException( + 'Unknown cargokit option type. Must be "cargo" or "precompiled_binaries".', + entry.key.span); + } + } + return CargokitCrateOptions( + cargo: options, + precompiledBinaries: precompiledBinaries, + ); + } + + static CargokitCrateOptions load({ + required String manifestDir, + }) { + final uri = Uri.file(path.join(manifestDir, "cargokit.yaml")); + final file = File.fromUri(uri); + if (file.existsSync()) { + final contents = loadYamlNode(file.readAsStringSync(), sourceUrl: uri); + return parse(contents); + } else { + return CargokitCrateOptions(); + } + } +} + +class CargokitUserOptions { + // When Rustup is installed always build locally unless user opts into + // using precompiled binaries. + static bool defaultUsePrecompiledBinaries() { + return Rustup.executablePath() == null; + } + + CargokitUserOptions({ + required this.usePrecompiledBinaries, + required this.verboseLogging, + }); + + CargokitUserOptions._() + : usePrecompiledBinaries = defaultUsePrecompiledBinaries(), + verboseLogging = false; + + static CargokitUserOptions parse(YamlNode node) { + if (node is! YamlMap) { + throw SourceSpanException('Cargokit options must be a map', node.span); + } + bool usePrecompiledBinaries = defaultUsePrecompiledBinaries(); + bool verboseLogging = false; + + for (final entry in node.nodes.entries) { + if (entry.key case YamlScalar(value: 'use_precompiled_binaries')) { + if (entry.value case YamlScalar(value: bool value)) { + usePrecompiledBinaries = value; + continue; + } + throw SourceSpanException( + 'Invalid value for "use_precompiled_binaries". Must be a boolean.', + entry.value.span); + } else if (entry.key case YamlScalar(value: 'verbose_logging')) { + if (entry.value case YamlScalar(value: bool value)) { + verboseLogging = value; + continue; + } + throw SourceSpanException( + 'Invalid value for "verbose_logging". Must be a boolean.', + entry.value.span); + } else { + throw SourceSpanException( + 'Unknown cargokit option type. Must be "use_precompiled_binaries" or "verbose_logging".', + entry.key.span); + } + } + return CargokitUserOptions( + usePrecompiledBinaries: usePrecompiledBinaries, + verboseLogging: verboseLogging, + ); + } + + static CargokitUserOptions load() { + String fileName = "cargokit_options.yaml"; + var userProjectDir = Directory(Environment.rootProjectDir); + + while (userProjectDir.parent.path != userProjectDir.path) { + final configFile = File(path.join(userProjectDir.path, fileName)); + if (configFile.existsSync()) { + final contents = loadYamlNode( + configFile.readAsStringSync(), + sourceUrl: configFile.uri, + ); + final res = parse(contents); + if (res.verboseLogging) { + _log.info('Found user options file at ${configFile.path}'); + } + return res; + } + userProjectDir = userProjectDir.parent; + } + return CargokitUserOptions._(); + } + + final bool usePrecompiledBinaries; + final bool verboseLogging; +} diff --git a/cargokit/build_tool/lib/src/precompile_binaries.dart b/cargokit/build_tool/lib/src/precompile_binaries.dart new file mode 100644 index 00000000..39ffafc4 --- /dev/null +++ b/cargokit/build_tool/lib/src/precompile_binaries.dart @@ -0,0 +1,199 @@ +import 'dart:io'; + +import 'package:ed25519_edwards/ed25519_edwards.dart'; +import 'package:github/github.dart'; +import 'package:logging/logging.dart'; +import 'package:path/path.dart' as path; + +import 'artifacts_provider.dart'; +import 'builder.dart'; +import 'cargo.dart'; +import 'crate_hash.dart'; +import 'options.dart'; +import 'rustup.dart'; +import 'target.dart'; + +final _log = Logger('precompile_binaries'); + +class PrecompileBinaries { + PrecompileBinaries({ + required this.privateKey, + required this.githubToken, + required this.repositorySlug, + required this.manifestDir, + required this.targets, + this.androidSdkLocation, + this.androidNdkVersion, + this.androidMinSdkVersion, + this.tempDir, + }); + + final PrivateKey privateKey; + final String githubToken; + final RepositorySlug repositorySlug; + final String manifestDir; + final List targets; + final String? androidSdkLocation; + final String? androidNdkVersion; + final int? androidMinSdkVersion; + final String? tempDir; + + static String fileName(Target target, String name) { + return '${target.rust}_$name'; + } + + static String signatureFileName(Target target, String name) { + return '${target.rust}_$name.sig'; + } + + Future run() async { + final crateInfo = CrateInfo.load(manifestDir); + + final targets = List.of(this.targets); + if (targets.isEmpty) { + targets.addAll([ + ...Target.buildableTargets(), + if (androidSdkLocation != null) ...Target.androidTargets(), + ]); + } + + _log.info('Precompiling binaries for $targets'); + + final hash = CrateHash.compute(manifestDir); + _log.info('Computed crate hash: $hash'); + + final String tagName = 'precompiled_$hash'; + + final github = GitHub(auth: Authentication.withToken(githubToken)); + final repo = github.repositories; + final release = await _getOrCreateRelease( + repo: repo, + tagName: tagName, + packageName: crateInfo.packageName, + hash: hash, + ); + + final tempDir = this.tempDir != null + ? Directory(this.tempDir!) + : Directory.systemTemp.createTempSync('precompiled_'); + + tempDir.createSync(recursive: true); + + final crateOptions = CargokitCrateOptions.load( + manifestDir: manifestDir, + ); + + final buildEnvironment = BuildEnvironment( + configuration: BuildConfiguration.release, + crateOptions: crateOptions, + targetTempDir: tempDir.path, + manifestDir: manifestDir, + crateInfo: crateInfo, + isAndroid: androidSdkLocation != null, + androidSdkPath: androidSdkLocation, + androidNdkVersion: androidNdkVersion, + androidMinSdkVersion: androidMinSdkVersion, + ); + + final rustup = Rustup(); + + for (final target in targets) { + final artifactNames = getArtifactNames( + target: target, + libraryName: crateInfo.packageName, + remote: true, + ); + + if (artifactNames.every((name) { + final fileName = PrecompileBinaries.fileName(target, name); + return (release.assets ?? []).any((e) => e.name == fileName); + })) { + _log.info("All artifacts for $target already exist - skipping"); + continue; + } + + _log.info('Building for $target'); + + final builder = + RustBuilder(target: target, environment: buildEnvironment); + builder.prepare(rustup); + final res = await builder.build(); + + final assets = []; + for (final name in artifactNames) { + final file = File(path.join(res, name)); + if (!file.existsSync()) { + throw Exception('Missing artifact: ${file.path}'); + } + + final data = file.readAsBytesSync(); + final create = CreateReleaseAsset( + name: PrecompileBinaries.fileName(target, name), + contentType: "application/octet-stream", + assetData: data, + ); + final signature = sign(privateKey, data); + final signatureCreate = CreateReleaseAsset( + name: signatureFileName(target, name), + contentType: "application/octet-stream", + assetData: signature, + ); + bool verified = verify(public(privateKey), data, signature); + if (!verified) { + throw Exception('Signature verification failed'); + } + assets.add(create); + assets.add(signatureCreate); + } + _log.info('Uploading assets: ${assets.map((e) => e.name)}'); + for (final asset in assets) { + // This seems to be failing on CI so do it one by one + int retryCount = 0; + while (true) { + try { + await repo.uploadReleaseAssets(release, [asset]); + break; + } on Exception catch (e) { + if (retryCount == 10) { + rethrow; + } + ++retryCount; + _log.shout( + 'Upload failed (attempt $retryCount, will retry): ${e.toString()}'); + await Future.delayed(Duration(seconds: 2)); + } + } + } + } + + _log.info('Cleaning up'); + tempDir.deleteSync(recursive: true); + } + + Future _getOrCreateRelease({ + required RepositoriesService repo, + required String tagName, + required String packageName, + required String hash, + }) async { + Release release; + try { + _log.info('Fetching release $tagName'); + release = await repo.getReleaseByTagName(repositorySlug, tagName); + } on ReleaseNotFound { + _log.info('Release not found - creating release $tagName'); + release = await repo.createRelease( + repositorySlug, + CreateRelease.from( + tagName: tagName, + name: 'Precompiled binaries ${hash.substring(0, 8)}', + targetCommitish: null, + isDraft: false, + isPrerelease: false, + body: 'Precompiled binaries for crate $packageName, ' + 'crate hash $hash.', + )); + } + return release; + } +} diff --git a/cargokit/build_tool/lib/src/rustup.dart b/cargokit/build_tool/lib/src/rustup.dart new file mode 100644 index 00000000..f284179a --- /dev/null +++ b/cargokit/build_tool/lib/src/rustup.dart @@ -0,0 +1,133 @@ +import 'dart:io'; + +import 'package:collection/collection.dart'; +import 'package:path/path.dart' as path; + +import 'util.dart'; + +class _Toolchain { + _Toolchain( + this.name, + this.targets, + ); + + final String name; + final List targets; +} + +class Rustup { + List? installedTargets(String toolchain) { + final targets = _installedTargets(toolchain); + return targets != null ? List.unmodifiable(targets) : null; + } + + void installToolchain(String toolchain) { + log.info("Installing Rust toolchain: $toolchain"); + runCommand("rustup", ['toolchain', 'install', toolchain]); + _installedToolchains + .add(_Toolchain(toolchain, _getInstalledTargets(toolchain))); + } + + void installTarget( + String target, { + required String toolchain, + }) { + log.info("Installing Rust target: $target"); + runCommand("rustup", [ + 'target', + 'add', + '--toolchain', + toolchain, + target, + ]); + _installedTargets(toolchain)?.add(target); + } + + final List<_Toolchain> _installedToolchains; + + Rustup() : _installedToolchains = _getInstalledToolchains(); + + List? _installedTargets(String toolchain) => _installedToolchains + .firstWhereOrNull( + (e) => e.name == toolchain || e.name.startsWith('$toolchain-')) + ?.targets; + + static List<_Toolchain> _getInstalledToolchains() { + String extractToolchainName(String line) { + // ignore (default) after toolchain name + final parts = line.split(' '); + return parts[0]; + } + + final res = runCommand("rustup", ['toolchain', 'list']); + + // To list all non-custom toolchains, we need to filter out lines that + // don't start with "stable", "beta", or "nightly". + Pattern nonCustom = RegExp(r"^(stable|beta|nightly)"); + final lines = res.stdout + .toString() + .split('\n') + .where((e) => e.isNotEmpty && e.startsWith(nonCustom)) + .map(extractToolchainName) + .toList(growable: true); + + return lines + .map( + (name) => _Toolchain( + name, + _getInstalledTargets(name), + ), + ) + .toList(growable: true); + } + + static List _getInstalledTargets(String toolchain) { + final res = runCommand("rustup", [ + 'target', + 'list', + '--toolchain', + toolchain, + '--installed', + ]); + final lines = res.stdout + .toString() + .split('\n') + .where((e) => e.isNotEmpty) + .toList(growable: true); + return lines; + } + + bool _didInstallRustSrcForNightly = false; + + void installRustSrcForNightly() { + if (_didInstallRustSrcForNightly) { + return; + } + // Useful for -Z build-std + runCommand( + "rustup", + ['component', 'add', 'rust-src', '--toolchain', 'nightly'], + ); + _didInstallRustSrcForNightly = true; + } + + static String? executablePath() { + final envPath = Platform.environment['PATH']; + final envPathSeparator = Platform.isWindows ? ';' : ':'; + final home = Platform.isWindows + ? Platform.environment['USERPROFILE'] + : Platform.environment['HOME']; + final paths = [ + if (home != null) path.join(home, '.cargo', 'bin'), + if (envPath != null) ...envPath.split(envPathSeparator), + ]; + for (final p in paths) { + final rustup = Platform.isWindows ? 'rustup.exe' : 'rustup'; + final rustupPath = path.join(p, rustup); + if (File(rustupPath).existsSync()) { + return rustupPath; + } + } + return null; + } +} diff --git a/cargokit/build_tool/lib/src/target.dart b/cargokit/build_tool/lib/src/target.dart new file mode 100644 index 00000000..9287b23c --- /dev/null +++ b/cargokit/build_tool/lib/src/target.dart @@ -0,0 +1,137 @@ +import 'dart:io'; + +import 'package:collection/collection.dart'; + +import 'util.dart'; + +class Target { + Target({ + required this.rust, + this.flutter, + this.android, + this.androidMinSdkVersion, + this.darwinPlatform, + this.darwinArch, + }); + + static final all = [ + Target( + rust: 'armv7-linux-androideabi', + flutter: 'android-arm', + android: 'armeabi-v7a', + androidMinSdkVersion: 16, + ), + Target( + rust: 'aarch64-linux-android', + flutter: 'android-arm64', + android: 'arm64-v8a', + androidMinSdkVersion: 21, + ), + Target( + rust: 'i686-linux-android', + flutter: 'android-x86', + android: 'x86', + androidMinSdkVersion: 16, + ), + Target( + rust: 'x86_64-linux-android', + flutter: 'android-x64', + android: 'x86_64', + androidMinSdkVersion: 21, + ), + Target( + rust: 'x86_64-pc-windows-msvc', + flutter: 'windows-x64', + ), + Target( + rust: 'x86_64-unknown-linux-gnu', + flutter: 'linux-x64', + ), + Target( + rust: 'aarch64-unknown-linux-gnu', + flutter: 'linux-arm64', + ), + Target( + rust: 'x86_64-apple-darwin', + darwinPlatform: 'macosx', + darwinArch: 'x86_64', + ), + Target( + rust: 'aarch64-apple-darwin', + darwinPlatform: 'macosx', + darwinArch: 'arm64', + ), + Target( + rust: 'aarch64-apple-ios', + darwinPlatform: 'iphoneos', + darwinArch: 'arm64', + ), + Target( + rust: 'aarch64-apple-ios-sim', + darwinPlatform: 'iphonesimulator', + darwinArch: 'arm64', + ), + Target( + rust: 'x86_64-apple-ios', + darwinPlatform: 'iphonesimulator', + darwinArch: 'x86_64', + ), + ]; + + static Target? forFlutterName(String flutterName) { + return all.firstWhereOrNull((element) => element.flutter == flutterName); + } + + static Target? forDarwin({ + required String platformName, + required String darwinAarch, + }) { + return all.firstWhereOrNull((element) => // + element.darwinPlatform == platformName && + element.darwinArch == darwinAarch); + } + + static Target? forRustTriple(String triple) { + return all.firstWhereOrNull((element) => element.rust == triple); + } + + static List androidTargets() { + return all + .where((element) => element.android != null) + .toList(growable: false); + } + + /// Returns buildable targets on current host platform ignoring Android targets. + static List buildableTargets() { + if (Platform.isLinux) { + // Right now we don't support cross-compiling on Linux. So we just return + // the host target. + final arch = runCommand('arch', []).stdout as String; + if (arch.trim() == 'aarch64') { + return [Target.forRustTriple('aarch64-unknown-linux-gnu')!]; + } else { + return [Target.forRustTriple('x86_64-unknown-linux-gnu')!]; + } + } + return all.where((target) { + if (Platform.isWindows) { + return target.rust.contains('-windows-'); + } else if (Platform.isMacOS) { + return target.darwinPlatform != null; + } + return false; + }).toList(growable: false); + } + + @override + String toString() { + return rust; + } + + final String? flutter; + final String rust; + final String? android; + final int? androidMinSdkVersion; + final String? darwinPlatform; + final String? darwinArch; +} diff --git a/cargokit/build_tool/lib/src/util.dart b/cargokit/build_tool/lib/src/util.dart new file mode 100644 index 00000000..d8e30196 --- /dev/null +++ b/cargokit/build_tool/lib/src/util.dart @@ -0,0 +1,169 @@ +import 'dart:convert'; +import 'dart:io'; + +import 'package:logging/logging.dart'; +import 'package:path/path.dart' as path; + +import 'logging.dart'; +import 'rustup.dart'; + +final log = Logger("process"); + +class CommandFailedException implements Exception { + final String executable; + final List arguments; + final ProcessResult result; + + CommandFailedException({ + required this.executable, + required this.arguments, + required this.result, + }); + + @override + String toString() { + final stdout = result.stdout.toString().trim(); + final stderr = result.stderr.toString().trim(); + return [ + "External Command: $executable ${arguments.map((e) => '"$e"').join(' ')}", + "Returned Exit Code: ${result.exitCode}", + kSeparator, + "STDOUT:", + if (stdout.isNotEmpty) stdout, + kSeparator, + "STDERR:", + if (stderr.isNotEmpty) stderr, + ].join('\n'); + } +} + +class TestRunCommandArgs { + final String executable; + final List arguments; + final String? workingDirectory; + final Map? environment; + final bool includeParentEnvironment; + final bool runInShell; + final Encoding? stdoutEncoding; + final Encoding? stderrEncoding; + + TestRunCommandArgs({ + required this.executable, + required this.arguments, + this.workingDirectory, + this.environment, + this.includeParentEnvironment = true, + this.runInShell = false, + this.stdoutEncoding, + this.stderrEncoding, + }); +} + +class TestRunCommandResult { + TestRunCommandResult({ + this.pid = 1, + this.exitCode = 0, + this.stdout = '', + this.stderr = '', + }); + + final int pid; + final int exitCode; + final String stdout; + final String stderr; +} + +TestRunCommandResult Function(TestRunCommandArgs args)? testRunCommandOverride; + +ProcessResult runCommand( + String executable, + List arguments, { + String? workingDirectory, + Map? environment, + bool includeParentEnvironment = true, + bool runInShell = false, + Encoding? stdoutEncoding = systemEncoding, + Encoding? stderrEncoding = systemEncoding, +}) { + if (testRunCommandOverride != null) { + final result = testRunCommandOverride!(TestRunCommandArgs( + executable: executable, + arguments: arguments, + workingDirectory: workingDirectory, + environment: environment, + includeParentEnvironment: includeParentEnvironment, + runInShell: runInShell, + stdoutEncoding: stdoutEncoding, + stderrEncoding: stderrEncoding, + )); + return ProcessResult( + result.pid, + result.exitCode, + result.stdout, + result.stderr, + ); + } + log.finer('Running command $executable ${arguments.join(' ')}'); + final res = Process.runSync( + _resolveExecutable(executable), + arguments, + workingDirectory: workingDirectory, + environment: environment, + includeParentEnvironment: includeParentEnvironment, + runInShell: runInShell, + stderrEncoding: stderrEncoding, + stdoutEncoding: stdoutEncoding, + ); + if (res.exitCode != 0) { + throw CommandFailedException( + executable: executable, + arguments: arguments, + result: res, + ); + } else { + return res; + } +} + +class RustupNotFoundException implements Exception { + @override + String toString() { + return [ + ' ', + 'rustup not found in PATH.', + ' ', + 'Maybe you need to install Rust? It only takes a minute:', + ' ', + if (Platform.isWindows) 'https://www.rust-lang.org/tools/install', + if (hasHomebrewRustInPath()) ...[ + '\$ brew unlink rust # Unlink homebrew Rust from PATH', + ], + if (!Platform.isWindows) + "\$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh", + ' ', + ].join('\n'); + } + + static bool hasHomebrewRustInPath() { + if (!Platform.isMacOS) { + return false; + } + final envPath = Platform.environment['PATH'] ?? ''; + final paths = envPath.split(':'); + return paths.any((p) { + return p.contains('homebrew') && File(path.join(p, 'rustc')).existsSync(); + }); + } +} + +String _resolveExecutable(String executable) { + if (executable == 'rustup') { + final resolved = Rustup.executablePath(); + if (resolved != null) { + return resolved; + } + throw RustupNotFoundException(); + } else { + return executable; + } +} diff --git a/cargokit/build_tool/lib/src/verify_binaries.dart b/cargokit/build_tool/lib/src/verify_binaries.dart new file mode 100644 index 00000000..0094c644 --- /dev/null +++ b/cargokit/build_tool/lib/src/verify_binaries.dart @@ -0,0 +1,81 @@ +import 'dart:io'; + +import 'package:ed25519_edwards/ed25519_edwards.dart'; +import 'package:http/http.dart'; + +import 'artifacts_provider.dart'; +import 'cargo.dart'; +import 'crate_hash.dart'; +import 'options.dart'; +import 'precompile_binaries.dart'; +import 'target.dart'; + +class VerifyBinaries { + VerifyBinaries({ + required this.manifestDir, + }); + + final String manifestDir; + + Future run() async { + final crateInfo = CrateInfo.load(manifestDir); + + final config = CargokitCrateOptions.load(manifestDir: manifestDir); + final precompiledBinaries = config.precompiledBinaries; + if (precompiledBinaries == null) { + stdout.writeln('Crate does not support precompiled binaries.'); + } else { + final crateHash = CrateHash.compute(manifestDir); + stdout.writeln('Crate hash: $crateHash'); + + for (final target in Target.all) { + final message = 'Checking ${target.rust}...'; + stdout.write(message.padRight(40)); + stdout.flush(); + + final artifacts = getArtifactNames( + target: target, + libraryName: crateInfo.packageName, + remote: true, + ); + + final prefix = precompiledBinaries.uriPrefix; + + bool ok = true; + + for (final artifact in artifacts) { + final fileName = PrecompileBinaries.fileName(target, artifact); + final signatureFileName = + PrecompileBinaries.signatureFileName(target, artifact); + + final url = Uri.parse('$prefix$crateHash/$fileName'); + final signatureUrl = + Uri.parse('$prefix$crateHash/$signatureFileName'); + + final signature = await get(signatureUrl); + if (signature.statusCode != 200) { + stdout.writeln('MISSING'); + ok = false; + break; + } + final asset = await get(url); + if (asset.statusCode != 200) { + stdout.writeln('MISSING'); + ok = false; + break; + } + + if (!verify(precompiledBinaries.publicKey, asset.bodyBytes, + signature.bodyBytes)) { + stdout.writeln('INVALID SIGNATURE'); + ok = false; + } + } + + if (ok) { + stdout.writeln('OK'); + } + } + } + } +} diff --git a/cargokit/build_tool/pubspec.lock b/cargokit/build_tool/pubspec.lock new file mode 100644 index 00000000..343bdd36 --- /dev/null +++ b/cargokit/build_tool/pubspec.lock @@ -0,0 +1,453 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + sha256: eb376e9acf6938204f90eb3b1f00b578640d3188b4c8a8ec054f9f479af8d051 + url: "https://pub.dev" + source: hosted + version: "64.0.0" + adaptive_number: + dependency: transitive + description: + name: adaptive_number + sha256: "3a567544e9b5c9c803006f51140ad544aedc79604fd4f3f2c1380003f97c1d77" + url: "https://pub.dev" + source: hosted + version: "1.0.0" + analyzer: + dependency: transitive + description: + name: analyzer + sha256: "69f54f967773f6c26c7dcb13e93d7ccee8b17a641689da39e878d5cf13b06893" + url: "https://pub.dev" + source: hosted + version: "6.2.0" + args: + dependency: "direct main" + description: + name: args + sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596 + url: "https://pub.dev" + source: hosted + version: "2.4.2" + async: + dependency: transitive + description: + name: async + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + url: "https://pub.dev" + source: hosted + version: "2.11.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + collection: + dependency: "direct main" + description: + name: collection + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + url: "https://pub.dev" + source: hosted + version: "1.18.0" + convert: + dependency: "direct main" + description: + name: convert + sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" + url: "https://pub.dev" + source: hosted + version: "3.1.1" + coverage: + dependency: transitive + description: + name: coverage + sha256: "2fb815080e44a09b85e0f2ca8a820b15053982b2e714b59267719e8a9ff17097" + url: "https://pub.dev" + source: hosted + version: "1.6.3" + crypto: + dependency: "direct main" + description: + name: crypto + sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab + url: "https://pub.dev" + source: hosted + version: "3.0.3" + ed25519_edwards: + dependency: "direct main" + description: + name: ed25519_edwards + sha256: "6ce0112d131327ec6d42beede1e5dfd526069b18ad45dcf654f15074ad9276cd" + url: "https://pub.dev" + source: hosted + version: "0.3.1" + file: + dependency: transitive + description: + name: file + sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d" + url: "https://pub.dev" + source: hosted + version: "6.1.4" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + frontend_server_client: + dependency: transitive + description: + name: frontend_server_client + sha256: "408e3ca148b31c20282ad6f37ebfa6f4bdc8fede5b74bc2f08d9d92b55db3612" + url: "https://pub.dev" + source: hosted + version: "3.2.0" + github: + dependency: "direct main" + description: + name: github + sha256: "9966bc13bf612342e916b0a343e95e5f046c88f602a14476440e9b75d2295411" + url: "https://pub.dev" + source: hosted + version: "9.17.0" + glob: + dependency: transitive + description: + name: glob + sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + hex: + dependency: "direct main" + description: + name: hex + sha256: "4e7cd54e4b59ba026432a6be2dd9d96e4c5205725194997193bf871703b82c4a" + url: "https://pub.dev" + source: hosted + version: "0.2.0" + http: + dependency: "direct main" + description: + name: http + sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + http_multi_server: + dependency: transitive + description: + name: http_multi_server + sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b" + url: "https://pub.dev" + source: hosted + version: "3.2.1" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" + url: "https://pub.dev" + source: hosted + version: "4.0.2" + io: + dependency: transitive + description: + name: io + sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" + url: "https://pub.dev" + source: hosted + version: "1.0.4" + js: + dependency: transitive + description: + name: js + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 + url: "https://pub.dev" + source: hosted + version: "0.6.7" + json_annotation: + dependency: transitive + description: + name: json_annotation + sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467 + url: "https://pub.dev" + source: hosted + version: "4.8.1" + lints: + dependency: "direct dev" + description: + name: lints + sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + logging: + dependency: "direct main" + description: + name: logging + sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + matcher: + dependency: transitive + description: + name: matcher + sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" + url: "https://pub.dev" + source: hosted + version: "0.12.16" + meta: + dependency: transitive + description: + name: meta + sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + url: "https://pub.dev" + source: hosted + version: "1.9.1" + mime: + dependency: transitive + description: + name: mime + sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e + url: "https://pub.dev" + source: hosted + version: "1.0.4" + node_preamble: + dependency: transitive + description: + name: node_preamble + sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db" + url: "https://pub.dev" + source: hosted + version: "2.0.2" + package_config: + dependency: transitive + description: + name: package_config + sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" + url: "https://pub.dev" + source: hosted + version: "2.1.0" + path: + dependency: "direct main" + description: + name: path + sha256: "2ad4cddff7f5cc0e2d13069f2a3f7a73ca18f66abd6f5ecf215219cdb3638edb" + url: "https://pub.dev" + source: hosted + version: "1.8.0" + petitparser: + dependency: transitive + description: + name: petitparser + sha256: cb3798bef7fc021ac45b308f4b51208a152792445cce0448c9a4ba5879dd8750 + url: "https://pub.dev" + source: hosted + version: "5.4.0" + pool: + dependency: transitive + description: + name: pool + sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" + url: "https://pub.dev" + source: hosted + version: "1.5.1" + pub_semver: + dependency: transitive + description: + name: pub_semver + sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + shelf: + dependency: transitive + description: + name: shelf + sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4 + url: "https://pub.dev" + source: hosted + version: "1.4.1" + shelf_packages_handler: + dependency: transitive + description: + name: shelf_packages_handler + sha256: "89f967eca29607c933ba9571d838be31d67f53f6e4ee15147d5dc2934fee1b1e" + url: "https://pub.dev" + source: hosted + version: "3.0.2" + shelf_static: + dependency: transitive + description: + name: shelf_static + sha256: a41d3f53c4adf0f57480578c1d61d90342cd617de7fc8077b1304643c2d85c1e + url: "https://pub.dev" + source: hosted + version: "1.1.2" + shelf_web_socket: + dependency: transitive + description: + name: shelf_web_socket + sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1" + url: "https://pub.dev" + source: hosted + version: "1.0.4" + source_map_stack_trace: + dependency: transitive + description: + name: source_map_stack_trace + sha256: "84cf769ad83aa6bb61e0aa5a18e53aea683395f196a6f39c4c881fb90ed4f7ae" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + source_maps: + dependency: transitive + description: + name: source_maps + sha256: "708b3f6b97248e5781f493b765c3337db11c5d2c81c3094f10904bfa8004c703" + url: "https://pub.dev" + source: hosted + version: "0.10.12" + source_span: + dependency: "direct main" + description: + name: source_span + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + url: "https://pub.dev" + source: hosted + version: "1.10.0" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + url: "https://pub.dev" + source: hosted + version: "1.11.1" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + url: "https://pub.dev" + source: hosted + version: "2.1.2" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + test: + dependency: "direct dev" + description: + name: test + sha256: "9b0dd8e36af4a5b1569029949d50a52cb2a2a2fdaa20cebb96e6603b9ae241f9" + url: "https://pub.dev" + source: hosted + version: "1.24.6" + test_api: + dependency: transitive + description: + name: test_api + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + url: "https://pub.dev" + source: hosted + version: "0.6.1" + test_core: + dependency: transitive + description: + name: test_core + sha256: "4bef837e56375537055fdbbbf6dd458b1859881f4c7e6da936158f77d61ab265" + url: "https://pub.dev" + source: hosted + version: "0.5.6" + toml: + dependency: "direct main" + description: + name: toml + sha256: "157c5dca5160fced243f3ce984117f729c788bb5e475504f3dbcda881accee44" + url: "https://pub.dev" + source: hosted + version: "0.14.0" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c + url: "https://pub.dev" + source: hosted + version: "1.3.2" + version: + dependency: "direct main" + description: + name: version + sha256: "2307e23a45b43f96469eeab946208ed63293e8afca9c28cd8b5241ff31c55f55" + url: "https://pub.dev" + source: hosted + version: "3.0.0" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: "0fae432c85c4ea880b33b497d32824b97795b04cdaa74d270219572a1f50268d" + url: "https://pub.dev" + source: hosted + version: "11.9.0" + watcher: + dependency: transitive + description: + name: watcher + sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b + url: "https://pub.dev" + source: hosted + version: "2.4.0" + webkit_inspection_protocol: + dependency: transitive + description: + name: webkit_inspection_protocol + sha256: "67d3a8b6c79e1987d19d848b0892e582dbb0c66c57cc1fef58a177dd2aa2823d" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + yaml: + dependency: "direct main" + description: + name: yaml + sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" + url: "https://pub.dev" + source: hosted + version: "3.1.2" +sdks: + dart: ">=3.0.0 <4.0.0" diff --git a/cargokit/build_tool/pubspec.yaml b/cargokit/build_tool/pubspec.yaml new file mode 100644 index 00000000..e01aa0ae --- /dev/null +++ b/cargokit/build_tool/pubspec.yaml @@ -0,0 +1,30 @@ +name: build_tool +description: Cargokit build_tool. Facilitates the build of Rust crate during Flutter application build. +publish_to: none +version: 1.0.0 + +environment: + sdk: ">=3.0.0 <4.0.0" + +# Add regular dependencies here. +dependencies: + # these are pinned on purpose because the bundle_tool_runner doesn't have + # pubspec.lock. See run_build_tool.sh + logging: 1.2.0 + path: 1.8.0 + version: 3.0.0 + collection: 1.18.0 + ed25519_edwards: 0.3.1 + hex: 0.2.0 + yaml: 3.1.2 + source_span: 1.10.0 + github: 9.17.0 + args: 2.4.2 + crypto: 3.0.3 + convert: 3.1.1 + http: 1.1.0 + toml: 0.14.0 + +dev_dependencies: + lints: ^2.1.0 + test: ^1.24.0 diff --git a/cargokit/build_tool/test/builder_test.dart b/cargokit/build_tool/test/builder_test.dart new file mode 100644 index 00000000..e92852e5 --- /dev/null +++ b/cargokit/build_tool/test/builder_test.dart @@ -0,0 +1,28 @@ +import 'package:build_tool/src/builder.dart'; +import 'package:test/test.dart'; + +void main() { + test('parseBuildConfiguration', () { + var b = BuildEnvironment.parseBuildConfiguration('debug'); + expect(b, BuildConfiguration.debug); + + b = BuildEnvironment.parseBuildConfiguration('profile'); + expect(b, BuildConfiguration.profile); + + b = BuildEnvironment.parseBuildConfiguration('release'); + expect(b, BuildConfiguration.release); + + b = BuildEnvironment.parseBuildConfiguration('debug-dev'); + expect(b, BuildConfiguration.debug); + + b = BuildEnvironment.parseBuildConfiguration('profile'); + expect(b, BuildConfiguration.profile); + + b = BuildEnvironment.parseBuildConfiguration('profile-prod'); + expect(b, BuildConfiguration.profile); + + // fallback to release + b = BuildEnvironment.parseBuildConfiguration('unknown'); + expect(b, BuildConfiguration.release); + }); +} diff --git a/cargokit/build_tool/test/cargo_test.dart b/cargokit/build_tool/test/cargo_test.dart new file mode 100644 index 00000000..00afe29f --- /dev/null +++ b/cargokit/build_tool/test/cargo_test.dart @@ -0,0 +1,28 @@ +import 'package:build_tool/src/cargo.dart'; +import 'package:test/test.dart'; + +final _cargoToml = """ +[workspace] + +[profile.release] +lto = true +panic = "abort" +opt-level = "z" +# strip = "symbols" + +[package] +name = "super_native_extensions" +version = "0.1.0" +edition = "2021" +resolver = "2" + +[lib] +crate-type = ["cdylib", "staticlib"] +"""; + +void main() { + test('parseCargoToml', () { + final info = CrateInfo.parseManifest(_cargoToml); + expect(info.packageName, 'super_native_extensions'); + }); +} diff --git a/cargokit/build_tool/test/options_test.dart b/cargokit/build_tool/test/options_test.dart new file mode 100644 index 00000000..25a85b6a --- /dev/null +++ b/cargokit/build_tool/test/options_test.dart @@ -0,0 +1,75 @@ +import 'package:build_tool/src/builder.dart'; +import 'package:build_tool/src/options.dart'; +import 'package:hex/hex.dart'; +import 'package:test/test.dart'; +import 'package:yaml/yaml.dart'; + +void main() { + test('parseCargoBuildOptions', () { + final yaml = """ +toolchain: nightly +extra_flags: + - -Z + # Comment here + - build-std=panic_abort,std +"""; + final node = loadYamlNode(yaml); + final options = CargoBuildOptions.parse(node); + expect(options.toolchain, Toolchain.nightly); + expect(options.flags, ['-Z', 'build-std=panic_abort,std']); + }); + + test('parsePrecompiledBinaries', () { + final yaml = """ +url_prefix: https://url-prefix +public_key: a4c3433798eb2c36edf2b94dbb4dd899d57496ca373a8982d8a792410b7f6445 +"""; + final precompiledBinaries = PrecompiledBinaries.parse(loadYamlNode(yaml)); + final key = HEX.decode( + 'a4c3433798eb2c36edf2b94dbb4dd899d57496ca373a8982d8a792410b7f6445'); + expect(precompiledBinaries.uriPrefix, 'https://url-prefix'); + expect(precompiledBinaries.publicKey.bytes, key); + }); + + test('parseCargokitOptions', () { + const yaml = ''' +cargo: + # For smalles binaries rebuilt the standard library with panic=abort + debug: + toolchain: nightly + extra_flags: + - -Z + # Comment here + - build-std=panic_abort,std + release: + toolchain: beta + +precompiled_binaries: + url_prefix: https://url-prefix + public_key: a4c3433798eb2c36edf2b94dbb4dd899d57496ca373a8982d8a792410b7f6445 +'''; + final options = CargokitCrateOptions.parse(loadYamlNode(yaml)); + expect(options.precompiledBinaries?.uriPrefix, 'https://url-prefix'); + final key = HEX.decode( + 'a4c3433798eb2c36edf2b94dbb4dd899d57496ca373a8982d8a792410b7f6445'); + expect(options.precompiledBinaries?.publicKey.bytes, key); + + final debugOptions = options.cargo[BuildConfiguration.debug]!; + expect(debugOptions.toolchain, Toolchain.nightly); + expect(debugOptions.flags, ['-Z', 'build-std=panic_abort,std']); + + final releaseOptions = options.cargo[BuildConfiguration.release]!; + expect(releaseOptions.toolchain, Toolchain.beta); + expect(releaseOptions.flags, []); + }); + + test('parseCargokitUserOptions', () { + const yaml = ''' +use_precompiled_binaries: false +verbose_logging: true +'''; + final options = CargokitUserOptions.parse(loadYamlNode(yaml)); + expect(options.usePrecompiledBinaries, false); + expect(options.verboseLogging, true); + }); +} diff --git a/cargokit/build_tool/test/rustup_test.dart b/cargokit/build_tool/test/rustup_test.dart new file mode 100644 index 00000000..af95303c --- /dev/null +++ b/cargokit/build_tool/test/rustup_test.dart @@ -0,0 +1,66 @@ +import 'package:build_tool/src/rustup.dart'; +import 'package:build_tool/src/util.dart'; +import 'package:test/test.dart'; + +void main() { + test('rustup with no toolchains', () { + bool didListToolchains = false; + bool didInstallStable = false; + bool didListTargets = false; + testRunCommandOverride = (args) { + expect(args.executable, 'rustup'); + switch (args.arguments) { + case ['toolchain', 'list']: + didListToolchains = true; + return TestRunCommandResult(stdout: 'no installed toolchains\n'); + case ['toolchain', 'install', 'stable']: + didInstallStable = true; + return TestRunCommandResult(); + case ['target', 'list', '--toolchain', 'stable', '--installed']: + didListTargets = true; + return TestRunCommandResult( + stdout: 'x86_64-unknown-linux-gnu\nx86_64-apple-darwin\n'); + default: + throw Exception('Unexpected call: ${args.arguments}'); + } + }; + final rustup = Rustup(); + rustup.installToolchain('stable'); + expect(didInstallStable, true); + expect(didListToolchains, true); + expect(didListTargets, true); + expect(rustup.installedTargets('stable'), [ + 'x86_64-unknown-linux-gnu', + 'x86_64-apple-darwin', + ]); + testRunCommandOverride = null; + }); + + test('rustup with esp toolchain', () { + final targetsQueried = []; + testRunCommandOverride = (args) { + expect(args.executable, 'rustup'); + switch (args.arguments) { + case ['toolchain', 'list']: + return TestRunCommandResult( + stdout: 'stable-aarch64-apple-darwin (default)\n' + 'nightly-aarch64-apple-darwin\n' + 'esp\n'); + case ['target', 'list', '--toolchain', String toolchain, '--installed']: + targetsQueried.add(toolchain); + return TestRunCommandResult(stdout: '$toolchain:target\n'); + default: + throw Exception('Unexpected call: ${args.arguments}'); + } + }; + final rustup = Rustup(); + expect(targetsQueried, [ + 'stable-aarch64-apple-darwin', + 'nightly-aarch64-apple-darwin', + ]); + expect(rustup.installedTargets('stable'), + ['stable-aarch64-apple-darwin:target']); + expect(rustup.installedTargets('nightly'), + ['nightly-aarch64-apple-darwin:target']); + }); +} diff --git a/cargokit/cmake/cargokit.cmake b/cargokit/cmake/cargokit.cmake new file mode 100644 index 00000000..ddd05df9 --- /dev/null +++ b/cargokit/cmake/cargokit.cmake @@ -0,0 +1,99 @@ +SET(cargokit_cmake_root "${CMAKE_CURRENT_LIST_DIR}/..") + +# Workaround for https://github.com/dart-lang/pub/issues/4010 +get_filename_component(cargokit_cmake_root "${cargokit_cmake_root}" REALPATH) + +if(WIN32) + # REALPATH does not properly resolve symlinks on windows :-/ + execute_process(COMMAND powershell -ExecutionPolicy Bypass -File "${CMAKE_CURRENT_LIST_DIR}/resolve_symlinks.ps1" "${cargokit_cmake_root}" OUTPUT_VARIABLE cargokit_cmake_root OUTPUT_STRIP_TRAILING_WHITESPACE) +endif() + +# Arguments +# - target: CMAKE target to which rust library is linked +# - manifest_dir: relative path from current folder to directory containing cargo manifest +# - lib_name: cargo package name +# - any_symbol_name: name of any exported symbol from the library. +# used on windows to force linking with library. +function(apply_cargokit target manifest_dir lib_name any_symbol_name) + + set(CARGOKIT_LIB_NAME "${lib_name}") + set(CARGOKIT_LIB_FULL_NAME "${CMAKE_SHARED_MODULE_PREFIX}${CARGOKIT_LIB_NAME}${CMAKE_SHARED_MODULE_SUFFIX}") + if (CMAKE_CONFIGURATION_TYPES) + set(CARGOKIT_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/$") + set(OUTPUT_LIB "${CMAKE_CURRENT_BINARY_DIR}/$/${CARGOKIT_LIB_FULL_NAME}") + else() + set(CARGOKIT_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}") + set(OUTPUT_LIB "${CMAKE_CURRENT_BINARY_DIR}/${CARGOKIT_LIB_FULL_NAME}") + endif() + set(CARGOKIT_TEMP_DIR "${CMAKE_CURRENT_BINARY_DIR}/cargokit_build") + + if (FLUTTER_TARGET_PLATFORM) + set(CARGOKIT_TARGET_PLATFORM "${FLUTTER_TARGET_PLATFORM}") + else() + set(CARGOKIT_TARGET_PLATFORM "windows-x64") + endif() + + set(CARGOKIT_ENV + "CARGOKIT_CMAKE=${CMAKE_COMMAND}" + "CARGOKIT_CONFIGURATION=$" + "CARGOKIT_MANIFEST_DIR=${CMAKE_CURRENT_SOURCE_DIR}/${manifest_dir}" + "CARGOKIT_TARGET_TEMP_DIR=${CARGOKIT_TEMP_DIR}" + "CARGOKIT_OUTPUT_DIR=${CARGOKIT_OUTPUT_DIR}" + "CARGOKIT_TARGET_PLATFORM=${CARGOKIT_TARGET_PLATFORM}" + "CARGOKIT_TOOL_TEMP_DIR=${CARGOKIT_TEMP_DIR}/tool" + "CARGOKIT_ROOT_PROJECT_DIR=${CMAKE_SOURCE_DIR}" + ) + + if (WIN32) + set(SCRIPT_EXTENSION ".cmd") + set(IMPORT_LIB_EXTENSION ".lib") + else() + set(SCRIPT_EXTENSION ".sh") + set(IMPORT_LIB_EXTENSION "") + execute_process(COMMAND chmod +x "${cargokit_cmake_root}/run_build_tool${SCRIPT_EXTENSION}") + endif() + + # Using generators in custom command is only supported in CMake 3.20+ + if (CMAKE_CONFIGURATION_TYPES AND ${CMAKE_VERSION} VERSION_LESS "3.20.0") + foreach(CONFIG IN LISTS CMAKE_CONFIGURATION_TYPES) + add_custom_command( + OUTPUT + "${CMAKE_CURRENT_BINARY_DIR}/${CONFIG}/${CARGOKIT_LIB_FULL_NAME}" + "${CMAKE_CURRENT_BINARY_DIR}/_phony_" + COMMAND ${CMAKE_COMMAND} -E env ${CARGOKIT_ENV} + "${cargokit_cmake_root}/run_build_tool${SCRIPT_EXTENSION}" build-cmake + VERBATIM + ) + endforeach() + else() + add_custom_command( + OUTPUT + ${OUTPUT_LIB} + "${CMAKE_CURRENT_BINARY_DIR}/_phony_" + COMMAND ${CMAKE_COMMAND} -E env ${CARGOKIT_ENV} + "${cargokit_cmake_root}/run_build_tool${SCRIPT_EXTENSION}" build-cmake + VERBATIM + ) + endif() + + + set_source_files_properties("${CMAKE_CURRENT_BINARY_DIR}/_phony_" PROPERTIES SYMBOLIC TRUE) + + if (TARGET ${target}) + # If we have actual cmake target provided create target and make existing + # target depend on it + add_custom_target("${target}_cargokit" DEPENDS ${OUTPUT_LIB}) + add_dependencies("${target}" "${target}_cargokit") + target_link_libraries("${target}" PRIVATE "${OUTPUT_LIB}${IMPORT_LIB_EXTENSION}") + if(WIN32) + target_link_options(${target} PRIVATE "/INCLUDE:${any_symbol_name}") + endif() + else() + # Otherwise (FFI) just use ALL to force building always + add_custom_target("${target}_cargokit" ALL DEPENDS ${OUTPUT_LIB}) + endif() + + # Allow adding the output library to plugin bundled libraries + set("${target}_cargokit_lib" ${OUTPUT_LIB} PARENT_SCOPE) + +endfunction() diff --git a/cargokit/cmake/resolve_symlinks.ps1 b/cargokit/cmake/resolve_symlinks.ps1 new file mode 100644 index 00000000..3d10d283 --- /dev/null +++ b/cargokit/cmake/resolve_symlinks.ps1 @@ -0,0 +1,27 @@ +function Resolve-Symlinks { + [CmdletBinding()] + [OutputType([string])] + param( + [Parameter(Position = 0, Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] + [string] $Path + ) + + [string] $separator = '/' + [string[]] $parts = $Path.Split($separator) + + [string] $realPath = '' + foreach ($part in $parts) { + if ($realPath -and !$realPath.EndsWith($separator)) { + $realPath += $separator + } + $realPath += $part + $item = Get-Item $realPath + if ($item.Target) { + $realPath = $item.Target.Replace('\', '/') + } + } + $realPath +} + +$path=Resolve-Symlinks -Path $args[0] +Write-Host $path diff --git a/cargokit/docs/architecture.md b/cargokit/docs/architecture.md new file mode 100644 index 00000000..d9bcf4e2 --- /dev/null +++ b/cargokit/docs/architecture.md @@ -0,0 +1,104 @@ +# Cargokit Architecture + +Note: This is mostly relevant for plugins authors that want to see a bit under the hood rather then just following a tutorial. + +In ideal conditions the end-developer using the plugin should not even be aware of Cargokit existence. + +## Integration + +Cargokit is meant to be included in Flutter plugin (or application) that contains the Rust crate to be built during the Flutter build process. + +Cargokit can be either incuded as git submodule or git subtree (required for plugins - as pub does not support submodules for git dependencies). + +For a step by step tutorial on integrating Cargokit with a Flutter plugin see https://matejknopp.com/post/flutter_plugin_in_rust_with_no_prebuilt_binaries/. + +## build_tool + +Build tool is the core of cargokit. It is a Dart command line package that facilitates the build of Rust crate. It is invoked during the Flutter build process to build (or download) Rust artifacts, but it can be also used as a standalone tool. + +It handles the following commands: + +### build-cmake + +This is invoked from `cargokit.cmake` and it is used to build the Rust crate into a dynamic library on Linux and Windows (which use CMake as build system). + +The command takes no additional arguments, everything is controlled during environment variables set by `cargokit.cmake`. + +### build-gradle + +This is invoked from `plugin.gradle` and it is used to build the Rust crate into a dynamic library on Android. The command takes no additional arguments, everything is controlled during environment variables set by `plugin.gradle`. + +The build_tool installs NDK if needed, configures the Rust environment for cross compilation and then invokes `cargo build` with appropriate arguments and environment variables. + +The build-tool also acts a linker driver. + +### build-pod + +This is invoked from plugin's podspec `script_phase` through `build_pod.sh`. Bundle tool will build the Rust crate into a static library that gets linked into the plugin Framework. In this case must have `:execution_position` set to `:before_compile`. + +Cargokit will build binaries for all active architectures from XCode build and lipo them togherer. + +When using Cargokit to integrate Rust code with an application (not a plugin) you can also configure the `Cargo.toml` to just build a dynamic library. When Cargokit finds that the crate only built a dylib and no static lib, it will attempt to replace the Cocoapod framework binary with the dylib. In this case the script `:execution_position` must be set to `:after_compile`. This is *not* recommended for plugins and it's quite experimental. + +### gen-key, precompile-binaries, verify-binaries + +These are used as when providing precompiled binaries for Plugin. See [precompiled_binaries.md](precompiled_binaries.md) for more information. + +## Launching the build_tool during build. + +During Flutter build, the build tool can not be launched directly using `dart run`. Rather it is launched through `run_build_tool.sh` and `run_build_tool.cmd`. Because the `build_tool` is shipped as part of plugin, we generally don't want to write into the plugin directory during build, which would happen if the `build_tool` was simply invoked through `dart run` (For example the `.dart_tool/package_config.json` file would get written inside the `build_tool` directory). + +Instead the `run_build_tool` script creates a minimal Dart command line package in the build directory and references the `build_tool` as package. That way the `.dart_tool/package_config.json` file is created in the temporary build folder and not in the plugin itself. The script also precompiles the Dart code to speed up subsequent invocations. + +## Configuring Cargokit + +### Configuration for the Rust crate + +Cargokit can be configured through a `cargokit.yaml` file, which can be used to control the build of the Rust package and is placed into the Rust crate next to `Cargo.toml`. + +Here is an example `cargokit.yaml` with comments: +```yaml +cargo: + debug: # Configuration of cargo execution during debug builds + toolchain: stable # default + release: # Configuration of cargo execution for release builds + toolchain: nightly # rustup will be invoked with nightly toolchain + extra_flags: # extra arguments passed to cargo build + - -Z + - build-std=panic_abort,std + +# If crate ships with precompiled binaries, they can be configured here. +precompiled_binaries: + # Uri prefix used when downloading precompiled binaries. + url_prefix: https://github.com/superlistapp/super_native_extensions/releases/download/precompiled_ + + # Public key for verifying downloaded precompiled binaries. + public_key: 3a257ef1c7d72d84225ac4658d24812ada50a7a7a8a2138c2a91353389fdc514 +``` + +### Configuration for the application consuming the plugin + +A `cargokit_options.yaml` file can also be placed by developer using plugin to the root of the application package. In which case the file can be used to specify following options: + +```yaml +# Enables verbose logging of Cargokit during build +verbose_logging: true + +# Opts out of using precompiled binaries. If crate has configured +# and deployed precompiled binaries, these will be by default used whenever Rustup +# is not installed. With `use_precompiled_binaries` set to false, the build will +# instead be aborted prompting user to install Rustup. +use_precompiled_binaries: false +``` + +## Detecting Rustup + +When the plugin doesn't come with precompiled libraries (or user opt-out), `build_tool` will need to invoke Rustup during build to ensure that required Rust targets and toolchain are installed for current build and to build the Rust crate. + +Cargokit will attempt to detect Rustup in the default Rustup installation location (`~/.cargo/rustup`) as well as in PATH. This is done so that if user install Rustup but doesn't properly configure PATH, Cargokit will still work. + +If `build_tool` doesn't find Rustup, it will about the build with a message showing instructions to install Rustup specific to current platform. + +On macOS it will also detect a homebrew Rust installation in PATH and will prompt user to call `brew unlink rust` first to remove homebrew Rust installation from PATH, because it may interfere with Rustup. + +Homebrew Rust installation can not be used by Cargokit, because it can only build for host platform. Cargokit needs to be able to cross compile the Rust crate for iOS and Android and thus needs full Rustup installation. diff --git a/cargokit/docs/precompiled_binaries.md b/cargokit/docs/precompiled_binaries.md new file mode 100644 index 00000000..2026e867 --- /dev/null +++ b/cargokit/docs/precompiled_binaries.md @@ -0,0 +1,95 @@ +# Precompiled Binaries + +Because Cargokit builds the Rust crate during Flutter build, it is inherently +dependend on the Rust toolchain being installed on the developer's machine. + +To decrease the friction, it is possible for Cargokit to use precompiled binaries instead. + +This is how the process of using precompiled binaries looks from the perspective of the build on developer machine: + +1. Cargokit checks if there is `cargokit_options.yaml` file in the root folder of target application. If there is one, it will be checked for `use_precompiled_binaries` options to see if user opted out of using precompiled binaries. In which case Cargokit will insist on building from source. Cargokit will also build from source if the configuration file is absent, but user has Rustup installed. + +2. Cargokit checks if there is `cargokit.yaml` file placed in the Rust crate. If there is one, it will be checked for `precompiled_binaries` section to see if crate supports precompiled binaries. The configuration section must contain a public key and URL prefix. + +3. Cargokit computes a `crate-hash`. This is a SHA256 hash value computed from all Rust files inside crate, `Cargo.toml`, `Cargo.lock` and `cargokit.yaml`. This uniquely identifies the crate and it is used to find the correct precompiled binaries. + +4. Cargokit will attempt to download the precompiled binaries for target platform and `crate_hash` combination and a signature file for each downloaded binary. If download succeeds, the binary content will be verified against the signature and public key included in `cargokit.yaml` (which is part of Rust crate and thus part of published Flutter package). + +5. If the verification succeeds, the precompiled binaries will be used. Otherwise the binary will be discarded and Cargokit will insist on building from source. + +## Providing precompiled binaries + +Note that this assumes that precompiled binaries will be generated during github actions and deployed as github releases. + +### Use `build_tool` to generate a key-pair: + +``` +dart run build_tool gen-key +``` + +This will print the private key and public key. Store the private key securely. It needs to be provided as a secret to github action. + +The public key should be included in `cargokit.yaml` file in the Rust crate. + +### Provide a `cargokit.yaml` file in the Rust crate + +The file must be placed alongside Cargo.toml. + +```yaml +precompiled_binaries: + # Uri prefix used when downloading precompiled binaries. + url_prefix: https://github.com///releases/download/precompiled_ + + # Public key for verifying downloaded precompiled binaries. + public_key: +``` + +### Configure a github action to build and upload precompiled binaries. + +The github action should be run at every commit to main branch (and possibly other branches). + +The action needs two secrets - private key for signing binaries and GitHub token for uploading binaries as releases. Here is example action that precompiles and uploads binaries for all supported targets. + +```yaml +on: + push: + branches: [ main ] + +name: Precompile Binaries + +jobs: + Precompile: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: + - ubuntu-latest + - macOS-latest + - windows-latest + steps: + - uses: actions/checkout@v2 + - uses: dart-lang/setup-dart@v1 + - name: Install GTK + if: (matrix.os == 'ubuntu-latest') + run: sudo apt-get update && sudo apt-get install libgtk-3-dev + - name: Precompile + if: (matrix.os == 'macOS-latest') || (matrix.os == 'windows-latest') + run: dart run build_tool precompile-binaries -v --manifest-dir=../../rust --repository=superlistapp/super_native_extensions + working-directory: super_native_extensions/cargokit/build_tool + env: + GITHUB_TOKEN: ${{ secrets.RELEASE_GITHUB_TOKEN }} + PRIVATE_KEY: ${{ secrets.RELEASE_PRIVATE_KEY }} + - name: Precompile (with Android) + if: (matrix.os == 'ubuntu-latest') + run: dart run build_tool precompile-binaries -v --manifest-dir=../../rust --repository=superlistapp/super_native_extensions --android-sdk-location=/usr/local/lib/android/sdk --android-ndk-version=24.0.8215888 --android-min-sdk-version=23 + working-directory: super_native_extensions/cargokit/build_tool + env: + GITHUB_TOKEN: ${{ secrets.RELEASE_GITHUB_TOKEN }} + PRIVATE_KEY: ${{ secrets.RELEASE_PRIVATE_KEY }} +``` + +By default the `built_tool precompile-binaries` commands build and uploads the binaries for all targets buildable from current host. This can be overriden using the `--target ` argument. + +Android binaries will be built when `--android-sdk-location` and `--android-ndk-version` arguments are provided. + diff --git a/cargokit/gradle/plugin.gradle b/cargokit/gradle/plugin.gradle new file mode 100644 index 00000000..37dd086a --- /dev/null +++ b/cargokit/gradle/plugin.gradle @@ -0,0 +1,176 @@ +import java.nio.file.Paths +import org.apache.tools.ant.taskdefs.condition.Os + +CargoKitPlugin.file = buildscript.sourceFile + +apply plugin: CargoKitPlugin + +class CargoKitExtension { + String manifestDir; // Relative path to folder containing Cargo.toml + String libname; // Library name within Cargo.toml. Must be a cdylib +} + +abstract class CargoKitBuildTask extends DefaultTask { + + @Input + String buildMode + + @Input + String buildDir + + @Input + String outputDir + + @Input + String ndkVersion + + @Input + String sdkDirectory + + @Input + int compileSdkVersion; + + @Input + int minSdkVersion; + + @Input + String pluginFile + + @Input + List targetPlatforms + + @TaskAction + def build() { + if (project.cargokit.manifestDir == null) { + throw new GradleException("Property 'manifestDir' must be set on cargokit extension"); + } + + if (project.cargokit.libname == null) { + throw new GradleException("Property 'libname' must be set on cargokit extension"); + } + + def executableName = Os.isFamily(Os.FAMILY_WINDOWS) ? "run_build_tool.cmd" : "run_build_tool.sh" + def path = Paths.get(new File(pluginFile).parent, "..", executableName); + + def manifestDir = Paths.get(project.buildscript.sourceFile.parent, project.cargokit.manifestDir) + + def rootProjectDir = project.rootProject.projectDir + + if (!Os.isFamily(Os.FAMILY_WINDOWS)) { + project.exec { + commandLine 'chmod', '+x', path + } + } + + project.exec { + executable path + args "build-gradle" + environment "CARGOKIT_ROOT_PROJECT_DIR", rootProjectDir + environment "CARGOKIT_TOOL_TEMP_DIR", "${buildDir}/build_tool" + environment "CARGOKIT_MANIFEST_DIR", manifestDir + environment "CARGOKIT_CONFIGURATION", buildMode + environment "CARGOKIT_TARGET_TEMP_DIR", buildDir + environment "CARGOKIT_OUTPUT_DIR", outputDir + environment "CARGOKIT_NDK_VERSION", ndkVersion + environment "CARGOKIT_SDK_DIR", sdkDirectory + environment "CARGOKIT_COMPILE_SDK_VERSION", compileSdkVersion + environment "CARGOKIT_MIN_SDK_VERSION", minSdkVersion + environment "CARGOKIT_TARGET_PLATFORMS", targetPlatforms.join(",") + environment "CARGOKIT_JAVA_HOME", System.properties['java.home'] + } + } +} + +class CargoKitPlugin implements Plugin { + + static String file; + + private Plugin findFlutterPlugin(Project rootProject) { + _findFlutterPlugin(rootProject.childProjects) + } + + private Plugin _findFlutterPlugin(Map projects) { + for (project in projects) { + for (plugin in project.value.getPlugins()) { + if (plugin.class.name == "FlutterPlugin") { + return plugin; + } + } + def plugin = _findFlutterPlugin(project.value.childProjects); + if (plugin != null) { + return plugin; + } + } + return null; + } + + @Override + void apply(Project project) { + def plugin = findFlutterPlugin(project.rootProject); + + project.extensions.create("cargokit", CargoKitExtension) + + if (plugin == null) { + print("Flutter plugin not found, CargoKit plugin will not be applied.") + return; + } + + def cargoBuildDir = "${project.buildDir}/build" + + // Determine if the project is an application or library + def isApplication = plugin.project.plugins.hasPlugin('com.android.application') + def variants = isApplication ? plugin.project.android.applicationVariants : plugin.project.android.libraryVariants + + variants.all { variant -> + + final buildType = variant.buildType.name + + def cargoOutputDir = "${project.buildDir}/jniLibs/${buildType}"; + def jniLibs = project.android.sourceSets.maybeCreate(buildType).jniLibs; + jniLibs.srcDir(new File(cargoOutputDir)) + + def platforms = plugin.getTargetPlatforms().collect() + + // Same thing addFlutterDependencies does in flutter.gradle + if (buildType == "debug") { + platforms.add("android-x86") + platforms.add("android-x64") + } + + // The task name depends on plugin properties, which are not available + // at this point + project.getGradle().afterProject { + def taskName = "cargokitCargoBuild${project.cargokit.libname.capitalize()}${buildType.capitalize()}"; + + if (project.tasks.findByName(taskName)) { + return + } + + if (plugin.project.android.ndkVersion == null) { + throw new GradleException("Please set 'android.ndkVersion' in 'app/build.gradle'.") + } + + def task = project.tasks.create(taskName, CargoKitBuildTask.class) { + buildMode = variant.buildType.name + buildDir = cargoBuildDir + outputDir = cargoOutputDir + ndkVersion = plugin.project.android.ndkVersion + sdkDirectory = plugin.project.android.sdkDirectory + minSdkVersion = plugin.project.android.defaultConfig.minSdkVersion.apiLevel as int + compileSdkVersion = plugin.project.android.compileSdkVersion.substring(8) as int + targetPlatforms = platforms + pluginFile = CargoKitPlugin.file + } + def onTask = { newTask -> + if (newTask.name == "merge${buildType.capitalize()}NativeLibs") { + newTask.dependsOn task + // Fix gradle 7.4.2 not picking up JNI library changes + newTask.outputs.upToDateWhen { false } + } + } + project.tasks.each onTask + project.tasks.whenTaskAdded onTask + } + } + } +} diff --git a/cargokit/run_build_tool.cmd b/cargokit/run_build_tool.cmd new file mode 100644 index 00000000..c45d0aa8 --- /dev/null +++ b/cargokit/run_build_tool.cmd @@ -0,0 +1,91 @@ +@echo off +setlocal + +setlocal ENABLEDELAYEDEXPANSION + +SET BASEDIR=%~dp0 + +if not exist "%CARGOKIT_TOOL_TEMP_DIR%" ( + mkdir "%CARGOKIT_TOOL_TEMP_DIR%" +) +cd /D "%CARGOKIT_TOOL_TEMP_DIR%" + +SET BUILD_TOOL_PKG_DIR=%BASEDIR%build_tool +SET DART=%FLUTTER_ROOT%\bin\cache\dart-sdk\bin\dart + +set BUILD_TOOL_PKG_DIR_POSIX=%BUILD_TOOL_PKG_DIR:\=/% + +( + echo name: build_tool_runner + echo version: 1.0.0 + echo publish_to: none + echo. + echo environment: + echo sdk: '^>=3.0.0 ^<4.0.0' + echo. + echo dependencies: + echo build_tool: + echo path: %BUILD_TOOL_PKG_DIR_POSIX% +) >pubspec.yaml + +if not exist bin ( + mkdir bin +) + +( + echo import 'package:build_tool/build_tool.dart' as build_tool; + echo void main^(List^ args^) ^{ + echo build_tool.runMain^(args^); + echo ^} +) >bin\build_tool_runner.dart + +SET PRECOMPILED=bin\build_tool_runner.dill + +REM To detect changes in package we compare output of DIR /s (recursive) +set PREV_PACKAGE_INFO=.dart_tool\package_info.prev +set CUR_PACKAGE_INFO=.dart_tool\package_info.cur + +DIR "%BUILD_TOOL_PKG_DIR%" /s > "%CUR_PACKAGE_INFO%_orig" + +REM Last line in dir output is free space on harddrive. That is bound to +REM change between invocation so we need to remove it +( + Set "Line=" + For /F "UseBackQ Delims=" %%A In ("%CUR_PACKAGE_INFO%_orig") Do ( + SetLocal EnableDelayedExpansion + If Defined Line Echo !Line! + EndLocal + Set "Line=%%A") +) >"%CUR_PACKAGE_INFO%" +DEL "%CUR_PACKAGE_INFO%_orig" + +REM Compare current directory listing with previous +FC /B "%CUR_PACKAGE_INFO%" "%PREV_PACKAGE_INFO%" > nul 2>&1 + +If %ERRORLEVEL% neq 0 ( + REM Changed - copy current to previous and remove precompiled kernel + if exist "%PREV_PACKAGE_INFO%" ( + DEL "%PREV_PACKAGE_INFO%" + ) + MOVE /Y "%CUR_PACKAGE_INFO%" "%PREV_PACKAGE_INFO%" + if exist "%PRECOMPILED%" ( + DEL "%PRECOMPILED%" + ) +) + +REM There is no CUR_PACKAGE_INFO it was renamed in previous step to %PREV_PACKAGE_INFO% +REM which means we need to do pub get and precompile +if not exist "%PRECOMPILED%" ( + echo Running pub get in "%cd%" + "%DART%" pub get --no-precompile + "%DART%" compile kernel bin/build_tool_runner.dart +) + +"%DART%" "%PRECOMPILED%" %* + +REM 253 means invalid snapshot version. +If %ERRORLEVEL% equ 253 ( + "%DART%" pub get --no-precompile + "%DART%" compile kernel bin/build_tool_runner.dart + "%DART%" "%PRECOMPILED%" %* +) diff --git a/cargokit/run_build_tool.sh b/cargokit/run_build_tool.sh new file mode 100755 index 00000000..24b0ed89 --- /dev/null +++ b/cargokit/run_build_tool.sh @@ -0,0 +1,99 @@ +#!/usr/bin/env bash + +set -e + +BASEDIR=$(dirname "$0") + +mkdir -p "$CARGOKIT_TOOL_TEMP_DIR" + +cd "$CARGOKIT_TOOL_TEMP_DIR" + +# Write a very simple bin package in temp folder that depends on build_tool package +# from Cargokit. This is done to ensure that we don't pollute Cargokit folder +# with .dart_tool contents. + +BUILD_TOOL_PKG_DIR="$BASEDIR/build_tool" + +if [[ -z $FLUTTER_ROOT ]]; then # not defined + DART=dart +else + DART="$FLUTTER_ROOT/bin/cache/dart-sdk/bin/dart" +fi + +cat << EOF > "pubspec.yaml" +name: build_tool_runner +version: 1.0.0 +publish_to: none + +environment: + sdk: '>=3.0.0 <4.0.0' + +dependencies: + build_tool: + path: "$BUILD_TOOL_PKG_DIR" +EOF + +mkdir -p "bin" + +cat << EOF > "bin/build_tool_runner.dart" +import 'package:build_tool/build_tool.dart' as build_tool; +void main(List args) { + build_tool.runMain(args); +} +EOF + +# Create alias for `shasum` if it does not exist and `sha1sum` exists +if ! [ -x "$(command -v shasum)" ] && [ -x "$(command -v sha1sum)" ]; then + shopt -s expand_aliases + alias shasum="sha1sum" +fi + +# Dart run will not cache any package that has a path dependency, which +# is the case for our build_tool_runner. So instead we precompile the package +# ourselves. +# To invalidate the cached kernel we use the hash of ls -LR of the build_tool +# package directory. This should be good enough, as the build_tool package +# itself is not meant to have any path dependencies. + +if [[ "$OSTYPE" == "darwin"* ]]; then + PACKAGE_HASH=$(ls -lTR "$BUILD_TOOL_PKG_DIR" | shasum) +else + PACKAGE_HASH=$(ls -lR --full-time "$BUILD_TOOL_PKG_DIR" | shasum) +fi + +PACKAGE_HASH_FILE=".package_hash" + +if [ -f "$PACKAGE_HASH_FILE" ]; then + EXISTING_HASH=$(cat "$PACKAGE_HASH_FILE") + if [ "$PACKAGE_HASH" != "$EXISTING_HASH" ]; then + rm "$PACKAGE_HASH_FILE" + fi +fi + +# Run pub get if needed. +if [ ! -f "$PACKAGE_HASH_FILE" ]; then + "$DART" pub get --no-precompile + "$DART" compile kernel bin/build_tool_runner.dart + echo "$PACKAGE_HASH" > "$PACKAGE_HASH_FILE" +fi + +# Rebuild the tool if it was deleted by Android Studio +if [ ! -f "bin/build_tool_runner.dill" ]; then + "$DART" compile kernel bin/build_tool_runner.dart +fi + +set +e + +"$DART" bin/build_tool_runner.dill "$@" + +exit_code=$? + +# 253 means invalid snapshot version. +if [ $exit_code == 253 ]; then + "$DART" pub get --no-precompile + "$DART" compile kernel bin/build_tool_runner.dart + "$DART" bin/build_tool_runner.dill "$@" + exit_code=$? +fi + +exit $exit_code diff --git a/example/android/build.gradle b/example/android/build.gradle index e06c40a4..9b862e7a 100644 --- a/example/android/build.gradle +++ b/example/android/build.gradle @@ -1,3 +1,6 @@ +// group 'com.cypherstack.epic_cash_wallet' +// version '1.0' + buildscript { ext.kotlin_version = '1.8.0' repositories { @@ -29,3 +32,10 @@ subprojects { tasks.register("clean", Delete) { delete rootProject.buildDir } + +apply from: "../cargokit/gradle/plugin.gradle" + +cargokit { + manifestDir = "../rust" + libname = "epic_cash_wallet" +} diff --git a/example/linux/CMakeLists.txt b/example/linux/CMakeLists.txt index 9c99df83..594fff49 100644 --- a/example/linux/CMakeLists.txt +++ b/example/linux/CMakeLists.txt @@ -116,13 +116,14 @@ install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR} install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" COMPONENT Runtime) -if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") - install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../../linux/bin/aarch64-unknown-linux-gnu/release/libepic_cash_wallet.so" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" - COMPONENT Runtime) -else() - install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../../linux/bin/x86_64-unknown-linux-gnu/release/libepic_cash_wallet.so" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" - COMPONENT Runtime) -endif() +# To be replaced with irondash/cargokit: +# if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") +# install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../../linux/bin/aarch64-unknown-linux-gnu/release/libepic_cash_wallet.so" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" +# COMPONENT Runtime) +# else() +# install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../../linux/bin/x86_64-unknown-linux-gnu/release/libepic_cash_wallet.so" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" +# COMPONENT Runtime) +# endif() foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES}) install(FILES "${bundled_library}" diff --git a/ios/flutter_libepiccash.podspec b/ios/flutter_libepiccash.podspec index 80220010..74dd26d0 100644 --- a/ios/flutter_libepiccash.podspec +++ b/ios/flutter_libepiccash.podspec @@ -12,11 +12,31 @@ A new Flutter plugin project. s.homepage = 'http://example.com' s.license = { :file => '../LICENSE' } s.author = { 'Your Company' => 'email@example.com' } + s.source = { :path => '.' } - s.public_header_files = 'Classes**/*.h' + # s.public_header_files = 'Classes**/*.h' s.source_files = 'Classes/**/*' - s.static_framework = true - s.vendored_libraries = "**/*.a" + + # s.static_framework = true + # s.vendored_libraries = "**/*.a" # Disabled for cargokit. + + s.script_phase = { + :name => 'Build Rust library', + # First argument is relative path to the `rust` folder, second is name of rust library + :script => 'sh "$PODS_TARGET_SRCROOT/../cargokit/build_pod.sh" ../rust epic_cash_wallet', + :execution_position => :before_compile, + :input_files => ['${BUILT_PRODUCTS_DIR}/cargokit_phony'], + # Let XCode know that the static library referenced in -force_load below is + # created by this build step. + :output_files => ["${BUILT_PRODUCTS_DIR}/libepic_cash_wallet.a"], + } + s.pod_target_xcconfig = { + 'DEFINES_MODULE' => 'YES', + # Flutter.framework does not contain a i386 slice. + 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386', + 'OTHER_LDFLAGS' => '-force_load ${BUILT_PRODUCTS_DIR}/libepic_cash_wallet.a', + } + s.dependency 'Flutter' s.platform = :ios, '9.0' diff --git a/linux/CMakeLists.txt b/linux/CMakeLists.txt index 4489797b..aa0b05ea 100644 --- a/linux/CMakeLists.txt +++ b/linux/CMakeLists.txt @@ -38,10 +38,14 @@ target_include_directories(${PLUGIN_NAME} INTERFACE target_link_libraries(${PLUGIN_NAME} PRIVATE flutter) target_link_libraries(${PLUGIN_NAME} PRIVATE PkgConfig::GTK) +include("../cargokit/cmake/cargokit.cmake") +apply_cargokit(${PROJECT_NAME} ../rust epic_cash_wallet "") + # 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_libepiccash_bundled_libraries - "" + # Replace original target file with the one produced by Cargokit: + "${${PROJECT_NAME}_cargokit_lib}" PARENT_SCOPE ) diff --git a/macos/flutter_libepiccash.podspec b/macos/flutter_libepiccash.podspec index 55998d4f..fbd1f942 100644 --- a/macos/flutter_libepiccash.podspec +++ b/macos/flutter_libepiccash.podspec @@ -14,12 +14,30 @@ A new Flutter plugin project. s.author = { 'Your Company' => 'email@example.com' } s.source = { :path => '.' } - s.source_files = 'Classes/**/*' - s.static_framework = true - s.vendored_libraries = "**/*.a" - s.dependency 'FlutterMacOS' + s.source_files = 'Classes/**/*' + # s.vendored_libraries = "**/*.a" # Disabled for cargokit. + + s.script_phase = { + :name => 'Build Rust library', + # First argument is relative path to the `rust` folder, second is name of rust library + :script => 'sh "$PODS_TARGET_SRCROOT/../cargokit/build_pod.sh" ../rust epic_cash_wallet', + :execution_position => :before_compile, + :input_files => ['${BUILT_PRODUCTS_DIR}/cargokit_phony'], + # Let XCode know that the static library referenced in -force_load below is + # created by this build step. + :output_files => ["${BUILT_PRODUCTS_DIR}/libepic_cash_wallet.a"], + } + s.pod_target_xcconfig = { + 'DEFINES_MODULE' => 'YES', + # Flutter.framework does not contain a i386 slice. + 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386', + 'OTHER_LDFLAGS' => '-force_load ${BUILT_PRODUCTS_DIR}/libepic_cash_wallet.a', + } s.platform = :osx, '10.11' s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=macosx*]' => 'x86_64' } + # TODO: Double-check if x86_64 works with the cargokit build once that's done. s.swift_version = '5.0' + + s.dependency 'FlutterMacOS' end diff --git a/rust/.gitignore b/rust/.gitignore index ea8c4bf7..d01bd1a9 100644 --- a/rust/.gitignore +++ b/rust/.gitignore @@ -1 +1,21 @@ -/target +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb + +# RustRover +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ \ No newline at end of file diff --git a/rust/Cargo.lock b/rust/Cargo.lock deleted file mode 100644 index c8bf27b7..00000000 --- a/rust/Cargo.lock +++ /dev/null @@ -1,5434 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 4 - -[[package]] -name = "addr2line" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - -[[package]] -name = "aead" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" -dependencies = [ - "crypto-common", - "generic-array 0.14.7", -] - -[[package]] -name = "aho-corasick" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" -dependencies = [ - "memchr 2.7.1", -] - -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - -[[package]] -name = "android_log-sys" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85965b6739a430150bdd138e2374a98af0c3ee0d030b3bb7fc3bddff58d0102e" - -[[package]] -name = "android_logger" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8619b80c242aa7bd638b5c7ddd952addeecb71f69c75e33f1d47b2804f8f883a" -dependencies = [ - "android_log-sys", - "env_logger 0.10.2", - "log 0.4.25", - "once_cell", -] - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - -[[package]] -name = "ansi_term" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" -dependencies = [ - "winapi 0.3.9", -] - -[[package]] -name = "antidote" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34fde25430d87a9388dadbe6e34d7f72a462c8b43ac8d309b42b0a8505d7e2a5" - -[[package]] -name = "anyhow" -version = "1.0.80" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1" - -[[package]] -name = "arc-swap" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc4662175ead9cd84451d5c35070517777949a2ed84551764129cedb88384841" - -[[package]] -name = "arrayref" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" - -[[package]] -name = "arrayvec" -version = "0.3.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06f59fe10306bb78facd90d28c2038ad23ffaaefa85bac43c8a434cde383334f" -dependencies = [ - "nodrop", - "odds", -] - -[[package]] -name = "arrayvec" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9" -dependencies = [ - "nodrop", -] - -[[package]] -name = "arrayvec" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" - -[[package]] -name = "ascii" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eab1c04a571841102f5345a8fc0f6bb3d31c315dec879b5c6e42e40ce7ffa34e" - -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi 0.1.19", - "libc", - "winapi 0.3.9", -] - -[[package]] -name = "autocfg" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dde43e75fd43e8a1bf86103336bc699aa8d17ad1be60c76c0bdfd4828e19b78" -dependencies = [ - "autocfg 1.1.0", -] - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "backtrace" -version = "0.3.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" -dependencies = [ - "addr2line", - "cc", - "cfg-if 1.0.0", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", -] - -[[package]] -name = "base64" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643" -dependencies = [ - "byteorder", - "safemem", -] - -[[package]] -name = "base64" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" - -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - -[[package]] -name = "base64" -version = "0.21.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" - -[[package]] -name = "base64ct" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" - -[[package]] -name = "bigint" -version = "4.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0e8c8a600052b52482eff2cf4d810e462fdff1f656ac1ecb6232132a1ed7def" -dependencies = [ - "byteorder", - "crunchy 0.1.6", -] - -[[package]] -name = "bit-vec" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" - -[[package]] -name = "bitflags" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" - -[[package]] -name = "bitvec" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" -dependencies = [ - "funty", - "radium", - "tap", - "wyz", -] - -[[package]] -name = "blake2-rfc" -version = "0.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400" -dependencies = [ - "arrayvec 0.4.12", - "constant_time_eq", -] - -[[package]] -name = "blake2b_simd" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afa748e348ad3be8263be728124b24a24f268266f6f5d58af9d75f6a40b5c587" -dependencies = [ - "arrayref", - "arrayvec 0.5.2", - "constant_time_eq", -] - -[[package]] -name = "block-buffer" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" -dependencies = [ - "block-padding", - "byte-tools", - "byteorder", - "generic-array 0.12.4", -] - -[[package]] -name = "block-buffer" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" -dependencies = [ - "generic-array 0.14.7", -] - -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array 0.14.7", -] - -[[package]] -name = "block-padding" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" -dependencies = [ - "byte-tools", -] - -[[package]] -name = "bumpalo" -version = "3.15.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea184aa71bb362a1157c896979544cc23974e08fd265f29ea96b59f0b4a555b" - -[[package]] -name = "byte-tools" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" - -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - -[[package]] -name = "bytes" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" -dependencies = [ - "byteorder", - "iovec", -] - -[[package]] -name = "bytes" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38" - -[[package]] -name = "bytes" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" - -[[package]] -name = "cc" -version = "1.0.88" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02f341c093d19155a6e41631ce5971aac4e9a868262212153124c15fa22d1cdc" - -[[package]] -name = "cesu8" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" - -[[package]] -name = "cfg-if" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "chacha20" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" -dependencies = [ - "cfg-if 1.0.0", - "cipher", - "cpufeatures", -] - -[[package]] -name = "chacha20poly1305" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" -dependencies = [ - "aead", - "chacha20", - "cipher", - "poly1305", - "zeroize", -] - -[[package]] -name = "chrono" -version = "0.4.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bc015644b92d5890fab7489e49d21f879d5c990186827d42ec511919404f38b" -dependencies = [ - "android-tzdata", - "iana-time-zone", - "js-sys", - "num-traits 0.2.18", - "serde", - "wasm-bindgen", - "windows-targets 0.52.3", -] - -[[package]] -name = "cipher" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" -dependencies = [ - "crypto-common", - "inout", - "zeroize", -] - -[[package]] -name = "clap" -version = "2.34.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" -dependencies = [ - "ansi_term", - "atty", - "bitflags 1.3.2", - "strsim", - "textwrap", - "unicode-width", - "vec_map", - "yaml-rust 0.3.5", -] - -[[package]] -name = "clear_on_drop" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38508a63f4979f0048febc9966fadbd48e5dab31fd0ec6a3f151bbf4a74f7423" -dependencies = [ - "cc", -] - -[[package]] -name = "cloudabi" -version = "0.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" -dependencies = [ - "bitflags 1.3.2", -] - -[[package]] -name = "combine" -version = "3.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da3da6baa321ec19e1cc41d31bf599f00c783d0517095cdaf0332e3fe8d20680" -dependencies = [ - "ascii", - "byteorder", - "either", - "memchr 2.7.1", - "unreachable", -] - -[[package]] -name = "constant_time_eq" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" - -[[package]] -name = "core-foundation" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" - -[[package]] -name = "cpufeatures" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" -dependencies = [ - "libc", -] - -[[package]] -name = "crc32fast" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" -dependencies = [ - "cfg-if 1.0.0", -] - -[[package]] -name = "croaring" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7266f0a7275b00ce4c4f4753e8c31afdefe93828101ece83a06e2ddab1dd1010" -dependencies = [ - "byteorder", - "croaring-sys", -] - -[[package]] -name = "croaring-sys" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e47112498c394a7067949ebc07ef429b7384a413cf0efcf675846a47bcd307fb" -dependencies = [ - "cc", -] - -[[package]] -name = "crossbeam-deque" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c20ff29ded3204c5106278a81a38f4b482636ed4fa1e6cfbeef193291beb29ed" -dependencies = [ - "crossbeam-epoch 0.8.2", - "crossbeam-utils 0.7.2", - "maybe-uninit", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" -dependencies = [ - "crossbeam-epoch 0.9.18", - "crossbeam-utils 0.8.19", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" -dependencies = [ - "autocfg 1.1.0", - "cfg-if 0.1.10", - "crossbeam-utils 0.7.2", - "lazy_static", - "maybe-uninit", - "memoffset", - "scopeguard 1.2.0", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" -dependencies = [ - "crossbeam-utils 0.8.19", -] - -[[package]] -name = "crossbeam-queue" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570" -dependencies = [ - "cfg-if 0.1.10", - "crossbeam-utils 0.7.2", - "maybe-uninit", -] - -[[package]] -name = "crossbeam-utils" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" -dependencies = [ - "autocfg 1.1.0", - "cfg-if 0.1.10", - "lazy_static", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" - -[[package]] -name = "crunchy" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2f4a431c5c9f662e1200b7c7f02c34e91361150e382089a8f2dec3ba680cbda" - -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array 0.14.7", - "rand_core 0.6.4", - "typenum", -] - -[[package]] -name = "crypto-mac" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" -dependencies = [ - "generic-array 0.14.7", - "subtle", -] - -[[package]] -name = "csv" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef22b37c7a51c564a365892c012dc0271221fdcc64c69b19ba4d6fa8bd96d9c" -dependencies = [ - "byteorder", - "memchr 1.0.2", - "rustc-serialize", -] - -[[package]] -name = "csv" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac574ff4d437a7b5ad237ef331c17ccca63c46479e5b5453eb8e10bb99a759fe" -dependencies = [ - "csv-core", - "itoa 1.0.10", - "ryu", - "serde", -] - -[[package]] -name = "csv-core" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70" -dependencies = [ - "memchr 2.7.1", -] - -[[package]] -name = "ctrlc" -version = "3.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b467862cc8610ca6fc9a1532d7777cee0804e678ab45410897b9396495994a0b" -dependencies = [ - "nix", - "windows-sys 0.52.0", -] - -[[package]] -name = "curve25519-dalek" -version = "1.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c0d59fed08e452f286b251f88b2fc64a01f50a7b263aa09557ad7285d9e7fa" -dependencies = [ - "byteorder", - "clear_on_drop", - "digest 0.8.1", - "rand_core 0.3.1", - "subtle", -] - -[[package]] -name = "curve25519-dalek" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a9b85542f99a2dfa2a1b8e192662741c9859a846b296bef1c92ef9b58b5a216" -dependencies = [ - "byteorder", - "digest 0.8.1", - "rand_core 0.5.1", - "subtle", - "zeroize", -] - -[[package]] -name = "data-encoding" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" - -[[package]] -name = "digest" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" -dependencies = [ - "generic-array 0.12.4", -] - -[[package]] -name = "digest" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" -dependencies = [ - "generic-array 0.14.7", -] - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer 0.10.4", - "crypto-common", -] - -[[package]] -name = "dirs" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fd78930633bd1c6e35c4b42b1df7b0cbc6bc191146e512bb3bedf243fcc3901" -dependencies = [ - "libc", - "redox_users 0.3.5", - "winapi 0.3.9", -] - -[[package]] -name = "dirs" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3" -dependencies = [ - "cfg-if 0.1.10", - "dirs-sys", -] - -[[package]] -name = "dirs-next" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" -dependencies = [ - "cfg-if 1.0.0", - "dirs-sys-next", -] - -[[package]] -name = "dirs-sys" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" -dependencies = [ - "libc", - "redox_users 0.4.4", - "winapi 0.3.9", -] - -[[package]] -name = "dirs-sys-next" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" -dependencies = [ - "libc", - "redox_users 0.4.4", - "winapi 0.3.9", -] - -[[package]] -name = "doc-comment" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" - -[[package]] -name = "easy-jsonrpc-mw" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3b1a91569d50e3bba3c9febb22ef54d78c6e8a8d8dd91ae859896c8ba05f4e3" -dependencies = [ - "easy-jsonrpc-proc-macro-mw", - "jsonrpc-core", - "rand 0.6.5", - "serde", - "serde_json", -] - -[[package]] -name = "easy-jsonrpc-proc-macro-mw" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6368dbd2c6685fb84fc6e6a4749917ddc98905793fd06341c7e11a2504f2724" -dependencies = [ - "heck", - "proc-macro2 0.4.30", - "quote 0.6.13", - "syn 0.15.44", -] - -[[package]] -name = "ed25519-dalek" -version = "1.0.0-pre.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81956bcf7ef761fb4e1d88de3fa181358a0d26cbcb9755b587a08f9119824b86" -dependencies = [ - "clear_on_drop", - "curve25519-dalek 1.2.6", - "failure", - "rand 0.6.5", - "sha2 0.8.2", -] - -[[package]] -name = "either" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" - -[[package]] -name = "emoji" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32e9309870371f7fa7767752e5048fc0c0675b017a27d9c601dffe9a1efe0301" -dependencies = [ - "fuzzy-matcher", - "itertools", - "lazy_static", - "phf", -] - -[[package]] -name = "encode_unicode" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" - -[[package]] -name = "encode_unicode" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" - -[[package]] -name = "encoding_rs" -version = "0.8.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" -dependencies = [ - "cfg-if 1.0.0", -] - -[[package]] -name = "enum_primitive" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4551092f4d519593039259a9ed8daedf0da12e5109c5280338073eaeb81180" -dependencies = [ - "num-traits 0.1.43", -] - -[[package]] -name = "env_logger" -version = "0.5.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15b0a4d2e39f8420210be8b27eeda28029729e2fd4291019455016c348240c38" -dependencies = [ - "atty", - "humantime", - "log 0.4.25", - "regex", - "termcolor", -] - -[[package]] -name = "env_logger" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" -dependencies = [ - "log 0.4.25", - "regex", -] - -[[package]] -name = "epic-cash-wallet" -version = "0.1.0" -dependencies = [ - "android_logger", - "anyhow", - "chrono", - "clap", - "ctrlc", - "failure", - "failure_derive", - "ffi_helpers", - "futures 0.3.30", - "hex", - "jni", - "log 0.4.25", - "openssl", - "prettytable-rs 0.7.0", - "rand 0.6.5", - "reqwest 0.11.24", - "rpassword", - "rustc-serialize", - "serde", - "serde_derive", - "serde_json", - "simplelog", - "stack_epic_core", - "stack_epic_keychain", - "stack_epic_util", - "stack_epic_wallet_api", - "stack_epic_wallet_config", - "stack_epic_wallet_controller", - "stack_epic_wallet_impls", - "stack_epic_wallet_libwallet", - "stack_epic_wallet_util", - "tokio 0.2.25", - "tokio-tungstenite", - "tungstenite", - "url 2.5.0", - "uuid 0.7.4", - "websocket", - "ws", - "zeroize", -] - -[[package]] -name = "equivalent" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" - -[[package]] -name = "errno" -version = "0.3.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "error-chain" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc" -dependencies = [ - "version_check 0.9.4", -] - -[[package]] -name = "failure" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86" -dependencies = [ - "backtrace", - "failure_derive", -] - -[[package]] -name = "failure_derive" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" -dependencies = [ - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 1.0.109", - "synstructure", -] - -[[package]] -name = "fake-simd" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" - -[[package]] -name = "fastrand" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" - -[[package]] -name = "ffi_helpers" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06bad5d1e3a9cfc3825643e055eb7f1fc1b1d52d1543c8ddb9107abd6497f2e" -dependencies = [ - "anyhow", - "libc", - "thiserror", -] - -[[package]] -name = "fixed-hash" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1a683d1234507e4f3bf2736eeddf0de1dc65996dc0164d57eba0a74bcf29489" -dependencies = [ - "byteorder", - "rand 0.5.6", - "rustc-hex", - "static_assertions", -] - -[[package]] -name = "flate2" -version = "1.0.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" -dependencies = [ - "crc32fast", - "miniz_oxide", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - -[[package]] -name = "form_urlencoded" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" -dependencies = [ - "percent-encoding 2.3.1", -] - -[[package]] -name = "fuchsia-cprng" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" - -[[package]] -name = "fuchsia-zircon" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" -dependencies = [ - "bitflags 1.3.2", - "fuchsia-zircon-sys", -] - -[[package]] -name = "fuchsia-zircon-sys" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" - -[[package]] -name = "funty" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" - -[[package]] -name = "futures" -version = "0.1.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678" - -[[package]] -name = "futures" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" - -[[package]] -name = "futures-executor" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" - -[[package]] -name = "futures-macro" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" -dependencies = [ - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 2.0.51", -] - -[[package]] -name = "futures-sink" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" - -[[package]] -name = "futures-task" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" - -[[package]] -name = "futures-util" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr 2.7.1", - "pin-project-lite 0.2.13", - "pin-utils", - "slab", -] - -[[package]] -name = "fuzzy-matcher" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54614a3312934d066701a80f20f15fa3b56d67ac7722b39eea5b4c9dd1d66c94" -dependencies = [ - "thread_local", -] - -[[package]] -name = "generic-array" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" -dependencies = [ - "typenum", -] - -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check 0.9.4", -] - -[[package]] -name = "getrandom" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" -dependencies = [ - "cfg-if 1.0.0", - "libc", - "wasi 0.9.0+wasi-snapshot-preview1", -] - -[[package]] -name = "getrandom" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" -dependencies = [ - "cfg-if 1.0.0", - "libc", - "wasi 0.11.0+wasi-snapshot-preview1", -] - -[[package]] -name = "getrandom" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" -dependencies = [ - "cfg-if 1.0.0", - "libc", - "wasi 0.13.3+wasi-0.2.2", - "windows-targets 0.52.3", -] - -[[package]] -name = "gimli" -version = "0.28.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" - -[[package]] -name = "grin_secp256k1zkp" -version = "0.7.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b04798e32404c0af082b6a209ad4df1ab1d9ec3a5a5056f284cfa32f74e59ce6" -dependencies = [ - "arrayvec 0.3.25", - "cc", - "libc", - "rand 0.5.6", - "rustc-serialize", - "serde", - "serde_json", - "zeroize", -] - -[[package]] -name = "h2" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e4728fd124914ad25e99e3d15a9361a879f6620f63cb56bbb08f95abb97a535" -dependencies = [ - "bytes 0.5.6", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http 0.2.11", - "indexmap 1.9.3", - "slab", - "tokio 0.2.25", - "tokio-util 0.3.1", - "tracing", - "tracing-futures", -] - -[[package]] -name = "h2" -version = "0.3.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9" -dependencies = [ - "bytes 1.5.0", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http 0.2.11", - "indexmap 2.2.3", - "slab", - "tokio 1.36.0", - "tokio-util 0.7.10", - "tracing", -] - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - -[[package]] -name = "hashbrown" -version = "0.14.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" - -[[package]] -name = "heck" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - -[[package]] -name = "hermit-abi" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "379dada1584ad501b383485dd706b8afb7a70fcbc7f4da7d780638a5a6124a60" - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "hmac" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" -dependencies = [ - "crypto-mac", - "digest 0.9.0", -] - -[[package]] -name = "http" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" -dependencies = [ - "bytes 1.5.0", - "fnv", - "itoa 1.0.10", -] - -[[package]] -name = "http" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b32afd38673a8016f7c9ae69e5af41a58f81b1d31689040f2f1959594ce194ea" -dependencies = [ - "bytes 1.5.0", - "fnv", - "itoa 1.0.10", -] - -[[package]] -name = "http-body" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13d5ff830006f7646652e057693569bfe0d51760c0085a071769d142a205111b" -dependencies = [ - "bytes 0.5.6", - "http 0.2.11", -] - -[[package]] -name = "http-body" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" -dependencies = [ - "bytes 1.5.0", - "http 0.2.11", - "pin-project-lite 0.2.13", -] - -[[package]] -name = "httparse" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" - -[[package]] -name = "httpdate" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "494b4d60369511e7dea41cf646832512a94e542f68bb9c49e54518e0f468eb47" - -[[package]] -name = "httpdate" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" - -[[package]] -name = "humantime" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" -dependencies = [ - "quick-error", -] - -[[package]] -name = "hyper" -version = "0.10.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a0652d9a2609a968c14be1a9ea00bf4b1d64e2e1f53a1b51b6fff3a6e829273" -dependencies = [ - "base64 0.9.3", - "httparse", - "language-tags", - "log 0.3.9", - "mime 0.2.6", - "num_cpus", - "time", - "traitobject", - "typeable", - "unicase 1.4.2", - "url 1.7.2", -] - -[[package]] -name = "hyper" -version = "0.13.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a6f157065790a3ed2f88679250419b5cdd96e714a0d65f7797fd337186e96bb" -dependencies = [ - "bytes 0.5.6", - "futures-channel", - "futures-core", - "futures-util", - "h2 0.2.7", - "http 0.2.11", - "http-body 0.3.1", - "httparse", - "httpdate 0.3.2", - "itoa 0.4.8", - "pin-project", - "socket2 0.3.19", - "tokio 0.2.25", - "tower-service", - "tracing", - "want", -] - -[[package]] -name = "hyper" -version = "0.14.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" -dependencies = [ - "bytes 1.5.0", - "futures-channel", - "futures-core", - "futures-util", - "h2 0.3.24", - "http 0.2.11", - "http-body 0.4.6", - "httparse", - "httpdate 1.0.3", - "itoa 1.0.10", - "pin-project-lite 0.2.13", - "socket2 0.5.6", - "tokio 1.36.0", - "tower-service", - "tracing", - "want", -] - -[[package]] -name = "hyper-rustls" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37743cc83e8ee85eacfce90f2f4102030d9ff0a95244098d781e9bee4a90abb6" -dependencies = [ - "bytes 0.5.6", - "futures-util", - "hyper 0.13.10", - "log 0.4.25", - "rustls 0.18.1", - "tokio 0.2.25", - "tokio-rustls 0.14.1", - "webpki", -] - -[[package]] -name = "hyper-rustls" -version = "0.24.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" -dependencies = [ - "futures-util", - "http 0.2.11", - "hyper 0.14.28", - "log 0.4.25", - "rustls 0.21.10", - "rustls-native-certs", - "tokio 1.36.0", - "tokio-rustls 0.24.1", -] - -[[package]] -name = "hyper-timeout" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" -dependencies = [ - "hyper 0.14.28", - "pin-project-lite 0.2.13", - "tokio 1.36.0", - "tokio-io-timeout", -] - -[[package]] -name = "hyper-tls" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d979acc56dcb5b8dddba3917601745e877576475aa046df3226eabdecef78eed" -dependencies = [ - "bytes 0.5.6", - "hyper 0.13.10", - "native-tls", - "tokio 0.2.25", - "tokio-tls 0.3.1", -] - -[[package]] -name = "hyper-tls" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" -dependencies = [ - "bytes 1.5.0", - "hyper 0.14.28", - "native-tls", - "tokio 1.36.0", - "tokio-native-tls", -] - -[[package]] -name = "iana-time-zone" -version = "0.1.60" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "wasm-bindgen", - "windows-core", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - -[[package]] -name = "idna" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" -dependencies = [ - "matches", - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "idna" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "impl-codec" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2050d823639fbeae26b2b5ba09aca8907793117324858070ade0673c49f793b" -dependencies = [ - "parity-codec", -] - -[[package]] -name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg 1.1.0", - "hashbrown 0.12.3", -] - -[[package]] -name = "indexmap" -version = "2.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233cf39063f058ea2caae4091bf4a3ef70a653afbc026f5c4a4135d114e3c177" -dependencies = [ - "equivalent", - "hashbrown 0.14.3", -] - -[[package]] -name = "inout" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" -dependencies = [ - "generic-array 0.14.7", -] - -[[package]] -name = "iovec" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" -dependencies = [ - "libc", -] - -[[package]] -name = "ipnet" -version = "2.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" - -[[package]] -name = "is-terminal" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" -dependencies = [ - "hermit-abi 0.3.8", - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "itertools" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" - -[[package]] -name = "itoa" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" - -[[package]] -name = "jni" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ecfa3b81afc64d9a6539c4eece96ac9a93c551c713a313800dade8e33d7b5c1" -dependencies = [ - "cesu8", - "combine", - "error-chain", - "jni-sys", - "log 0.4.25", - "walkdir", -] - -[[package]] -name = "jni-sys" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" - -[[package]] -name = "js-sys" -version = "0.3.68" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "406cda4b368d531c842222cf9d2600a9a4acce8d29423695379c6868a143a9ee" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "jsonrpc-core" -version = "10.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc15eef5f8b6bef5ac5f7440a957ff95d036e2f98706947741bfc93d1976db4c" -dependencies = [ - "futures 0.1.31", - "log 0.4.25", - "serde", - "serde_derive", - "serde_json", -] - -[[package]] -name = "keccak" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" -dependencies = [ - "cpufeatures", -] - -[[package]] -name = "keccak-hash" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69e8ee697b9aa6dcc34d7657565fa5052763a1627a5b59e4c3c0ae3ed0d70a65" -dependencies = [ - "primitive-types", - "tiny-keccak", -] - -[[package]] -name = "kernel32-sys" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" -dependencies = [ - "winapi 0.2.8", - "winapi-build", -] - -[[package]] -name = "language-tags" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "lazycell" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" - -[[package]] -name = "libc" -version = "0.2.169" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" - -[[package]] -name = "liblmdb-sys" -version = "0.2.2" -source = "git+https://github.com/i1skn/lmdb-rs#54c9c0fe4e9371e8fb76593023c72150858f376f" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "libredox" -version = "0.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" -dependencies = [ - "bitflags 2.4.2", - "libc", - "redox_syscall 0.4.1", -] - -[[package]] -name = "linked-hash-map" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" - -[[package]] -name = "linux-raw-sys" -version = "0.4.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" - -[[package]] -name = "lmdb-zero" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13416eee745b087c22934f35f1f24da22da41ba2a5ce197143d168ce055cc58d" -dependencies = [ - "bitflags 0.9.1", - "libc", - "liblmdb-sys", - "supercow", -] - -[[package]] -name = "lock_api" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c" -dependencies = [ - "owning_ref", - "scopeguard 0.3.3", -] - -[[package]] -name = "lock_api" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75" -dependencies = [ - "scopeguard 1.2.0", -] - -[[package]] -name = "lock_api" -version = "0.4.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" -dependencies = [ - "autocfg 1.1.0", - "scopeguard 1.2.0", -] - -[[package]] -name = "log" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" -dependencies = [ - "log 0.4.25", -] - -[[package]] -name = "log" -version = "0.4.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" -dependencies = [ - "serde", -] - -[[package]] -name = "log-mdc" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a94d21414c1f4a51209ad204c1776a3d0765002c76c6abcb602a6f09f1e881c7" - -[[package]] -name = "log4rs" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "100052474df98158c0738a7d3f4249c99978490178b5f9f68cd835ac57adbd1b" -dependencies = [ - "antidote", - "arc-swap", - "chrono", - "flate2", - "fnv", - "humantime", - "libc", - "log 0.4.25", - "log-mdc", - "serde", - "serde-value", - "serde_derive", - "serde_json", - "serde_yaml", - "thread-id", - "typemap", - "winapi 0.3.9", -] - -[[package]] -name = "lru-cache" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" -dependencies = [ - "linked-hash-map", -] - -[[package]] -name = "matches" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" - -[[package]] -name = "maybe-uninit" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" - -[[package]] -name = "md5" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e6bcd6433cff03a4bfc3d9834d504467db1f1cf6d0ea765d37d330249ed629d" - -[[package]] -name = "memchr" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "148fab2e51b4f1cfc66da2a7c32981d1d3c083a803978268bb11fe4b86925e7a" -dependencies = [ - "libc", -] - -[[package]] -name = "memchr" -version = "2.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" - -[[package]] -name = "memmap" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" -dependencies = [ - "libc", - "winapi 0.3.9", -] - -[[package]] -name = "memoffset" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa" -dependencies = [ - "autocfg 1.1.0", -] - -[[package]] -name = "mime" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba626b8a6de5da682e1caa06bdb42a335aee5a84db8e5046a3e8ab17ba0a3ae0" -dependencies = [ - "log 0.3.9", -] - -[[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - -[[package]] -name = "mime_guess" -version = "2.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" -dependencies = [ - "mime 0.3.17", - "unicase 2.7.0", -] - -[[package]] -name = "miniz_oxide" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" -dependencies = [ - "adler", -] - -[[package]] -name = "mio" -version = "0.6.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4" -dependencies = [ - "cfg-if 0.1.10", - "fuchsia-zircon", - "fuchsia-zircon-sys", - "iovec", - "kernel32-sys", - "libc", - "log 0.4.25", - "miow 0.2.2", - "net2", - "slab", - "winapi 0.2.8", -] - -[[package]] -name = "mio" -version = "0.8.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" -dependencies = [ - "libc", - "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.48.0", -] - -[[package]] -name = "mio-extras" -version = "2.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52403fe290012ce777c4626790c8951324a2b9e3316b3143779c72b029742f19" -dependencies = [ - "lazycell", - "log 0.4.25", - "mio 0.6.23", - "slab", -] - -[[package]] -name = "mio-named-pipes" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0840c1c50fd55e521b247f949c241c9997709f23bd7f023b9762cd561e935656" -dependencies = [ - "log 0.4.25", - "mio 0.6.23", - "miow 0.3.7", - "winapi 0.3.9", -] - -[[package]] -name = "mio-uds" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0" -dependencies = [ - "iovec", - "libc", - "mio 0.6.23", -] - -[[package]] -name = "miow" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d" -dependencies = [ - "kernel32-sys", - "net2", - "winapi 0.2.8", - "ws2_32-sys", -] - -[[package]] -name = "miow" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" -dependencies = [ - "winapi 0.3.9", -] - -[[package]] -name = "native-tls" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" -dependencies = [ - "lazy_static", - "libc", - "log 0.4.25", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - -[[package]] -name = "net2" -version = "0.2.39" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b13b648036a2339d06de780866fbdfda0dde886de7b3af2ddeba8b14f4ee34ac" -dependencies = [ - "cfg-if 0.1.10", - "libc", - "winapi 0.3.9", -] - -[[package]] -name = "nix" -version = "0.27.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" -dependencies = [ - "bitflags 2.4.2", - "cfg-if 1.0.0", - "libc", -] - -[[package]] -name = "nodrop" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" - -[[package]] -name = "num" -version = "0.1.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4703ad64153382334aa8db57c637364c322d3372e097840c72000dabdcf6156e" -dependencies = [ - "num-bigint 0.1.44", - "num-complex 0.1.43", - "num-integer", - "num-iter", - "num-rational 0.1.42", - "num-traits 0.2.18", -] - -[[package]] -name = "num" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8536030f9fea7127f841b45bb6243b27255787fb4eb83958aa1ef9d2fdc0c36" -dependencies = [ - "num-bigint 0.2.6", - "num-complex 0.2.4", - "num-integer", - "num-iter", - "num-rational 0.2.4", - "num-traits 0.2.18", -] - -[[package]] -name = "num-bigint" -version = "0.1.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e63899ad0da84ce718c14936262a41cee2c79c981fc0a0e7c7beb47d5a07e8c1" -dependencies = [ - "num-integer", - "num-traits 0.2.18", - "rand 0.4.6", - "rustc-serialize", -] - -[[package]] -name = "num-bigint" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" -dependencies = [ - "autocfg 1.1.0", - "num-integer", - "num-traits 0.2.18", -] - -[[package]] -name = "num-complex" -version = "0.1.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b288631d7878aaf59442cffd36910ea604ecd7745c36054328595114001c9656" -dependencies = [ - "num-traits 0.2.18", - "rustc-serialize", -] - -[[package]] -name = "num-complex" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6b19411a9719e753aff12e5187b74d60d3dc449ec3f4dc21e3989c3f554bc95" -dependencies = [ - "autocfg 1.1.0", - "num-traits 0.2.18", -] - -[[package]] -name = "num-integer" -version = "0.1.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" -dependencies = [ - "num-traits 0.2.18", -] - -[[package]] -name = "num-iter" -version = "0.1.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d869c01cc0c455284163fd0092f1f93835385ccab5a98a0dcc497b2f8bf055a9" -dependencies = [ - "autocfg 1.1.0", - "num-integer", - "num-traits 0.2.18", -] - -[[package]] -name = "num-rational" -version = "0.1.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee314c74bd753fc86b4780aa9475da469155f3848473a261d2d18e35245a784e" -dependencies = [ - "num-bigint 0.1.44", - "num-integer", - "num-traits 0.2.18", - "rustc-serialize", -] - -[[package]] -name = "num-rational" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c000134b5dbf44adc5cb772486d335293351644b801551abe8f75c84cfa4aef" -dependencies = [ - "autocfg 1.1.0", - "num-bigint 0.2.6", - "num-integer", - "num-traits 0.2.18", -] - -[[package]] -name = "num-traits" -version = "0.1.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" -dependencies = [ - "num-traits 0.2.18", -] - -[[package]] -name = "num-traits" -version = "0.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" -dependencies = [ - "autocfg 1.1.0", -] - -[[package]] -name = "num_cpus" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" -dependencies = [ - "hermit-abi 0.3.8", - "libc", -] - -[[package]] -name = "object" -version = "0.32.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" -dependencies = [ - "memchr 2.7.1", -] - -[[package]] -name = "odds" -version = "0.2.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eae0151b9dacf24fcc170d9995e511669a082856a91f958a2fe380bfab3fb22" - -[[package]] -name = "once_cell" -version = "1.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" - -[[package]] -name = "opaque-debug" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" - -[[package]] -name = "opaque-debug" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" - -[[package]] -name = "openssl" -version = "0.10.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" -dependencies = [ - "bitflags 2.4.2", - "cfg-if 1.0.0", - "foreign-types", - "libc", - "once_cell", - "openssl-macros", - "openssl-sys", -] - -[[package]] -name = "openssl-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" -dependencies = [ - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 2.0.51", -] - -[[package]] -name = "openssl-probe" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" - -[[package]] -name = "openssl-src" -version = "300.2.3+3.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cff92b6f71555b61bb9315f7c64da3ca43d87531622120fea0195fc761b4843" -dependencies = [ - "cc", -] - -[[package]] -name = "openssl-sys" -version = "0.9.101" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dda2b0f344e78efc2facf7d195d098df0dd72151b26ab98da807afc26c198dff" -dependencies = [ - "cc", - "libc", - "openssl-src", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "ordered-float" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3305af35278dd29f46fcdd139e0b1fbfae2153f0e5928b39b035542dd31e37b7" -dependencies = [ - "num-traits 0.2.18", -] - -[[package]] -name = "owning_ref" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ff55baddef9e4ad00f88b6c743a2a8062d4c6ade126c2a528644b8e444d52ce" -dependencies = [ - "stable_deref_trait", -] - -[[package]] -name = "parity-codec" -version = "3.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b9df1283109f542d8852cd6b30e9341acc2137481eb6157d2e62af68b0afec9" -dependencies = [ - "arrayvec 0.4.12", - "serde", -] - -[[package]] -name = "parking_lot" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0802bff09003b291ba756dc7e79313e51cc31667e94afbe847def490424cde5" -dependencies = [ - "lock_api 0.1.5", - "parking_lot_core 0.3.1", -] - -[[package]] -name = "parking_lot" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" -dependencies = [ - "lock_api 0.3.4", - "parking_lot_core 0.6.3", - "rustc_version", -] - -[[package]] -name = "parking_lot" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" -dependencies = [ - "lock_api 0.4.11", - "parking_lot_core 0.9.9", -] - -[[package]] -name = "parking_lot_core" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad7f7e6ebdc79edff6fdcb87a55b620174f7a989e3eb31b65231f4af57f00b8c" -dependencies = [ - "libc", - "rand 0.5.6", - "rustc_version", - "smallvec 0.6.14", - "winapi 0.3.9", -] - -[[package]] -name = "parking_lot_core" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66b810a62be75176a80873726630147a5ca780cd33921e0b5709033e66b0a" -dependencies = [ - "cfg-if 0.1.10", - "cloudabi", - "libc", - "redox_syscall 0.1.57", - "rustc_version", - "smallvec 0.6.14", - "winapi 0.3.9", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" -dependencies = [ - "cfg-if 1.0.0", - "libc", - "redox_syscall 0.4.1", - "smallvec 1.13.1", - "windows-targets 0.48.5", -] - -[[package]] -name = "password-hash" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77e0b28ace46c5a396546bcf443bf422b57049617433d8854227352a4a9b24e7" -dependencies = [ - "base64ct", - "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "pbkdf2" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d95f5254224e617595d2cc3cc73ff0a5eaf2637519e25f03388154e9378b6ffa" -dependencies = [ - "base64ct", - "crypto-mac", - "hmac", - "password-hash", - "sha2 0.9.9", -] - -[[package]] -name = "percent-encoding" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" - -[[package]] -name = "percent-encoding" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" - -[[package]] -name = "phf" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" -dependencies = [ - "phf_macros", - "phf_shared", - "proc-macro-hack", -] - -[[package]] -name = "phf_generator" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526" -dependencies = [ - "phf_shared", - "rand 0.7.3", -] - -[[package]] -name = "phf_macros" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f6fde18ff429ffc8fe78e2bf7f8b7a5a5a6e2a8b58bc5a9ac69198bbda9189c" -dependencies = [ - "phf_generator", - "phf_shared", - "proc-macro-hack", - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 1.0.109", -] - -[[package]] -name = "phf_shared" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7" -dependencies = [ - "siphasher 0.3.11", -] - -[[package]] -name = "pin-project" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0302c4a0442c456bd56f841aee5c3bfd17967563f6fadc9ceb9f9c23cf3807e0" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "266c042b60c9c76b8d53061e52b2e0d1116abc57cefc8c5cd671619a56ac3690" -dependencies = [ - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 2.0.51", -] - -[[package]] -name = "pin-project-lite" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "257b64915a082f7811703966789728173279bdebb956b143dbcd23f6f970a777" - -[[package]] -name = "pin-project-lite" -version = "0.2.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "pkg-config" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" - -[[package]] -name = "poly1305" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" -dependencies = [ - "cpufeatures", - "opaque-debug 0.3.0", - "universal-hash", -] - -[[package]] -name = "ppv-lite86" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - -[[package]] -name = "prettytable-rs" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5511ca4c805aa35f0abff6be7923231d664408b60c09f44ef715f2bce106cd9e" -dependencies = [ - "atty", - "csv 0.15.0", - "encode_unicode 0.3.6", - "lazy_static", - "term 0.5.2", - "unicode-width", -] - -[[package]] -name = "prettytable-rs" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eea25e07510aa6ab6547308ebe3c036016d162b8da920dbb079e3ba8acf3d95a" -dependencies = [ - "csv 1.3.0", - "encode_unicode 1.0.0", - "is-terminal", - "lazy_static", - "term 0.7.0", - "unicode-width", -] - -[[package]] -name = "primitive-types" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2288eb2a39386c4bc817974cc413afe173010dc80e470fcb1e9a35580869f024" -dependencies = [ - "fixed-hash", - "impl-codec", - "uint", -] - -[[package]] -name = "proc-macro-hack" -version = "0.5.20+deprecated" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" - -[[package]] -name = "proc-macro2" -version = "0.4.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" -dependencies = [ - "unicode-xid 0.1.0", -] - -[[package]] -name = "proc-macro2" -version = "1.0.78" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quick-error" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" - -[[package]] -name = "quote" -version = "0.6.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" -dependencies = [ - "proc-macro2 0.4.30", -] - -[[package]] -name = "quote" -version = "1.0.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" -dependencies = [ - "proc-macro2 1.0.78", -] - -[[package]] -name = "radium" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" - -[[package]] -name = "rand" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" -dependencies = [ - "fuchsia-cprng", - "libc", - "rand_core 0.3.1", - "rdrand", - "winapi 0.3.9", -] - -[[package]] -name = "rand" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c618c47cd3ebd209790115ab837de41425723956ad3ce2e6a7f09890947cacb9" -dependencies = [ - "cloudabi", - "fuchsia-cprng", - "libc", - "rand_core 0.3.1", - "winapi 0.3.9", -] - -[[package]] -name = "rand" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" -dependencies = [ - "autocfg 0.1.8", - "libc", - "rand_chacha 0.1.1", - "rand_core 0.4.2", - "rand_hc 0.1.0", - "rand_isaac", - "rand_jitter", - "rand_os", - "rand_pcg 0.1.2", - "rand_xorshift", - "winapi 0.3.9", -] - -[[package]] -name = "rand" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "getrandom 0.1.16", - "libc", - "rand_chacha 0.2.2", - "rand_core 0.5.1", - "rand_hc 0.2.0", - "rand_pcg 0.2.1", -] - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" -dependencies = [ - "autocfg 0.1.8", - "rand_core 0.3.1", -] - -[[package]] -name = "rand_chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -dependencies = [ - "ppv-lite86", - "rand_core 0.5.1", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_core" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" -dependencies = [ - "rand_core 0.4.2", -] - -[[package]] -name = "rand_core" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -dependencies = [ - "getrandom 0.1.16", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.12", -] - -[[package]] -name = "rand_hc" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" -dependencies = [ - "rand_core 0.3.1", -] - -[[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -dependencies = [ - "rand_core 0.5.1", -] - -[[package]] -name = "rand_isaac" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" -dependencies = [ - "rand_core 0.3.1", -] - -[[package]] -name = "rand_jitter" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" -dependencies = [ - "libc", - "rand_core 0.4.2", - "winapi 0.3.9", -] - -[[package]] -name = "rand_os" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" -dependencies = [ - "cloudabi", - "fuchsia-cprng", - "libc", - "rand_core 0.4.2", - "rdrand", - "winapi 0.3.9", -] - -[[package]] -name = "rand_pcg" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" -dependencies = [ - "autocfg 0.1.8", - "rand_core 0.4.2", -] - -[[package]] -name = "rand_pcg" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" -dependencies = [ - "rand_core 0.5.1", -] - -[[package]] -name = "rand_xorshift" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" -dependencies = [ - "rand_core 0.3.1", -] - -[[package]] -name = "rayon" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7237101a77a10773db45d62004a272517633fbcc3df19d96455ede1122e051" -dependencies = [ - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" -dependencies = [ - "crossbeam-deque 0.8.5", - "crossbeam-utils 0.8.19", -] - -[[package]] -name = "rdrand" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" -dependencies = [ - "rand_core 0.3.1", -] - -[[package]] -name = "redox_syscall" -version = "0.1.57" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" - -[[package]] -name = "redox_syscall" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" -dependencies = [ - "bitflags 1.3.2", -] - -[[package]] -name = "redox_users" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de0737333e7a9502c789a36d7c7fa6092a49895d4faa31ca5df163857ded2e9d" -dependencies = [ - "getrandom 0.1.16", - "redox_syscall 0.1.57", - "rust-argon2", -] - -[[package]] -name = "redox_users" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" -dependencies = [ - "getrandom 0.2.12", - "libredox", - "thiserror", -] - -[[package]] -name = "regex" -version = "1.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" -dependencies = [ - "aho-corasick", - "memchr 2.7.1", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" -dependencies = [ - "aho-corasick", - "memchr 2.7.1", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" - -[[package]] -name = "reqwest" -version = "0.10.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0718f81a8e14c4dbb3b34cf23dc6aaf9ab8a0dfec160c534b3dbca1aaa21f47c" -dependencies = [ - "base64 0.13.1", - "bytes 0.5.6", - "encoding_rs", - "futures-core", - "futures-util", - "http 0.2.11", - "http-body 0.3.1", - "hyper 0.13.10", - "hyper-rustls 0.21.0", - "hyper-tls 0.4.3", - "ipnet", - "js-sys", - "lazy_static", - "log 0.4.25", - "mime 0.3.17", - "mime_guess", - "native-tls", - "percent-encoding 2.3.1", - "pin-project-lite 0.2.13", - "rustls 0.18.1", - "serde", - "serde_urlencoded", - "tokio 0.2.25", - "tokio-rustls 0.14.1", - "tokio-socks", - "tokio-tls 0.3.1", - "url 2.5.0", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "webpki-roots", - "winreg 0.7.0", -] - -[[package]] -name = "reqwest" -version = "0.11.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6920094eb85afde5e4a138be3f2de8bbdf28000f0029e72c45025a56b042251" -dependencies = [ - "base64 0.21.7", - "bytes 1.5.0", - "encoding_rs", - "futures-core", - "futures-util", - "h2 0.3.24", - "http 0.2.11", - "http-body 0.4.6", - "hyper 0.14.28", - "hyper-tls 0.5.0", - "ipnet", - "js-sys", - "log 0.4.25", - "mime 0.3.17", - "native-tls", - "once_cell", - "percent-encoding 2.3.1", - "pin-project-lite 0.2.13", - "rustls-pemfile", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper", - "system-configuration", - "tokio 1.36.0", - "tokio-native-tls", - "tower-service", - "url 2.5.0", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "winreg 0.50.0", -] - -[[package]] -name = "ring" -version = "0.16.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" -dependencies = [ - "cc", - "libc", - "once_cell", - "spin 0.5.2", - "untrusted 0.7.1", - "web-sys", - "winapi 0.3.9", -] - -[[package]] -name = "ring" -version = "0.17.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" -dependencies = [ - "cc", - "cfg-if 1.0.0", - "getrandom 0.2.12", - "libc", - "spin 0.9.8", - "untrusted 0.9.0", - "windows-sys 0.52.0", -] - -[[package]] -name = "ripemd160" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eca4ecc81b7f313189bf73ce724400a07da2a6dac19588b03c8bd76a2dcc251" -dependencies = [ - "block-buffer 0.9.0", - "digest 0.9.0", - "opaque-debug 0.3.0", -] - -[[package]] -name = "rpassword" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d37473170aedbe66ffa3ad3726939ba677d83c646ad4fd99e5b4bc38712f45ec" -dependencies = [ - "kernel32-sys", - "libc", - "winapi 0.2.8", -] - -[[package]] -name = "rust-argon2" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b18820d944b33caa75a71378964ac46f58517c92b6ae5f762636247c09e78fb" -dependencies = [ - "base64 0.13.1", - "blake2b_simd", - "constant_time_eq", - "crossbeam-utils 0.8.19", -] - -[[package]] -name = "rustc-demangle" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" - -[[package]] -name = "rustc-hex" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" - -[[package]] -name = "rustc-serialize" -version = "0.3.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe834bc780604f4674073badbad26d7219cadfb4a2275802db12cbae17498401" - -[[package]] -name = "rustc_version" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -dependencies = [ - "semver", -] - -[[package]] -name = "rustix" -version = "0.38.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" -dependencies = [ - "bitflags 2.4.2", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.52.0", -] - -[[package]] -name = "rustls" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d1126dcf58e93cee7d098dbda643b5f92ed724f1f6a63007c1116eed6700c81" -dependencies = [ - "base64 0.12.3", - "log 0.4.25", - "ring 0.16.20", - "sct 0.6.1", - "webpki", -] - -[[package]] -name = "rustls" -version = "0.21.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba" -dependencies = [ - "log 0.4.25", - "ring 0.17.8", - "rustls-webpki", - "sct 0.7.1", -] - -[[package]] -name = "rustls-native-certs" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" -dependencies = [ - "openssl-probe", - "rustls-pemfile", - "schannel", - "security-framework", -] - -[[package]] -name = "rustls-pemfile" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" -dependencies = [ - "base64 0.21.7", -] - -[[package]] -name = "rustls-webpki" -version = "0.101.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" -dependencies = [ - "ring 0.17.8", - "untrusted 0.9.0", -] - -[[package]] -name = "rustversion" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" - -[[package]] -name = "ryu" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" - -[[package]] -name = "safemem" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "schannel" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" -dependencies = [ - "windows-sys 0.52.0", -] - -[[package]] -name = "scoped-tls" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "332ffa32bf586782a3efaeb58f127980944bbc8c4d6913a86107ac2a5ab24b28" - -[[package]] -name = "scopeguard" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "sct" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce" -dependencies = [ - "ring 0.16.20", - "untrusted 0.7.1", -] - -[[package]] -name = "sct" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" -dependencies = [ - "ring 0.17.8", - "untrusted 0.9.0", -] - -[[package]] -name = "security-framework" -version = "2.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "semver" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -dependencies = [ - "semver-parser", -] - -[[package]] -name = "semver-parser" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" - -[[package]] -name = "serde" -version = "1.0.197" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde-value" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a663f873dedc4eac1a559d4c6bc0d0b2c34dc5ac4702e105014b8281489e44f" -dependencies = [ - "ordered-float", - "serde", -] - -[[package]] -name = "serde_derive" -version = "1.0.197" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" -dependencies = [ - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 2.0.51", -] - -[[package]] -name = "serde_json" -version = "1.0.114" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" -dependencies = [ - "itoa 1.0.10", - "ryu", - "serde", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa 1.0.10", - "ryu", - "serde", -] - -[[package]] -name = "serde_yaml" -version = "0.8.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578a7433b776b56a35785ed5ce9a7e777ac0598aac5a6dd1b4b18a307c7fc71b" -dependencies = [ - "indexmap 1.9.3", - "ryu", - "serde", - "yaml-rust 0.4.5", -] - -[[package]] -name = "sha-1" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" -dependencies = [ - "block-buffer 0.7.3", - "digest 0.8.1", - "fake-simd", - "opaque-debug 0.2.3", -] - -[[package]] -name = "sha1" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1da05c97445caa12d05e848c4a4fcbbea29e748ac28f7e80e9b010392063770" -dependencies = [ - "sha1_smol", -] - -[[package]] -name = "sha1" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" -dependencies = [ - "cfg-if 1.0.0", - "cpufeatures", - "digest 0.10.7", -] - -[[package]] -name = "sha1_smol" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" - -[[package]] -name = "sha2" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a256f46ea78a0c0d9ff00077504903ac881a1dafdc20da66545699e7776b3e69" -dependencies = [ - "block-buffer 0.7.3", - "digest 0.8.1", - "fake-simd", - "opaque-debug 0.2.3", -] - -[[package]] -name = "sha2" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" -dependencies = [ - "block-buffer 0.9.0", - "cfg-if 1.0.0", - "cpufeatures", - "digest 0.9.0", - "opaque-debug 0.3.0", -] - -[[package]] -name = "sha3" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd26bc0e7a2e3a7c959bc494caf58b72ee0c71d67704e9520f736ca7e4853ecf" -dependencies = [ - "block-buffer 0.7.3", - "byte-tools", - "digest 0.8.1", - "keccak", - "opaque-debug 0.2.3", -] - -[[package]] -name = "signal-hook-registry" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" -dependencies = [ - "libc", -] - -[[package]] -name = "simplelog" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cf9a002ccce717d066b3ccdb8a28829436249867229291e91b25d99bd723f0d" -dependencies = [ - "chrono", - "log 0.4.25", - "term 0.6.1", -] - -[[package]] -name = "siphasher" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac" - -[[package]] -name = "siphasher" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" - -[[package]] -name = "slab" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg 1.1.0", -] - -[[package]] -name = "smallvec" -version = "0.6.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97fcaeba89edba30f044a10c6a3cc39df9c3f17d7cd829dd1446cab35f890e0" -dependencies = [ - "maybe-uninit", -] - -[[package]] -name = "smallvec" -version = "1.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" - -[[package]] -name = "socket2" -version = "0.3.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e" -dependencies = [ - "cfg-if 1.0.0", - "libc", - "winapi 0.3.9", -] - -[[package]] -name = "socket2" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" - -[[package]] -name = "sqlite" -version = "0.31.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05439db7afa0ce0b38f6d1b4c691f368adde108df021e15e900fec6a1af92488" -dependencies = [ - "libc", - "sqlite3-sys", -] - -[[package]] -name = "sqlite3-src" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfc95a51a1ee38839599371685b9d4a926abb51791f0bc3bf8c3bb7867e6e454" -dependencies = [ - "cc", - "pkg-config", -] - -[[package]] -name = "sqlite3-sys" -version = "0.15.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2752c669433e40ebb08fde824146f50d9628aa0b66a3b7fc6be34db82a8063b" -dependencies = [ - "libc", - "sqlite3-src", -] - -[[package]] -name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - -[[package]] -name = "stack_epic_api" -version = "3.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f47126ab82379499282a75557c6f2e8dd6da988f5c52c1c85f21958361ddb3" -dependencies = [ - "bigint", - "bytes 0.5.6", - "easy-jsonrpc-mw", - "futures 0.3.30", - "http 0.2.11", - "hyper 0.14.28", - "hyper-rustls 0.24.2", - "hyper-timeout", - "hyper-tls 0.5.0", - "lazy_static", - "log 0.4.25", - "regex", - "ring 0.16.20", - "rustls 0.21.10", - "rustls-pemfile", - "serde", - "serde_derive", - "serde_json", - "stack_epic_chain", - "stack_epic_core", - "stack_epic_p2p", - "stack_epic_pool", - "stack_epic_store", - "stack_epic_util", - "thiserror", - "tokio 1.36.0", - "tokio-rustls 0.24.1", - "url 2.5.0", -] - -[[package]] -name = "stack_epic_chain" -version = "3.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64f12c3933ce52b25fbd79a208d28f13606ad826b8cb9ac943c128ada2a9da77" -dependencies = [ - "bigint", - "bit-vec", - "bitflags 1.3.2", - "byteorder", - "chrono", - "croaring", - "failure", - "failure_derive", - "lazy_static", - "log 0.4.25", - "lru-cache", - "regex", - "serde", - "serde_derive", - "stack_epic_core", - "stack_epic_keychain", - "stack_epic_store", - "stack_epic_util", -] - -[[package]] -name = "stack_epic_core" -version = "3.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9275a150f9c8208111b0a82ed68c88f3f15b74f0e110e79f91c4ba5ea4a911e" -dependencies = [ - "bigint", - "blake2-rfc", - "byteorder", - "chrono", - "croaring", - "enum_primitive", - "failure", - "failure_derive", - "keccak-hash", - "lazy_static", - "log 0.4.25", - "lru-cache", - "md5", - "num 0.2.1", - "num-bigint 0.2.6", - "rand 0.6.5", - "serde", - "serde_derive", - "serde_json", - "sha2 0.8.2", - "siphasher 0.2.3", - "stack_epic_keychain", - "stack_epic_util", - "uuid 0.6.5", - "zeroize", -] - -[[package]] -name = "stack_epic_keychain" -version = "3.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "907e57824ea3a4218612246360515acf5d2afbe1859c93526cc506e9cea20497" -dependencies = [ - "blake2-rfc", - "byteorder", - "digest 0.9.0", - "hmac", - "lazy_static", - "log 0.4.25", - "pbkdf2", - "rand 0.6.5", - "ripemd160", - "serde", - "serde_derive", - "serde_json", - "sha2 0.9.9", - "stack_epic_util", - "uuid 0.6.5", - "zeroize", -] - -[[package]] -name = "stack_epic_p2p" -version = "3.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879b1754f3c8ba59de3b222a39b0c1d97daf3990e3b1bf259b47ed541a001323" -dependencies = [ - "bitflags 1.3.2", - "bytes 0.4.12", - "chrono", - "enum_primitive", - "log 0.4.25", - "lru-cache", - "net2", - "num 0.1.42", - "rand 0.6.5", - "serde", - "serde_derive", - "stack_epic_chain", - "stack_epic_core", - "stack_epic_store", - "stack_epic_util", - "tempfile", -] - -[[package]] -name = "stack_epic_pool" -version = "3.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0184885a484f7e5ad3e3c7d9ffa380a6e5926ef1e478889a892d555a3a678cb" -dependencies = [ - "blake2-rfc", - "chrono", - "failure", - "failure_derive", - "log 0.4.25", - "rand 0.6.5", - "serde", - "serde_derive", - "stack_epic_core", - "stack_epic_keychain", - "stack_epic_store", - "stack_epic_util", -] - -[[package]] -name = "stack_epic_store" -version = "3.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c85a06f322911dc88a63136ecd2cca7c883a7983316d94549ab651928ced7298" -dependencies = [ - "byteorder", - "croaring", - "env_logger 0.5.13", - "failure", - "failure_derive", - "libc", - "lmdb-zero", - "log 0.4.25", - "memmap", - "serde", - "serde_derive", - "stack_epic_core", - "stack_epic_util", - "tempfile", -] - -[[package]] -name = "stack_epic_util" -version = "3.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2abdbeed56a95d9749ba929747e0e63f9a1010f2d5cbc3f6073303695843e9a2" -dependencies = [ - "backtrace", - "base64 0.9.3", - "byteorder", - "grin_secp256k1zkp", - "lazy_static", - "log 0.4.25", - "log4rs", - "parking_lot 0.6.4", - "rand 0.6.5", - "serde", - "serde_derive", - "walkdir", - "zeroize", - "zip", -] - -[[package]] -name = "stack_epic_wallet_api" -version = "3.6.0" -dependencies = [ - "base64 0.9.3", - "chrono", - "easy-jsonrpc-mw", - "ed25519-dalek", - "failure", - "failure_derive", - "log 0.4.25", - "rand 0.5.6", - "ring 0.16.20", - "serde", - "serde_derive", - "serde_json", - "stack_epic_wallet_config", - "stack_epic_wallet_impls", - "stack_epic_wallet_libwallet", - "stack_epic_wallet_util", - "uuid 0.7.4", -] - -[[package]] -name = "stack_epic_wallet_config" -version = "3.6.0" -dependencies = [ - "dirs 1.0.5", - "rand 0.5.6", - "serde", - "serde_derive", - "stack_epic_wallet_util", - "toml", -] - -[[package]] -name = "stack_epic_wallet_controller" -version = "3.6.0" -dependencies = [ - "chrono", - "easy-jsonrpc-mw", - "futures 0.3.30", - "hyper 0.14.28", - "lazy_static", - "log 0.4.25", - "prettytable-rs 0.10.0", - "rand 0.5.6", - "ring 0.16.20", - "serde", - "serde_derive", - "serde_json", - "stack_epic_wallet_api", - "stack_epic_wallet_config", - "stack_epic_wallet_impls", - "stack_epic_wallet_libwallet", - "stack_epic_wallet_util", - "term 0.5.2", - "thiserror", - "tokio 1.36.0", - "tungstenite", - "url 1.7.2", - "uuid 0.7.4", -] - -[[package]] -name = "stack_epic_wallet_impls" -version = "3.6.0" -dependencies = [ - "bitvec", - "blake2-rfc", - "byteorder", - "chrono", - "data-encoding", - "ed25519-dalek", - "emoji", - "failure", - "failure_derive", - "futures 0.3.30", - "http 0.2.11", - "hyper-rustls 0.24.2", - "hyper-timeout", - "lazy_static", - "log 0.4.25", - "parking_lot 0.6.4", - "rand 0.5.6", - "regex", - "reqwest 0.10.10", - "ring 0.16.20", - "rustls 0.21.10", - "semver", - "serde", - "serde_derive", - "serde_json", - "sqlite", - "stack_epic_wallet_config", - "stack_epic_wallet_libwallet", - "stack_epic_wallet_util", - "sysinfo", - "thiserror", - "timer", - "tokio 0.2.25", - "tungstenite", - "uuid 0.7.4", - "x25519-dalek", -] - -[[package]] -name = "stack_epic_wallet_libwallet" -version = "3.6.0" -dependencies = [ - "aead", - "blake2-rfc", - "byteorder", - "chacha20poly1305", - "chrono", - "data-encoding", - "digest 0.9.0", - "ed25519-dalek", - "lazy_static", - "log 0.4.25", - "rand 0.5.6", - "regex", - "ring 0.16.20", - "serde", - "serde_derive", - "serde_json", - "sha2 0.9.9", - "sha3", - "sqlite", - "stack_epic_wallet_config", - "stack_epic_wallet_util", - "strum", - "strum_macros", - "thiserror", - "tungstenite", - "uuid 0.7.4", -] - -[[package]] -name = "stack_epic_wallet_util" -version = "3.6.0" -dependencies = [ - "dirs 1.0.5", - "rand 0.5.6", - "serde", - "serde_derive", - "stack_epic_api", - "stack_epic_chain", - "stack_epic_core", - "stack_epic_keychain", - "stack_epic_store", - "stack_epic_util", - "toml", -] - -[[package]] -name = "static_assertions" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c19be23126415861cb3a23e501d34a708f7f9b2183c5252d690941c2e69199d5" - -[[package]] -name = "strsim" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" - -[[package]] -name = "strum" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d1c33039533f051704951680f1adfd468fd37ac46816ded0d9ee068e60f05f" - -[[package]] -name = "strum_macros" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47cd23f5c7dee395a00fa20135e2ec0fffcdfa151c56182966d7a3261343432e" -dependencies = [ - "heck", - "proc-macro2 0.4.30", - "quote 0.6.13", - "syn 0.15.44", -] - -[[package]] -name = "subtle" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" - -[[package]] -name = "supercow" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "171758edb47aa306a78dfa4ab9aeb5167405bd4e3dc2b64e88f6a84bbe98bd63" - -[[package]] -name = "syn" -version = "0.15.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" -dependencies = [ - "proc-macro2 0.4.30", - "quote 0.6.13", - "unicode-xid 0.1.0", -] - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2 1.0.78", - "quote 1.0.35", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.51" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ab617d94515e94ae53b8406c628598680aa0c9587474ecbe58188f7b345d66c" -dependencies = [ - "proc-macro2 1.0.78", - "quote 1.0.35", - "unicode-ident", -] - -[[package]] -name = "sync_wrapper" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" - -[[package]] -name = "synstructure" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" -dependencies = [ - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 1.0.109", - "unicode-xid 0.2.4", -] - -[[package]] -name = "sysinfo" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f4b2468c629cffba39c0a4425849ab3cdb03d9dfacba69684609aea04d08ff9" -dependencies = [ - "cfg-if 0.1.10", - "doc-comment", - "libc", - "rayon", - "winapi 0.3.9", -] - -[[package]] -name = "system-configuration" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "system-configuration-sys", -] - -[[package]] -name = "system-configuration-sys" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "tap" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" - -[[package]] -name = "tempfile" -version = "3.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38c246215d7d24f48ae091a2902398798e05d978b24315d6efbc00ede9a8bb91" -dependencies = [ - "cfg-if 1.0.0", - "fastrand", - "getrandom 0.3.1", - "once_cell", - "rustix", - "windows-sys 0.52.0", -] - -[[package]] -name = "term" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edd106a334b7657c10b7c540a0106114feadeb4dc314513e97df481d5d966f42" -dependencies = [ - "byteorder", - "dirs 1.0.5", - "winapi 0.3.9", -] - -[[package]] -name = "term" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0863a3345e70f61d613eab32ee046ccd1bcc5f9105fe402c61fcd0c13eeb8b5" -dependencies = [ - "dirs 2.0.2", - "winapi 0.3.9", -] - -[[package]] -name = "term" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" -dependencies = [ - "dirs-next", - "rustversion", - "winapi 0.3.9", -] - -[[package]] -name = "termcolor" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "textwrap" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -dependencies = [ - "unicode-width", -] - -[[package]] -name = "thiserror" -version = "1.0.57" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.57" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" -dependencies = [ - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 2.0.51", -] - -[[package]] -name = "thread-id" -version = "3.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7fbf4c9d56b320106cd64fd024dadfa0be7cb4706725fc44a7d7ce952d820c1" -dependencies = [ - "libc", - "redox_syscall 0.1.57", - "winapi 0.3.9", -] - -[[package]] -name = "thread_local" -version = "1.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" -dependencies = [ - "cfg-if 1.0.0", - "once_cell", -] - -[[package]] -name = "time" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" -dependencies = [ - "libc", - "wasi 0.10.0+wasi-snapshot-preview1", - "winapi 0.3.9", -] - -[[package]] -name = "timer" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31d42176308937165701f50638db1c31586f183f1aab416268216577aec7306b" -dependencies = [ - "chrono", -] - -[[package]] -name = "tiny-keccak" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d8a021c69bb74a44ccedb824a046447e2c84a01df9e5c20779750acb38e11b2" -dependencies = [ - "crunchy 0.2.2", -] - -[[package]] -name = "tinyvec" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "tokio" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a09c0b5bb588872ab2f09afa13ee6e9dac11e10a0ec9e8e3ba39a5a5d530af6" -dependencies = [ - "bytes 0.4.12", - "futures 0.1.31", - "mio 0.6.23", - "num_cpus", - "tokio-codec", - "tokio-current-thread", - "tokio-executor", - "tokio-fs", - "tokio-io", - "tokio-reactor", - "tokio-sync", - "tokio-tcp", - "tokio-threadpool", - "tokio-timer", - "tokio-udp", - "tokio-uds", -] - -[[package]] -name = "tokio" -version = "0.2.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6703a273949a90131b290be1fe7b039d0fc884aa1935860dfcbe056f28cd8092" -dependencies = [ - "bytes 0.5.6", - "fnv", - "futures-core", - "iovec", - "lazy_static", - "libc", - "memchr 2.7.1", - "mio 0.6.23", - "mio-named-pipes", - "mio-uds", - "num_cpus", - "pin-project-lite 0.1.12", - "signal-hook-registry", - "slab", - "tokio-macros 0.2.6", - "winapi 0.3.9", -] - -[[package]] -name = "tokio" -version = "1.36.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" -dependencies = [ - "backtrace", - "bytes 1.5.0", - "libc", - "mio 0.8.10", - "num_cpus", - "parking_lot 0.12.1", - "pin-project-lite 0.2.13", - "signal-hook-registry", - "socket2 0.5.6", - "tokio-macros 2.2.0", - "windows-sys 0.48.0", -] - -[[package]] -name = "tokio-codec" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25b2998660ba0e70d18684de5d06b70b70a3a747469af9dea7618cc59e75976b" -dependencies = [ - "bytes 0.4.12", - "futures 0.1.31", - "tokio-io", -] - -[[package]] -name = "tokio-core" -version = "0.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87b1395334443abca552f63d4f61d0486f12377c2ba8b368e523f89e828cffd4" -dependencies = [ - "bytes 0.4.12", - "futures 0.1.31", - "iovec", - "log 0.4.25", - "mio 0.6.23", - "scoped-tls", - "tokio 0.1.22", - "tokio-executor", - "tokio-io", - "tokio-reactor", - "tokio-timer", -] - -[[package]] -name = "tokio-current-thread" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1de0e32a83f131e002238d7ccde18211c0a5397f60cbfffcb112868c2e0e20e" -dependencies = [ - "futures 0.1.31", - "tokio-executor", -] - -[[package]] -name = "tokio-executor" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb2d1b8f4548dbf5e1f7818512e9c406860678f29c300cdf0ebac72d1a3a1671" -dependencies = [ - "crossbeam-utils 0.7.2", - "futures 0.1.31", -] - -[[package]] -name = "tokio-fs" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "297a1206e0ca6302a0eed35b700d292b275256f596e2f3fea7729d5e629b6ff4" -dependencies = [ - "futures 0.1.31", - "tokio-io", - "tokio-threadpool", -] - -[[package]] -name = "tokio-io" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57fc868aae093479e3131e3d165c93b1c7474109d13c90ec0dda2a1bbfff0674" -dependencies = [ - "bytes 0.4.12", - "futures 0.1.31", - "log 0.4.25", -] - -[[package]] -name = "tokio-io-timeout" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" -dependencies = [ - "pin-project-lite 0.2.13", - "tokio 1.36.0", -] - -[[package]] -name = "tokio-macros" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e44da00bfc73a25f814cd8d7e57a68a5c31b74b3152a0a1d1f590c97ed06265a" -dependencies = [ - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 1.0.109", -] - -[[package]] -name = "tokio-macros" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" -dependencies = [ - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 2.0.51", -] - -[[package]] -name = "tokio-native-tls" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" -dependencies = [ - "native-tls", - "tokio 1.36.0", -] - -[[package]] -name = "tokio-reactor" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09bc590ec4ba8ba87652da2068d150dcada2cfa2e07faae270a5e0409aa51351" -dependencies = [ - "crossbeam-utils 0.7.2", - "futures 0.1.31", - "lazy_static", - "log 0.4.25", - "mio 0.6.23", - "num_cpus", - "parking_lot 0.9.0", - "slab", - "tokio-executor", - "tokio-io", - "tokio-sync", -] - -[[package]] -name = "tokio-rustls" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e12831b255bcfa39dc0436b01e19fea231a37db570686c06ee72c423479f889a" -dependencies = [ - "futures-core", - "rustls 0.18.1", - "tokio 0.2.25", - "webpki", -] - -[[package]] -name = "tokio-rustls" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" -dependencies = [ - "rustls 0.21.10", - "tokio 1.36.0", -] - -[[package]] -name = "tokio-socks" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d611fd5d241872372d52a0a3d309c52d0b95a6a67671a6c8f7ab2c4a37fb2539" -dependencies = [ - "bytes 0.4.12", - "either", - "futures 0.3.30", - "thiserror", - "tokio 0.2.25", -] - -[[package]] -name = "tokio-sync" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edfe50152bc8164fcc456dab7891fa9bf8beaf01c5ee7e1dd43a397c3cf87dee" -dependencies = [ - "fnv", - "futures 0.1.31", -] - -[[package]] -name = "tokio-tcp" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98df18ed66e3b72e742f185882a9e201892407957e45fbff8da17ae7a7c51f72" -dependencies = [ - "bytes 0.4.12", - "futures 0.1.31", - "iovec", - "mio 0.6.23", - "tokio-io", - "tokio-reactor", -] - -[[package]] -name = "tokio-threadpool" -version = "0.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df720b6581784c118f0eb4310796b12b1d242a7eb95f716a8367855325c25f89" -dependencies = [ - "crossbeam-deque 0.7.4", - "crossbeam-queue", - "crossbeam-utils 0.7.2", - "futures 0.1.31", - "lazy_static", - "log 0.4.25", - "num_cpus", - "slab", - "tokio-executor", -] - -[[package]] -name = "tokio-timer" -version = "0.2.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93044f2d313c95ff1cb7809ce9a7a05735b012288a888b62d4434fd58c94f296" -dependencies = [ - "crossbeam-utils 0.7.2", - "futures 0.1.31", - "slab", - "tokio-executor", -] - -[[package]] -name = "tokio-tls" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "354b8cd83825b3c20217a9dc174d6a0c67441a2fae5c41bcb1ea6679f6ae0f7c" -dependencies = [ - "futures 0.1.31", - "native-tls", - "tokio-io", -] - -[[package]] -name = "tokio-tls" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a70f4fcd7b3b24fb194f837560168208f669ca8cb70d0c4b862944452396343" -dependencies = [ - "native-tls", - "tokio 0.2.25", -] - -[[package]] -name = "tokio-tungstenite" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c83b561d025642014097b66e6c1bb422783339e0909e4429cde4749d1990bc38" -dependencies = [ - "futures-util", - "log 0.4.25", - "tokio 1.36.0", - "tungstenite", -] - -[[package]] -name = "tokio-udp" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2a0b10e610b39c38b031a2fcab08e4b82f16ece36504988dcbd81dbba650d82" -dependencies = [ - "bytes 0.4.12", - "futures 0.1.31", - "log 0.4.25", - "mio 0.6.23", - "tokio-codec", - "tokio-io", - "tokio-reactor", -] - -[[package]] -name = "tokio-uds" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab57a4ac4111c8c9dbcf70779f6fc8bc35ae4b2454809febac840ad19bd7e4e0" -dependencies = [ - "bytes 0.4.12", - "futures 0.1.31", - "iovec", - "libc", - "log 0.4.25", - "mio 0.6.23", - "mio-uds", - "tokio-codec", - "tokio-io", - "tokio-reactor", -] - -[[package]] -name = "tokio-util" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be8242891f2b6cbef26a2d7e8605133c2c554cd35b3e4948ea892d6d68436499" -dependencies = [ - "bytes 0.5.6", - "futures-core", - "futures-sink", - "log 0.4.25", - "pin-project-lite 0.1.12", - "tokio 0.2.25", -] - -[[package]] -name = "tokio-util" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" -dependencies = [ - "bytes 1.5.0", - "futures-core", - "futures-sink", - "pin-project-lite 0.2.13", - "tokio 1.36.0", - "tracing", -] - -[[package]] -name = "toml" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f" -dependencies = [ - "serde", -] - -[[package]] -name = "tower-service" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" - -[[package]] -name = "tracing" -version = "0.1.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" -dependencies = [ - "log 0.4.25", - "pin-project-lite 0.2.13", - "tracing-core", -] - -[[package]] -name = "tracing-core" -version = "0.1.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" -dependencies = [ - "once_cell", -] - -[[package]] -name = "tracing-futures" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" -dependencies = [ - "pin-project", - "tracing", -] - -[[package]] -name = "traitobject" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079" - -[[package]] -name = "try-lock" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" - -[[package]] -name = "tungstenite" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ef1a641ea34f399a848dea702823bbecfb4c486f911735368f1f137cb8257e1" -dependencies = [ - "byteorder", - "bytes 1.5.0", - "data-encoding", - "http 1.0.0", - "httparse", - "log 0.4.25", - "native-tls", - "rand 0.8.5", - "sha1 0.10.6", - "thiserror", - "url 2.5.0", - "utf-8", -] - -[[package]] -name = "typeable" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1410f6f91f21d1612654e7cc69193b0334f909dcf2c790c4826254fbb86f8887" - -[[package]] -name = "typemap" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "653be63c80a3296da5551e1bfd2cca35227e13cdd08c6668903ae2f4f77aa1f6" -dependencies = [ - "unsafe-any", -] - -[[package]] -name = "typenum" -version = "1.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" - -[[package]] -name = "uint" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2143cded94692b156c356508d92888acc824db5bffc0b4089732264c6fcf86d4" -dependencies = [ - "byteorder", - "crunchy 0.2.2", - "rustc-hex", -] - -[[package]] -name = "unicase" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4765f83163b74f957c797ad9253caf97f103fb064d3999aea9568d09fc8a33" -dependencies = [ - "version_check 0.1.5", -] - -[[package]] -name = "unicase" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" -dependencies = [ - "version_check 0.9.4", -] - -[[package]] -name = "unicode-bidi" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" - -[[package]] -name = "unicode-ident" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" - -[[package]] -name = "unicode-normalization" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "unicode-segmentation" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" - -[[package]] -name = "unicode-width" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" - -[[package]] -name = "unicode-xid" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" - -[[package]] -name = "unicode-xid" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" - -[[package]] -name = "universal-hash" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" -dependencies = [ - "crypto-common", - "subtle", -] - -[[package]] -name = "unreachable" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" -dependencies = [ - "void", -] - -[[package]] -name = "unsafe-any" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30360d7979f5e9c6e6cea48af192ea8fab4afb3cf72597154b8f08935bc9c7f" -dependencies = [ - "traitobject", -] - -[[package]] -name = "untrusted" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" - -[[package]] -name = "untrusted" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" - -[[package]] -name = "url" -version = "1.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a" -dependencies = [ - "idna 0.1.5", - "matches", - "percent-encoding 1.0.1", -] - -[[package]] -name = "url" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" -dependencies = [ - "form_urlencoded", - "idna 0.5.0", - "percent-encoding 2.3.1", -] - -[[package]] -name = "utf-8" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" - -[[package]] -name = "uuid" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1436e58182935dcd9ce0add9ea0b558e8a87befe01c1a301e6020aeb0876363" -dependencies = [ - "cfg-if 0.1.10", - "rand 0.4.6", - "serde", -] - -[[package]] -name = "uuid" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90dbc611eb48397705a6b0f6e917da23ae517e4d127123d2cf7674206627d32a" -dependencies = [ - "rand 0.6.5", - "serde", -] - -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - -[[package]] -name = "vec_map" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" - -[[package]] -name = "version_check" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "void" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" - -[[package]] -name = "walkdir" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" -dependencies = [ - "same-file", - "winapi-util", -] - -[[package]] -name = "want" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" -dependencies = [ - "try-lock", -] - -[[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" - -[[package]] -name = "wasi" -version = "0.10.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "wasi" -version = "0.13.3+wasi-0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" -dependencies = [ - "wit-bindgen-rt", -] - -[[package]] -name = "wasm-bindgen" -version = "0.2.91" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1e124130aee3fb58c5bdd6b639a0509486b0338acaaae0c84a5124b0f588b7f" -dependencies = [ - "cfg-if 1.0.0", - "serde", - "serde_json", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.91" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e7e1900c352b609c8488ad12639a311045f40a35491fb69ba8c12f758af70b" -dependencies = [ - "bumpalo", - "log 0.4.25", - "once_cell", - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 2.0.51", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.41" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877b9c3f61ceea0e56331985743b13f3d25c406a7098d45180fb5f09bc19ed97" -dependencies = [ - "cfg-if 1.0.0", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.91" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b30af9e2d358182b5c7449424f017eba305ed32a7010509ede96cdc4696c46ed" -dependencies = [ - "quote 1.0.35", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.91" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66" -dependencies = [ - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 2.0.51", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.91" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838" - -[[package]] -name = "web-sys" -version = "0.3.68" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96565907687f7aceb35bc5fc03770a8a0471d82e479f25832f54a0e3f4b28446" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "webpki" -version = "0.21.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" -dependencies = [ - "ring 0.16.20", - "untrusted 0.7.1", -] - -[[package]] -name = "webpki-roots" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f20dea7535251981a9670857150d571846545088359b28e4951d350bdaf179f" -dependencies = [ - "webpki", -] - -[[package]] -name = "websocket" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9faed2bff8af2ea6b9f8b917d3d00b467583f6781fe3def174a9e33c879703" -dependencies = [ - "base64 0.9.3", - "bitflags 0.9.1", - "byteorder", - "bytes 0.4.12", - "futures 0.1.31", - "hyper 0.10.16", - "native-tls", - "rand 0.5.6", - "sha1 0.6.1", - "tokio-core", - "tokio-io", - "tokio-tls 0.2.1", - "unicase 1.4.2", - "url 1.7.2", -] - -[[package]] -name = "winapi" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-build" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" -dependencies = [ - "winapi 0.3.9", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows-core" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" -dependencies = [ - "windows-targets 0.52.3", -] - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", -] - -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets 0.52.3", -] - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", -] - -[[package]] -name = "windows-targets" -version = "0.52.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d380ba1dc7187569a8a9e91ed34b8ccfc33123bbacb8c0aed2d1ad7f3ef2dc5f" -dependencies = [ - "windows_aarch64_gnullvm 0.52.3", - "windows_aarch64_msvc 0.52.3", - "windows_i686_gnu 0.52.3", - "windows_i686_msvc 0.52.3", - "windows_x86_64_gnu 0.52.3", - "windows_x86_64_gnullvm 0.52.3", - "windows_x86_64_msvc 0.52.3", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68e5dcfb9413f53afd9c8f86e56a7b4d86d9a2fa26090ea2dc9e40fba56c6ec6" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8dab469ebbc45798319e69eebf92308e541ce46760b49b18c6b3fe5e8965b30f" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a4e9b6a7cac734a8b4138a4e1044eac3404d8326b6c0f939276560687a033fb" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28b0ec9c422ca95ff34a78755cfa6ad4a51371da2a5ace67500cf7ca5f232c58" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "704131571ba93e89d7cd43482277d6632589b18ecf4468f591fbae0a8b101614" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42079295511643151e98d61c38c0acc444e52dd42ab456f7ccfd5152e8ecf21c" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0770833d60a970638e989b3fa9fd2bb1aaadcf88963d1659fd7d9990196ed2d6" - -[[package]] -name = "winreg" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69" -dependencies = [ - "winapi 0.3.9", -] - -[[package]] -name = "winreg" -version = "0.50.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" -dependencies = [ - "cfg-if 1.0.0", - "windows-sys 0.48.0", -] - -[[package]] -name = "wit-bindgen-rt" -version = "0.33.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" -dependencies = [ - "bitflags 2.4.2", -] - -[[package]] -name = "ws" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25fe90c75f236a0a00247d5900226aea4f2d7b05ccc34da9e7a8880ff59b5848" -dependencies = [ - "byteorder", - "bytes 0.4.12", - "httparse", - "log 0.4.25", - "mio 0.6.23", - "mio-extras", - "rand 0.7.3", - "sha-1", - "slab", - "url 2.5.0", -] - -[[package]] -name = "ws2_32-sys" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" -dependencies = [ - "winapi 0.2.8", - "winapi-build", -] - -[[package]] -name = "wyz" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" -dependencies = [ - "tap", -] - -[[package]] -name = "x25519-dalek" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "637ff90c9540fa3073bb577e65033069e4bae7c79d49d74aa3ffdf5342a53217" -dependencies = [ - "curve25519-dalek 2.1.3", - "rand_core 0.5.1", - "zeroize", -] - -[[package]] -name = "yaml-rust" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e66366e18dc58b46801afbf2ca7661a9f59cc8c5962c29892b6039b4f86fa992" - -[[package]] -name = "yaml-rust" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" -dependencies = [ - "linked-hash-map", -] - -[[package]] -name = "zeroize" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" -dependencies = [ - "zeroize_derive", -] - -[[package]] -name = "zeroize_derive" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" -dependencies = [ - "proc-macro2 1.0.78", - "quote 1.0.35", - "syn 2.0.51", -] - -[[package]] -name = "zip" -version = "0.5.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93ab48844d61251bb3835145c521d88aa4031d7139e8485990f60ca911fa0815" -dependencies = [ - "byteorder", - "crc32fast", - "thiserror", -] diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 47422fc2..8e896af4 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -1,11 +1,18 @@ [package] -name = "epic-cash-wallet" -version = "0.1.0" +name = "epic-ffi" +version = "0.0.1" edition = "2021" +authors = ["Grin developers", "Epic developers", "Cypher Stack and Stack Wallet developers", "Likho", "@blacktyg3r", "sneurlax"] +description = "Epic Cash oriented for usage via FFI." +license = "Apache-2.0" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[lib] +name = "epic_ffi" +path = "src/lib.rs" +crate-type = ["cdylib", "staticlib"] [dependencies] +openssl = { version = "0.10", features = ["vendored"] } clap = { version = "2.31", features = ["yaml"] } rpassword = "2.0.0" ctrlc = { version = "3.1", features = ["termination"] } @@ -19,43 +26,38 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1" serde_derive = "1" simplelog = "^0.7.4" -openssl = { version = "0.10", features = ["vendored"] } zeroize = { version = "1.1.0", features = ["derive"] } rand = "0.6" reqwest = { version = "0.11", features = ["blocking", "json"] } rustc-serialize = "0.3.24" android_logger = "0.11.0" -chrono = "0.4.24" +chrono = "0.4.19" + +epic_keychain = { path = "epic/keychain" } +epic_util = { path = "epic/util" } +epic_core = { path = "epic/core" } -stack_epic_keychain = "3.6.0" -stack_epic_util = "3.6.0" -stack_epic_core = "3.6.0" +epic_wallet_api = { path = "epic-wallet/api" } +epic_wallet_impls = { path = "epic-wallet/impls" } +epic_wallet_libwallet = { path = "epic-wallet/libwallet" } +epic_wallet_config = { path = "epic-wallet/config" } +epic_wallet_util = { path = "epic-wallet/util" } +epic_wallet_controller = { path = "epic-wallet/controller" } -stack_epic_wallet_api = { path = "epic-wallet/api" } -stack_epic_wallet_impls = { path = "epic-wallet/impls" } -stack_epic_wallet_libwallet = { path = "epic-wallet/libwallet" } -stack_epic_wallet_config = { path = "epic-wallet/config" } -stack_epic_wallet_util = { path = "epic-wallet/util" } -stack_epic_wallet_controller = { path = "epic-wallet/controller" } +# Epic box for posting slates and getting epic addresses. +epicboxlib = { path = "epicbox/epicboxlib" } url = "2.1.0" futures = "0.3.15" -tokio-tungstenite = "0.21.0" -tungstenite = { version = "0.21", default-features = false } +tokio-tungstenite = "0.10.0" +tungstenite = { version = "0.10", default-features = false } tokio = { version = "0.2.0", features = ["full"]} websocket = "0.21.1" ws = "0.9.2" -ffi_helpers = "0.3.0" -anyhow = "1.0.69" - -[dev-dependencies] -hex = "0.4.3" - - -[lib] -name = "epic_cash_wallet" -crate-type = ["staticlib", "cdylib"] [patch.crates-io] liblmdb-sys = { git = "https://github.com/i1skn/lmdb-rs" } +[build-dependencies] +cbindgen = "0.24.3" +glob = "0.3.1" diff --git a/rust/LICENSE b/rust/LICENSE new file mode 100644 index 00000000..8a156621 --- /dev/null +++ b/rust/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2025 Cypher Stack + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/rust/README.md b/rust/README.md new file mode 100644 index 00000000..762775e2 --- /dev/null +++ b/rust/README.md @@ -0,0 +1,8 @@ +# `epic-ffi` +Epic Cash oriented for use by FFI. + +A fork of +[epic-cash-rust-lib](https://github.com/blacktyger/epic-wallet-rust-lib), which +is in turn a fork of +[flutter_libepiccash](https://github.com/cypherstack/flutter_libepiccash). See +the latter for docs until that README is adapted here. diff --git a/rust/epic b/rust/epic new file mode 160000 index 00000000..b7b8247e --- /dev/null +++ b/rust/epic @@ -0,0 +1 @@ +Subproject commit b7b8247e6fdb6a01870c8837187c87c4d679b23e diff --git a/rust/epic-wallet b/rust/epic-wallet index 70d4806b..c51cee51 160000 --- a/rust/epic-wallet +++ b/rust/epic-wallet @@ -1 +1 @@ -Subproject commit 70d4806b8e14f00acd21cba0f769d798736f2746 +Subproject commit c51cee516161bb1580e84b5266b5c41b9950794d diff --git a/rust/epicbox b/rust/epicbox new file mode 160000 index 00000000..cf6478ad --- /dev/null +++ b/rust/epicbox @@ -0,0 +1 @@ +Subproject commit cf6478ad37f5bb20ee4db548d4b21be791d379d8 diff --git a/rust/src/config.rs b/rust/src/config.rs deleted file mode 100644 index 6e3d67e4..00000000 --- a/rust/src/config.rs +++ /dev/null @@ -1,66 +0,0 @@ -use serde::{Deserialize, Serialize}; -use stack_epic_wallet_util::epic_core::global::ChainTypes; -use std::path::Path; -use stack_epic_wallet_config::WalletConfig; - -use crate::Error; - -/// Epic Wallet Config. -#[derive(Serialize, Deserialize, Clone, Debug)] -pub struct Config { - pub wallet_dir: String, - pub check_node_api_http_addr: String, - pub chain: String, - pub account: Option, - pub api_listen_port: u16, - pub api_listen_interface: String -} - -/// Implement the Config struct. -impl Config { - pub fn from_str(json: &str) -> Result { - let result = match serde_json::from_str::(json) { - Ok(config) => { - config - }, Err(err) => { - return Err(err); - } - }; - Ok(result) - } -} - -/// Create a wallet config. -pub fn create_wallet_config(config: Config) -> Result { - let chain_type = match config.chain.as_ref() { - "mainnet" => ChainTypes::Mainnet, - "floonet" => ChainTypes::Floonet, - "usertesting" => ChainTypes::UserTesting, - "automatedtesting" => ChainTypes::AutomatedTesting, // TODO: Use for tests. - _ => ChainTypes::Floonet, - }; - - let api_secret_path = config.wallet_dir.clone() + "/.api_secret"; - let api_listen_port = config.api_listen_port; - - Ok(WalletConfig { - chain_type: Some(chain_type), - api_listen_interface: config.api_listen_interface, - api_listen_port, - api_secret_path: None, - node_api_secret_path: if Path::new(&api_secret_path).exists() { - Some(api_secret_path) - } else { - None - }, - check_node_api_http_addr: config.check_node_api_http_addr, - data_file_dir: config.wallet_dir, - tls_certificate_file: None, - tls_certificate_key: None, - dark_background_color_scheme: Some(true), - keybase_notify_ttl: Some(1440), - no_commit_cache: Some(false), - owner_api_include_foreign: Some(false), - owner_api_listen_port: Some(WalletConfig::default_owner_api_listen_port()), - }) -} diff --git a/rust/src/ffi.rs b/rust/src/ffi.rs deleted file mode 100644 index d71c5c96..00000000 --- a/rust/src/ffi.rs +++ /dev/null @@ -1,1124 +0,0 @@ -use std::ffi::{c_void, CStr, CString}; -use std::os::raw::c_char; - -use uuid::Uuid; - -use stack_epic_wallet_api::{self, Owner}; -use stack_epic_wallet_config::EpicboxConfig; -use stack_epic_wallet_libwallet::Error; -use stack_epic_wallet_controller::Error as EpicWalletControllerError; - -use stack_epic_util::secp::key::SecretKey; - -use crate::config::Config; - -use crate::mnemonic::mnemonic; -use crate::mnemonic::create_seed; -use crate::mnemonic::_get_mnemonic; - -use crate::wallet::Wallet; -use crate::wallet::create_wallet; -use crate::wallet::recover_from_mnemonic; -use crate::wallet::open_wallet; -use crate::wallet::get_wallet_info; -use crate::wallet::validate_address; -use crate::wallet::wallet_scan_outputs; -use crate::wallet::tx_strategies; -use crate::wallet::tx_create; -use crate::wallet::txs_get; -use crate::wallet::tx_cancel; -use crate::wallet::delete_wallet; -use crate::wallet::tx_send_http; -use crate::wallet::get_chain_height; - -use crate::listener::Listener; -use crate::listener::listener_spawn; -use crate::listener::listener_cancel; -use crate::listener::listener_cancelled; -use crate::init_logger; - -use ffi_helpers::task::TaskHandle; - -/// Initialize a new wallet via FFI. -#[no_mangle] -pub unsafe extern "C" fn wallet_init( - config: *const c_char, - mnemonic: *const c_char, - password: *const c_char, - name: *const c_char -) -> *const c_char { - - let result = match _wallet_init(config, mnemonic, password, name) { - Ok(created) => { - created - }, Err(e ) => { - let error_msg = format!("Error {}", &e.to_string()); - let error_msg_ptr = CString::new(error_msg).unwrap(); - let ptr = error_msg_ptr.as_ptr(); - std::mem::forget(error_msg_ptr); - ptr - } - }; - result -} - -/// Get a new mnemonic. -#[no_mangle] -pub unsafe extern "C" fn get_mnemonic() -> *const c_char { - let result = match _get_mnemonic() { - Ok(phrase) => { - phrase - }, Err(e ) => { - let error_msg = format!("Error {}", &e.to_string()); - let error_msg_ptr = CString::new(error_msg).unwrap(); - let ptr = error_msg_ptr.as_ptr(); - std::mem::forget(error_msg_ptr); - ptr - } - }; - result -} - -/// A helper to initialize a new wallet. -fn _wallet_init( - config: *const c_char, - mnemonic: *const c_char, - password: *const c_char, - name: *const c_char -) -> Result<*const c_char, Error> { - - let config = unsafe { CStr::from_ptr(config) }; - let mnemonic = unsafe { CStr::from_ptr(mnemonic) }; - let password = unsafe { CStr::from_ptr(password) }; - let name = unsafe { CStr::from_ptr(name) }; - - let str_password = match password.to_str() { - Ok(str_pass) => {str_pass}, Err(e) => {return Err( - Error::from(EpicWalletControllerError::GenericError(format!("{}", e.to_string()))) - )} - }; - - let str_config = match config.to_str() { - Ok(str_conf) => {str_conf}, Err(e) => {return Err( - Error::from(EpicWalletControllerError::GenericError(format!("{}", e.to_string()))) - )} - }; - - let phrase = match mnemonic.to_str() { - Ok(str_phrase) => {str_phrase}, Err(e) => {return Err( - Error::from(EpicWalletControllerError::GenericError(format!("{}", e.to_string()))) - )} - }; - - let str_name = match name.to_str() { - Ok(str_name) => {str_name}, Err(e) => {return Err( - Error::from(EpicWalletControllerError::GenericError(format!("{}", e.to_string()))) - )} - }; - - let mut create_msg = "".to_string(); - match create_wallet(str_config, phrase, str_password, str_name) { - Ok(_) => { - create_msg.push_str(""); - },Err(e) => { - return Err(e); - } - } - let s = CString::new(create_msg).unwrap(); - let p = s.as_ptr(); - std::mem::forget(s); // Give up the responsibility of cleaning up/freeing s. - Ok(p) -} - -/// Open a wallet via FFI. -#[no_mangle] -pub unsafe extern "C" fn rust_open_wallet( - config: *const c_char, - password: *const c_char, -) -> *const c_char { - init_logger(); - let result = match _open_wallet( - config, - password - ) { - Ok(wallet) => { - wallet - }, Err(e ) => { - let error_msg = format!("Error {}", &e.to_string()); - let error_msg_ptr = CString::new(error_msg).unwrap(); - let ptr = error_msg_ptr.as_ptr(); - std::mem::forget(error_msg_ptr); - ptr - } - }; - result -} - -/// A helper to open a wallet. -fn _open_wallet( - config: *const c_char, - password: *const c_char, -) -> Result<*const c_char, Error> { - let c_conf = unsafe { CStr::from_ptr(config) }; - let c_password = unsafe { CStr::from_ptr(password) }; - - let str_config = c_conf.to_str().unwrap(); - let str_password = c_password.to_str().unwrap(); - - let mut result = String::from(""); - match open_wallet(&str_config, str_password) { - Ok(res) => { - let wlt = res.0; - let sek_key = res.1; - let wallet_int = Box::into_raw(Box::new(wlt)) as i64; - let wallet_data = (wallet_int, sek_key); - let wallet_ptr = serde_json::to_string(&wallet_data).unwrap(); - result.push_str(&wallet_ptr); - } - Err(err) => { - return Err(err); - } - }; - - let s = CString::new(result).unwrap(); - let p = s.as_ptr(); - std::mem::forget(s); // Give up the responsibility of cleaning up/freeing s. - Ok(p) -} - -/// Get wallet balances via FFI. -#[no_mangle] -pub unsafe extern "C" fn rust_wallet_balances( - wallet: *const c_char, - refresh: *const c_char, - min_confirmations: *const c_char, -) -> *const c_char { - let wallet_ptr = CStr::from_ptr(wallet); - let c_refresh = CStr::from_ptr(refresh); - let minimum_confirmations = CStr::from_ptr(min_confirmations); - let minimum_confirmations: u64 = minimum_confirmations.to_str().unwrap().to_string().parse().unwrap(); - - let refresh_from_node: u64 = c_refresh.to_str().unwrap().to_string().parse().unwrap(); - let refresh = match refresh_from_node { - 0 => false, - _=> true - }; - - let wallet_data = wallet_ptr.to_str().unwrap(); - let tuple_wallet_data: (i64, Option) = serde_json::from_str(wallet_data).unwrap(); - let wlt = tuple_wallet_data.0; - let sek_key = tuple_wallet_data.1; - - ensure_wallet!(wlt, wallet); - - let result = match _wallet_balances( - wallet, - sek_key, - refresh, - minimum_confirmations - ) { - Ok(balances) => { - balances - }, Err(e ) => { - let error_msg = format!("Error {}", &e.to_string()); - let error_msg_ptr = CString::new(error_msg).unwrap(); - let ptr = error_msg_ptr.as_ptr(); - std::mem::forget(error_msg_ptr); - ptr - } - }; - result -} - -/// A helper to get wallet balances. -fn _wallet_balances( - wallet: &Wallet, - keychain_mask: Option, - refresh: bool, - min_confirmations: u64, -) -> Result<*const c_char, Error> { - // Print arguments for debugging/test-vector use. - println!( - ">> _wallet_balances called with refresh={refresh}, min_confirmations={min_confirmations}" - ); - - let mut wallet_info_str = String::new(); - - // Call get_wallet_info under the hood. - match get_wallet_info(wallet, keychain_mask, refresh, min_confirmations) { - Ok(info) => { - // Print intermediate data - println!(">> _wallet_balances got info: {:?}", info); - - // Convert to JSON. - let str_wallet_info = serde_json::to_string(&info).unwrap(); - wallet_info_str.push_str(&str_wallet_info); - } - Err(e) => { - println!(">> _wallet_balances encountered error: {e}"); - return Err(e); - } - } - - // Convert final string result into a *const c_char. - let s = CString::new(wallet_info_str).unwrap(); - let p = s.as_ptr(); - std::mem::forget(s); // Hand off responsibility to caller. - Ok(p) -} - -/// Recover a wallet from a mnemonic via FFI. -#[no_mangle] -pub unsafe extern "C" fn rust_recover_from_mnemonic( - config: *const c_char, - password: *const c_char, - mnemonic: *const c_char, - name: *const c_char -) -> *const c_char { - - let result = match _recover_from_mnemonic( - config, - password, - mnemonic, - name - ) { - Ok(recovered) => { - recovered - }, Err(e ) => { - let error_msg = format!("Error {}", &e.to_string()); - let error_msg_ptr = CString::new(error_msg).unwrap(); - let ptr = error_msg_ptr.as_ptr(); - std::mem::forget(error_msg_ptr); - ptr - } - }; - result -} - -/// A helper to recover a wallet from a mnemonic. -fn _recover_from_mnemonic( - config: *const c_char, - password: *const c_char, - mnemonic: *const c_char, - name: *const c_char -) -> Result<*const c_char, Error> { - let c_conf = unsafe { CStr::from_ptr(config) }; - let c_password = unsafe { CStr::from_ptr(password) }; - let c_mnemonic = unsafe { CStr::from_ptr(mnemonic) }; - let c_name = unsafe { CStr::from_ptr(name) }; - - let input_conf = c_conf.to_str().unwrap(); - let str_password = c_password.to_str().unwrap(); - let wallet_config = match Config::from_str(&input_conf.to_string()) { - Ok(config) => { - config - }, Err(err) => { - return Err(Error::from(EpicWalletControllerError::GenericError(format!( - "Wallet config error : {}", - err.to_string() - )))) - } - }; - let phrase = c_mnemonic.to_str().unwrap(); - let name = c_name.to_str().unwrap(); - - let mut recover_response = "".to_string(); - match recover_from_mnemonic(phrase, str_password, &wallet_config, name) { - Ok(_)=> { - recover_response.push_str("recovered"); - }, - Err(e)=> { - return Err(e); - } - } - let s = CString::new(recover_response).unwrap(); - let p = s.as_ptr(); - std::mem::forget(s); // Give up the responsibility of cleaning up/freeing s. - Ok(p) -} - -/// Validate an address via FFI. -#[no_mangle] -pub unsafe extern "C" fn rust_wallet_scan_outputs( - wallet: *const c_char, - start_height: *const c_char, - number_of_blocks: *const c_char, -) -> *const c_char { - let wallet_ptr = CStr::from_ptr(wallet); - let c_start_height = CStr::from_ptr(start_height); - let c_number_of_blocks = CStr::from_ptr(number_of_blocks); - let start_height: u64 = c_start_height.to_str().unwrap().to_string().parse().unwrap(); - let number_of_blocks: u64 = c_number_of_blocks.to_str().unwrap().to_string().parse().unwrap(); - - let wallet_data = wallet_ptr.to_str().unwrap(); - let tuple_wallet_data: (i64, Option) = serde_json::from_str(wallet_data).unwrap(); - let wlt = tuple_wallet_data.0; - let sek_key = tuple_wallet_data.1; - - ensure_wallet!(wlt, wallet); - - let result = match _wallet_scan_outputs( - wallet, - sek_key, - start_height, - number_of_blocks - ) { - Ok(scan) => { - scan - }, Err(e ) => { - let error_msg = format!("Error {}", &e.to_string()); - let error_msg_ptr = CString::new(error_msg).unwrap(); - let ptr = error_msg_ptr.as_ptr(); - std::mem::forget(error_msg_ptr); - ptr - } - }; - result -} - -/// A helper to scan outputs. -fn _wallet_scan_outputs( - wallet: &Wallet, - keychain_mask: Option, - start_height: u64, - number_of_blocks: u64 -) -> Result<*const c_char, Error> { - // Print arguments for debugging/test-vector use. - println!( - ">> _wallet_scan_outputs called with start_height={start_height}, number_of_blocks={number_of_blocks}" - ); - - let mut scan_result = String::new(); - - // Call wallet_scan_outputs under the hood. - match wallet_scan_outputs( - wallet, - keychain_mask, - Some(start_height), - Some(number_of_blocks) - ) { - Ok(scan_str) => { - // Print intermediate data. - println!(">> _wallet_scan_outputs result: {scan_str}"); - scan_result.push_str(&scan_str); - }, - Err(err) => { - println!(">> _wallet_scan_outputs encountered error: {err}"); - return Err(err); - }, - } - - // Convert final string result into a *const c_char. - let s = CString::new(scan_result).unwrap(); - let p = s.as_ptr(); - std::mem::forget(s); // Hand off responsibility to caller. - Ok(p) -} - -/// Create a transaction via FFI. -#[no_mangle] -pub unsafe extern "C" fn rust_create_tx( - wallet: *const c_char, - amount: *const c_char, - to_address: *const c_char, - secret_key_index: *const c_char, - epicbox_config: *const c_char, - confirmations: *const c_char, - note: *const c_char -) -> *const c_char { - - let wallet_data = CStr::from_ptr(wallet).to_str().unwrap(); - let min_confirmations: u64 = CStr::from_ptr(confirmations).to_str().unwrap().to_string().parse().unwrap(); - let amount: u64 = CStr::from_ptr(amount).to_str().unwrap().to_string().parse().unwrap(); - let address = CStr::from_ptr(to_address).to_str().unwrap(); - let note = CStr::from_ptr(note).to_str().unwrap(); - let key_index: u32 = CStr::from_ptr(secret_key_index).to_str().unwrap().parse().unwrap(); - let epicbox_config = CStr::from_ptr(epicbox_config).to_str().unwrap(); - - let tuple_wallet_data: (i64, Option) = serde_json::from_str(wallet_data).unwrap(); - - let listen = Listener { - wallet_ptr_str: wallet_data.to_string(), - epicbox_config: epicbox_config.parse().unwrap() - }; - - let handle = listener_spawn(&listen); - listener_cancel(handle); - debug!("LISTENER CANCELLED IS {}", listener_cancelled(handle)); - - let wlt = tuple_wallet_data.0; - let sek_key = tuple_wallet_data.1; - - ensure_wallet!(wlt, wallet); - - let result = match _create_tx( - wallet, - sek_key, - amount, - address, - key_index, - epicbox_config, - min_confirmations, - note - ) { - Ok(slate) => { - // Spawn listener again. - listener_spawn(&listen); - slate - }, Err(e ) => { - let error_msg = format!("Error {}", &e.to_string()); - let error_msg_ptr = CString::new(error_msg).unwrap(); - let ptr = error_msg_ptr.as_ptr(); - std::mem::forget(error_msg_ptr); - ptr - } - }; - result - -} - -/// A helper to create a transaction. -fn _create_tx( - wallet: &Wallet, - keychain_mask: Option, - amount: u64, - address: &str, - _secret_key_index: u32, - epicbox_config: &str, - minimum_confirmations: u64, - note: &str -) -> Result<*const c_char, Error> { - let mut message = String::from(""); - match tx_create( - &wallet, - keychain_mask.clone(), - amount, - minimum_confirmations, - false, - epicbox_config, - address, - note) { - Ok(slate) => { - let empty_json = format!(r#"{{"slate_msg": ""}}"#); - let create_response = (&slate, &empty_json); - let str_create_response = serde_json::to_string(&create_response).unwrap(); - message.push_str(&str_create_response); - }, - Err(e) => { - message.push_str(&e.to_string()); - return Err(e); - } - } - - let s = CString::new(message).unwrap(); - let p = s.as_ptr(); - std::mem::forget(s); // Give up the responsibility of cleaning up/freeing s. - Ok(p) - - -} - -/// Get transactions via FFI. -#[no_mangle] -pub unsafe extern "C" fn rust_txs_get( - wallet: *const c_char, - refresh_from_node: *const c_char, -) -> *const c_char { - let c_wallet = CStr::from_ptr(wallet); - let c_refresh_from_node = CStr::from_ptr(refresh_from_node); - let refresh_from_node: u64 = c_refresh_from_node.to_str().unwrap().to_string().parse().unwrap(); - let refresh = match refresh_from_node { - 0 => false, - _=> true - }; - - let wallet_data = c_wallet.to_str().unwrap(); - let tuple_wallet_data: (i64, Option) = serde_json::from_str(wallet_data).unwrap(); - let wlt = tuple_wallet_data.0; - let sek_key = tuple_wallet_data.1; - - ensure_wallet!(wlt, wallet); - - let result = match _txs_get( - wallet, - sek_key, - refresh, - ) { - Ok(txs) => { - txs - }, Err(e ) => { - let error_msg = format!("Error {}", &e.to_string()); - let error_msg_ptr = CString::new(error_msg).unwrap(); - let ptr = error_msg_ptr.as_ptr(); - std::mem::forget(error_msg_ptr); - ptr - } - }; - result -} - -/// A helper to get transactions. -fn _txs_get( - wallet: &Wallet, - keychain_mask: Option, - refresh_from_node: bool, -) -> Result<*const c_char, Error> { - let mut txs_result = "".to_string(); - match txs_get( - wallet, - keychain_mask, - refresh_from_node - ) { - Ok(txs) => { - txs_result.push_str(&txs); - }, - Err(err) => { - return Err(err); - }, - } - - let s = CString::new(txs_result).unwrap(); - let p = s.as_ptr(); - std::mem::forget(s); // Give up the responsibility of cleaning up/freeing s. - Ok(p) -} - -/// Cancel a transaction via FFI. -#[no_mangle] -pub unsafe extern "C" fn rust_tx_cancel( - wallet: *const c_char, - tx_id: *const c_char, -) -> *const c_char { - let wallet_ptr = CStr::from_ptr(wallet); - let tx_id = CStr::from_ptr(tx_id); - let tx_id = tx_id.to_str().unwrap(); - let uuid = Uuid::parse_str(tx_id).map_err(|e| EpicWalletControllerError::GenericError(e.to_string())).unwrap(); - - let wallet_data = wallet_ptr.to_str().unwrap(); - let tuple_wallet_data: (i64, Option) = serde_json::from_str(wallet_data).unwrap(); - let wlt = tuple_wallet_data.0; - let sek_key = tuple_wallet_data.1; - - ensure_wallet!(wlt, wallet); - - let result = match _tx_cancel( - wallet, - sek_key, - uuid, - ) { - Ok(cancelled) => { - cancelled - }, Err(e ) => { - let error_msg = format!("Error {}", &e.to_string()); - let error_msg_ptr = CString::new(error_msg).unwrap(); - let ptr = error_msg_ptr.as_ptr(); - std::mem::forget(error_msg_ptr); - ptr - } - }; - result -} - -/// A helper to cancel a transaction. -fn _tx_cancel( - wallet: &Wallet, - keychain_mask: Option, - tx_id: Uuid, -) -> Result<*const c_char, Error>{ - let mut cancel_msg = "".to_string(); - match tx_cancel(wallet, keychain_mask, tx_id) { - Ok(_) => { - cancel_msg.push_str(""); - },Err(err) => { - return Err(err); - } - } - let s = CString::new(cancel_msg).unwrap(); - let p = s.as_ptr(); - std::mem::forget(s); // Give up the responsibility of cleaning up/freeing s. - Ok(p) -} - -/// Get chain height via FFI. -#[no_mangle] -pub unsafe extern "C" fn rust_get_chain_height( - config: *const c_char, -) -> *const c_char { - let result = match _get_chain_height( - config - ) { - Ok(chain_height) => { - chain_height - }, Err(e ) => { - let error_msg = format!("Error {}", &e.to_string()); - let error_msg_ptr = CString::new(error_msg).unwrap(); - let ptr = error_msg_ptr.as_ptr(); - std::mem::forget(error_msg_ptr); - ptr - } - }; - result -} - -/// A helper to get chain height. -fn _get_chain_height(config: *const c_char) -> Result<*const c_char, Error> { - let c_config = unsafe { CStr::from_ptr(config) }; - let str_config = c_config.to_str().unwrap(); - let mut chain_height = "".to_string(); - match get_chain_height(&str_config) { - Ok(chain_tip) => { - chain_height.push_str(&chain_tip.to_string()); - }, - Err(e) => { - debug!("CHAIN_HEIGHT_ERROR {}", e.to_string()); - return Err(e); - }, - } - let s = CString::new(chain_height).unwrap(); - let p = s.as_ptr(); - std::mem::forget(s); // Give up the responsibility of cleaning up/freeing s. - Ok(p) -} - -/// Delete a wallet via FFI. -#[no_mangle] -pub unsafe extern "C" fn rust_delete_wallet( - _wallet: *const c_char, - config: *const c_char, -) -> *const c_char { - let c_conf = CStr::from_ptr(config); - let _config = Config::from_str(c_conf.to_str().unwrap()).unwrap(); // TODO: handle error here. - - let result = match _delete_wallet( - _config, - ) { - Ok(deleted) => { - deleted - }, Err(err) => { - let error_msg = format!("Error deleting wallet from _delete_wallet in rust_delete_wallet {}", &err.to_string()); - let error_msg_ptr = CString::new(error_msg).unwrap(); - let ptr = error_msg_ptr.as_ptr(); - std::mem::forget(error_msg_ptr); - ptr - } - }; - result -} - -/// A helper to delete a wallet. -fn _delete_wallet( - config: Config, -) -> Result<*const c_char, Error> { - let mut delete_result = String::from(""); - match delete_wallet(config) { - Ok(deleted) => { - delete_result.push_str(&deleted); - }, - Err(err) => { - return Err(err); - }, - } - let s = CString::new(delete_result).unwrap(); - let p = s.as_ptr(); - std::mem::forget(s); // Give up the responsibility of cleaning up/freeing s. - Ok(p) - -} - -/// Send a transaction via FFI. -#[no_mangle] -pub unsafe extern "C" fn rust_tx_send_http( - wallet: *const c_char, - selection_strategy_is_use_all: *const c_char, - minimum_confirmations: *const c_char, - message: *const c_char, - amount: *const c_char, - address: *const c_char, -) -> *const c_char { - let c_wallet = CStr::from_ptr(wallet); - let c_strategy_is_use_all = CStr::from_ptr(selection_strategy_is_use_all); - let strategy_is_use_all: u64 = c_strategy_is_use_all.to_str().unwrap().to_string().parse().unwrap(); - let strategy_use_all = match strategy_is_use_all { - 0 => false, - _=> true - }; - let c_minimum_confirmations = CStr::from_ptr(minimum_confirmations); - let minimum_confirmations: u64 = c_minimum_confirmations.to_str().unwrap().to_string().parse().unwrap(); - let c_message = CStr::from_ptr(message); - let str_message = c_message.to_str().unwrap(); - let c_amount = CStr::from_ptr(amount); - let amount: u64 = c_amount.to_str().unwrap().to_string().parse().unwrap(); - let c_address = CStr::from_ptr(address); - let str_address = c_address.to_str().unwrap(); - - let wallet_data = c_wallet.to_str().unwrap(); - let tuple_wallet_data: (i64, Option) = serde_json::from_str(wallet_data).unwrap(); - let wlt = tuple_wallet_data.0; - let sek_key = tuple_wallet_data.1; - ensure_wallet!(wlt, wallet); - - let result = match _tx_send_http( - wallet, - sek_key, - strategy_use_all, - minimum_confirmations, - str_message, - amount, - str_address - ) { - Ok(tx_data) => { - tx_data - }, Err(err ) => { - let error_msg = format!("Error {}", &err.to_string()); - let error_msg_ptr = CString::new(error_msg).unwrap(); - let ptr = error_msg_ptr.as_ptr(); - std::mem::forget(error_msg_ptr); - ptr - } - }; - result -} - -/// A helper to send a transaction. -fn _tx_send_http( - wallet: &Wallet, - keychain_mask: Option, - selection_strategy_is_use_all: bool, - minimum_confirmations: u64, - message: &str, - amount: u64, - address: &str -) -> Result<*const c_char, Error> { - let mut send_result = String::from(""); - match tx_send_http( - wallet, - keychain_mask, - selection_strategy_is_use_all, - minimum_confirmations, - message, - amount, - address - ) { - Ok(sent) => { - let empty_json = format!(r#"{{"slate_msg": ""}}"#); - let create_response = (&sent, &empty_json); - let str_create_response = serde_json::to_string(&create_response).unwrap(); - send_result.push_str(&str_create_response); - }, - Err(err) => { - return Err(err); - }, - } - let s = CString::new(send_result).unwrap(); - let p = s.as_ptr(); - std::mem::forget(s); // Give up the responsibility of cleaning up/freeing s. - Ok(p) -} - -/// Get a wallet address via FFI. -#[no_mangle] -pub unsafe extern "C" fn rust_get_wallet_address( - wallet: *const c_char, - index: *const c_char, - epicbox_config: *const c_char, -) -> *const c_char { - let wallet_ptr = CStr::from_ptr(wallet); - let index = CStr::from_ptr(index); - let epicbox_config = CStr::from_ptr(epicbox_config); - let epicbox_config = epicbox_config.to_str().unwrap(); - let index: u32 = index.to_str().unwrap().to_string().parse().unwrap(); - - let wallet_data = wallet_ptr.to_str().unwrap(); - let tuple_wallet_data: (i64, Option) = serde_json::from_str(wallet_data).unwrap(); - let wlt = tuple_wallet_data.0; - let sek_key = tuple_wallet_data.1; - - ensure_wallet!(wlt, wallet); - let result = match _get_wallet_address( - wallet, - sek_key, - index, - epicbox_config - ) { - Ok(address) => { - address - }, Err(e ) => { - let error_msg = format!("Error {}", &e.to_string()); - let error_msg_ptr = CString::new(error_msg).unwrap(); - let ptr = error_msg_ptr.as_ptr(); - std::mem::forget(error_msg_ptr); - ptr - } - }; - result -} - -/// A helper to get a wallet address. -fn _get_wallet_address( - wallet: &Wallet, - keychain_mask: Option, - index: u32, - epicbox_config: &str -) -> Result<*const c_char, Error> { - let address = get_wallet_address(&wallet, keychain_mask, index, epicbox_config); - let s = CString::new(address).unwrap(); - let p = s.as_ptr(); - std::mem::forget(s); // Give up the responsibility of cleaning up/freeing s - Ok(p) -} - -/// Get a wallet address. -pub fn get_wallet_address( - wallet: &Wallet, - keychain_mask: Option, - index: u32, - epicbox_config: &str, -) -> String { - - let epicbox_conf = serde_json::from_str::(epicbox_config).unwrap(); - let api = Owner::new(wallet.clone(), None); - let address = api.get_public_address(keychain_mask.as_ref(), index).unwrap(); - format!("{}@{}", address.public_key, epicbox_conf.epicbox_domain.as_deref().unwrap_or("")) -} - -/// Validate an address via FFI. -#[no_mangle] -pub unsafe extern "C" fn rust_validate_address( - address: *const c_char, -) -> *const c_char { - let address = unsafe { CStr::from_ptr(address) }; - let str_address = address.to_str().unwrap(); - let validate = validate_address(str_address); - let return_value = match validate { - true => 1, - false => 0 - }; - - let s = CString::new(return_value.to_string()).unwrap(); - let p = s.as_ptr(); - std::mem::forget(s); // Give up the responsibility of cleaning up/freeing s. - p -} - -/// Validate an address. -#[no_mangle] -pub unsafe extern "C" fn rust_get_tx_fees( - wallet: *const c_char, - c_amount: *const c_char, - min_confirmations: *const c_char, -) -> *const c_char { - - let minimum_confirmations = CStr::from_ptr(min_confirmations); - let minimum_confirmations: u64 = minimum_confirmations.to_str().unwrap().to_string().parse().unwrap(); - let wallet_ptr = CStr::from_ptr(wallet); - - let amount = CStr::from_ptr(c_amount); - let amount: u64 = amount.to_str().unwrap().to_string().parse().unwrap(); - - let wallet_data = wallet_ptr.to_str().unwrap(); - let tuple_wallet_data: (i64, Option) = serde_json::from_str(wallet_data).unwrap(); - let wlt = tuple_wallet_data.0; - let sek_key = tuple_wallet_data.1; - - ensure_wallet!(wlt, wallet); - - let result = match _get_tx_fees( - &wallet, - sek_key, - amount, - minimum_confirmations, - ) { - Ok(fees) => { - fees - }, Err(e ) => { - let error_msg = format!("Error {}", &e.to_string()); - let error_msg_ptr = CString::new(error_msg).unwrap(); - let ptr = error_msg_ptr.as_ptr(); - std::mem::forget(error_msg_ptr); - ptr - } - }; - result -} - -/// A helper to get transaction fees. -fn _get_tx_fees( - wallet: &Wallet, - keychain_mask: Option, - amount: u64, - minimum_confirmations: u64, -) -> Result<*const c_char, Error> { - let mut fees_data = "".to_string(); - match tx_strategies(wallet, keychain_mask, amount, minimum_confirmations) { - Ok(fees) => { - fees_data.push_str(&fees); - }, Err(e) => { - return Err(e); - } - } - let s = CString::new(fees_data).unwrap(); - let p = s.as_ptr(); - std::mem::forget(s); // Give up the responsibility of cleaning up/freeing s. - Ok(p) -} - -/// Start a listener via FFI. -#[no_mangle] -pub unsafe extern "C" fn rust_epicbox_listener_start( - wallet: *const c_char, - epicbox_config: *const c_char, -) -> *mut c_void { - let wallet_ptr = CStr::from_ptr(wallet); - let epicbox_config = CStr::from_ptr(epicbox_config); - let epicbox_config = epicbox_config.to_str().unwrap(); - - let wallet_data = wallet_ptr.to_str().unwrap(); - // let tuple_wallet_data: (i64, Option) = serde_json::from_str(wallet_data).unwrap(); - let listen = Listener { - wallet_ptr_str: wallet_data.to_string(), - epicbox_config: epicbox_config.parse().unwrap() - }; - - let handler = listener_spawn(&listen); - let handler_value = handler.read(); - let boxed_handler = Box::new(handler_value); - Box::into_raw(boxed_handler) as *mut _ -} - -/// Cancel a listener via FFI. -#[no_mangle] -pub unsafe extern "C" fn _listener_cancel(handler: *mut c_void) -> *const c_char { - let handle = handler as *mut TaskHandle; - listener_cancel(handle); - let error_msg = format!("{}", listener_cancelled(handle)); - let error_msg_ptr = CString::new(error_msg).unwrap(); - let ptr = error_msg_ptr.as_ptr(); - std::mem::forget(error_msg_ptr); - ptr -} - -#[cfg(test)] -mod mnemonic_tests { - use super::*; - use std::collections::HashSet; - - // Test the create_seed function. - #[test] - fn test_create_seed() { - // Test with different seed lengths. - let lengths = [16, 24, 32]; - - for &length in lengths.iter() { - let seed = create_seed(length); - - // Verify seed length. - assert_eq!(seed.len(), length as usize, "Seed length should match requested length"); - - // Verify seed contains random values (not all zeros). - let unique_bytes: HashSet<_> = seed.iter().collect(); - assert!(unique_bytes.len() > 1, "Seed should contain random values"); - - println!("Successfully generated seed of length {}: {:?}", length, seed); - } - } - - // Test the mnemonic() function. - #[test] - fn test_mnemonic_generation() { - match mnemonic() { - Ok(phrase) => { - // Verify the mnemonic is not empty. - assert!(!phrase.is_empty(), "Mnemonic phrase should not be empty"); - - // Split into words and verify word count (should be 24 words for 32 bytes entropy). - let words: Vec<&str> = phrase.split_whitespace().collect(); - assert_eq!(words.len(), 24, "Mnemonic should contain 24 words"); - - // Verify all words are lowercase and contain only letters. - for word in &words { - assert!(word.chars().all(|c| c.is_ascii_lowercase()), - "Words should only contain lowercase letters"); - } - - println!("Successfully generated mnemonic phrase: {}", phrase); - }, - Err(e) => { - panic!("Failed to generate mnemonic: {:?}", e); - } - } - } - - // Test the _get_mnemonic FFI function. - #[test] - fn test_get_mnemonic_ffi() { - unsafe { - match _get_mnemonic() { - Ok(c_str_ptr) => { - // Convert C string pointer back to Rust string. - let c_str = CStr::from_ptr(c_str_ptr); - let phrase = c_str.to_str().expect("Invalid UTF-8 in mnemonic"); - - // Verify the mnemonic is valid. - assert!(!phrase.is_empty(), "Mnemonic phrase should not be empty"); - - let words: Vec<&str> = phrase.split_whitespace().collect(); - assert_eq!(words.len(), 24, "Mnemonic should contain 24 words"); - - println!("Successfully generated FFI mnemonic: {}", phrase); - - // Clean up the C string (since we're in a test). - let _ = CString::from_raw(c_str_ptr as *mut i8); - }, - Err(e) => { - panic!("Failed to generate FFI mnemonic: {:?}", e); - } - } - } - } - - // Test multiple mnemonic generations to ensure uniqueness. - #[test] - fn test_mnemonic_uniqueness() { - let mut phrases = HashSet::new(); - - // Generate multiple phrases and check that they're unique. - for i in 0..5 { - match mnemonic() { - Ok(phrase) => { - assert!(!phrases.contains(&phrase), - "Generated duplicate mnemonic on iteration {}", i); - phrases.insert(phrase.clone()); - println!("Generated unique mnemonic {}: {}", i + 1, phrase); - }, - Err(e) => { - panic!("Failed to generate mnemonic on iteration {}: {:?}", i, e); - } - } - } - } - - // Test that generated mnemonics can be parsed back into valid seeds. - #[test] - fn test_mnemonic_reversibility() { - use stack_epic_keychain::mnemonic::to_entropy; - - match mnemonic() { - Ok(phrase) => { - // Try to convert mnemonic back to entropy. - match to_entropy(&phrase) { - Ok(entropy) => { - assert_eq!(entropy.len(), 32, - "Entropy from mnemonic should be 32 bytes"); - println!("Successfully verified mnemonic reversibility for: {}", phrase); - }, - Err(e) => { - panic!("Failed to convert mnemonic back to entropy: {:?}", e); - } - } - }, - Err(e) => { - panic!("Failed to generate mnemonic: {:?}", e); - } - } - } -} diff --git a/rust/src/lib.rs b/rust/src/lib.rs index b55bcd0a..ea18ade6 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -1,32 +1,96 @@ -use stack_epic_wallet_libwallet::Error; -use stack_epic_wallet_controller::Error as EpicWalletControllerError; - -use crate::ffi::get_mnemonic; -use crate::ffi::wallet_init; -use crate::ffi::rust_open_wallet; -use crate::ffi::rust_wallet_balances; -// use crate::ffi::rust_wallet_scan_outputs; -// use crate::ffi::rust_create_tx; -// use crate::ffi::rust_txs_get; -// use crate::ffi::rust_tx_cancel; -// use crate::ffi::rust_get_chain_height; -// use crate::ffi::rust_epicbox_listener_start; -// use crate::ffi::_listener_cancel; -// use crate::ffi::rust_validate_address; -// use crate::ffi::rust_get_wallet_address; -// use crate::ffi::rust_get_tx_fees; -use crate::ffi::rust_delete_wallet; - +use std::cmp::Ordering; +use std::os::raw::{c_char}; +use std::ffi::{CString, CStr}; +use std::sync::Arc; +use std::path::{Path}; +use rand::thread_rng; +use serde::{Deserialize, Serialize}; +use rustc_serialize::json; +use uuid::Uuid; + +use stack_test_epic_wallet_api::{self, Foreign, ForeignCheckMiddlewareFn, Owner}; +use stack_test_epic_wallet_config::{WalletConfig}; +use stack_test_epic_wallet_libwallet::api_impl::types::{InitTxArgs, InitTxSendArgs}; +use stack_test_epic_wallet_libwallet::api_impl::owner; +use stack_test_epic_wallet_impls::{ + DefaultLCProvider, DefaultWalletImpl, HTTPNodeClient, +}; + +use ws::{ + CloseCode, Message, Error as WsError, ErrorKind as WsErrorKind, + Result as WSResult, Sender, Handler +}; + +use stack_test_epic_keychain::mnemonic; +use stack_test_epic_wallet_util::stack_test_epic_core::global::ChainTypes; +use stack_test_epic_util::file::get_first_line; +use stack_test_epic_wallet_util::stack_test_epic_util::ZeroingString; +use stack_test_epic_util::Mutex; +use stack_test_epic_wallet_libwallet::{address, scan, slate_versions, wallet_lock, NodeClient, NodeVersionInfo, Slate, WalletInst, WalletLCProvider, Error, ErrorKind, TxLogEntry, TxLogEntryType}; + +use stack_test_epic_wallet_util::stack_test_epic_keychain::{Keychain, ExtKeychain}; + +use stack_test_epic_util::secp::rand::Rng; + +use stack_test_epic_util::secp::key::{SecretKey, PublicKey}; +use stack_test_epic_util::secp::{Secp256k1}; + +use stack_test_epicboxlib::types::{EpicboxAddress, EpicboxMessage, TxProofErrorKind}; use android_logger::FilterBuilder; -pub mod config; -pub mod mnemonic; -pub mod wallet; -pub mod listener; +use std::env; +// mod main; + +#[derive(Serialize, Deserialize, Clone, RustcEncodable, Debug)] +pub struct Config { + pub wallet_dir: String, + pub check_node_api_http_addr: String, + pub chain: String, + pub account: Option, + pub api_listen_port: u16, + pub api_listen_interface: String +} + +#[derive(Clone)] +struct Client { + out: Sender, +} + +#[derive(Serialize, Deserialize, Clone, RustcEncodable, Debug)] +pub struct EpicBoxConfig { + domain: String, + port: u16 +} + +impl EpicBoxConfig { + fn from_str(json: &str) -> Result { + let result = match serde_json::from_str::(json) { + Ok(config) => { + config + }, Err(err) => { + return Err(err); + } + }; + Ok(result) + } +} + +type Wallet = Arc< + Mutex< + Box< + dyn WalletInst< + 'static, + DefaultLCProvider<'static, HTTPNodeClient, ExtKeychain>, + HTTPNodeClient, + ExtKeychain, + >, + >, + >, +>; -#[macro_export] macro_rules! ensure_wallet ( ($wallet_ptr:expr, $wallet:ident) => ( if ($wallet_ptr as *mut Wallet).as_mut().is_none() { + // let _ = $env.throw(serde_json::to_string(&format!("Wallet is NULL")).unwrap()); println!("{}", "WALLET_IS_NOT_OPEN"); } let $wallet = ($wallet_ptr as *mut Wallet).as_mut().unwrap(); @@ -42,427 +106,2481 @@ fn init_logger() { ); } +impl Config { + fn from_str(json: &str) -> Result { + let result = match serde_json::from_str::(json) { + Ok(config) => { + config + }, Err(err) => { + return Err(err); + } + }; + Ok(result) + } +} + +static mut SLATES_VECTOR: Vec = Vec::new(); +impl Handler for Client { + + fn on_message(&mut self, msg: Message) -> WSResult<()> { + // Close the connection when we get a response from the server + + let msg = match msg { + Message::Text(s) => { s } + _ => { panic!() } + }; + let parsed: serde_json::Value = serde_json::from_str(&msg).expect("Can't parse to JSON"); + if parsed["type"] == "Slate" { + //Push into the vector + unsafe { + SLATES_VECTOR.push(msg); + } + } + self.out.close(CloseCode::Normal) + } +} + +/* + Create Wallet config +*/ +fn create_wallet_config(config: Config) -> Result { + let chain_type = match config.chain.as_ref() { + "mainnet" => ChainTypes::Mainnet, + "floonet" => ChainTypes::Floonet, + "usertesting" => ChainTypes::UserTesting, + "automatedtesting" => ChainTypes::AutomatedTesting, + _ => ChainTypes::Floonet, + }; + + let api_secret_path = config.wallet_dir.clone() + "/.api_secret"; + let api_listen_port = config.api_listen_port; + + Ok(WalletConfig { + chain_type: Some(chain_type), + api_listen_interface: config.api_listen_interface, + api_listen_port, + api_secret_path: None, + node_api_secret_path: if Path::new(&api_secret_path).exists() { + Some(api_secret_path) + } else { + None + }, + check_node_api_http_addr: config.check_node_api_http_addr, + data_file_dir: config.wallet_dir, + tls_certificate_file: None, + tls_certificate_key: None, + dark_background_color_scheme: Some(true), + keybase_notify_ttl: Some(1440), + no_commit_cache: Some(false), + owner_api_include_foreign: Some(false), + owner_api_listen_port: Some(WalletConfig::default_owner_api_listen_port()), + }) +} + #[macro_use] extern crate log; extern crate android_logger; extern crate simplelog; use log::Level; use android_logger::Config as AndroidConfig; +use stack_test_epicboxlib::utils::crypto::{Hex, sign_challenge}; + +/* + Create a new wallet +*/ + +#[no_mangle] +pub unsafe extern "C" fn wallet_init( + config: *const c_char, + mnemonic: *const c_char, + password: *const c_char, + name: *const c_char +) -> *const c_char { + + let result = match _wallet_init(config, mnemonic, password, name) { + Ok(created) => { + created + }, Err(e ) => { + let error_msg = format!("Error {}", &e.to_string()); + let error_msg_ptr = CString::new(error_msg).unwrap(); + let ptr = error_msg_ptr.as_ptr(); // Get a pointer to the underlaying memory for s + std::mem::forget(error_msg_ptr); + ptr + } + }; + result +} -pub mod ffi; - -#[cfg(test)] -mod tests { - use super::*; - use std::ffi::{CStr, CString}; - use std::os::raw::c_char; - use std::fs; - use std::path::PathBuf; - - use crate::mnemonic::mnemonic; - use crate::wallet::validate_address; - use crate::wallet::get_chain_height; - use crate::wallet::get_wallet_info; - use crate::wallet::convert_deci_to_nano; - use crate::wallet::nano_to_deci; - - /// Helper to convert a Rust string to a *const c_char. - unsafe fn str_to_cchar_ptr(s: &str) -> *const c_char { - let cstring = CString::new(s).expect("CString::new failed"); - let ptr = cstring.as_ptr(); - // We deliberately `forget` here because we're passing it into FFI. - std::mem::forget(cstring); - ptr - } - - /// A basic test that demonstrates creating a wallet & fetching balances WITHOUT a node refresh - /// (refresh=0), which is causing issues such that testing it properly hasn't been achieved yet. - #[test] - fn test_create_and_check_balances_no_refresh() { - println!("--- BEGIN test_create_and_check_balances_no_refresh ---"); - - // 1. Setup config - let test_dir = PathBuf::from("test_wallet_dir_no_refresh"); - let _ = fs::create_dir_all(&test_dir); - - let config_json = serde_json::json!({ - "wallet_dir": test_dir.to_str().unwrap(), - "check_node_api_http_addr": "http://epiccash.stackwallet.com:3413", - "chain": "floonet", - "account": "default", - "api_listen_port": 3415, - "api_listen_interface": "epiccash.stackwallet.com", - }) - .to_string(); - - println!("Config JSON for no-refresh test:\n{config_json}"); - - // 2. Convert config & basic FFI pointers. - let config_ptr = unsafe { str_to_cchar_ptr(&config_json) }; - let password_ptr = unsafe { str_to_cchar_ptr("password123") }; - let wallet_name_ptr = unsafe { str_to_cchar_ptr("no_refresh_wallet") }; - - // 3. Generate a new mnemonic. - let c_mnemonic_ptr = unsafe { get_mnemonic() }; - let c_mnemonic_str = unsafe { CStr::from_ptr(c_mnemonic_ptr) } - .to_str() - .expect("Invalid mnemonic UTF-8"); - println!("(no-refresh) Generated Mnemonic: {c_mnemonic_str}"); - - // 4. Create a new wallet. - let mnemonic_ptr = unsafe { str_to_cchar_ptr(c_mnemonic_str) }; - let creation_result_ptr = unsafe { - wallet_init(config_ptr, mnemonic_ptr, password_ptr, wallet_name_ptr) - }; - let creation_result_str = unsafe { - CStr::from_ptr(creation_result_ptr).to_string_lossy().into_owned() - }; - if creation_result_str.is_empty() { - println!("(no-refresh) Wallet created successfully."); - } else { - println!("(no-refresh) Wallet creation error: {creation_result_str}"); +#[no_mangle] +pub unsafe extern "C" fn get_mnemonic() -> *const c_char { + let result = match _get_mnemonic() { + Ok(phrase) => { + phrase + }, Err(e ) => { + let error_msg = format!("Error {}", &e.to_string()); + let error_msg_ptr = CString::new(error_msg).unwrap(); + let ptr = error_msg_ptr.as_ptr(); // Get a pointer to the underlaying memory for s + std::mem::forget(error_msg_ptr); + ptr } + }; + result +} - // 5. Open the wallet. - let open_result_ptr = unsafe { rust_open_wallet(config_ptr, password_ptr) }; - let open_result_str = unsafe { - CStr::from_ptr(open_result_ptr).to_string_lossy().into_owned() - }; - println!("(no-refresh) Result from opening wallet: {open_result_str}"); - - // 6. Check wallet balances with refresh=0. - let wallet_ptr_cstr = open_result_str.clone(); - let refresh_str_ptr = unsafe { str_to_cchar_ptr("0") }; // No refresh. - let min_confirmations_str_ptr = unsafe { str_to_cchar_ptr("10") }; - - let balances_ptr = unsafe { - rust_wallet_balances( - str_to_cchar_ptr(&wallet_ptr_cstr), - refresh_str_ptr, - min_confirmations_str_ptr, - ) - }; - let balances_str = unsafe { - CStr::from_ptr(balances_ptr).to_string_lossy().into_owned() - }; - println!("(no-refresh) Wallet Balances: {balances_str}"); - - // 7. Clean up: delete the wallet directory. - let delete_ptr = unsafe { - rust_delete_wallet( - str_to_cchar_ptr(&wallet_ptr_cstr), - config_ptr, - ) - }; - let delete_str = unsafe { - CStr::from_ptr(delete_ptr).to_string_lossy().into_owned() - }; - println!("(no-refresh) Delete wallet result: {delete_str}"); - // Remove the ephemeral wallet directory. - let _ = fs::remove_dir_all(&test_dir); +fn _get_mnemonic() -> Result<*const c_char, stack_test_epic_keychain::mnemonic::Error> { + let mut wallet_phrase = "".to_string(); + match mnemonic() { + Ok(phrase) => { + wallet_phrase.push_str(&phrase); + },Err(e) => { + return Err(e); + } + } + let s = CString::new(wallet_phrase).unwrap(); + let p = s.as_ptr(); // Get a pointer to the underlaying memory for s + std::mem::forget(s); // Give up the responsibility of cleaning up/freeing s + Ok(p) +} - println!("--- END test_create_and_check_balances_no_refresh ---"); +fn _wallet_init( + config: *const c_char, + mnemonic: *const c_char, + password: *const c_char, + name: *const c_char +) -> Result<*const c_char, Error> { + + let config = unsafe { CStr::from_ptr(config) }; + let mnemonic = unsafe { CStr::from_ptr(mnemonic) }; + let password = unsafe { CStr::from_ptr(password) }; + let name = unsafe { CStr::from_ptr(name) }; + + let str_password = match password.to_str() { + Ok(str_pass) => {str_pass}, Err(e) => {return Err( + Error::from(ErrorKind::GenericError(format!("{}", e.to_string()))) + )} + }; + + let str_config = match config.to_str() { + Ok(str_conf) => {str_conf}, Err(e) => {return Err( + Error::from(ErrorKind::GenericError(format!("{}", e.to_string()))) + )} + }; + + let phrase = match mnemonic.to_str() { + Ok(str_phrase) => {str_phrase}, Err(e) => {return Err( + Error::from(ErrorKind::GenericError(format!("{}", e.to_string()))) + )} + }; + + let str_name = match name.to_str() { + Ok(str_name) => {str_name}, Err(e) => {return Err( + Error::from(ErrorKind::GenericError(format!("{}", e.to_string()))) + )} + }; + + let mut create_msg = "".to_string(); + match create_wallet(str_config, phrase, str_password, str_name) { + Ok(_) => { + create_msg.push_str(""); + },Err(e) => { + return Err(e); + } } + let s = CString::new(create_msg).unwrap(); + let p = s.as_ptr(); // Get a pointer to the underlaying memory for s + std::mem::forget(s); // Give up the responsibility of cleaning up/freeing s + Ok(p) +} - /// A basic test that demonstrates creating a wallet & fetching balances WITH a node refresh - #[test] - fn test_wallet_init_and_open_minimal() { - use std::fs; - use std::path::PathBuf; - use std::ffi::{CString, CStr}; - use std::os::raw::c_char; - println!("--- BEGIN test_wallet_init_and_open_minimal ---"); +#[no_mangle] +pub unsafe extern "C" fn rust_open_wallet( + config: *const c_char, + password: *const c_char, +) -> *const c_char { + let result = match _open_wallet( + config, + password + ) { + Ok(wallet) => { + wallet + }, Err(e ) => { + let error_msg = format!("Error {}", &e.to_string()); + let error_msg_ptr = CString::new(error_msg).unwrap(); + let ptr = error_msg_ptr.as_ptr(); // Get a pointer to the underlaying memory for s + std::mem::forget(error_msg_ptr); + ptr + } + }; + result +} - // 1. Setup wallet config. - let test_dir = PathBuf::from("test_wallet_init_open_minimal"); - let _ = fs::create_dir_all(&test_dir); +fn _open_wallet( + config: *const c_char, + password: *const c_char, +) -> Result<*const c_char, Error> { + let c_conf = unsafe { CStr::from_ptr(config) }; + let c_password = unsafe { CStr::from_ptr(password) }; + + let str_config = c_conf.to_str().unwrap(); + let str_password = c_password.to_str().unwrap(); + + let mut result = String::from(""); + match open_wallet(&str_config.clone(), str_password) { + Ok(res) => { + let wlt = res.0; + let sek_key = res.1; + let wallet_int = Box::into_raw(Box::new(wlt)) as i64; + let wallet_data = (wallet_int, sek_key); + let wallet_ptr = serde_json::to_string(&wallet_data).unwrap(); + result.push_str(&wallet_ptr); + } + Err(err) => { + return Err(err); + } + }; - let config_json = serde_json::json!({ - "wallet_dir": test_dir.to_str().unwrap(), - "check_node_api_http_addr": "http://epiccash.stackwallet.com:3413", - "chain": "floonet", - "account": "default", - "api_listen_port": 3415, - "api_listen_interface": "epiccash.stackwallet.com", - }) - .to_string(); + let s = CString::new(result).unwrap(); + let p = s.as_ptr(); // Get a pointer to the underlaying memory for s + std::mem::forget(s); // Give up the responsibility of cleaning up/freeing s + Ok(p) +} - // Helper to create a *const c_char from &str. - unsafe fn c_ptr(s: &str) -> *const c_char { - let cstring = CString::new(s).expect("CString::new failed"); - let ptr = cstring.as_ptr(); - std::mem::forget(cstring); + +/* + Get wallet info + This contains wallet balances +*/ +#[no_mangle] +pub unsafe extern "C" fn rust_wallet_balances( + wallet: *const c_char, + refresh: *const c_char, + min_confirmations: *const c_char, +) -> *const c_char { + let wallet_ptr = CStr::from_ptr(wallet); + let c_refresh = CStr::from_ptr(refresh); + let minimum_confirmations = CStr::from_ptr(min_confirmations); + let minimum_confirmations: u64 = minimum_confirmations.to_str().unwrap().to_string().parse().unwrap(); + + let refresh_from_node: u64 = c_refresh.to_str().unwrap().to_string().parse().unwrap(); + let refresh = match refresh_from_node { + 0 => false, + _=> true + }; + + let wallet_data = wallet_ptr.to_str().unwrap(); + let tuple_wallet_data: (i64, Option) = serde_json::from_str(wallet_data).unwrap(); + let wlt = tuple_wallet_data.0; + let sek_key = tuple_wallet_data.1; + + ensure_wallet!(wlt, wallet); + + let result = match _wallet_balances( + wallet, + sek_key, + refresh, + minimum_confirmations + ) { + Ok(balances) => { + balances + }, Err(e ) => { + let error_msg = format!("Error {}", &e.to_string()); + let error_msg_ptr = CString::new(error_msg).unwrap(); + let ptr = error_msg_ptr.as_ptr(); // Get a pointer to the underlaying memory for s + std::mem::forget(error_msg_ptr); ptr } + }; + result +} + +fn _wallet_balances( + wallet: &Wallet, + keychain_mask: Option, + refresh: bool, + min_confirmations: u64, +) -> Result<*const c_char, Error> { + let mut wallet_info = "".to_string(); + match get_wallet_info( + &wallet, + keychain_mask, + refresh, + min_confirmations + ) { + Ok(info) => { + let str_wallet_info = serde_json::to_string(&info).unwrap(); + wallet_info.push_str(&str_wallet_info); + },Err(e) => { + return Err(e); + } + } + let s = CString::new(wallet_info).unwrap(); + let p = s.as_ptr(); // Get a pointer to the underlaying memory for s + std::mem::forget(s); // Give up the responsibility of cleaning up/freeing s + Ok(p) +} - let config_ptr = unsafe { c_ptr(&config_json) }; - let password_ptr = unsafe { c_ptr("testpassword") }; - let name_ptr = unsafe { c_ptr("init_open_wallet") }; - // 2. Generate new mnemonic. - let mnemonic_ptr = unsafe { get_mnemonic() }; - let mnemonic_str = unsafe { CStr::from_ptr(mnemonic_ptr).to_string_lossy().into_owned() }; - println!("(init_open_minimal) Generated mnemonic: {mnemonic_str}"); - // 3. Create (init) the wallet with that mnemonic. - let creation_res_ptr = unsafe { - wallet_init(config_ptr, c_ptr(&mnemonic_str), password_ptr, name_ptr) - }; - let creation_res_str = unsafe { - CStr::from_ptr(creation_res_ptr).to_string_lossy().into_owned() - }; - if creation_res_str.is_empty() { - println!("(init_open_minimal) Wallet created successfully."); - } else { - println!("(init_open_minimal) Wallet creation error: {creation_res_str}"); +#[no_mangle] +pub unsafe extern "C" fn rust_recover_from_mnemonic( + config: *const c_char, + password: *const c_char, + mnemonic: *const c_char, + name: *const c_char +) -> *const c_char { + + let result = match _recover_from_mnemonic( + config, + password, + mnemonic, + name + ) { + Ok(recovered) => { + recovered + }, Err(e ) => { + let error_msg = format!("Error {}", &e.to_string()); + let error_msg_ptr = CString::new(error_msg).unwrap(); + let ptr = error_msg_ptr.as_ptr(); // Get a pointer to the underlaying memory for s + std::mem::forget(error_msg_ptr); + ptr } + }; + result +} - // 4. Open the wallet. - let open_res_ptr = unsafe { - rust_open_wallet(config_ptr, password_ptr) - }; - let open_res_str = unsafe { - CStr::from_ptr(open_res_ptr).to_string_lossy().into_owned() - }; - println!("(init_open_minimal) rust_open_wallet returned: {open_res_str}"); +fn _recover_from_mnemonic( + config: *const c_char, + password: *const c_char, + mnemonic: *const c_char, + name: *const c_char +) -> Result<*const c_char, Error> { + let c_conf = unsafe { CStr::from_ptr(config) }; + let c_password = unsafe { CStr::from_ptr(password) }; + let c_mnemonic = unsafe { CStr::from_ptr(mnemonic) }; + let c_name = unsafe { CStr::from_ptr(name) }; + + let input_conf = c_conf.to_str().unwrap(); + let str_password = c_password.to_str().unwrap(); + let wallet_config = match Config::from_str(&input_conf.to_string()) { + Ok(config) => { + config + }, Err(err) => { + return Err(Error::from(ErrorKind::GenericError(format!( + "Wallet config error : {}", + err.to_string() + )))) + } + }; + let phrase = c_mnemonic.to_str().unwrap(); + let name = c_name.to_str().unwrap(); + + let mut recover_response = "".to_string(); + match recover_from_mnemonic(phrase, str_password, &wallet_config, name) { + Ok(_)=> { + recover_response.push_str("recovered"); + }, + Err(e)=> { + return Err(e); + } + } + let s = CString::new(recover_response).unwrap(); + let p = s.as_ptr(); // Get a pointer to the underlaying memory for s + std::mem::forget(s); // Give up the responsibility of cleaning up/freeing s + Ok(p) +} - // 5. Clean up wallet data - let _ = fs::remove_dir_all(&test_dir); +#[no_mangle] +pub unsafe extern "C" fn rust_wallet_scan_outputs( + wallet: *const c_char, + start_height: *const c_char, + number_of_blocks: *const c_char, +) -> *const c_char { + let wallet_ptr = CStr::from_ptr(wallet); + let c_start_height = CStr::from_ptr(start_height); + let c_number_of_blocks = CStr::from_ptr(number_of_blocks); + let start_height: u64 = c_start_height.to_str().unwrap().to_string().parse().unwrap(); + let number_of_blocks: u64 = c_number_of_blocks.to_str().unwrap().to_string().parse().unwrap(); + + let wallet_data = wallet_ptr.to_str().unwrap(); + let tuple_wallet_data: (i64, Option) = serde_json::from_str(wallet_data).unwrap(); + let wlt = tuple_wallet_data.0; + let sek_key = tuple_wallet_data.1; + + ensure_wallet!(wlt, wallet); + + let result = match _wallet_scan_outputs( + wallet, + sek_key, + start_height, + number_of_blocks + ) { + Ok(scan) => { + scan + }, Err(e ) => { + let error_msg = format!("Error {}", &e.to_string()); + let error_msg_ptr = CString::new(error_msg).unwrap(); + let ptr = error_msg_ptr.as_ptr(); // Get a pointer to the underlaying memory for s + std::mem::forget(error_msg_ptr); + ptr + } + }; + result +} - println!("--- END test_wallet_init_and_open_minimal ---"); +fn _wallet_scan_outputs( + wallet: &Wallet, + keychain_mask: Option, + start_height: u64, + number_of_blocks: u64 +) -> Result<*const c_char, Error> { + let mut scan_result = String::from(""); + match wallet_scan_outputs( + &wallet, + keychain_mask, + Some(start_height), + Some(number_of_blocks) + ) { + Ok(scan) => { + scan_result.push_str(&scan); + }, + Err(err) => { + return Err(err); + }, } - #[test] - fn test_wallet_balances_no_refresh() { - use std::fs; - use std::path::PathBuf; - use std::ffi::{CString, CStr}; - use std::os::raw::c_char; + let s = CString::new(scan_result).unwrap(); + let p = s.as_ptr(); // Get a pointer to the underlaying memory for s + std::mem::forget(s); // Give up the responsibility of cleaning up/freeing s + Ok(p) +} - println!("--- BEGIN test_wallet_balances_no_refresh ---"); +#[no_mangle] +pub unsafe extern "C" fn rust_encrypt_slate( + wallet: *const c_char, + to_address: *const c_char, + secret_key_index: *const c_char, + epicbox_config: *const c_char, + slate: *const c_char, +) -> *const c_char { + + let wallet_ptr = CStr::from_ptr(wallet); + let c_address = CStr::from_ptr(to_address); + let key_index = CStr::from_ptr(secret_key_index); + let epicbox_config = CStr::from_ptr(epicbox_config); + let slate = CStr::from_ptr(slate); + + let address = c_address.to_str().unwrap(); + let key_index: u32 = key_index.to_str().unwrap().to_string().parse().unwrap(); + let epicbox_config = epicbox_config.to_str().unwrap(); + let slate = slate.to_str().unwrap(); + + let wallet_data = wallet_ptr.to_str().unwrap(); + let tuple_wallet_data: (i64, Option) = serde_json::from_str(wallet_data).unwrap(); + let wlt = tuple_wallet_data.0; + let sek_key = tuple_wallet_data.1; + + ensure_wallet!(wlt, wallet); + + let result = match _encrypt_slate( + &wallet, + sek_key, + address, + key_index, + epicbox_config, + slate + ) { + Ok(post_late_request) => { + post_late_request + }, Err(e ) => { + let error_msg = format!("Error {}", &e.to_string()); + let error_msg_ptr = CString::new(error_msg).unwrap(); + let ptr = error_msg_ptr.as_ptr(); // Get a pointer to the underlaying memory for s + std::mem::forget(error_msg_ptr); + ptr + } + }; + result +} - let test_dir = PathBuf::from("test_wallet_balances_no_refresh"); - let _ = fs::create_dir_all(&test_dir); +fn _encrypt_slate( + wallet: &Wallet, + keychain_mask: Option, + address: &str, + secret_key_index: u32, + epicbox_config: &str, + slate: &str, +) -> Result<*const c_char, Error>{ + let epicbox_conf = match EpicBoxConfig::from_str(&epicbox_config.to_string()) { + Ok(config) => { + config + }, Err(err) => { + return Err(Error::from(ErrorKind::GenericError(format!( + "epicbox config error {}", + err.to_string() + )))) + } + }; - let config_json = serde_json::json!({ - "wallet_dir": test_dir.to_str().unwrap(), - "check_node_api_http_addr": "http://epiccash.stackwallet.com:3413", - "chain": "floonet", - "account": "default", - "api_listen_port": 3415, - "api_listen_interface": "epiccash.stackwallet.com", - }) - .to_string(); + let key_pair = match get_wallet_secret_key_pair( + wallet, keychain_mask, secret_key_index + ) { + Ok(sec_pub_pair) => { + sec_pub_pair + } + Err(err) => { + return Err(err); + } + }; + let slate_msg = build_post_slate_request( + address, + key_pair, + slate.to_string(), + epicbox_conf); + + let s = CString::new(slate_msg).unwrap(); + let p = s.as_ptr(); // Get a pointer to the underlaying memory for s + std::mem::forget(s); // Give up the responsibility of cleaning up/freeing s + Ok(p) +} - unsafe fn c_ptr(s: &str) -> *const c_char { - let cstring = CString::new(s).expect("CString::new failed"); - let ptr = cstring.as_ptr(); - std::mem::forget(cstring); +#[no_mangle] +pub unsafe extern "C" fn rust_create_tx( + wallet: *const c_char, + amount: *const c_char, + to_address: *const c_char, + secret_key_index: *const c_char, + epicbox_config: *const c_char, + min_confirmations: *const c_char, +) -> *const c_char { + let wallet_ptr = CStr::from_ptr(wallet); + let minimum_confirmations = CStr::from_ptr(min_confirmations); + let minimum_confirmations: u64 = minimum_confirmations.to_str().unwrap().to_string().parse().unwrap(); + let amount = CStr::from_ptr(amount); + let c_address = CStr::from_ptr(to_address); + let key_index = CStr::from_ptr(secret_key_index); + let epicbox_config = CStr::from_ptr(epicbox_config); + + let amount: u64 = amount.to_str().unwrap().to_string().parse().unwrap(); + let address = c_address.to_str().unwrap(); + let key_index: u32 = key_index.to_str().unwrap().to_string().parse().unwrap(); + let epicbox_config = epicbox_config.to_str().unwrap(); + + let wallet_data = wallet_ptr.to_str().unwrap(); + let tuple_wallet_data: (i64, Option) = serde_json::from_str(wallet_data).unwrap(); + let wlt = tuple_wallet_data.0; + let sek_key = tuple_wallet_data.1; + + ensure_wallet!(wlt, wallet); + + let result = match _create_tx( + wallet, + sek_key, + amount, + address, + key_index, + epicbox_config, + minimum_confirmations, + ) { + Ok(slate) => { + slate + }, Err(e ) => { + let error_msg = format!("Error {}", &e.to_string()); + let error_msg_ptr = CString::new(error_msg).unwrap(); + let ptr = error_msg_ptr.as_ptr(); // Get a pointer to the underlaying memory for s + std::mem::forget(error_msg_ptr); ptr } + }; + result - let config_ptr = unsafe { c_ptr(&config_json) }; - let password_ptr = unsafe { c_ptr("testpassword") }; +} - // 1. Create and open the wallet. - let mnemonic_ptr = unsafe { get_mnemonic() }; - let mnemonic_str = unsafe { CStr::from_ptr(mnemonic_ptr).to_string_lossy().into_owned() }; - println!("(balances_no_refresh) Generated mnemonic: {mnemonic_str}"); +fn _create_tx( + wallet: &Wallet, + keychain_mask: Option, + amount: u64, + address: &str, + secret_key_index: u32, + epicbox_config: &str, + minimum_confirmations: u64, +) -> Result<*const c_char, Error> { + let epicbox_conf = match EpicBoxConfig::from_str(&epicbox_config.to_string()) { + Ok(config) => { + config + }, Err(err) => { + return Err(Error::from(ErrorKind::GenericError(format!( + "EPICBOX_CONFIG_ERROR {}", + err.to_string() + )))) + } + }; + + let mut message = String::from(""); + match tx_create( + &wallet, + keychain_mask.clone(), + amount, + minimum_confirmations, + false) { + Ok(slate) => { + //Get Secret key at given Index, build epicbox request + let key_pair = get_wallet_secret_key_pair( + &wallet, keychain_mask, secret_key_index + ).unwrap(); + let slate_msg = build_post_slate_request( + address, + key_pair, + slate.clone(), + epicbox_conf.clone()); + + let create_response = (&slate, &slate_msg); + let str_create_response = serde_json::to_string(&create_response).unwrap(); + message.push_str(&str_create_response); + }, + Err(e) => { + message.push_str(&e.to_string()); + return Err(e); + } + } - let creation_res_ptr = unsafe { - wallet_init(config_ptr, c_ptr(&mnemonic_str), password_ptr, c_ptr("test_balances_wallet")) - }; - let creation_res_str = unsafe { - CStr::from_ptr(creation_res_ptr).to_string_lossy().into_owned() - }; - println!("(balances_no_refresh) wallet_init result: {creation_res_str}"); + let s = CString::new(message).unwrap(); + let p = s.as_ptr(); // Get a pointer to the underlaying memory for s + std::mem::forget(s); // Give up the responsibility of cleaning up/freeing s + Ok(p) - let open_res_ptr = unsafe { rust_open_wallet(config_ptr, password_ptr) }; - let open_res_str = unsafe { CStr::from_ptr(open_res_ptr).to_string_lossy().into_owned() }; - println!("(balances_no_refresh) rust_open_wallet: {open_res_str}"); - // 2. Check balances, no refresh. - let min_conf_ptr = unsafe { c_ptr("10") }; - let refresh_ptr = unsafe { c_ptr("0") }; // "0" => no refresh +} - let balances_ptr = unsafe { - rust_wallet_balances(c_ptr(&open_res_str), refresh_ptr, min_conf_ptr) - }; - let balances_str = unsafe { - CStr::from_ptr(balances_ptr).to_string_lossy().into_owned() - }; - println!("(balances_no_refresh) wallet balances: {balances_str}"); +#[no_mangle] +pub unsafe extern "C" fn rust_txs_get( + wallet: *const c_char, + refresh_from_node: *const c_char, +) -> *const c_char { + let c_wallet = CStr::from_ptr(wallet); + let c_refresh_from_node = CStr::from_ptr(refresh_from_node); + let refresh_from_node: u64 = c_refresh_from_node.to_str().unwrap().to_string().parse().unwrap(); + let refresh = match refresh_from_node { + 0 => false, + _=> true + }; + + let wallet_data = c_wallet.to_str().unwrap(); + let tuple_wallet_data: (i64, Option) = serde_json::from_str(wallet_data).unwrap(); + let wlt = tuple_wallet_data.0; + let sek_key = tuple_wallet_data.1; + + ensure_wallet!(wlt, wallet); + + let result = match _txs_get( + wallet, + sek_key, + refresh, + ) { + Ok(txs) => { + txs + }, Err(e ) => { + let error_msg = format!("Error {}", &e.to_string()); + let error_msg_ptr = CString::new(error_msg).unwrap(); + let ptr = error_msg_ptr.as_ptr(); // Get a pointer to the underlaying memory for s + std::mem::forget(error_msg_ptr); + ptr + } + }; + result +} + +fn _txs_get( + wallet: &Wallet, + keychain_mask: Option, + refresh_from_node: bool, +) -> Result<*const c_char, Error> { + let mut txs_result = "".to_string(); + match txs_get( + wallet, + keychain_mask, + refresh_from_node + ) { + Ok(txs) => { + txs_result.push_str(&txs); + }, + Err(err) => { + return Err(err); + }, + } + + let s = CString::new(txs_result).unwrap(); + let p = s.as_ptr(); // Get a pointer to the underlaying memory for s + std::mem::forget(s); // Give up the responsibility of cleaning up/freeing s + Ok(p) +} + +#[no_mangle] +pub unsafe extern "C" fn rust_tx_cancel( + wallet: *const c_char, + tx_id: *const c_char, +) -> *const c_char { + + let wallet_ptr = CStr::from_ptr(wallet); + let tx_id = CStr::from_ptr(tx_id); + let tx_id = tx_id.to_str().unwrap(); + let uuid = Uuid::parse_str(tx_id).map_err(|e| ErrorKind::GenericError(e.to_string())).unwrap(); + + let wallet_data = wallet_ptr.to_str().unwrap(); + let tuple_wallet_data: (i64, Option) = serde_json::from_str(wallet_data).unwrap(); + let wlt = tuple_wallet_data.0; + let sek_key = tuple_wallet_data.1; + + ensure_wallet!(wlt, wallet); + + let result = match _tx_cancel( + wallet, + sek_key, + uuid, + ) { + Ok(cancelled) => { + cancelled + }, Err(e ) => { + let error_msg = format!("Error {}", &e.to_string()); + let error_msg_ptr = CString::new(error_msg).unwrap(); + let ptr = error_msg_ptr.as_ptr(); // Get a pointer to the underlaying memory for s + std::mem::forget(error_msg_ptr); + ptr + } + }; + result +} + +fn _tx_cancel( + wallet: &Wallet, + keychain_mask: Option, + tx_id: Uuid, +) -> Result<*const c_char, Error>{ + let mut cancel_msg = "".to_string(); + match tx_cancel(wallet, keychain_mask, tx_id) { + Ok(_) => { + cancel_msg.push_str(""); + },Err(err) => { + return Err(err); + } + } + let s = CString::new(cancel_msg).unwrap(); + let p = s.as_ptr(); // Get a pointer to the underlaying memory for s + std::mem::forget(s); // Give up the responsibility of cleaning up/freeing s + Ok(p) +} + +#[no_mangle] +pub unsafe extern "C" fn rust_decrypt_unprocessed_slates( + wallet: *const c_char, + secret_key_index: *const c_char, + slate: *const c_char, +) -> *const c_char { + let wallet_ptr = CStr::from_ptr(wallet); + let key_index = CStr::from_ptr(secret_key_index); + let slate = CStr::from_ptr(slate); + + let key_index: u32 = key_index.to_str().unwrap().to_string().parse().unwrap(); + let slate = slate.to_str().unwrap(); + let wallet_data = wallet_ptr.to_str().unwrap(); + let tuple_wallet_data: (i64, Option) = serde_json::from_str(wallet_data).unwrap(); + let wlt = tuple_wallet_data.0; + let sek_key = tuple_wallet_data.1; + + ensure_wallet!(wlt, wallet); + + let result = match _decrypt_unprocessed_slates( + wallet, + sek_key, + key_index, + slate, + ) { + Ok(pending_slates) => { + pending_slates + }, Err(e ) => { + let error_msg = format!("Error {}", &e.to_string()); + let error_msg_ptr = CString::new(error_msg).unwrap(); + let ptr = error_msg_ptr.as_ptr(); // Get a pointer to the underlaying memory for s + std::mem::forget(error_msg_ptr); + ptr + } + }; + result +} - // 3. Clean up. - let _ = fs::remove_dir_all(&test_dir); +fn _decrypt_unprocessed_slates( + wallet: &Wallet, + keychain_mask: Option, + secret_key_index: u32, + slates: &str +) -> Result<*const c_char, Error> { + + let key_pair = get_wallet_secret_key_pair( + wallet, keychain_mask, secret_key_index + ).unwrap(); + let mut pending_slates = "".to_string(); + let slates_to_lower = slates.to_lowercase(); + if slates_to_lower.contains("error") || slates_to_lower.is_empty() { + return Err(Error::from(ErrorKind::GenericError(format!( + "{}", + "Unable to format slates, please check data" + )))); + } + match decrypt_epicbox_slates(key_pair, &slates) { + Ok(decrypted) => { + let str_slates = serde_json::to_string(&decrypted).unwrap(); + pending_slates.push_str(&str_slates); + }, Err(e) => { + return Err(e); + } + }; + let s = CString::new(pending_slates).unwrap(); + let p = s.as_ptr(); // Get a pointer to the underlaying memory for s + std::mem::forget(s); // Give up the responsibility of cleaning up/freeing s + Ok(p) +} - println!("--- END test_wallet_balances_no_refresh ---"); + +#[no_mangle] +pub unsafe extern "C" fn rust_process_pending_slates( + wallet: *const c_char, + slates: *const c_char, +) -> *const c_char { + let wallet_ptr = CStr::from_ptr(wallet); + let slates = CStr::from_ptr(slates); + let pending_slates = slates.to_str().unwrap(); + + let wallet_data = wallet_ptr.to_str().unwrap(); + let tuple_wallet_data: (i64, Option) = serde_json::from_str(wallet_data).unwrap(); + let wlt = tuple_wallet_data.0; + let sek_key = tuple_wallet_data.1; + + ensure_wallet!(wlt, wallet); + + let result = match _process_pending_slates( + wallet, + sek_key, + pending_slates + ) { + Ok(processed_slates) => { + processed_slates + }, Err(e ) => { + let error_msg = format!("Error {}", &e.to_string()); + let error_msg_ptr = CString::new(error_msg).unwrap(); + let ptr = error_msg_ptr.as_ptr(); // Get a pointer to the underlaying memory for s + std::mem::forget(error_msg_ptr); + ptr + } + }; + result +} + +fn _process_pending_slates( + wallet: &Wallet, + keychain_mask: Option, + slates: &str +) -> Result<*const c_char, Error> { + + let mut processed_slates = "".to_string(); + match process_received_slates( + wallet, + keychain_mask, + slates + ) { + Ok(slates) => { + processed_slates.push_str(&slates); + }, Err(e) => { + return Err(e); + } } + let s = CString::new(processed_slates).unwrap(); + let p = s.as_ptr(); // Get a pointer to the underlaying memory for s + std::mem::forget(s); // Give up the responsibility of cleaning up/freeing s + Ok(p) +} - use serde_json::json; +#[no_mangle] +pub unsafe extern "C" fn rust_get_chain_height( + config: *const c_char, +) -> *const c_char { + let result = match _get_chain_height( + config + ) { + Ok(chain_height) => { + chain_height + }, Err(e ) => { + let error_msg = format!("Error {}", &e.to_string()); + let error_msg_ptr = CString::new(error_msg).unwrap(); + let ptr = error_msg_ptr.as_ptr(); // Get a pointer to the underlaying memory for s + std::mem::forget(error_msg_ptr); + ptr + } + }; + result +} - /// A helper function to convert a Rust string to a *const c_char. - unsafe fn str_to_cchar(s: &str) -> *const c_char { - let cstring = CString::new(s).expect("CString::new failed"); - let ptr = cstring.as_ptr(); - std::mem::forget(cstring); - ptr +fn _get_chain_height(config: *const c_char) -> Result<*const c_char, Error> { + debug!("{}", "GETTING_CHAIN_HEIGHT"); + let c_config = unsafe { CStr::from_ptr(config) }; + let str_config = c_config.to_str().unwrap(); + let mut chain_height = "".to_string(); + match get_chain_height(&str_config) { + Ok(chain_tip) => { + debug!("CHAIN_HEIGHT {}", chain_tip); + chain_height.push_str(&chain_tip.to_string()); + }, + Err(e) => { + debug!("CHAIN_HEIGHT_ERROR {}", e.to_string()); + return Err(e); + }, } + let s = CString::new(chain_height).unwrap(); + let p = s.as_ptr(); // Get a pointer to the underlaying memory for s + std::mem::forget(s); // Give up the responsibility of cleaning up/freeing s + Ok(p) +} - /// A helper function to setup a test directory. - fn setup_test_dir(name: &str) -> PathBuf { - let dir = PathBuf::from(format!("test_wallet_dir_{}", name)); - let _ = fs::create_dir_all(&dir); - dir +#[no_mangle] +pub unsafe extern "C" fn rust_delete_wallet( + wallet: *const c_char, +) -> *const c_char { + let wallet_ptr = CStr::from_ptr(wallet); + let wallet_data = wallet_ptr.to_str().unwrap(); + let tuple_wallet_data: (i64, Option) = serde_json::from_str(wallet_data).unwrap(); + let wlt = tuple_wallet_data.0; + let sek_key = tuple_wallet_data.1; + ensure_wallet!(wlt, wallet); + let result = match _delete_wallet( + wallet + ) { + Ok(deleted) => { + deleted + }, Err(err ) => { + let error_msg = format!("Error {}", &err.to_string()); + let error_msg_ptr = CString::new(error_msg).unwrap(); + let ptr = error_msg_ptr.as_ptr(); // Get a pointer to the underlaying memory for s + std::mem::forget(error_msg_ptr); + ptr + } + }; + result +} + +fn _delete_wallet( + wallet: &Wallet, +) -> Result<*const c_char, Error> { + + let mut delete_result = String::from(""); + match delete_wallet(wallet) { + Ok(deleted) => { + delete_result.push_str(&deleted); + }, + Err(err) => { + return Err(err); + }, } + let s = CString::new(delete_result).unwrap(); + let p = s.as_ptr(); // Get a pointer to the underlaying memory for s + std::mem::forget(s); // Give up the responsibility of cleaning up/freeing s + Ok(p) + +} + +#[no_mangle] +pub unsafe extern "C" fn rust_tx_send_http( + wallet: *const c_char, + selection_strategy_is_use_all: *const c_char, + minimum_confirmations: *const c_char, + message: *const c_char, + amount: *const c_char, + address: *const c_char, +) -> *const c_char { + let c_wallet = CStr::from_ptr(wallet); + let c_strategy_is_use_all = CStr::from_ptr(selection_strategy_is_use_all); + let strategy_is_use_all: u64 = c_strategy_is_use_all.to_str().unwrap().to_string().parse().unwrap(); + let strategy_use_all = match strategy_is_use_all { + 0 => false, + _=> true + }; + let c_minimum_confirmations = CStr::from_ptr(minimum_confirmations); + let minimum_confirmations: u64 = c_minimum_confirmations.to_str().unwrap().to_string().parse().unwrap(); + let c_message = CStr::from_ptr(message); + let str_message = c_message.to_str().unwrap(); + let c_amount = CStr::from_ptr(amount); + let amount: u64 = c_amount.to_str().unwrap().to_string().parse().unwrap(); + let c_address = CStr::from_ptr(address); + let str_address = c_address.to_str().unwrap(); + + let wallet_data = c_wallet.to_str().unwrap(); + let tuple_wallet_data: (i64, Option) = serde_json::from_str(wallet_data).unwrap(); + let wlt = tuple_wallet_data.0; + let sek_key = tuple_wallet_data.1; + ensure_wallet!(wlt, wallet); + + let result = match _tx_send_http( + wallet, + sek_key, + strategy_use_all, + minimum_confirmations, + str_message, + amount, + str_address + ) { + Ok(tx_data) => { + tx_data + }, Err(err ) => { + let error_msg = format!("Error {}", &err.to_string()); + let error_msg_ptr = CString::new(error_msg).unwrap(); + let ptr = error_msg_ptr.as_ptr(); // Get a pointer to the underlaying memory for s + std::mem::forget(error_msg_ptr); + ptr + } + }; + result +} - /// A helper function to cleanup a test directory. - fn cleanup_test_dir(dir: &PathBuf) { - let _ = fs::remove_dir_all(dir); +fn _tx_send_http( + wallet: &Wallet, + keychain_mask: Option, + selection_strategy_is_use_all: bool, + minimum_confirmations: u64, + message: &str, + amount: u64, + address: &str +) -> Result<*const c_char, Error> { + let mut send_result = String::from(""); + match tx_send_http( + wallet, + keychain_mask, + selection_strategy_is_use_all, + minimum_confirmations, + message, + amount, + address + ) { + Ok(sent) => { + let empty_json = format!(r#"{{"slate_msg": ""}}"#); + let create_response = (&sent, &empty_json); + let str_create_response = serde_json::to_string(&create_response).unwrap(); + send_result.push_str(&str_create_response); + }, + Err(err) => { + return Err(err); + }, } + let s = CString::new(send_result).unwrap(); + let p = s.as_ptr(); // Get a pointer to the underlaying memory for s + std::mem::forget(s); // Give up the responsibility of cleaning up/freeing s + Ok(p) +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct EpicboxInfo { + pub address: String, + pub public_key: String, + pub secret_key: String, +} + +#[no_mangle] +pub unsafe extern "C" fn rust_get_wallet_address( + wallet: *const c_char, + index: *const c_char, + epicbox_config: *const c_char, +) -> *const c_char { + let wallet_ptr = CStr::from_ptr(wallet); + let index = CStr::from_ptr(index); + let epicbox_config = CStr::from_ptr(epicbox_config); + let epicbox_config = epicbox_config.to_str().unwrap(); + let index: u32 = index.to_str().unwrap().to_string().parse().unwrap(); + + let wallet_data = wallet_ptr.to_str().unwrap(); + let tuple_wallet_data: (i64, Option) = serde_json::from_str(wallet_data).unwrap(); + let wlt = tuple_wallet_data.0; + let sek_key = tuple_wallet_data.1; + + ensure_wallet!(wlt, wallet); + let result = match _get_wallet_address( + wallet, + sek_key, + index, + epicbox_config + ) { + Ok(address) => { + address + }, Err(e ) => { + let error_msg = format!("Error {}", &e.to_string()); + let error_msg_ptr = CString::new(error_msg).unwrap(); + let ptr = error_msg_ptr.as_ptr(); // Get a pointer to the underlaying memory for s + std::mem::forget(error_msg_ptr); + ptr + } + }; + result +} + +fn _get_wallet_address( + wallet: &Wallet, + keychain_mask: Option, + index: u32, + epicbox_config: &str +) -> Result<*const c_char, Error> { + let epicbox_conf = match EpicBoxConfig::from_str(&epicbox_config.to_string()) { + Ok(config) => { + config + }, Err(e) => { + return Err(Error::from(ErrorKind::GenericError(format!( + "{}", + "Unable to get epicbox config" + )))) + } + }; + + let key_pair = get_wallet_secret_key_pair(wallet, keychain_mask, index).unwrap(); + let wallet_address = get_epicbox_address(key_pair.1, &epicbox_conf.domain, Some(epicbox_conf.port)).public_key; + let s = CString::new(wallet_address).unwrap(); + let p = s.as_ptr(); // Get a pointer to the underlaying memory for s + std::mem::forget(s); // Give up the responsibility of cleaning up/freeing s + Ok(p) +} + +#[no_mangle] +pub unsafe extern "C" fn rust_validate_address( + address: *const c_char, +) -> *const c_char { + let address = unsafe { CStr::from_ptr(address) }; + let str_address = address.to_str().unwrap(); + let validate = validate_address(str_address); + let return_value = match validate { + true => 1, + false => 0 + }; + + let s = CString::new(return_value.to_string()).unwrap(); + let p = s.as_ptr(); // Get a pointer to the underlaying memory for s + std::mem::forget(s); // Give up the responsibility of cleaning up/freeing s + p +} - /// A helper function to create a test wallet configuration. - fn create_test_config(dir: &PathBuf) -> String { - json!({ - "wallet_dir": dir.to_str().unwrap(), - "check_node_api_http_addr": "http://epiccash.stackwallet.com:3413", - "chain": "floonet", - "account": "default", - "api_listen_port": 3415, - "api_listen_interface": "epiccash.stackwallet.com", - }).to_string() +#[no_mangle] +pub unsafe extern "C" fn rust_get_tx_fees( + wallet: *const c_char, + c_amount: *const c_char, + min_confirmations: *const c_char, +) -> *const c_char { + + let minimum_confirmations = CStr::from_ptr(min_confirmations); + let minimum_confirmations: u64 = minimum_confirmations.to_str().unwrap().to_string().parse().unwrap(); + let wallet_ptr = CStr::from_ptr(wallet); + + let amount = CStr::from_ptr(c_amount); + let amount: u64 = amount.to_str().unwrap().to_string().parse().unwrap(); + + let wallet_data = wallet_ptr.to_str().unwrap(); + let tuple_wallet_data: (i64, Option) = serde_json::from_str(wallet_data).unwrap(); + let wlt = tuple_wallet_data.0; + let sek_key = tuple_wallet_data.1; + + ensure_wallet!(wlt, wallet); + + let result = match _get_tx_fees( + &wallet, + sek_key, + amount, + minimum_confirmations, + ) { + Ok(fees) => { + fees + }, Err(e ) => { + let error_msg = format!("Error {}", &e.to_string()); + let error_msg_ptr = CString::new(error_msg).unwrap(); + let ptr = error_msg_ptr.as_ptr(); // Get a pointer to the underlaying memory for s + std::mem::forget(error_msg_ptr); + ptr + } + }; + result +} + +fn _get_tx_fees( + wallet: &Wallet, + keychain_mask: Option, + amount: u64, + minimum_confirmations: u64, +) -> Result<*const c_char, Error> { + let mut fees_data = "".to_string(); + match tx_strategies(wallet, keychain_mask, amount, minimum_confirmations) { + Ok(fees) => { + fees_data.push_str(&fees); + }, Err(e) => { + return Err(e); + } } + let s = CString::new(fees_data).unwrap(); + let p = s.as_ptr(); // Get a pointer to the underlaying memory for s + std::mem::forget(s); // Give up the responsibility of cleaning up/freeing s + Ok(p) +} + +#[no_mangle] +pub unsafe extern "C" fn rust_post_slate_to_node( + wallet: *const c_char, + tx_slate_id: *const c_char, +) -> *const c_char { + let wallet_ptr = CStr::from_ptr(wallet); + let tx_slate_id = CStr::from_ptr(tx_slate_id); + let tx_slate_id = tx_slate_id.to_str().unwrap(); + + let wallet_data = wallet_ptr.to_str().unwrap(); + let tuple_wallet_data: (i64, Option) = serde_json::from_str(wallet_data).unwrap(); + let wlt = tuple_wallet_data.0; + let sek_key = tuple_wallet_data.1; + + ensure_wallet!(wlt, wallet); + + let result = match _post_slate_to_node( + wallet, + sek_key, + tx_slate_id + ) { + Ok(posted) => { + posted + }, Err(e ) => { + let error_msg = format!("Error {}", &e.to_string()); + let error_msg_ptr = CString::new(error_msg).unwrap(); + let ptr = error_msg_ptr.as_ptr(); // Get a pointer to the underlaying memory for s + std::mem::forget(error_msg_ptr); + ptr + } + }; + result +} + +pub fn create_wallet(config: &str, phrase: &str, password: &str, name: &str) -> Result { + let wallet_pass = ZeroingString::from(password); + let wallet_config = match Config::from_str(&config) { + Ok(config) => { + config + }, Err(e) => { + return Err(Error::from(ErrorKind::GenericError(format!( + "Error getting wallet config: {}", + e.to_string() + )))); + } + }; + + let wallet = match get_wallet(&wallet_config) { + Ok(wllet) => { + wllet + } + Err(e) => { + return Err(e); + } + }; + let mut wallet_lock = wallet.lock(); + let lc = match wallet_lock.lc_provider() { + Ok(wallet_lc) => { + wallet_lc + } + Err(e) => { + return Err(e); + } + }; + let rec_phrase = ZeroingString::from(phrase.clone()); + let result = match lc.create_wallet( + Some(name), + Some(rec_phrase), + 32, + wallet_pass, + false, + ) { + Ok(_) => { + "".to_string() + }, + Err(e) => { + e.to_string() + }, + }; + Ok(result) +} + +pub fn get_wallet_secret_key_pair( + wallet: &Wallet, keychain_mask: Option, index: u32 +) -> Result<(SecretKey, PublicKey), Error>{ + let parent_key_id = { + wallet_lock!(wallet, w); + w.parent_key_id().clone() + }; + wallet_lock!(wallet, w); + + let k = match w.keychain(keychain_mask.as_ref()) { + Ok(keychain) => { + keychain + } + Err(err) => { + return Err(err); + } + }; + let s = Secp256k1::new(); + let sec_key = match address::address_from_derivation_path( + &k, &parent_key_id, index + ) { + Ok(s_key) => { + s_key + } + Err(err) => { + return Err(err); + } + }; + let pub_key = match PublicKey::from_secret_key(&s, &sec_key) { + Ok(p_key) => { + p_key + } + Err(err) => { + return Err(Error::from( + ErrorKind::GenericError( + format!("{}", err.to_string()) + ) + )); + } + }; - /// Test vectors for mnemonic generation. - #[test] - fn test_mnemonic_generation_with_vectors() { - println!("=== Mnemonic Generation Test Vectors ==="); + Ok((sec_key, pub_key)) +} - // Test multiple mnemonic generations. - for i in 1..=3 { - match mnemonic() { - Ok(phrase) => { - println!("Test Vector {i}:"); - println!("Generated Mnemonic: {}", phrase); +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct WalletInfoFormatted { + pub last_confirmed_height: u64, + pub minimum_confirmations: u64, + pub total: f64, + pub amount_awaiting_finalization: f64, + pub amount_awaiting_confirmation: f64, + pub amount_immature: f64, + pub amount_currently_spendable: f64, + pub amount_locked: f64, +} - // Get word count and characteristics. - let words: Vec<&str> = phrase.split_whitespace().collect(); - println!("Word Count: {}", words.len()); +/* + Get wallet info + This contains wallet balances +*/ +pub fn get_wallet_info( + wallet: &Wallet, + keychain_mask: Option, + refresh_from_node: bool, + min_confirmations: u64 +) -> Result { + let api = Owner::new(wallet.clone()); + + match api.retrieve_summary_info(keychain_mask.as_ref(), refresh_from_node, min_confirmations) { + Ok((_, wallet_summary)) => { + Ok(WalletInfoFormatted { + last_confirmed_height: wallet_summary.last_confirmed_height, + minimum_confirmations: wallet_summary.minimum_confirmations, + total: nano_to_deci(wallet_summary.total), + amount_awaiting_finalization: nano_to_deci(wallet_summary.amount_awaiting_finalization), + amount_awaiting_confirmation: nano_to_deci(wallet_summary.amount_awaiting_confirmation), + amount_immature: nano_to_deci(wallet_summary.amount_immature), + amount_currently_spendable: nano_to_deci(wallet_summary.amount_currently_spendable), + amount_locked: nano_to_deci(wallet_summary.amount_locked) + }) + }, Err(e) => { + return Err(e); + } + } - // Generate entropy from mnemonic. - use stack_epic_keychain::mnemonic::to_entropy; - match to_entropy(&phrase) { - Ok(entropy) => { - println!("Entropy (hex): {}", hex::encode(&entropy)); - println!("Entropy length: {} bytes", entropy.len()); - }, - Err(e) => println!("Entropy generation failed: {}", e), +} + +/* + Recover wallet from mnemonic +*/ +pub fn recover_from_mnemonic(mnemonic: &str, password: &str, config: &Config, name: &str) -> Result<(), Error> { + let wallet = match get_wallet(&config) { + Ok(conf) => { + conf + } + Err(e) => { + return Err(e); + } + }; + let mut w_lock = wallet.lock(); + let lc = match w_lock.lc_provider() { + Ok(wallet_lc) => { + wallet_lc + } + Err(e) => { + return Err(e); + } + }; + + //First check if wallet seed directory exists, if not create + if let Ok(exists_wallet_seed) = lc.wallet_exists(None) { + if exists_wallet_seed { + match lc.recover_from_mnemonic( + ZeroingString::from(mnemonic), ZeroingString::from(password) + ) { + Ok(_) => { + return Ok(()); + } + Err(e) => { + return Err(e); + } + } + } else { + match lc.create_wallet( + Some(&name), + Some(ZeroingString::from(mnemonic)), + 32, + ZeroingString::from(password), + false, + ) { + Ok(_) => { + return Ok(()); + } + Err(e) => { + return Err(e); + } + } + } + } + Ok(()) +} + +/* + Create a new wallet seed +*/ +pub fn mnemonic() -> Result { + let seed = create_seed(32); + match mnemonic::from_entropy(&seed) { + Ok(mnemonic_str) => { + Ok(mnemonic_str) + }, Err(e) => { + return Err(e); + } + } +} + +fn create_seed(seed_length: u64) -> Vec { + let mut seed: Vec = vec![]; + let mut rng = thread_rng(); + for _ in 0..seed_length { + seed.push(rng.gen()); + } + seed +} + +/* + Get wallet that will be used for calls to epic wallet +*/ +fn get_wallet(config: &Config) -> Result { + let wallet_config = match create_wallet_config(config.clone()) { + Ok(conf) => { + conf + } Err(e) => { + return Err(e); + } + }; + let node_api_secret = get_first_line(wallet_config.node_api_secret_path.clone()); + let node_client = HTTPNodeClient::new(&wallet_config.check_node_api_http_addr, node_api_secret); + let wallet = match inst_wallet::< + DefaultLCProvider, + HTTPNodeClient, + ExtKeychain, + >(wallet_config.clone(), node_client) { + Ok(wallet_inst) => { + wallet_inst + } + Err(e) => { + return Err(e); + } + }; + return Ok(wallet); +} +/* + New wallet instance +*/ +fn inst_wallet( + config: WalletConfig, + node_client: C, +) -> Result>>>, Error> + where + DefaultWalletImpl<'static, C>: WalletInst<'static, L, C, K>, + L: WalletLCProvider<'static, C, K>, + C: NodeClient + 'static, + K: Keychain + 'static, +{ + let mut wallet = Box::new(DefaultWalletImpl::<'static, C>::new(node_client.clone()).unwrap()) + as Box>; + let lc = match wallet.lc_provider() { + Ok(wallet_lc) => { + wallet_lc + } + Err(err) => { + return Err(err); + } + }; + match lc.set_top_level_directory(&config.data_file_dir) { + Ok(_) => { + () + } + Err(err) => { + return Err(err); + } + }; + Ok(Arc::new(Mutex::new(wallet))) +} + +pub fn get_chain_height(config: &str) -> Result { + let config = match Config::from_str(&config.to_string()) { + Ok(config) => { + config + }, Err(e) => { + return Err(Error::from(ErrorKind::GenericError(format!( + "{}", + "Unable to get wallet config" + )))) + } + }; + let wallet_config = match create_wallet_config(config.clone()) { + Ok(wallet_conf) => { + wallet_conf + } + Err(e) => { + return Err(e); + } + }; + let node_api_secret = get_first_line(wallet_config.node_api_secret_path.clone()); + let node_client = HTTPNodeClient::new(&wallet_config.check_node_api_http_addr, node_api_secret); + let chain_tip = match node_client.chain_height() { + Ok(tip) => { + tip + } + Err(err) => { + return Err(err); + } + }; + Ok(chain_tip.0) +} + + +/* + +*/ +pub fn wallet_scan_outputs( + wallet: &Wallet, + keychain_mask: Option, + start_height: Option, + number_of_blocks_to_scan: Option +) -> Result { + let tip = { + wallet_lock!(wallet, w); + match w.w2n_client().get_chain_tip() { + Ok(chain_tip) => { + chain_tip.0 + }, + Err(_e) => { + 0 + } + } + }; + + if tip == 0 { + return Err(Error::from(ErrorKind::GenericError(format!( + "{}", + "Unable to scan, could not determine chain height" + )))); + } + + let start_height: u64 = match start_height { + Some(h) => h, + None => 1, + }; + + let number_of_blocks_to_scan: u64 = match number_of_blocks_to_scan { + Some(h) => h, + None => 0, + }; + + let last_block = start_height + number_of_blocks_to_scan; + let end_height: u64 = match last_block.cmp(&tip) { + Ordering::Less => { + last_block + }, + Ordering::Greater => { + tip + }, + Ordering::Equal => { + last_block + } + }; + + match scan( + wallet.clone(), + keychain_mask.as_ref(), + false, + start_height, + end_height, + &None + ) { + Ok(info) => { + + + let parent_key_id = { + wallet_lock!(wallet, w); + w.parent_key_id().clone() + }; + + { + wallet_lock!(wallet, w); + let mut batch = match w.batch(keychain_mask.as_ref()) { + Ok(wallet_output_batch) => { + wallet_output_batch + } + Err(err) => { + return Err(err); } - println!("---"); + }; + match batch.save_last_confirmed_height(&parent_key_id, info.height) { + Ok(_) => { + () + } + Err(err) => { + return Err(err); + } + }; + match batch.commit() { + Ok(_) => { + () + } + Err(err) => { + return Err(err); + } + } + }; + + + let result = info.height; + Ok(serde_json::to_string(&result).unwrap()) + }, Err(e) => { + return Err(e); + } + } +} + + +#[derive(Serialize, Deserialize)] +struct Strategy { + selection_strategy_is_use_all: bool, + total: u64, + fee: u64, +} + +/* + Get transaction fees + all possible Coin/Output selection strategies. +*/ +pub fn tx_strategies( + wallet: &Wallet, + keychain_mask: Option, + amount: u64, + minimum_confirmations: u64, +) -> Result { + + let mut result = vec![]; + wallet_lock!(wallet, w); + + for selection_strategy_is_use_all in vec![false].into_iter() { + let args = InitTxArgs { + src_acct_name: None, + amount, + minimum_confirmations, + max_outputs: 500, + num_change_outputs: 1, + estimate_only: Some(true), + message: None, + ..Default::default() + }; + + match owner::init_send_tx(&mut **w, keychain_mask.as_ref(), args, true) { + Ok(slate) => { + result.push(Strategy { + selection_strategy_is_use_all, + total: slate.amount, + fee: slate.fee, + + }); + }, Err(e) => { + return Err(e); + } + } + } + Ok(serde_json::to_string(&result).unwrap()) +} + +pub fn txs_get( + wallet: &Wallet, + keychain_mask: Option, + refresh_from_node: bool, +) -> Result { + let api = Owner::new(wallet.clone()); + let txs = match api.retrieve_txs( + keychain_mask.as_ref(), + refresh_from_node, + None, + None + ) { + Ok((_, tx_entries)) => { + tx_entries + }, Err(e) => { + return Err(e); + } + }; + + let result = txs; + Ok(serde_json::to_string(&result).unwrap()) +} + +/* + Init tx as sender +*/ +pub fn tx_create( + wallet: &Wallet, + keychain_mask: Option, + amount: u64, + minimum_confirmations: u64, + selection_strategy_is_use_all: bool, +) -> Result { + let owner_api = Owner::new(wallet.clone()); + let accounts = match owner_api.accounts(keychain_mask.as_ref()) { + Ok(accounts_list) => { + accounts_list + }, Err(e) => { + return Err(e); + } + }; + let account = &accounts[0].label; + + let args = InitTxArgs { + src_acct_name: Some(account.clone()), + amount, + minimum_confirmations, + max_outputs: 500, + num_change_outputs: 1, + selection_strategy_is_use_all, + message: None, + ..Default::default() + }; + + match owner_api.init_send_tx(keychain_mask.as_ref(), args) { + Ok(slate)=> { + //Lock slate uptputs + match owner_api.tx_lock_outputs( + keychain_mask.as_ref(), + &slate, + 0 + ) { + Ok(_) => { + () + } + Err(err) => { + return Err(err); + } + }; + //Get transaction for the slate, we will use type to determing if we should finalize or receive tx + let txs = match owner_api.retrieve_txs( + keychain_mask.as_ref(), + false, + None, + Some(slate.id) + ) { + Ok(txs_result) => { + txs_result + }, Err(e) => { + return Err(e); + } + }; + let final_result = ( + serde_json::to_string(&txs.1).unwrap(), + serde_json::to_string(&slate).unwrap() + ); + let str_result = serde_json::to_string(&final_result).unwrap(); + Ok(str_result) + }, + Err(e)=> { + return Err(e); + } + } +} + +#[no_mangle] +pub unsafe extern "C" fn subscribe_request( + wallet: *const c_char, + secret_key_index: *const c_char, + epicbox_config: *const c_char, +) -> *const c_char { + let wallet_ptr = CStr::from_ptr(wallet); + let key_index = CStr::from_ptr(secret_key_index); + let epicbox_config = CStr::from_ptr(epicbox_config); + let epicbox_config = epicbox_config.to_str().unwrap(); + let key_index: u32 = key_index.to_str().unwrap().to_string().parse().unwrap(); + + let wallet_data = wallet_ptr.to_str().unwrap(); + let tuple_wallet_data: (i64, Option) = serde_json::from_str(wallet_data).unwrap(); + let wlt = tuple_wallet_data.0; + let sek_key = tuple_wallet_data.1; + + ensure_wallet!(wlt, wallet); + + + let result = match _subscribe_request( + wallet, + sek_key, + key_index, + epicbox_config, + ) { + Ok(subscribe_request) => { + subscribe_request + }, Err(e ) => { + let error_msg = format!("Error {}", &e.to_string()); + let error_msg_ptr = CString::new(error_msg).unwrap(); + let ptr = error_msg_ptr.as_ptr(); // Get a pointer to the underlaying memory for s + std::mem::forget(error_msg_ptr); + ptr + } + }; + result +} + +fn _subscribe_request( + wallet: &Wallet, + keychain_mask: Option, + secret_key_index: u32, + epicbox_config: &str, +) -> Result<*const c_char, Error> { + let key_pair = get_wallet_secret_key_pair( + wallet, keychain_mask, secret_key_index + ).unwrap(); + let epicbox_conf = match EpicBoxConfig::from_str(&epicbox_config.to_string()) { + Ok(config) => { + config + }, Err(e) => { + return Err(Error::from(ErrorKind::GenericError(format!( + "{}", + "Unable to get epicbox config" + )))) + } + }; + + let subscribe_request = _build_subscribe_request( + key_pair, + epicbox_conf.clone() + ); + let s = CString::new(subscribe_request).unwrap(); + let p = s.as_ptr(); // Get a pointer to the underlaying memory for s + std::mem::forget(s); // Give up the responsibility of cleaning up/freeing s + Ok(p) +} + +fn _post_slate_to_node( + wallet: &Wallet, + keychain_mask: Option, + tx_slate_id: &str, +) -> Result<*const c_char, Error> { + + let mut tx_post_message = String::from(""); + match tx_post(wallet, keychain_mask, tx_slate_id) { + Ok(posted) => { + tx_post_message.push_str(&posted); + }, Err(e) => { + tx_post_message.push_str(&e.to_string()); + } + } + let s = CString::new(tx_post_message).unwrap(); + let p = s.as_ptr(); // Get a pointer to the underlaying memory for s + std::mem::forget(s); // Give up the responsibility of cleaning up/freeing s + Ok(p) +} + + + +pub fn decrypt_epicbox_slates( + secret_pub_key_pair: (SecretKey, PublicKey), encrypted_slates: &str +) -> Result, Error>{ + let messages: Vec = serde_json::from_str(&encrypted_slates).unwrap(); + let mut decrypted_slates: Vec = Vec::new(); + for message in messages.into_iter() { + let parsed: serde_json::Value = serde_json::from_str(&message).expect("Can't parse to JSON"); + match decrypt_message(&secret_pub_key_pair.0, parsed.clone()) { + Ok(decrypted_msg) => { + let sender_address = parsed.get("from").unwrap().as_str().unwrap(); + let return_data = (decrypted_msg, sender_address); + + decrypted_slates.push(serde_json::to_string(&return_data).unwrap()); + }, Err(e) => { + let error_msg = format!("Error : {}", e.to_string()); + decrypted_slates.push(error_msg); + } + }; + } + Ok(decrypted_slates) + +} + +pub fn process_received_slates( + wallet: &Wallet, keychain_mask: Option, message: &str +) -> Result { + + let mut process_result = "".to_string(); + let process = process_epic_box_slate(&wallet, keychain_mask.clone(), &message); + match process { + Ok(slate) => { + let msg_tuple: (String, String) = serde_json::from_str(&message).unwrap(); + let transaction: Vec = serde_json::from_str(&msg_tuple.0).unwrap(); + + match transaction[0].tx_type { + TxLogEntryType::TxSent => { + //Push into receive array + let message_status = format!(r#"{{"status": "PendingProcessing"}}"#); + let return_data = (message_status, slate); + process_result.push_str(&serde_json::to_string(&return_data).unwrap()); }, - Err(e) => println!("Failed to generate mnemonic {}: {:?}", i, e), + TxLogEntryType::TxReceived => { + let message_status = format!(r#"{{"status": "Finalised"}}"#); + let return_data = (message_status, slate); + process_result.push_str(&serde_json::to_string(&return_data).unwrap()); + }, + _ => {} } + }, + Err(err) => { + return Err(err); + } + }; + Ok(process_result) +} + +/* + Cancel tx by id +*/ +pub fn tx_cancel(wallet: &Wallet, keychain_mask: Option, tx_slate_id: Uuid) -> Result { + let api = Owner::new(wallet.clone()); + match api.cancel_tx(keychain_mask.as_ref(), None, Some(tx_slate_id)) { + Ok(_) => { + Ok("cancelled".to_owned()) + },Err(e) => { + return Err(e); } } +} - /// Test vectors for wallet creation. - #[test] - fn test_wallet_creation_vectors() { - println!("=== Wallet Creation Test Vectors ==="); +/* + Get transaction by slate id +*/ +pub fn tx_get(wallet: &Wallet, refresh_from_node: bool, tx_slate_id: &str) -> Result { + let api = Owner::new(wallet.clone()); + let uuid = Uuid::parse_str(tx_slate_id).map_err(|e| ErrorKind::GenericError(e.to_string())).unwrap(); + let txs = api.retrieve_txs(None, refresh_from_node, None, Some(uuid)).unwrap(); + Ok(serde_json::to_string(&txs.1).unwrap()) +} - let test_dir = setup_test_dir("creation"); - let config_json = create_test_config(&test_dir); +/* + Check slate version +*/ +fn check_middleware( + name: ForeignCheckMiddlewareFn, + node_version_info: Option, + slate: Option<&Slate>, +) -> Result<(), Error> { + match name { + // allow coinbases to be built regardless + ForeignCheckMiddlewareFn::BuildCoinbase => Ok(()), + _ => { + let mut bhv = 3; + if let Some(n) = node_version_info { + bhv = n.block_header_version; + } + if let Some(s) = slate { + if bhv > 4 + && s.version_info.block_header_version + < slate_versions::EPIC_BLOCK_HEADER_VERSION + { + Err(ErrorKind::Compatibility( + "Incoming Slate is not compatible with this wallet. Please upgrade the node or use a different one." + .into(), + ))?; + } + } + Ok(()) + } + } +} - unsafe { - // Generate test vectors for wallet creation. - let config_ptr = str_to_cchar(&config_json); - let password_ptr = str_to_cchar("test_password"); - let name_ptr = str_to_cchar("test_wallet"); +pub fn tx_receive(wallet: &Wallet, keychain_mask: Option, account: &str, str_slate: &str) -> Result { + let slate = match Slate::deserialize_upgrade(str_slate) { + Ok(result) => { + result + } + Err(err) => { + return Err(err); + } + }; + let owner_api = Owner::new(wallet.clone()); + let foreign_api = Foreign::new( + wallet.clone(), + keychain_mask.clone(), + Some(check_middleware)); + + match foreign_api.receive_tx(&slate, Some(&account), None) { + Ok(slate)=> { + let txs = match owner_api.retrieve_txs( + keychain_mask.as_ref(), + false, + None, + Some(slate.id) + ) { + Ok(slate_txs) => { + slate_txs + }, Err(e) => { + return Err(e); + } + }; + + let final_result = ( + serde_json::to_string(&txs.1).unwrap(), + serde_json::to_string(&slate).unwrap() + ); + Ok(serde_json::to_string(&final_result).unwrap()) + }, + Err(e)=> { + return Err(e); + } + } +} - // Get a mnemonic. - let mnemonic_ptr = get_mnemonic(); - let mnemonic = CStr::from_ptr(mnemonic_ptr).to_str().unwrap(); - println!("Input Mnemonic: {}", mnemonic); +/* - // Create wallet. - let result_ptr = wallet_init( - config_ptr, - str_to_cchar(mnemonic), - password_ptr, - name_ptr +*/ +pub fn tx_finalize( + wallet: &Wallet, keychain_mask: Option, str_slate: &str +) -> Result { + let slate = match Slate::deserialize_upgrade(str_slate) { + Ok(result) => { + result + } + Err(err) => { + return Err(err); + } + }; + let owner_api = Owner::new(wallet.clone()); + let response = owner_api.finalize_tx(keychain_mask.as_ref(), &slate); + match response { + Ok(slate)=> { + let txs = match owner_api.retrieve_txs( + keychain_mask.as_ref(), + false, + None, + Some(slate.id) + ) { + Ok(transactions) => { + transactions + }, Err(e) => { + return Err(e); + } + }; + let final_result = ( + serde_json::to_string(&txs.1).unwrap(), + serde_json::to_string(&slate).unwrap() ); - let result = CStr::from_ptr(result_ptr).to_str().unwrap(); - println!("Wallet Creation Result: {}", result); + Ok(serde_json::to_string(&final_result).unwrap()) + }, + Err(e)=> { + return Err(e); + } + } +} - // Try to open the wallet. - let open_result_ptr = rust_open_wallet(config_ptr, password_ptr); - let open_result = CStr::from_ptr(open_result_ptr).to_str().unwrap(); - println!("Wallet Open Result: {}", open_result); +/* + Post transaction to the node after finalising +*/ +pub fn tx_post( + wallet: &Wallet, keychain_mask: Option, tx_slate_id: &str +) -> Result { + let owner_api = Owner::new(wallet.clone()); + let tx_uuid = + Uuid::parse_str(tx_slate_id).map_err(|e| ErrorKind::GenericError(e.to_string()))?; + let (_, txs) = match owner_api.retrieve_txs( + keychain_mask.as_ref(), + false, + None, + Some(tx_uuid.clone()) + ) { + Ok(result) => { + result } + Err(err) => { + return Err(err); + } + }; + println!("TX IS ::: {:?}", txs[0]); + if txs[0].confirmed { + return Err(Error::from(ErrorKind::GenericError(format!( + "Transaction with id {} is already confirmed. Not posting.", + tx_slate_id + )))); + } - cleanup_test_dir(&test_dir); + let stored_tx = owner_api.get_stored_tx( + keychain_mask.as_ref(), + &txs[0])?; + match stored_tx { + Some(stored_tx) => { + match owner_api.post_tx(keychain_mask.as_ref(), &stored_tx, true) { + Ok(()) => { + Ok("tx_posted_to_node".to_owned()) + }, + Err(err)=> { + return Err(err); + } + } + } + None => Err(Error::from(ErrorKind::GenericError(format!( + "Transaction with id {} does not have transaction data. Not posting.", + tx_slate_id + )))), } +} + +/* + Get epic box address for receiving slates + */ +pub fn get_epicbox_address( + public_key: PublicKey, + domain: &str, + port: Option) -> EpicboxAddress +{ + let domain = domain.to_string(); + EpicboxAddress::new(public_key, Some(domain), port) +} + +pub fn derive_public_key_from_address(address: &str) -> PublicKey { + let address = EpicboxAddress::from_str(address).unwrap(); + let public_key = address.public_key().unwrap(); + public_key +} + +pub fn build_post_slate_request( + receiver_address: &str, + secret_pub_key_pair: (SecretKey, PublicKey), + tx: String, + epicbox_config: EpicBoxConfig +) -> String { + let address_sender = get_epicbox_address( + secret_pub_key_pair.1, + &epicbox_config.domain, + Some(epicbox_config.port) + ); + + let address_receiver = EpicboxAddress::from_str(receiver_address).unwrap(); + let pub_key_receiver = address_receiver.public_key().unwrap(); + let address_receiver = get_epicbox_address( + pub_key_receiver, &epicbox_config.domain, Some(epicbox_config.port)); + + let mut challenge = String::new(); + let message = EpicboxMessage::new( + tx, + &address_receiver.clone(), + &address_receiver.public_key().unwrap(), + &secret_pub_key_pair.0 + ).map_err(|_| WsError::new(WsErrorKind::Protocol, "could not encrypt slate!")).unwrap(); + let message_ser = serde_json::to_string(&message).unwrap(); + + let to_address = format!("{}", address_receiver.public_key); + let from_address = format!("{}", address_sender.public_key); + challenge.push_str(&message_ser); + let signature = sign_challenge(&challenge, &secret_pub_key_pair.0).unwrap().to_hex(); + let json_request = format!(r#"{{"type": "PostSlate", "from": "{}", "to": "{}", "str": {}, "signature": "{}"}}"#, + from_address, + to_address, + json::as_json(&message_ser), + signature); + + json_request +} + +pub fn _build_subscribe_request( + secret_pub_key_pair: (SecretKey, PublicKey) + , epicbox_config: EpicBoxConfig +) -> String { + let address = get_epicbox_address(secret_pub_key_pair.1, &epicbox_config.domain, Some(epicbox_config.port)); + + // The signed message binds to the request type (subscription) and the intended address (with domain) + // WARNING: This request does not bind to _any_ other context, and could be vulnerable to replay + let challenge = String::from(format!("SubscribeRequest_{}", address.public_key)); - /// Test vectors for address validation. - #[test] - fn test_address_validation_vectors() { - println!("=== Address Validation Test Vectors ==="); + let signature = sign_challenge(&challenge, &secret_pub_key_pair.0).unwrap().to_hex(); + let subscribe_str = format!(r#"{{"type": "Subscribe", "address": "{}", "signature": "{}"}}"#, address.public_key, signature); + subscribe_str +} - let test_addresses = [ - "epic1xdp9qkz8tqhlqv4ryy5kv780kzfsxwjvlxjxkhz4vw9r6fz4hc5qzezyzj@epicbox.epic.tech", - "invalid_address", - "epic1abc@epicbox.epic.tech", - "@epicbox.epic.tech", - "epic1xdp9qkz8tqhlqv4ryy5kv780kzfsxwjvlxjxkhz4vw9r6fz4hc5qzezyzj", - ]; - // TODO: Figure out why eg. - // "esXrtQYZzs7DveZV4pxmXr8nntSjEkmxLddCF4hoEjVUh9nQYP7j@epicbox.stackwallet.com" throws. +pub fn convert_deci_to_nano(amount: f64) -> u64 { + let base_nano = 100000000; + let nano = amount * base_nano as f64; + nano as u64 +} - for address in &test_addresses { - println!("Testing address: {}", address); - let is_valid = validate_address(address); - println!("Validation result: {}", is_valid); +pub fn nano_to_deci(amount: u64) -> f64 { + let base_nano = 100000000; + let decimal = amount as f64 / base_nano as f64; + decimal +} + +/* + Decrypt slate retreived from epic box +*/ +pub fn decrypt_message(receiver_key: &SecretKey, msg_json: serde_json::Value) -> Result { + let sender_address = msg_json.get("from").unwrap().as_str().unwrap(); + let sender_public_key: PublicKey = EpicboxAddress::from_str(sender_address).unwrap().public_key() + .unwrap(); + + let message = msg_json.get("str").unwrap().as_str().unwrap(); + let encrypted_message: EpicboxMessage = + serde_json::from_str(message).map_err(|_| TxProofErrorKind::ParseEpicboxMessage).unwrap(); + + let key = encrypted_message.key(&sender_public_key, &receiver_key).unwrap(); + let decrypted_message = match encrypted_message.decrypt_with_key(&key) { + Ok(decrypted) => { + decrypted + }, Err(e) => { + format!("Error {}", e.to_string()) } - } + }; - /// Test vectors for wallet info retrieval. - #[test] - fn test_chain_height_vectors() { - println!("=== Chain Height Test Vectors ==="); + Ok(decrypted_message) +} + +/* + Process received slate from Epicbox, and return processed slate for posting +*/ +pub fn process_epic_box_slate(wallet: &Wallet, keychain_mask: Option, slate_info: &str +) -> Result { + let msg_tuple: (String, String) = serde_json::from_str(&slate_info).unwrap(); + let transaction: Vec = serde_json::from_str(&msg_tuple.0).unwrap(); + + match transaction[0].tx_type { + TxLogEntryType::TxSent => { + match tx_receive(&wallet, keychain_mask.clone(), "default", &msg_tuple.1) { + Ok(slate) => { + Ok(slate) + }, + Err(e) => { + return Err(e); + } + } + }, + TxLogEntryType::TxReceived => { + let finalize = tx_finalize(&wallet, keychain_mask.clone(), &msg_tuple.1); + match finalize { + Ok(str_slate) => { + Ok(str_slate) + }, + Err(e)=> { + Err(e) + } + } + }, + TxLogEntryType::ConfirmedCoinbase => { + Err(Error::from(ErrorKind::GenericError(format!( + "The provided slate has already been confirmed, not processed.", + )))) + }, + _ => { + Err(Error::from(ErrorKind::GenericError(format!( + "The provided slate could not be processed, cancelled by user.", + )))) + } + } - let test_dir = setup_test_dir("chain_height"); - let config_json = create_test_config(&test_dir); +} - match get_chain_height(&config_json) { - Ok(height) => println!("Chain height: {}", height), - Err(e) => println!("Error getting chain height: {}", e), +/* + +*/ +pub fn open_wallet(config_json: &str, password: &str) -> Result<(Wallet, Option), Error> { + let config = match Config::from_str(&config_json.to_string()) { + Ok(config) => { + config + }, Err(e) => { + return Err(Error::from(ErrorKind::GenericError(format!( + "{}", + "Unable to get wallet config" + )))) + } + }; + let wallet = match get_wallet(&config) { + Ok(wllet) => { + wllet + } + Err(err) => { + return Err(err); + } + }; + let mut secret_key = None; + let mut opened = false; + { + let mut wallet_lock = wallet.lock(); + let lc = match wallet_lock.lc_provider() { + Ok(lc_provider) => { + lc_provider + } + Err(err) => { + return Err(err); + } + }; + if let Ok(exists_wallet) = lc.wallet_exists(None) { + if exists_wallet { + let temp = match lc.open_wallet( + None, + ZeroingString::from(password), + true, + false) { + Ok(tmp_key) => { + tmp_key + } + Err(err) => { + return Err(err); + } + }; + secret_key = temp; + let wallet_inst = match lc.wallet_inst() { + Ok(wallet_backend) => { + wallet_backend + } + Err(err) => { + return Err(err); + } + }; + if let Some(account) = config.account { + match wallet_inst.set_parent_key_id_by_name(&account) { + Ok(_) => { + () + } + Err(err) => { + return Err(err); + } + } + opened = true; + } + } } + } + if opened { + Ok((wallet, secret_key)) + } else { + Err(Error::from(ErrorKind::WalletSeedDoesntExist)) + } +} + - cleanup_test_dir(&test_dir); +pub fn close_wallet(wallet: &Wallet) -> Result { + let mut wallet_lock = wallet.lock(); + let lc = wallet_lock.lc_provider()?; + if let Ok(open_wallet) = lc.wallet_exists(None) { + if open_wallet { + lc.close_wallet(None)?; + } } + Ok("Wallet has been closed".to_owned()) +} - /// Test vectors for wallet info retrieval. - #[test] - fn test_nano_conversion_vectors() { - println!("=== Nano Conversion Test Vectors ==="); +pub fn validate_address(address: &str) -> bool { + let address = EpicboxAddress::from_str(address); + match address { + Ok(_) => { + true + }, + _ => { + false + } + } +} - let test_values = [ - 0.00000001, - 1.0, - 123.45678, - 9999.99999999, - 0.123456789, - ]; +pub fn delete_wallet(wallet: &Wallet) -> Result { + //First close the wallet + let mut result = String::from(""); + if let Ok(closed) = close_wallet(&wallet) { + let api = Owner::new(wallet.clone()); + match api.delete_wallet(None) { + Ok(_) => { + result.push_str("deleted"); + } + Err(err) => { + return Err(err); + } + }; + } else { + return return Err( + Error::from(ErrorKind::GenericError(format!("{}", "Error closing wallet"))) + ); + } + Ok(result) +} - for &value in &test_values { - println!("Original value (EPIC): {}", value); - let nano = convert_deci_to_nano(value); - println!("Converted to nano: {}", nano); - let back_to_epic = nano_to_deci(nano); - println!("Converted back to EPIC: {}", back_to_epic); - println!("---"); +pub fn tx_send_http( + wallet: &Wallet, + keychain_mask: Option, + selection_strategy_is_use_all: bool, + minimum_confirmations: u64, + message: &str, + amount: u64, + address: &str, +) -> Result{ + let api = Owner::new(wallet.clone()); + let initSendArgs = InitTxSendArgs { + method: "http".to_string(), + dest: address.to_string(), + finalize: true, + post_tx: true, + fluff: true + }; + + let args = InitTxArgs { + src_acct_name: Some("default".to_string()), + amount, + minimum_confirmations, + max_outputs: 500, + num_change_outputs: 1, + selection_strategy_is_use_all, + message: Some(message.to_string()), + send_args: Some(initSendArgs), + ..Default::default() + }; + + match api.init_send_tx(keychain_mask.as_ref(), args) { + Ok(slate) => { + println!("{}", "CREATE_TX_SUCCESS"); + //Get transaction for slate, for UI display + let txs = match api.retrieve_txs( + keychain_mask.as_ref(), + false, + None, + Some(slate.id) + ) { + Ok(txs_result) => { + txs_result + }, Err(e) => { + return Err(e); + } + }; + + let tx_data = ( + serde_json::to_string(&txs.1).unwrap(), + serde_json::to_string(&slate).unwrap() + ); + let str_tx_data = serde_json::to_string(&tx_data).unwrap(); + Ok(str_tx_data) + } Err(err) => { + println!("CREATE_TX_ERROR_IN_HTTP_SEND {}", err.to_string()); + return Err(err); } } } diff --git a/rust/src/listener.rs b/rust/src/listener.rs deleted file mode 100644 index 2578037b..00000000 --- a/rust/src/listener.rs +++ /dev/null @@ -1,65 +0,0 @@ -use std::sync::Arc; -use ffi_helpers::{export_task, Task}; -use ffi_helpers::task::CancellationToken; -use stack_epic_util::Mutex; -use stack_epic_util::secp::SecretKey; -use stack_epic_wallet_config::EpicboxConfig; -use stack_epic_wallet_impls::EpicboxListenChannel; - -use crate::wallet::Wallet; - -/// Listener task. -#[derive(Debug, Clone)] -pub struct Listener { - pub wallet_ptr_str: String, - // pub wallet_data: (i64, Option), - pub epicbox_config: String -} - -/// Spawn a listener task. -impl Task for Listener { - type Output = usize; - - fn run(&self, cancel_tok: &CancellationToken) -> Result { - let mut spins = 0; - - let wallet_data_str = &self.wallet_ptr_str; - // let wallet_data = wallet_ptr.to_str().unwrap(); - let tuple_wallet_data: (i64, Option) = serde_json::from_str(wallet_data_str).unwrap(); - let wlt = tuple_wallet_data.0; - let sek_key = tuple_wallet_data.1; - - // let wallet_data = &self.wallet_data; - // let wlt = wallet_data.clone().0; - unsafe { - let epicbox_conf = serde_json::from_str::(&self.epicbox_config.as_str()).unwrap(); - // let wallet_data = &self.wallet_data; - // let wlt = wallet_data.0; - // let sek_key = wallet_data.clone().1; - crate::ensure_wallet!(wlt, wallet); - while !cancel_tok.cancelled() { - let listener = EpicboxListenChannel::new().unwrap(); - let mut reconnections = 0; - listener.listen( - wallet.clone(), - Arc::new(Mutex::new(sek_key.clone())), - epicbox_conf.clone(), - &mut reconnections, - ).expect("TODO: Error Listening on Epicbox"); - spins += 1; - } - } - Ok(spins) - } -} - -export_task! { - Task: Listener; - spawn: listener_spawn; - wait: listener_wait; - poll: listener_poll; - cancel: listener_cancel; - cancelled: listener_cancelled; - handle_destroy: listener_handle_destroy; - result_destroy: listener_result_destroy; -} diff --git a/rust/src/mnemonic.rs b/rust/src/mnemonic.rs deleted file mode 100644 index 933d23bf..00000000 --- a/rust/src/mnemonic.rs +++ /dev/null @@ -1,214 +0,0 @@ - -use stack_epic_keychain::mnemonic; -use rand::thread_rng; -use std::ffi::{CStr, CString}; -use std::os::raw::c_char; -use rand::Rng; - -pub fn mnemonic() -> Result { - let seed = create_seed(32); - match mnemonic::from_entropy(&seed) { - Ok(mnemonic_str) => { - Ok(mnemonic_str) - }, Err(e) => { - return Err(e); - } - } -} - -pub fn create_seed(seed_length: u64) -> Vec { - let mut seed: Vec = vec![]; - let mut rng = thread_rng(); - for _ in 0..seed_length { - seed.push(rng.gen()); - } - seed -} - -pub fn _get_mnemonic() -> Result<*const c_char, mnemonic::Error> { - let mut wallet_phrase = "".to_string(); - match mnemonic() { - Ok(phrase) => { - wallet_phrase.push_str(&phrase); - },Err(e) => { - return Err(e); - } - } - let s = CString::new(wallet_phrase).unwrap(); - let p = s.as_ptr(); // Get a pointer to the underlaying memory for s - std::mem::forget(s); // Give up the responsibility of cleaning up/freeing s - Ok(p) -} - -#[cfg(test)] -mod mnemonic_tests { - use super::*; - use std::collections::HashSet; - - // Test the create_seed function. - #[test] - fn test_create_seed() { - // Test with different seed lengths. - let lengths = [16, 24, 32]; - - for &length in lengths.iter() { - let seed = create_seed(length); - - // Verify seed length. - assert_eq!(seed.len(), length as usize, "Seed length should match requested length"); - - // Verify seed contains random values (not all zeros). - let unique_bytes: HashSet<_> = seed.iter().collect(); - assert!(unique_bytes.len() > 1, "Seed should contain random values"); - - println!("Successfully generated seed of length {}: {:?}", length, seed); - } - } - - // Test the mnemonic() function. - #[test] - fn test_mnemonic_generation() { - match mnemonic() { - Ok(phrase) => { - // Verify the mnemonic is not empty. - assert!(!phrase.is_empty(), "Mnemonic phrase should not be empty"); - - // Split into words and verify word count (should be 24 words for 32 bytes entropy). - let words: Vec<&str> = phrase.split_whitespace().collect(); - assert_eq!(words.len(), 24, "Mnemonic should contain 24 words"); - - // Verify all words are lowercase and contain only letters. - for word in &words { - assert!(word.chars().all(|c| c.is_ascii_lowercase()), - "Words should only contain lowercase letters"); - } - - println!("Successfully generated mnemonic phrase: {}", phrase); - }, - Err(e) => { - panic!("Failed to generate mnemonic: {:?}", e); - } - } - } - - // Test the _get_mnemonic FFI function. - #[test] - fn test_get_mnemonic_ffi() { - unsafe { - match _get_mnemonic() { - Ok(c_str_ptr) => { - // Convert C string pointer back to Rust string. - let c_str = CStr::from_ptr(c_str_ptr); - let phrase = c_str.to_str().expect("Invalid UTF-8 in mnemonic"); - - // Verify the mnemonic is valid. - assert!(!phrase.is_empty(), "Mnemonic phrase should not be empty"); - - let words: Vec<&str> = phrase.split_whitespace().collect(); - assert_eq!(words.len(), 24, "Mnemonic should contain 24 words"); - - println!("Successfully generated FFI mnemonic: {}", phrase); - - // Clean up the C string (since we're in a test). - let _ = CString::from_raw(c_str_ptr as *mut i8); - }, - Err(e) => { - panic!("Failed to generate FFI mnemonic: {:?}", e); - } - } - } - } - - // Test multiple mnemonic generations to ensure uniqueness. - #[test] - fn test_mnemonic_uniqueness() { - let mut phrases = HashSet::new(); - - // Generate multiple phrases and check that they're unique. - for i in 0..5 { - match mnemonic() { - Ok(phrase) => { - assert!(!phrases.contains(&phrase), - "Generated duplicate mnemonic on iteration {}", i); - phrases.insert(phrase.clone()); - println!("Generated unique mnemonic {}: {}", i + 1, phrase); - }, - Err(e) => { - panic!("Failed to generate mnemonic on iteration {}: {:?}", i, e); - } - } - } - } - - // Test that generated mnemonics can be parsed back into valid seeds. - #[test] - fn test_mnemonic_reversibility() { - use stack_epic_keychain::mnemonic::to_entropy; - - match mnemonic() { - Ok(phrase) => { - // Try to convert mnemonic back to entropy. - match to_entropy(&phrase) { - Ok(entropy) => { - assert_eq!(entropy.len(), 32, - "Entropy from mnemonic should be 32 bytes"); - println!("Successfully verified mnemonic reversibility for: {}", phrase); - }, - Err(e) => { - panic!("Failed to convert mnemonic back to entropy: {:?}", e); - } - } - }, - Err(e) => { - panic!("Failed to generate mnemonic: {:?}", e); - } - } - } - - /// Test a specific mnemonic phrase against known entropy values. - #[test] - fn test_mnemonic_vector() { - use stack_epic_keychain::mnemonic::to_entropy; - - let mnemonic = "march journey switch frame cloud since course twice cement pen random snow volume warrior film traffic loan tomorrow speed surprise thought remember ill whip"; - // Alternate vector used elsewhere in tests or otherwise committed: - // let mnemonic = "give tube absurd fossil bike nurse huge neither equip claim tattoo fly stool gauge convince ask cat short bind original mule bundle feature tonight"; - - // Known correct values: - let expected_bytes: [u8; 32] = [ - 135, 207, 15, 112, 174, 66, 191, 146, - 76, 87, 90, 37, 52, 78, 198, 230, - 207, 93, 238, 149, 151, 54, 131, 28, - 135, 68, 109, 62, 15, 107, 92, 63 - ]; - let expected_hex = "87cf0f70ae42bf924c575a25344ec6e6cf5dee959736831c87446d3e0f6b5c3f"; - - match to_entropy(mnemonic) { - Ok(entropy) => { - println!("Testing mnemonic entropy matches expected values:"); - println!("Mnemonic: {}", mnemonic); - println!("Entropy (bytes): {:?}", entropy); - println!("Entropy (hex): {}", hex::encode(&entropy)); - - // Verify exact byte values match. - assert_eq!( - entropy.as_slice(), - expected_bytes.as_slice(), - "Entropy bytes don't match expected values" - ); - - // Verify hex representation matches. - assert_eq!( - hex::encode(&entropy), - expected_hex, - "Hex representation doesn't match expected value" - ); - - println!("\nEntropy verification passed! ✓"); - }, - Err(e) => { - panic!("Failed to convert specific mnemonic to entropy: {:?}", e); - } - } - } -} diff --git a/rust/src/wallet.rs b/rust/src/wallet.rs deleted file mode 100644 index b9670230..00000000 --- a/rust/src/wallet.rs +++ /dev/null @@ -1,785 +0,0 @@ -use std::sync::Arc; -use serde_derive::{Deserialize, Serialize}; -use stack_epic_keychain::ExtKeychain; -use stack_epic_util::{Mutex, ZeroingString}; -use stack_epic_util::file::get_first_line; -use stack_epic_util::secp::{PublicKey, Secp256k1, SecretKey}; -use stack_epic_wallet_api::Owner; -use stack_epic_wallet_config::{EpicboxConfig, WalletConfig}; -use stack_epic_wallet_impls::{DefaultLCProvider, HTTPNodeClient}; -use stack_epic_wallet_libwallet::{address, scan, wallet_lock, AddressType, EpicboxAddress, Error, InitTxArgs, InitTxSendArgs, WalletInst}; -use stack_epic_wallet_libwallet::api_impl::owner; -use uuid::Uuid; -use crate::config::{create_wallet_config, Config}; -use crate::EpicWalletControllerError; -use stack_epic_wallet_libwallet::Address; -use stack_epic_wallet_libwallet::WalletLCProvider; -use stack_epic_wallet_libwallet::NodeClient; -use stack_epic_keychain::Keychain; -use stack_epic_wallet_impls::DefaultWalletImpl; -use std::cmp::Ordering; - -/// Wallet type. -pub type Wallet = Arc< - Mutex< - Box< - dyn WalletInst< - 'static, - DefaultLCProvider<'static, HTTPNodeClient, ExtKeychain>, - HTTPNodeClient, - ExtKeychain, - >, - >, - >, ->; - -/// Wallet information. -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct WalletInfoFormatted { - pub last_confirmed_height: u64, - pub minimum_confirmations: u64, - pub total: f64, - pub amount_awaiting_finalization: f64, - pub amount_awaiting_confirmation: f64, - pub amount_immature: f64, - pub amount_currently_spendable: f64, - pub amount_locked: f64, -} - -/// Strategy for transaction. -#[derive(Serialize, Deserialize)] -struct Strategy { - selection_strategy_is_use_all: bool, - total: u64, - fee: u64, -} - -/// Get transaction strategies. -pub fn tx_strategies( - wallet: &Wallet, - keychain_mask: Option, - amount: u64, - minimum_confirmations: u64, -) -> Result { - let mut result = vec![]; - wallet_lock!(wallet, w); - - let args = InitTxArgs { - src_acct_name: None, - amount, - minimum_confirmations, - max_outputs: 500, - num_change_outputs: 1, - estimate_only: Some(true), - message: None, - ..Default::default() - }; - - match owner::init_send_tx(&mut **w, keychain_mask.as_ref(), args, true) { - Ok(slate) => { - result.push(Strategy { - selection_strategy_is_use_all: false, - total: slate.amount, - fee: slate.fee, - - }); - }, Err(e) => { - return Err(e); - } - } - - Ok(serde_json::to_string(&result).unwrap()) -} - -/// Get wallet transactions. -pub fn txs_get( - wallet: &Wallet, - keychain_mask: Option, - refresh_from_node: bool, -) -> Result { - let api = Owner::new(wallet.clone(), None); - let txs = match api.retrieve_txs( - keychain_mask.as_ref(), - refresh_from_node, - None, - None - ) { - Ok((_, tx_entries)) => { - tx_entries - }, Err(e) => { - return Err(e); - } - }; - - let result = txs; - Ok(serde_json::to_string(&result).unwrap()) -} - -/// Initialize a transaction as sender. -pub fn tx_create( - wallet: &Wallet, - keychain_mask: Option, - amount: u64, - minimum_confirmations: u64, - selection_strategy_is_use_all: bool, - epicbox_config: &str, - address: &str, - note: &str, -) -> Result { - let owner_api = Owner::new(wallet.clone(), None); - let epicbox_conf = serde_json::from_str::(epicbox_config).unwrap(); - - owner_api.set_epicbox_config(Some(epicbox_conf)); - let init_send_args = InitTxSendArgs { - method: "epicbox".to_string(), - dest: address.to_string(), - finalize: false, - post_tx: false, - fluff: false - }; - - let args = InitTxArgs { - src_acct_name: Some("default".to_string()), - amount, - minimum_confirmations, - max_outputs: 500, - num_change_outputs: 1, - selection_strategy_is_use_all, - send_args: Some(init_send_args), - message: Some(note.to_string()), - ..Default::default() - }; - - match owner_api.init_send_tx(keychain_mask.as_ref(), args) { - Ok(slate)=> { - debug!("SLATE SEND RESPONSE IS {:?}", slate); - // Get transaction for the slate, we will use type to determing if we should finalize or receive tx. - let txs = match owner_api.retrieve_txs( - keychain_mask.as_ref(), - false, - None, - Some(slate.id) - ) { - Ok(txs_result) => { - txs_result - }, Err(e) => { - return Err(e); - } - }; - let final_result = ( - serde_json::to_string(&txs.1).unwrap(), - serde_json::to_string(&slate).unwrap() - ); - let str_result = serde_json::to_string(&final_result).unwrap(); - Ok(str_result) - }, - Err(e)=> { - return Err(e); - } - } -} - -/// Cancel a transaction by ID. -pub fn tx_cancel(wallet: &Wallet, keychain_mask: Option, tx_slate_id: Uuid) -> Result { - let api = Owner::new(wallet.clone(), None); - match api.cancel_tx(keychain_mask.as_ref(), None, Some(tx_slate_id)) { - Ok(_) => { - Ok("cancelled".to_owned()) - },Err(e) => { - return Err(e); - } - } -} - -/// Get a transaction by slate ID. -pub fn tx_get(wallet: &Wallet, refresh_from_node: bool, tx_slate_id: &str) -> Result { - let api = Owner::new(wallet.clone(), None); - let uuid = Uuid::parse_str(tx_slate_id).map_err(|e| EpicWalletControllerError::GenericError(e.to_string())).unwrap(); - let txs = api.retrieve_txs(None, refresh_from_node, None, Some(uuid)).unwrap(); - Ok(serde_json::to_string(&txs.1).unwrap()) -} - -/// Convert decimal to nano. -pub fn convert_deci_to_nano(amount: f64) -> u64 { - let base_nano = 100000000; - let nano = amount * base_nano as f64; - nano as u64 -} - -/// Convert nano to decimal. -pub fn nano_to_deci(amount: u64) -> f64 { - let base_nano = 100000000; - let decimal = amount as f64 / base_nano as f64; - decimal -} - -/// Open a wallet. -pub fn open_wallet(config_json: &str, password: &str) -> Result<(Wallet, Option), Error> { - let config = match Config::from_str(&config_json.to_string()) { - Ok(config) => { - config - }, Err(_e) => { - return Err(Error::from(EpicWalletControllerError::GenericError(format!( - "{}", - "Unable to get wallet config" - )))) - } - }; - let wallet = match get_wallet(&config) { - Ok(wllet) => { - wllet - } - Err(err) => { - return Err(err); - } - }; - let mut secret_key = None; - let mut opened = false; - { - let mut wallet_lock = wallet.lock(); - let lc = match wallet_lock.lc_provider() { - Ok(lc_provider) => { - lc_provider - } - Err(err) => { - return Err(err); - } - }; - if let Ok(exists_wallet) = lc.wallet_exists(None) { - if exists_wallet { - let temp = match lc.open_wallet( - None, - ZeroingString::from(password), - true, - false) { - Ok(tmp_key) => { - tmp_key - } - Err(err) => { - return Err(err); - } - }; - secret_key = temp; - let wallet_inst = match lc.wallet_inst() { - Ok(wallet_backend) => { - wallet_backend - } - Err(err) => { - return Err(err); - } - }; - if let Some(account) = config.account { - match wallet_inst.set_parent_key_id_by_name(&account) { - Ok(_) => { - () - } - Err(err) => { - return Err(err); - } - } - opened = true; - } - } - } - } - if opened { - Ok((wallet, secret_key)) - } else { - Err(Error::from(EpicWalletControllerError::WalletSeedDoesntExist)) - } -} - -/// Close a wallet. -pub fn close_wallet(wallet: &Wallet) -> Result { - let mut wallet_lock = wallet.lock(); - let lc = wallet_lock.lc_provider()?; - match lc.wallet_exists(None)? { - true => { - lc.close_wallet(None)? - } - false => { - return Err( - Error::from(EpicWalletControllerError::WalletSeedDoesntExist) - ); - } - } - Ok("Wallet has been closed".to_owned()) -} - -/// Validate an address. -pub fn validate_address(str_address: &str) -> bool { - match EpicboxAddress::from_str(str_address) { - Ok(addr) => { - if addr.address_type() == AddressType::Epicbox { - return true; - } - false - } - Err(_) => { - false - } - } -} - -/// Delete a wallet. -pub fn delete_wallet(config: Config) -> Result { - let mut result = String::from(""); - // get wallet object in order to use class methods - let wallet = match get_wallet(&config) { - Ok(wllet) => { - wllet - } - Err(e) => { - return Err(e); - } - }; - //First close the wallet - if let Ok(_) = close_wallet(&wallet) { - let api = Owner::new(wallet.clone(), None); - match api.delete_wallet(None) { - Ok(_) => { - result.push_str("deleted"); - } - Err(err) => { - return Err(err); - } - }; - } else { - return Err( - Error::from(EpicWalletControllerError::GenericError(format!("{}", "Error closing wallet"))) - ); - } - Ok(result) -} - -/// Send a transaction via HTTP. -pub fn tx_send_http( - wallet: &Wallet, - keychain_mask: Option, - selection_strategy_is_use_all: bool, - minimum_confirmations: u64, - message: &str, - amount: u64, - address: &str, -) -> Result{ - let api = Owner::new(wallet.clone(), None); - let init_send_args = InitTxSendArgs { - method: "http".to_string(), - dest: address.to_string(), - finalize: true, - post_tx: true, - fluff: true - }; - - let args = InitTxArgs { - src_acct_name: Some("default".to_string()), - amount, - minimum_confirmations, - max_outputs: 500, - num_change_outputs: 1, - selection_strategy_is_use_all, - message: Some(message.to_string()), - send_args: Some(init_send_args), - ..Default::default() - }; - - match api.init_send_tx(keychain_mask.as_ref(), args) { - Ok(slate) => { - println!("{}", "CREATE_TX_SUCCESS"); - //Get transaction for slate, for UI display - let txs = match api.retrieve_txs( - keychain_mask.as_ref(), - false, - None, - Some(slate.id) - ) { - Ok(txs_result) => { - txs_result - }, Err(e) => { - return Err(e); - } - }; - - let tx_data = ( - serde_json::to_string(&txs.1).unwrap(), - serde_json::to_string(&slate).unwrap() - ); - let str_tx_data = serde_json::to_string(&tx_data).unwrap(); - Ok(str_tx_data) - } Err(err) => { - println!("CREATE_TX_ERROR_IN_HTTP_SEND {}", err.to_string()); - return Err(err); - } - } -} - -/// Create a wallet. -pub fn create_wallet(config: &str, phrase: &str, password: &str, name: &str) -> Result { - let wallet_pass = ZeroingString::from(password); - let wallet_config = match Config::from_str(&config) { - Ok(config) => { - config - }, Err(e) => { - return Err(Error::from(EpicWalletControllerError::GenericError(format!( - "Error getting wallet config: {}", - e.to_string() - )))); - } - }; - - let wallet = match get_wallet(&wallet_config) { - Ok(wllet) => { - wllet - } - Err(e) => { - return Err(e); - } - }; - let mut wallet_lock = wallet.lock(); - let lc = match wallet_lock.lc_provider() { - Ok(wallet_lc) => { - wallet_lc - } - Err(e) => { - return Err(e); - } - }; - let rec_phrase = ZeroingString::from(phrase); - let result = match lc.create_wallet( - Some(name), - Some(rec_phrase), - 32, - wallet_pass, - false, - ) { - Ok(_) => { - "".to_string() - }, - Err(e) => { - e.to_string() - }, - }; - Ok(result) -} - -/// Get a wallet's secret key pair. -pub fn get_wallet_secret_key_pair( - wallet: &Wallet, keychain_mask: Option, index: u32 -) -> Result<(SecretKey, PublicKey), Error>{ - let parent_key_id = { - wallet_lock!(wallet, w); - w.parent_key_id().clone() - }; - wallet_lock!(wallet, w); - - let k = match w.keychain(keychain_mask.as_ref()) { - Ok(keychain) => { - keychain - } - Err(err) => { - return Err(err); - } - }; - let s = Secp256k1::new(); - let sec_key = match address::address_from_derivation_path( - &k, &parent_key_id, index - ) { - Ok(s_key) => { - s_key - } - Err(err) => { - return Err(err); - } - }; - let pub_key = match PublicKey::from_secret_key(&s, &sec_key) { - Ok(p_key) => { - p_key - } - Err(err) => { - return Err(Error::from( - EpicWalletControllerError::GenericError( - format!("{}", err.to_string()) - ) - )); - } - }; - - Ok((sec_key, pub_key)) -} - -/// Get a summary of a wallet's state. -pub fn get_wallet_info( - wallet: &Wallet, - keychain_mask: Option, - refresh_from_node: bool, - min_confirmations: u64 -) -> Result { - println!(">> get_wallet_info called with refresh_from_node={refresh_from_node}, min_confirmations={min_confirmations}"); - let api = Owner::new(wallet.clone(), None); - - match api.retrieve_summary_info(keychain_mask.as_ref(), refresh_from_node, min_confirmations) { - Ok((_, wallet_summary)) => { - println!(">> raw wallet_summary: {wallet_summary:?}"); - Ok(WalletInfoFormatted { - last_confirmed_height: wallet_summary.last_confirmed_height, - minimum_confirmations: wallet_summary.minimum_confirmations, - total: nano_to_deci(wallet_summary.total), - amount_awaiting_finalization: nano_to_deci(wallet_summary.amount_awaiting_finalization), - amount_awaiting_confirmation: nano_to_deci(wallet_summary.amount_awaiting_confirmation), - amount_immature: nano_to_deci(wallet_summary.amount_immature), - amount_currently_spendable: nano_to_deci(wallet_summary.amount_currently_spendable), - amount_locked: nano_to_deci(wallet_summary.amount_locked) - }) - }, Err(e) => { - println!(">> get_wallet_info error: {e}"); - Err(e) - } - } -} - -/// Recover a wallet from a mnemonic. -pub fn recover_from_mnemonic(mnemonic: &str, password: &str, config: &Config, name: &str) -> Result<(), Error> { - let wallet = match get_wallet(&config) { - Ok(conf) => { - conf - } - Err(e) => { - return Err(e); - } - }; - let mut w_lock = wallet.lock(); - let lc = match w_lock.lc_provider() { - Ok(wallet_lc) => { - wallet_lc - } - Err(e) => { - return Err(e); - } - }; - - // First check if wallet seed directory exists, if not create. - if let Ok(exists_wallet_seed) = lc.wallet_exists(None) { - return if exists_wallet_seed { - match lc.recover_from_mnemonic( - ZeroingString::from(mnemonic), ZeroingString::from(password) - ) { - Ok(_) => { - Ok(()) - } - Err(e) => { - Err(e) - } - } - } else { - match lc.create_wallet( - Some(&name), - Some(ZeroingString::from(mnemonic)), - 32, - ZeroingString::from(password), - false, - ) { - Ok(_) => { - Ok(()) - } - Err(e) => { - Err(e) - } - } - } - } - Ok(()) -} - -/// Get a wallet. -pub fn get_wallet(config: &Config) -> Result { - let wallet_config = match create_wallet_config(config.clone()) { - Ok(conf) => { - conf - } Err(e) => { - return Err(e); - } - }; - let node_api_secret = get_first_line(wallet_config.node_api_secret_path.clone()); - let node_client = HTTPNodeClient::new(&wallet_config.check_node_api_http_addr, node_api_secret).unwrap(); - let wallet = match inst_wallet::< - DefaultLCProvider, - HTTPNodeClient, - ExtKeychain, - >(wallet_config.clone(), node_client) { - Ok(wallet_inst) => { - wallet_inst - } - Err(e) => { - return Err(e); - } - }; - return Ok(wallet); -} - -/// Instantiate a wallet. -fn inst_wallet( - config: WalletConfig, - node_client: C, -) -> Result>>>, Error> -where - DefaultWalletImpl<'static, C>: WalletInst<'static, L, C, K>, - L: WalletLCProvider<'static, C, K>, - C: NodeClient + 'static, - K: Keychain + 'static, -{ - let mut wallet = Box::new(DefaultWalletImpl::<'static, C>::new(node_client.clone()).unwrap()) - as Box>; - let lc = match wallet.lc_provider() { - Ok(wallet_lc) => { - wallet_lc - } - Err(err) => { - return Err(err); - } - }; - match lc.set_top_level_directory(&config.data_file_dir) { - Ok(_) => { - () - } - Err(err) => { - return Err(err); - } - }; - Ok(Arc::new(Mutex::new(wallet))) -} - -/// Get the chain height. -pub fn get_chain_height(config: &str) -> Result { - let config = match Config::from_str(&config.to_string()) { - Ok(config) => { - config - }, Err(_e) => { - return Err(Error::from(EpicWalletControllerError::GenericError(format!( - "{}", - "Unable to get wallet config" - )))) - } - }; - let wallet_config = match create_wallet_config(config.clone()) { - Ok(wallet_conf) => { - wallet_conf - } - Err(e) => { - return Err(e); - } - }; - let node_api_secret = get_first_line(wallet_config.node_api_secret_path.clone()); - let node_client = HTTPNodeClient::new(&wallet_config.check_node_api_http_addr, node_api_secret); - let chain_tip = match node_client?.chain_height() { - Ok(tip) => { - tip - } - Err(err) => { - return Err(err); - } - }; - Ok(chain_tip.0) -} - -/// Scan the wallet outputs. -pub fn wallet_scan_outputs( - wallet: &Wallet, - keychain_mask: Option, - start_height: Option, - number_of_blocks_to_scan: Option -) -> Result { - let tip = { - wallet_lock!(wallet, w); - match w.w2n_client().get_chain_tip() { - Ok(chain_tip) => { - chain_tip.0 - }, - Err(_e) => { - 0 - } - } - }; - - if tip == 0 { - return Err(Error::from(EpicWalletControllerError::GenericError(format!( - "{}", - "Unable to scan, could not determine chain height" - )))); - } - - let start_height: u64 = match start_height { - Some(h) => h, - None => 1, - }; - - let number_of_blocks_to_scan: u64 = match number_of_blocks_to_scan { - Some(h) => h, - None => 0, - }; - - let last_block = start_height.clone() + number_of_blocks_to_scan; - let end_height: u64 = match last_block.cmp(&tip) { - Ordering::Less => { - last_block - }, - Ordering::Greater => { - tip - }, - Ordering::Equal => { - last_block - } - }; - - match scan( - wallet.clone(), - keychain_mask.as_ref(), - false, - start_height, - end_height, - &None, - ) { - Ok(info) => { - println!("Info type: {:?}", info); - - let parent_key_id = { - wallet_lock!(wallet, w); - w.parent_key_id().clone() - }; - - { - wallet_lock!(wallet, w); - let mut batch = match w.batch(keychain_mask.as_ref()) { - Ok(wallet_output_batch) => { - wallet_output_batch - } - Err(err) => { - return Err(err); - } - }; - match batch.save_last_confirmed_height(&parent_key_id, info.clone().height) { - Ok(_) => { - () - } - Err(err) => { - return Err(err); - } - }; - match batch.commit() { - Ok(_) => { - () - } - Err(err) => { - return Err(err); - } - } - }; - - - let result = info.height; - Ok(serde_json::to_string(&result).unwrap()) - // Ok(serde_json::to_string(&info).unwrap()) - }, Err(e) => { - return Err(e); - } - } -} diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index 9e512178..1ef29c58 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -44,10 +44,14 @@ target_include_directories(${PLUGIN_NAME} INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/include") target_link_libraries(${PLUGIN_NAME} PRIVATE flutter flutter_wrapper_plugin) +include("../cargokit/cmake/cargokit.cmake") +apply_cargokit(${PROJECT_NAME} ../rust epic_cash_wallet "") + # 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_libepiccash_bundled_libraries - "" + # Replace original target file with the one produced by Cargokit: + "${${PROJECT_NAME}_cargokit_lib}" PARENT_SCOPE )