From f6965a9c8a09263e5191b45ac0d401fc256b0ae3 Mon Sep 17 00:00:00 2001 From: Luke Mann Date: Wed, 21 Jan 2026 15:12:17 -0800 Subject: [PATCH 01/10] swift and kotlin tooling --- .github/workflows/ci.yml | 42 +- .github/workflows/initiate-release.yml | 6 +- .github/workflows/release.yml | 31 +- .gitignore | 15 + kotlin/README.md | 26 ++ kotlin/build_kotlin.sh | 41 ++ kotlin/test_kotlin.sh | 113 +++++ kotlin/walletkit-android/build.gradle.kts | 80 ++++ kotlin/walletkit-android/consumer-rules.pro | 1 + kotlin/walletkit-tests/build.gradle.kts | 31 ++ .../kotlin/org/world/walletkit/SimpleTest.kt | 13 + swift/README.md | 57 +++ swift/archive_swift.sh | 95 +++++ swift/build_swift.sh | 127 ++++++ swift/local_swift.sh | 71 ++++ .../WalletKitTests/AuthenticatorTests.swift | 386 ------------------ swift/tests/WalletKitTests/SimpleTest.swift | 8 + 17 files changed, 737 insertions(+), 406 deletions(-) create mode 100644 kotlin/README.md create mode 100755 kotlin/build_kotlin.sh create mode 100755 kotlin/test_kotlin.sh create mode 100644 kotlin/walletkit-android/build.gradle.kts create mode 100644 kotlin/walletkit-android/consumer-rules.pro create mode 100644 kotlin/walletkit-tests/build.gradle.kts create mode 100644 kotlin/walletkit-tests/src/test/kotlin/org/world/walletkit/SimpleTest.kt create mode 100644 swift/README.md create mode 100755 swift/archive_swift.sh create mode 100755 swift/build_swift.sh create mode 100755 swift/local_swift.sh delete mode 100644 swift/tests/WalletKitTests/AuthenticatorTests.swift create mode 100644 swift/tests/WalletKitTests/SimpleTest.swift diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9e2203dae..24699114d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,5 +1,10 @@ name: CI +# Unless we are on the main branch, the workflow should stop and yield to a new run if new code is pushed. +concurrency: + group: ${{ github.workflow }}-${{ github.ref == 'refs/heads/main' && github.sha || github.ref }} + cancel-in-progress: ${{ !contains(github.ref, 'main')}} + on: push: branches: [main] @@ -56,6 +61,41 @@ jobs: - name: Run Swift foreign binding tests run: ./swift/test_swift.sh + - name: Install SwiftLint + run: | + brew install swiftlint + + - name: Lint Swift Tests + run: swiftlint swift/tests + + kotlin-build-and-test: + name: Kotlin Build & Foreign Binding Tests + runs-on: ubuntu-latest + permissions: + contents: read + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Rust + uses: dtolnay/rust-toolchain@master + with: + toolchain: 1.92.0 + + - name: Build and test Kotlin bindings + run: ./kotlin/test_kotlin.sh + + - name: Install ktlint + run: | + curl -sSLO https://github.com/pinterest/ktlint/releases/latest/download/ktlint && + chmod a+x ktlint && + sudo mv ktlint /usr/local/bin/ + + - name: Lint Kotlin Tests + run: | + ktlint kotlin/walletkit-tests/src/test/kotlin + test: name: Tests runs-on: ubuntu-latest @@ -112,7 +152,7 @@ jobs: - uses: EmbarkStudios/cargo-deny-action@v2 with: command: check ${{ matrix.checks }} - rust-version: stable + rust-version: 1.92.0 docs: name: Check docs diff --git a/.github/workflows/initiate-release.yml b/.github/workflows/initiate-release.yml index 57ac4a5d9..70dcc3f26 100644 --- a/.github/workflows/initiate-release.yml +++ b/.github/workflows/initiate-release.yml @@ -29,8 +29,8 @@ jobs: env: BUMP_TYPE: ${{ github.event.inputs.bump_type }} run: | - # Get current version from Cargo.toml - CURRENT_VERSION=$(grep -m 1 'version = ' Cargo.toml | cut -d '"' -f 2) + # Get current version from workspace package in Cargo.toml + CURRENT_VERSION=$(cargo metadata --no-deps --format-version 1 | jq -r '.workspace_members[0]' | cut -d '#' -f2) # Ensure CURRENT_VERSION is in semantic versioning format if [[ ! "$CURRENT_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then @@ -38,6 +38,8 @@ jobs: exit 1 fi + cargo metadata --no-deps --format-version 1 | jq -r '.workspace_members' + # Split version into components IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT_VERSION" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8d41638ba..a83a28a85 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -68,11 +68,11 @@ jobs: components: rustfmt - name: Build the project (iOS) - run: ./build_swift.sh + run: ./swift/build_swift.sh - name: Compress XCFramework binary run: | - zip -r WalletKit.xcframework.zip WalletKit.xcframework + zip -r WalletKit.xcframework.zip swift/WalletKit.xcframework - name: Checkout swift repo uses: actions/checkout@v4 @@ -112,11 +112,11 @@ jobs: run: | # Copy non-binary source files - cp -r Sources/ target-repo/Sources + cp -r swift/Sources/ target-repo/Sources # Prepare Package.swift brew install swiftlint - ./archive_swift.sh --asset-url "$ASSET_URL" --checksum "$CHECKSUM" --release-version "$NEW_VERSION" + ./swift/archive_swift.sh --asset-url "$ASSET_URL" --checksum "$CHECKSUM" --release-version "$NEW_VERSION" cp Package.swift target-repo/ # Commit changes @@ -210,7 +210,7 @@ jobs: - name: Move artifacts run: | - mkdir -p kotlin/lib/src/main/jniLibs && cd kotlin/lib/src/main/jniLibs + mkdir -p kotlin/walletkit-android/src/main/jniLibs && cd kotlin/walletkit-android/src/main/jniLibs mkdir armeabi-v7a arm64-v8a x86 x86_64 mv /home/runner/work/walletkit/walletkit/android-armv7-linux-androideabi/libwalletkit.so ./armeabi-v7a/libwalletkit.so mv /home/runner/work/walletkit/walletkit/android-aarch64-linux-android/libwalletkit.so ./arm64-v8a/libwalletkit.so @@ -219,11 +219,11 @@ jobs: - name: Generate bindings working-directory: kotlin - run: cargo run -p uniffi-bindgen generate ./lib/src/main/jniLibs/arm64-v8a/libwalletkit.so --library --language kotlin --no-format --out-dir lib/src/main/java + run: cargo run -p uniffi-bindgen generate ./walletkit-android/src/main/jniLibs/arm64-v8a/libwalletkit.so --library --language kotlin --no-format --out-dir walletkit-android/src/main/java - name: Publish working-directory: kotlin - run: ./gradlew lib:publish -PversionName=${{ needs.pre-release-checks.outputs.new_version }} + run: ./gradlew walletkit-android:publish env: GITHUB_ACTOR: wld-walletkit-bot GITHUB_TOKEN: ${{ github.token }} @@ -243,16 +243,13 @@ jobs: make_latest: true - name: Create Release in swift repo - uses: softprops/action-gh-release@v2 - with: - repository: worldcoin/walletkit-swift - token: ${{ secrets.WALLETKIT_BOT_TOKEN }} - name: ${{ needs.pre-release-checks.outputs.new_version }} - tag_name: ${{ needs.pre-release-checks.outputs.new_version }} - body: | - ## Version ${{ needs.pre-release-checks.outputs.new_version }} - For full release notes, see the [main repo release](https://github.com/worldcoin/walletkit/releases/tag/${{ needs.pre-release-checks.outputs.new_version }}). - make_latest: true + env: + GH_TOKEN: ${{ secrets.WALLETKIT_BOT_TOKEN }} + run: | + gh release edit ${{ needs.pre-release-checks.outputs.new_version }} \ + --repo worldcoin/walletkit-swift \ + --draft=false \ + --latest publish-to-crates-io: needs: [pre-release-checks, create-github-release] diff --git a/.gitignore b/.gitignore index a59439236..4f74359d5 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,17 @@ target/ # Swift build outputs are not committed to this repo. WalletKit.xcframework/ Sources/ +swift/WalletKit.xcframework/ +swift/Sources/ +swift/ios_build/ +swift/local_build/ +swift/tests/Sources/ +swift/tests/.build/ + +# Kotlin bindings and native libs +kotlin/libs/ +kotlin/walletkit-android/src/main/java/uniffi/ +kotlin/walletkit-tests/build/ .build/ @@ -21,4 +32,8 @@ Sources/ cache/ **/out/build-info +# Allow storage cache module sources. +!walletkit-core/src/storage/cache/ +!walletkit-core/src/storage/cache/** + # NOTE: Cargo.lock is not ignored because it is used for FFI builds (Swift & Kotlin) diff --git a/kotlin/README.md b/kotlin/README.md new file mode 100644 index 000000000..2373f1d6c --- /dev/null +++ b/kotlin/README.md @@ -0,0 +1,26 @@ +# Kotlin for WalletKit + +This folder contains support files for WalletKit to work in Kotlin: + +1. Script to build Kotlin/JNA bindings. +2. Foreign tests (JUnit) for Kotlin in the `walletkit-tests` module. + +## Building the Kotlin project + +```bash + # run from the walletkit directory + ./kotlin/build_kotlin.sh +``` + +## Running foreign tests for Kotlin + +```bash + # run from the walletkit directory + ./kotlin/test_kotlin.sh +``` + +## Kotlin project structure + +The Kotlin project has two members: +- `walletkit-android`: The main WalletKit library with UniFFI bindings for Kotlin. +- `walletkit-tests`: Unit tests to assert the Kotlin bindings behave as intended (foreign tests). diff --git a/kotlin/build_kotlin.sh b/kotlin/build_kotlin.sh new file mode 100755 index 000000000..73c19dcdd --- /dev/null +++ b/kotlin/build_kotlin.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Creates Kotlin/JNA bindings for the `walletkit` library. +# This mirrors the Bedrock Kotlin build flow. + +PROJECT_ROOT_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +KOTLIN_DIR="$PROJECT_ROOT_PATH/kotlin" +JAVA_SRC_DIR="$KOTLIN_DIR/walletkit-android/src/main/java" +LIBS_DIR="$KOTLIN_DIR/libs" + +# Clean previous artifacts +rm -rf "$JAVA_SRC_DIR" "$LIBS_DIR" +mkdir -p "$JAVA_SRC_DIR" "$LIBS_DIR" + +echo "๐ŸŸข Building Rust cdylib for host platform" +cargo build --package walletkit --release + +# Determine the correct library file extension and copy it +if [[ "$OSTYPE" == "darwin"* ]]; then + LIB_FILE="$PROJECT_ROOT_PATH/target/release/libwalletkit.dylib" + cp "$LIB_FILE" "$LIBS_DIR/" + echo "๐Ÿ“ฆ Copied libwalletkit.dylib for macOS" +elif [[ "$OSTYPE" == "linux-gnu"* ]]; then + LIB_FILE="$PROJECT_ROOT_PATH/target/release/libwalletkit.so" + cp "$LIB_FILE" "$LIBS_DIR/" + echo "๐Ÿ“ฆ Copied libwalletkit.so for Linux" +else + echo "โŒ Unsupported OS: $OSTYPE" + exit 1 +fi + +echo "๐ŸŸก Generating Kotlin bindings via uniffi-bindgen" +cargo run -p uniffi-bindgen -- generate \ + "$LIB_FILE" \ + --language kotlin \ + --library \ + --crate walletkit_core \ + --out-dir "$JAVA_SRC_DIR" + +echo "โœ… Kotlin bindings written to $JAVA_SRC_DIR" diff --git a/kotlin/test_kotlin.sh b/kotlin/test_kotlin.sh new file mode 100755 index 000000000..cfa043579 --- /dev/null +++ b/kotlin/test_kotlin.sh @@ -0,0 +1,113 @@ +#!/usr/bin/env bash +set -euo pipefail + +echo "=========================================" +echo "Running Kotlin/JVM Tests" +echo "=========================================" + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +BLUE='\033[0;34m' +YELLOW='\033[0;33m' +NC='\033[0m' # No Color + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" + +TEST_RESULTS_DIR="$ROOT_DIR/kotlin/walletkit-tests/build/test-results/test" +rm -rf "$TEST_RESULTS_DIR" + +cd "$ROOT_DIR" + +# Set JAVA_HOME if not already set (for CI environments) +if [ -z "${JAVA_HOME:-}" ]; then + if [ -d "/opt/homebrew/Cellar/openjdk@17" ]; then + # macOS with Homebrew - find latest 17.x version + LATEST_JDK=$(ls -v /opt/homebrew/Cellar/openjdk@17 | grep "^17\." | tail -n 1) + if [ -n "$LATEST_JDK" ]; then + export JAVA_HOME="/opt/homebrew/Cellar/openjdk@17/$LATEST_JDK/libexec/openjdk.jdk/Contents/Home" + echo -e "${BLUE}๐Ÿ”ง Set JAVA_HOME to: $JAVA_HOME${NC}" + else + echo -e "${YELLOW}โš ๏ธ No OpenJDK 17.x found in Homebrew${NC}" + fi + elif command -v java >/dev/null 2>&1; then + JAVA_PATH=$(which java) + export JAVA_HOME=$(dirname $(dirname $(readlink -f $JAVA_PATH))) + echo -e "${BLUE}๐Ÿ”ง Detected JAVA_HOME: $JAVA_HOME${NC}" + else + echo -e "${YELLOW}โš ๏ธ JAVA_HOME not set and Java not found in PATH${NC}" + fi +fi + +echo -e "${BLUE}๐Ÿ”จ Step 1: Building Kotlin bindings with build_kotlin.sh${NC}" +"$ROOT_DIR/kotlin/build_kotlin.sh" + +echo -e "${GREEN}โœ… Kotlin bindings built${NC}" + +echo -e "${BLUE}๐Ÿ“ฆ Step 2: Setting up Gradle test environment${NC}" +cd "$ROOT_DIR/kotlin" + +# Generate Gradle wrapper if missing +if [ ! -f "gradlew" ]; then + echo "Gradle wrapper missing, generating..." + GRADLE_VERSION="${GRADLE_VERSION:-8.14.3}" + DIST_URL="https://services.gradle.org/distributions/gradle-${GRADLE_VERSION}-bin.zip" + TMP_DIR="$(mktemp -d)" + ZIP_PATH="$TMP_DIR/gradle-${GRADLE_VERSION}.zip" + UNZIP_DIR="$TMP_DIR/unzip" + + echo "Downloading Gradle ${GRADLE_VERSION}..." + curl -sSL "$DIST_URL" -o "$ZIP_PATH" + mkdir -p "$UNZIP_DIR" + if command -v unzip >/dev/null 2>&1; then + unzip -q "$ZIP_PATH" -d "$UNZIP_DIR" + else + (cd "$UNZIP_DIR" && jar xvf "$ZIP_PATH" >/dev/null) + fi + + echo "Bootstrapping wrapper with Gradle ${GRADLE_VERSION}..." + "$UNZIP_DIR/gradle-${GRADLE_VERSION}/bin/gradle" wrapper --gradle-version "$GRADLE_VERSION" + + rm -rf "$TMP_DIR" +fi +echo -e "${GREEN}โœ… Gradle test environment ready${NC}" + +echo "" +echo -e "${BLUE}๐Ÿงช Step 3: Running Kotlin tests with verbose output...${NC}" +echo "" + +./gradlew --no-daemon walletkit-tests:test --info --continue + +echo "" +echo "๐Ÿ“Š Test Results Summary:" +echo "========================" + +if [ -d "$TEST_RESULTS_DIR" ]; then + echo "โœ… Test results found in: $TEST_RESULTS_DIR" + TOTAL_TESTS=$(find "$TEST_RESULTS_DIR" -name "*.xml" -exec grep -l "testcase" {} \; | wc -l | tr -d ' ') + if [ "$TOTAL_TESTS" -gt 0 ]; then + echo "๐Ÿ“‹ Total test files: $TOTAL_TESTS" + PASSED=$(find "$TEST_RESULTS_DIR" -name "*.xml" -exec grep -o "tests=\"[0-9]*\"" {} \; | cut -d'"' -f2 | awk '{sum+=$1} END {print sum+0}') + FAILURES=$(find "$TEST_RESULTS_DIR" -name "*.xml" -exec grep -o "failures=\"[0-9]*\"" {} \; | cut -d'"' -f2 | awk '{sum+=$1} END {print sum+0}') + ERRORS=$(find "$TEST_RESULTS_DIR" -name "*.xml" -exec grep -o "errors=\"[0-9]*\"" {} \; | cut -d'"' -f2 | awk '{sum+=$1} END {print sum+0}') + + echo "โœ… Tests passed: $PASSED" + echo "โŒ Tests failed: $FAILURES" + echo "โš ๏ธ Test errors: $ERRORS" + + if [ "$FAILURES" -gt 0 ] || [ "$ERRORS" -gt 0 ]; then + echo "" + echo -e "${YELLOW}โš ๏ธ Some tests failed${NC}" + exit 1 + else + echo "" + echo -e "${GREEN}๐ŸŽ‰ All tests passed!${NC}" + exit 0 + fi + fi +else + echo "โš ๏ธ No test results found" + echo "" + echo -e "${RED}โœ— Could not determine test results${NC}" + exit 1 +fi diff --git a/kotlin/walletkit-android/build.gradle.kts b/kotlin/walletkit-android/build.gradle.kts new file mode 100644 index 000000000..e6f047448 --- /dev/null +++ b/kotlin/walletkit-android/build.gradle.kts @@ -0,0 +1,80 @@ +import java.io.ByteArrayOutputStream + +plugins { + id("com.android.library") + id("org.jetbrains.kotlin.android") + id("maven-publish") +} + +kotlin { + jvmToolchain(17) +} + +android { + namespace = "org.world.walletkit" + compileSdk = 33 + + defaultConfig { + minSdk = 23 + @Suppress("deprecation") + targetSdk = 33 + consumerProguardFiles("consumer-rules.pro") + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } + + kotlinOptions { + jvmTarget = "17" + } + + publishing { + singleVariant("release") { + withSourcesJar() + } + } +} + +afterEvaluate { + publishing { + publications { + create("maven") { + groupId = "org.world" + artifactId = "walletkit-android" + + // Read version from Cargo.toml + val cargoToml = file("../../Cargo.toml") + val versionRegex = """version\s*=\s*"([^"]+)"""".toRegex() + val cargoContent = cargoToml.readText() + version = versionRegex.find(cargoContent)?.groupValues?.get(1) + ?: throw GradleException("Could not find version in Cargo.toml") + + afterEvaluate { + from(components["release"]) + } + } + } + + repositories { + maven { + name = "GitHubPackages" + url = uri("https://maven.pkg.github.com/worldcoin/walletkit") + credentials { + username = System.getenv("GITHUB_ACTOR") + password = System.getenv("GITHUB_TOKEN") + } + } + } + } +} + +dependencies { + // UniFFI requires JNA for native calls + implementation("net.java.dev.jna:jna:5.13.0") + implementation("androidx.core:core-ktx:1.8.0") + implementation("androidx.appcompat:appcompat:1.4.1") + implementation("com.google.android.material:material:1.5.0") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3") +} diff --git a/kotlin/walletkit-android/consumer-rules.pro b/kotlin/walletkit-android/consumer-rules.pro new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/kotlin/walletkit-android/consumer-rules.pro @@ -0,0 +1 @@ + diff --git a/kotlin/walletkit-tests/build.gradle.kts b/kotlin/walletkit-tests/build.gradle.kts new file mode 100644 index 000000000..d3a5f433c --- /dev/null +++ b/kotlin/walletkit-tests/build.gradle.kts @@ -0,0 +1,31 @@ +// This build.gradle uses a JVM-only testing engine for unit testing. +// Note this is separate from the build.gradle used for building and publishing the actual library. + +plugins { + kotlin("jvm") +} + +repositories { + mavenCentral() +} + +dependencies { + implementation("net.java.dev.jna:jna:5.13.0") + testImplementation("org.jetbrains.kotlin:kotlin-test") + testImplementation("junit:junit:4.13.2") + testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.3") +} + +sourceSets { + test { + kotlin.srcDirs( + "$rootDir/walletkit-android/src/main/java/uniffi/walletkit_core" + ) + } +} + +tasks.test { + useJUnit() + systemProperty("jna.library.path", "${rootDir}/libs") + reports.html.required.set(false) +} diff --git a/kotlin/walletkit-tests/src/test/kotlin/org/world/walletkit/SimpleTest.kt b/kotlin/walletkit-tests/src/test/kotlin/org/world/walletkit/SimpleTest.kt new file mode 100644 index 000000000..f0efbfac9 --- /dev/null +++ b/kotlin/walletkit-tests/src/test/kotlin/org/world/walletkit/SimpleTest.kt @@ -0,0 +1,13 @@ +package org.world.walletkit + +import java.io.File +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNull + +class SimpleTests { + @Test + fun simpleTest() { + assertEquals(1, 1) + } +} diff --git a/swift/README.md b/swift/README.md new file mode 100644 index 000000000..8f0e979b9 --- /dev/null +++ b/swift/README.md @@ -0,0 +1,57 @@ +# Swift for WalletKit + +This folder contains Swift support files for WalletKit: + +1. Script to cross-compile and build Swift bindings. +2. Script to build a Swift package for local development. +3. Foreign tests (XCTest suite) for Swift under `tests/`. + +## Building the Swift bindings + +To build the Swift project for release/distribution: + +```bash + # run from the walletkit directory + ./swift/build_swift.sh +``` + +## Testing WalletKit locally + +To build a Swift package that can be imported locally via Swift Package Manager: + +```bash + # run from the walletkit directory + ./swift/local_swift.sh +``` + +This creates a complete Swift package in `swift/local_build/` that you can import in your iOS project. + +## Integration via Package.swift + +Add the local package to your Package.swift dependencies: + +```swift +dependencies: [ + .package(name: "WalletKit", path: "../../../walletkit/swift/local_build"), + // ... other dependencies +], +``` + +Then add it to specific targets that need WalletKit functionality: + +```swift +.target( + name: "YourTarget", + dependencies: [ + .product(name: "WalletKit", package: "WalletKit"), + // ... other dependencies + ] +), +``` + +## Running foreign tests for Swift + +```bash + # run from the walletkit directory + ./swift/test_swift.sh +``` diff --git a/swift/archive_swift.sh b/swift/archive_swift.sh new file mode 100755 index 000000000..6a1351577 --- /dev/null +++ b/swift/archive_swift.sh @@ -0,0 +1,95 @@ +#!/bin/bash +set -e + +# Creates the dynamic Package.swift file for release. +# Usage: ./archive_swift.sh --asset-url --checksum --release-version + +# Initialize variables +ASSET_URL="" +CHECKSUM="" +RELEASE_VERSION="" + +# Function to show usage +show_usage() { + echo "โŒ Error: Missing required arguments" + echo "Usage: $0 --asset-url --checksum --release-version " + echo "" + echo "Example:" + echo " $0 --asset-url 'https://github.com/user/repo/releases/download/v1.0.0/WalletKit.xcframework.zip' --checksum 'abc123def456...' --release-version '1.0.0'" + exit 1 +} + +# Parse command line arguments +while [[ $# -gt 0 ]]; do + case $1 in + --asset-url) + ASSET_URL="$2" + shift 2 + ;; + --checksum) + CHECKSUM="$2" + shift 2 + ;; + --release-version) + RELEASE_VERSION="$2" + shift 2 + ;; + -h|--help) + show_usage + ;; + *) + echo "โŒ Unknown argument: $1" + show_usage + ;; + esac +done + +# Check if all required arguments are provided +if [ -z "$ASSET_URL" ] || [ -z "$CHECKSUM" ] || [ -z "$RELEASE_VERSION" ]; then + echo "โŒ Error: All arguments are required" + show_usage +fi + +echo "๐Ÿ”ง Creating Package.swift with:" +echo " Asset URL: $ASSET_URL" +echo " Checksum: $CHECKSUM" +echo " Release Version: $RELEASE_VERSION" +echo "" + +cat > Package.swift << EOF +// swift-tools-version: 5.7 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +// Release version: $RELEASE_VERSION + +import PackageDescription + +let package = Package( + name: "WalletKit", + platforms: [ + .iOS(.v13) + ], + products: [ + .library( + name: "WalletKit", + targets: ["WalletKit"]), + ], + targets: [ + .target( + name: "WalletKit", + dependencies: ["walletkit_coreFFI"], + path: "Sources/WalletKit" + ), + .binaryTarget( + name: "walletkit_coreFFI", + url: "$ASSET_URL", + checksum: "$CHECKSUM" + ) + ] +) +EOF + +swiftlint lint --autocorrect Package.swift + +echo "" +echo "โœ… Package.swift built successfully for version $RELEASE_VERSION!" diff --git a/swift/build_swift.sh b/swift/build_swift.sh new file mode 100755 index 000000000..90d5c32ad --- /dev/null +++ b/swift/build_swift.sh @@ -0,0 +1,127 @@ +#!/bin/bash +set -e + +# Creates a Swift build of the `WalletKit` library. +# This script can be used directly or called by other scripts. +# +# Usage: build_swift.sh [OUTPUT_DIR] +# OUTPUT_DIR: Directory where the XCFramework should be placed (default: swift/) + +PROJECT_ROOT_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +BASE_PATH="$PROJECT_ROOT_PATH/swift" # The base path for the Swift build +PACKAGE_NAME="walletkit" +TARGET_DIR="$PROJECT_ROOT_PATH/target" +FEATURES="v4" +SUPPORT_SOURCES_DIR="$BASE_PATH/support" + +# Default values +OUTPUT_DIR="$BASE_PATH" # Default to BASE_PATH if not provided +FRAMEWORK="WalletKit.xcframework" + +# Parse arguments +while [[ $# -gt 0 ]]; do + case $1 in + --help|-h) + echo "Usage: $0 [OUTPUT_DIR]" + echo "" + echo "Arguments:" + echo " OUTPUT_DIR Directory where the XCFramework should be placed (default: swift/)" + echo "" + exit 0 + ;; + *) + # Assume it's the output directory if it doesn't start with -- + if [[ ! "$1" =~ ^-- ]]; then + OUTPUT_DIR="$1" + else + echo "Unknown option: $1" + echo "Use --help for usage information" + exit 1 + fi + shift + ;; + esac +done + +# Resolve OUTPUT_DIR to absolute path if it's relative +if [[ "$OUTPUT_DIR" != /* ]]; then + OUTPUT_DIR="$BASE_PATH/$OUTPUT_DIR" +fi + +SWIFT_SOURCES_DIR="$OUTPUT_DIR/Sources/WalletKit" +SWIFT_HEADERS_DIR="$BASE_PATH/ios_build/Headers/WalletKit" +FRAMEWORK_OUTPUT="$OUTPUT_DIR/$FRAMEWORK" + +echo "Building $FRAMEWORK to $FRAMEWORK_OUTPUT" + +# Clean up previous builds +rm -rf "$BASE_PATH/ios_build" +rm -rf "$FRAMEWORK_OUTPUT" + +# Create necessary directories +mkdir -p "$BASE_PATH/ios_build/bindings" +mkdir -p "$BASE_PATH/ios_build/target/universal-ios-sim/release" +mkdir -p "$SWIFT_SOURCES_DIR" +mkdir -p "$SWIFT_HEADERS_DIR" + +echo "Building Rust packages for iOS targets..." + +export IPHONEOS_DEPLOYMENT_TARGET="13.0" +export RUSTFLAGS="-C link-arg=-Wl,-application_extension" + +# Build for all iOS targets +cargo build --package $PACKAGE_NAME --target aarch64-apple-ios-sim --release \ + --manifest-path "$PROJECT_ROOT_PATH/Cargo.toml" --target-dir "$TARGET_DIR" \ + --features "$FEATURES" +cargo build --package $PACKAGE_NAME --target aarch64-apple-ios --release \ + --manifest-path "$PROJECT_ROOT_PATH/Cargo.toml" --target-dir "$TARGET_DIR" \ + --features "$FEATURES" +cargo build --package $PACKAGE_NAME --target x86_64-apple-ios --release \ + --manifest-path "$PROJECT_ROOT_PATH/Cargo.toml" --target-dir "$TARGET_DIR" \ + --features "$FEATURES" + +echo "Rust packages built. Combining simulator targets into universal binary..." + +# Create universal binary for simulators +lipo -create "$TARGET_DIR/aarch64-apple-ios-sim/release/lib${PACKAGE_NAME}.a" \ + "$TARGET_DIR/x86_64-apple-ios/release/lib${PACKAGE_NAME}.a" \ + -output $BASE_PATH/ios_build/target/universal-ios-sim/release/lib${PACKAGE_NAME}.a + +lipo -info $BASE_PATH/ios_build/target/universal-ios-sim/release/lib${PACKAGE_NAME}.a + +echo "Generating Swift bindings..." + +# Generate Swift bindings using uniffi +cargo run -p uniffi-bindgen --manifest-path "$PROJECT_ROOT_PATH/Cargo.toml" \ + --target-dir "$TARGET_DIR" -- generate \ + "$TARGET_DIR/aarch64-apple-ios-sim/release/lib${PACKAGE_NAME}.dylib" \ + --library \ + --crate walletkit_core \ + --language swift \ + --no-format \ + --out-dir $BASE_PATH/ios_build/bindings + +# Move generated Swift file to Sources directory +mv $BASE_PATH/ios_build/bindings/walletkit_core.swift ${SWIFT_SOURCES_DIR}/walletkit.swift + +# Copy support Swift sources for the WalletKit module. +if [ -d "$SUPPORT_SOURCES_DIR" ]; then + rsync -a "$SUPPORT_SOURCES_DIR"/ "$SWIFT_SOURCES_DIR"/ +fi + +# Move headers +mv $BASE_PATH/ios_build/bindings/walletkit_coreFFI.h $SWIFT_HEADERS_DIR/ +cat $BASE_PATH/ios_build/bindings/walletkit_coreFFI.modulemap > $SWIFT_HEADERS_DIR/module.modulemap + +echo "Creating XCFramework..." + +# Create XCFramework +xcodebuild -create-xcframework \ + -library "$TARGET_DIR/aarch64-apple-ios/release/lib${PACKAGE_NAME}.a" -headers $BASE_PATH/ios_build/Headers \ + -library $BASE_PATH/ios_build/target/universal-ios-sim/release/lib${PACKAGE_NAME}.a -headers $BASE_PATH/ios_build/Headers \ + -output $FRAMEWORK_OUTPUT + +# Clean up intermediate build files +rm -rf $BASE_PATH/ios_build + +echo "โœ… Swift framework built successfully at: $FRAMEWORK_OUTPUT" diff --git a/swift/local_swift.sh b/swift/local_swift.sh new file mode 100755 index 000000000..11b74dcab --- /dev/null +++ b/swift/local_swift.sh @@ -0,0 +1,71 @@ +#!/bin/bash +set -e + +# Creates a Swift package of the `WalletKit` library for local development. +# This script builds the library and sets up the proper structure for importing +# via Swift Package Manager using a local file:// URL. +# All artifacts are placed in swift/local_build to keep the repo clean. + +PROJECT_ROOT_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +BASE_PATH="$PROJECT_ROOT_PATH/swift" # The base path for the Swift build +LOCAL_BUILD_PATH="$BASE_PATH/local_build" # Local build artifacts directory +FRAMEWORK="WalletKit.xcframework" + +echo "Building $FRAMEWORK for local iOS development" + +# Clean up previous builds +rm -rf "$LOCAL_BUILD_PATH" + +# Create the local build directory +mkdir -p "$LOCAL_BUILD_PATH" + +echo "Running core Swift build..." + +# Call the main build script with local build directory +bash "$BASE_PATH/build_swift.sh" "$LOCAL_BUILD_PATH" + +echo "Creating Package.swift for local development..." + +# Create Package.swift for local development +cat > $LOCAL_BUILD_PATH/Package.swift << EOF +// swift-tools-version: 5.7 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "WalletKit", + platforms: [ + .iOS(.v13) + ], + products: [ + .library( + name: "WalletKit", + targets: ["WalletKit"]), + ], + targets: [ + .target( + name: "WalletKit", + dependencies: ["walletkit_coreFFI"], + path: "Sources/WalletKit" + ), + .binaryTarget( + name: "walletkit_coreFFI", + path: "WalletKit.xcframework" + ) + ] +) +EOF + +echo "" +echo "โœ… Swift package built successfully!" +echo "" +echo "๐Ÿ“ฆ Package location: $LOCAL_BUILD_PATH" +echo "" +echo "To use this package in your iOS app:" +echo "1. In Xcode, go to File โ†’ Add Package Dependencies..." +echo "2. Click 'Add Local...' and select the local_build directory: $LOCAL_BUILD_PATH" +echo "3. Or add it to your Package.swift dependencies:" +echo " .package(path: \"$LOCAL_BUILD_PATH\")" +echo "" +echo "The package exports the 'WalletKit' library that you can import in your Swift code." diff --git a/swift/tests/WalletKitTests/AuthenticatorTests.swift b/swift/tests/WalletKitTests/AuthenticatorTests.swift deleted file mode 100644 index 411e42e63..000000000 --- a/swift/tests/WalletKitTests/AuthenticatorTests.swift +++ /dev/null @@ -1,386 +0,0 @@ -import XCTest -@testable import WalletKit - -final class AuthenticatorTests: XCTestCase { - - let testRpcUrl = "https://worldchain-sepolia.g.alchemy.com/public" - - // MARK: - Helper Functions - - func generateRandomSeed() -> Data { - var bytes = [UInt8](repeating: 0, count: 32) - for i in 0..<32 { - bytes[i] = UInt8.random(in: 0...255) - } - return Data(bytes) - } - - // MARK: - U256Wrapper Tests - - func testU256WrapperFromU64() { - let value: UInt64 = 12345 - let u256 = U256Wrapper.fromU64(value: value) - XCTAssertEqual(u256.toDecimalString(), "12345") - } - - func testU256WrapperFromU32() { - let value: UInt32 = 54321 - let u256 = U256Wrapper.fromU32(value: value) - XCTAssertEqual(u256.toDecimalString(), "54321") - } - - func testU256WrapperFromU64MaxValue() { - // Test with max u64 value - let maxU64 = UInt64.max - let u256 = U256Wrapper.fromU64(value: maxU64) - XCTAssertEqual(u256.toDecimalString(), "18446744073709551615") - XCTAssertEqual(u256.toHexString(), "0x000000000000000000000000000000000000000000000000ffffffffffffffff") - } - - func testU256WrapperFromU32MaxValue() { - // Test with max u32 value - let maxU32 = UInt32.max - let u256 = U256Wrapper.fromU32(value: maxU32) - XCTAssertEqual(u256.toDecimalString(), "4294967295") - } - - func testU256WrapperTryFromHexString() throws { - let hexString = "0x1a2b3c4d5e6f" - let u256 = try U256Wrapper.tryFromHexString(hexString: hexString) - XCTAssertNotNil(u256) - // Verify the hex round-trips correctly - XCTAssertTrue(u256.toHexString().hasSuffix("1a2b3c4d5e6f")) - } - - func testU256WrapperTryFromHexStringWithoutPrefix() throws { - let hexString = "1a2b3c4d5e6f" - let u256 = try U256Wrapper.tryFromHexString(hexString: hexString) - XCTAssertNotNil(u256) - } - - func testU256WrapperDeterministicHexParsing() throws { - // Test with known values from Rust tests - let testCases: [(String, String, String)] = [ - ( - "0x0000000000000000000000000000000000000000000000000000000000000001", - "1", - "0x0000000000000000000000000000000000000000000000000000000000000001" - ), - ( - "0x000000000000000000000000000000000000000000000000000000000000002a", - "42", - "0x000000000000000000000000000000000000000000000000000000000000002a" - ), - ( - "0x00000000000000000000000000000000000000000000000000000000000f423f", - "999999", - "0x00000000000000000000000000000000000000000000000000000000000f423f" - ), - ( - "0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6", - "80084422859880547211683076133703299733277748156566366325829078699459944778998", - "0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6" - ), - ] - - for (hexInput, expectedDecimal, expectedHex) in testCases { - let u256 = try U256Wrapper.tryFromHexString(hexString: hexInput) - XCTAssertEqual(u256.toDecimalString(), expectedDecimal, "Decimal mismatch for \(hexInput)") - XCTAssertEqual(u256.toHexString(), expectedHex, "Hex mismatch for \(hexInput)") - } - } - - func testU256WrapperHexRoundTrip() throws { - // Test that parsing and formatting hex strings round-trips correctly - let hexStrings = [ - "0x0000000000000000000000000000000000000000000000000000000000000001", - "0x00000000000000000000000000000000000000000000000000000000000000ff", - "0x0000000000000000000000000000000000000000000000000000000000001234", - "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - ] - - for hexString in hexStrings { - let u256 = try U256Wrapper.tryFromHexString(hexString: hexString) - XCTAssertEqual(u256.toHexString(), hexString, "Round-trip failed for \(hexString)") - } - } - - func testU256WrapperInvalidHexString() { - XCTAssertThrowsError(try U256Wrapper.tryFromHexString(hexString: "0xZZZ")) { error in - XCTAssertTrue(error is WalletKitError) - } - } - - func testU256WrapperInvalidHexStrings() { - // Test multiple invalid inputs - let invalidInputs = [ - "0xZZZZ", - "1g", - "not a hex string", - "0xGGGG", - ] - - for invalidInput in invalidInputs { - XCTAssertThrowsError(try U256Wrapper.tryFromHexString(hexString: invalidInput)) { error in - XCTAssertTrue(error is WalletKitError, "Should throw WalletKitError for: \(invalidInput)") - } - } - } - - func testU256WrapperEmptyString() throws { - // Empty string parses as 0 (after trimming "0x", "" is passed to radix parser) - let u256 = try U256Wrapper.tryFromHexString(hexString: "") - XCTAssertEqual(u256.toDecimalString(), "0") - XCTAssertEqual(u256.toHexString(), "0x0000000000000000000000000000000000000000000000000000000000000000") - } - - func testU256WrapperFromLimbs() throws { - // Test with simple value [1, 0, 0, 0] - let limbs: [UInt64] = [1, 0, 0, 0] - let u256 = try U256Wrapper.fromLimbs(limbs: limbs) - XCTAssertEqual(u256.toDecimalString(), "1") - } - - func testU256WrapperFromLimbsComplexValue() throws { - // Test with complex limb values from Rust tests - let limbs: [UInt64] = [1, 0, 0, 2161727821137838080] - let u256 = try U256Wrapper.fromLimbs(limbs: limbs) - XCTAssertEqual( - u256.toHexString(), - "0x1e00000000000000000000000000000000000000000000000000000000000001" - ) - } - - func testU256WrapperFromLimbsInvalidLength() { - // Must be exactly 4 limbs - XCTAssertThrowsError(try U256Wrapper.fromLimbs(limbs: [1, 0, 0])) { error in - XCTAssertTrue(error is WalletKitError) - } - - XCTAssertThrowsError(try U256Wrapper.fromLimbs(limbs: [1, 0, 0, 0, 5])) { error in - XCTAssertTrue(error is WalletKitError) - } - - XCTAssertThrowsError(try U256Wrapper.fromLimbs(limbs: [])) { error in - XCTAssertTrue(error is WalletKitError) - } - } - - func testU256WrapperToHexString() { - let u256 = U256Wrapper.fromU64(value: 42) - let hexString = u256.toHexString() - // Should be padded to 66 characters (0x + 64 hex digits) - XCTAssertEqual(hexString.count, 66) - XCTAssertTrue(hexString.hasPrefix("0x")) - XCTAssertTrue(hexString.hasSuffix("2a")) - } - - func testU256WrapperToHexStringPadding() { - // Test that small values are properly padded - let testCases: [(UInt64, String)] = [ - (1, "0x0000000000000000000000000000000000000000000000000000000000000001"), - (2, "0x0000000000000000000000000000000000000000000000000000000000000002"), - (255, "0x00000000000000000000000000000000000000000000000000000000000000ff"), - ] - - for (value, expectedHex) in testCases { - let u256 = U256Wrapper.fromU64(value: value) - XCTAssertEqual(u256.toHexString(), expectedHex) - } - } - - func testU256WrapperIntoLimbs() { - let u256 = U256Wrapper.fromU64(value: 12345) - let limbs = u256.intoLimbs() - XCTAssertEqual(limbs.count, 4) - XCTAssertEqual(limbs[0], 12345) - XCTAssertEqual(limbs[1], 0) - XCTAssertEqual(limbs[2], 0) - XCTAssertEqual(limbs[3], 0) - } - - func testU256WrapperLimbsRoundTrip() throws { - // Test that converting to/from limbs round-trips correctly - let originalLimbs: [UInt64] = [12345, 67890, 11111, 22222] - let u256 = try U256Wrapper.fromLimbs(limbs: originalLimbs) - let resultLimbs = u256.intoLimbs() - - XCTAssertEqual(resultLimbs, originalLimbs) - } - - func testU256WrapperZeroValue() { - let u256 = U256Wrapper.fromU64(value: 0) - XCTAssertEqual(u256.toDecimalString(), "0") - XCTAssertEqual(u256.toHexString(), "0x0000000000000000000000000000000000000000000000000000000000000000") - - let limbs = u256.intoLimbs() - XCTAssertEqual(limbs, [0, 0, 0, 0]) - } - - func testU256WrapperMultipleConversions() throws { - // Test creating U256 from different sources and verifying consistency - let value: UInt64 = 999999 - - let fromU64 = U256Wrapper.fromU64(value: value) - let fromHex = try U256Wrapper.tryFromHexString( - hexString: "0x00000000000000000000000000000000000000000000000000000000000f423f" - ) - let fromLimbs = try U256Wrapper.fromLimbs(limbs: [999999, 0, 0, 0]) - - // All should produce the same decimal string - XCTAssertEqual(fromU64.toDecimalString(), "999999") - XCTAssertEqual(fromHex.toDecimalString(), "999999") - XCTAssertEqual(fromLimbs.toDecimalString(), "999999") - - // All should produce the same hex string - let expectedHex = "0x00000000000000000000000000000000000000000000000000000000000f423f" - XCTAssertEqual(fromU64.toHexString(), expectedHex) - XCTAssertEqual(fromHex.toHexString(), expectedHex) - XCTAssertEqual(fromLimbs.toHexString(), expectedHex) - } - - // MARK: - Authenticator Initialization Tests - - func testInvalidSeedEmpty() async { - let emptySeed = Data() - - await XCTAssertThrowsErrorAsync( - try await Authenticator.initWithDefaults( - seed: emptySeed, - rpcUrl: testRpcUrl, - environment: .staging - ) - ) { error in - if let walletError = error as? WalletKitError, - case .InvalidInput(let attribute, _) = walletError { - XCTAssertEqual(attribute, "seed") - } else { - XCTFail("Expected InvalidInput for seed, got \(error)") - } - } - } - - func testInvalidSeedTooShort() async { - let shortSeed = Data(repeating: 0, count: 16) - - await XCTAssertThrowsErrorAsync( - try await Authenticator.initWithDefaults( - seed: shortSeed, - rpcUrl: testRpcUrl, - environment: .staging - ) - ) { error in - if let walletError = error as? WalletKitError, - case .InvalidInput(let attribute, _) = walletError { - XCTAssertEqual(attribute, "seed") - } else { - XCTFail("Expected InvalidInput for seed, got \(error)") - } - } - } - - func testInvalidSeedTooLong() async { - let longSeed = Data(repeating: 0, count: 64) - - await XCTAssertThrowsErrorAsync( - try await Authenticator.initWithDefaults( - seed: longSeed, - rpcUrl: testRpcUrl, - environment: .staging - ) - ) { error in - if let walletError = error as? WalletKitError, - case .InvalidInput(let attribute, _) = walletError { - XCTAssertEqual(attribute, "seed") - } else { - XCTFail("Expected InvalidInput for seed, got \(error)") - } - } - } - - func testInvalidRpcUrlEmpty() async { - let seed = generateRandomSeed() - - await XCTAssertThrowsErrorAsync( - try await Authenticator.initWithDefaults( - seed: seed, - rpcUrl: "", - environment: .staging - ) - ) { error in - if let walletError = error as? WalletKitError, - case .InvalidInput(let attribute, _) = walletError { - XCTAssertEqual(attribute, "rpc_url") - } else { - XCTFail("Expected InvalidInput for rpc_url, got \(error)") - } - } - } - - func testMultipleEnvironments() async { - let seed = generateRandomSeed() - let environments: [Environment] = [.staging, .production] - - for environment in environments { - await XCTAssertThrowsErrorAsync( - try await Authenticator.initWithDefaults( - seed: seed, - rpcUrl: testRpcUrl, - environment: environment - ) - ) { error in - // Should throw an error for non-existent account in any environment - XCTAssertTrue(error is WalletKitError, "Should throw WalletKitError for \(environment)") - } - } - } - - func testValidSeedLength() { - let validSeed = Data(repeating: 0, count: 32) - XCTAssertEqual(validSeed.count, 32, "Valid seed should be 32 bytes") - } - - func testGenerateRandomSeedLength() { - let seed = generateRandomSeed() - XCTAssertEqual(seed.count, 32, "Generated seed should be 32 bytes") - } - - func testGenerateRandomSeedRandomness() { - // Generate multiple seeds and verify they're different - let seed1 = generateRandomSeed() - let seed2 = generateRandomSeed() - let seed3 = generateRandomSeed() - - XCTAssertNotEqual(seed1, seed2, "Seeds should be random and different") - XCTAssertNotEqual(seed2, seed3, "Seeds should be random and different") - XCTAssertNotEqual(seed1, seed3, "Seeds should be random and different") - } - - // MARK: - Helper for async error assertions - - func XCTAssertThrowsErrorAsync( - _ expression: @autoclosure () async throws -> T, - _ message: @autoclosure () -> String = "", - file: StaticString = #filePath, - line: UInt = #line, - _ errorHandler: (_ error: Error) -> Void = { _ in } - ) async { - do { - _ = try await expression() - XCTFail(message(), file: file, line: line) - } catch { - errorHandler(error) - } - } - - // MARK: - Environment Tests - - func testEnvironmentValues() { - // Just verify environments exist and can be created - let staging = Environment.staging - let production = Environment.production - - XCTAssertNotNil(staging) - XCTAssertNotNil(production) - } -} diff --git a/swift/tests/WalletKitTests/SimpleTest.swift b/swift/tests/WalletKitTests/SimpleTest.swift new file mode 100644 index 000000000..fb30acd50 --- /dev/null +++ b/swift/tests/WalletKitTests/SimpleTest.swift @@ -0,0 +1,8 @@ +import XCTest +@testable import WalletKit + +final class SimpleTest: XCTestCase { + func simpleTest() { + XCTAssertEqual(1, 1) + } +} From 1babaf4d068ea57183c30b0cfb749134be05b421 Mon Sep 17 00:00:00 2001 From: Luke Mann Date: Wed, 21 Jan 2026 15:19:00 -0800 Subject: [PATCH 02/10] restore authenticator tests --- .../WalletKitTests/AuthenticatorTests.swift | 386 ++++++++++++++++++ 1 file changed, 386 insertions(+) create mode 100644 swift/tests/WalletKitTests/AuthenticatorTests.swift diff --git a/swift/tests/WalletKitTests/AuthenticatorTests.swift b/swift/tests/WalletKitTests/AuthenticatorTests.swift new file mode 100644 index 000000000..969ea8014 --- /dev/null +++ b/swift/tests/WalletKitTests/AuthenticatorTests.swift @@ -0,0 +1,386 @@ +import XCTest +@testable import WalletKit + +final class AuthenticatorTests: XCTestCase { + + let testRpcUrl = "https://worldchain-sepolia.g.alchemy.com/public" + + // MARK: - Helper Functions + + func generateRandomSeed() -> Data { + var bytes = [UInt8](repeating: 0, count: 32) + for i in 0..<32 { + bytes[i] = UInt8.random(in: 0...255) + } + return Data(bytes) + } + + // MARK: - U256Wrapper Tests + + func testU256WrapperFromU64() { + let value: UInt64 = 12345 + let u256 = U256Wrapper.fromU64(value: value) + XCTAssertEqual(u256.toDecimalString(), "12345") + } + + func testU256WrapperFromU32() { + let value: UInt32 = 54321 + let u256 = U256Wrapper.fromU32(value: value) + XCTAssertEqual(u256.toDecimalString(), "54321") + } + + func testU256WrapperFromU64MaxValue() { + // Test with max u64 value + let maxU64 = UInt64.max + let u256 = U256Wrapper.fromU64(value: maxU64) + XCTAssertEqual(u256.toDecimalString(), "18446744073709551615") + XCTAssertEqual(u256.toHexString(), "0x000000000000000000000000000000000000000000000000ffffffffffffffff") + } + + func testU256WrapperFromU32MaxValue() { + // Test with max u32 value + let maxU32 = UInt32.max + let u256 = U256Wrapper.fromU32(value: maxU32) + XCTAssertEqual(u256.toDecimalString(), "4294967295") + } + + func testU256WrapperTryFromHexString() throws { + let hexString = "0x1a2b3c4d5e6f" + let u256 = try U256Wrapper.tryFromHexString(hexString: hexString) + XCTAssertNotNil(u256) + // Verify the hex round-trips correctly + XCTAssertTrue(u256.toHexString().hasSuffix("1a2b3c4d5e6f")) + } + + func testU256WrapperTryFromHexStringWithoutPrefix() throws { + let hexString = "1a2b3c4d5e6f" + let u256 = try U256Wrapper.tryFromHexString(hexString: hexString) + XCTAssertNotNil(u256) + } + + func testU256WrapperDeterministicHexParsing() throws { + // Test with known values from Rust tests + let testCases: [(String, String, String)] = [ + ( + "0x0000000000000000000000000000000000000000000000000000000000000001", + "1", + "0x0000000000000000000000000000000000000000000000000000000000000001" + ), + ( + "0x000000000000000000000000000000000000000000000000000000000000002a", + "42", + "0x000000000000000000000000000000000000000000000000000000000000002a" + ), + ( + "0x00000000000000000000000000000000000000000000000000000000000f423f", + "999999", + "0x00000000000000000000000000000000000000000000000000000000000f423f" + ), + ( + "0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6", + "80084422859880547211683076133703299733277748156566366325829078699459944778998", + "0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6" + ), + ] + + for (hexInput, expectedDecimal, expectedHex) in testCases { + let u256 = try U256Wrapper.tryFromHexString(hexString: hexInput) + XCTAssertEqual(u256.toDecimalString(), expectedDecimal, "Decimal mismatch for \(hexInput)") + XCTAssertEqual(u256.toHexString(), expectedHex, "Hex mismatch for \(hexInput)") + } + } + + func testU256WrapperHexRoundTrip() throws { + // Test that parsing and formatting hex strings round-trips correctly + let hexStrings = [ + "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x00000000000000000000000000000000000000000000000000000000000000ff", + "0x0000000000000000000000000000000000000000000000000000000000001234", + "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + ] + + for hexString in hexStrings { + let u256 = try U256Wrapper.tryFromHexString(hexString: hexString) + XCTAssertEqual(u256.toHexString(), hexString, "Round-trip failed for \(hexString)") + } + } + + func testU256WrapperInvalidHexString() { + XCTAssertThrowsError(try U256Wrapper.tryFromHexString(hexString: "0xZZZ")) { error in + XCTAssertTrue(error is WalletKitError) + } + } + + func testU256WrapperInvalidHexStrings() { + // Test multiple invalid inputs + let invalidInputs = [ + "0xZZZZ", + "1g", + "not a hex string", + "0xGGGG", + ] + + for invalidInput in invalidInputs { + XCTAssertThrowsError(try U256Wrapper.tryFromHexString(hexString: invalidInput)) { error in + XCTAssertTrue(error is WalletKitError, "Should throw WalletKitError for: \(invalidInput)") + } + } + } + + func testU256WrapperEmptyString() throws { + // Empty string parses as 0 (after trimming "0x", "" is passed to radix parser) + let u256 = try U256Wrapper.tryFromHexString(hexString: "") + XCTAssertEqual(u256.toDecimalString(), "0") + XCTAssertEqual(u256.toHexString(), "0x0000000000000000000000000000000000000000000000000000000000000000") + } + + func testU256WrapperFromLimbs() throws { + // Test with simple value [1, 0, 0, 0] + let limbs: [UInt64] = [1, 0, 0, 0] + let u256 = try U256Wrapper.fromLimbs(limbs: limbs) + XCTAssertEqual(u256.toDecimalString(), "1") + } + + func testU256WrapperFromLimbsComplexValue() throws { + // Test with complex limb values from Rust tests + let limbs: [UInt64] = [1, 0, 0, 2161727821137838080] + let u256 = try U256Wrapper.fromLimbs(limbs: limbs) + XCTAssertEqual( + u256.toHexString(), + "0x1e00000000000000000000000000000000000000000000000000000000000001" + ) + } + + func testU256WrapperFromLimbsInvalidLength() { + // Must be exactly 4 limbs + XCTAssertThrowsError(try U256Wrapper.fromLimbs(limbs: [1, 0, 0])) { error in + XCTAssertTrue(error is WalletKitError) + } + + XCTAssertThrowsError(try U256Wrapper.fromLimbs(limbs: [1, 0, 0, 0, 5])) { error in + XCTAssertTrue(error is WalletKitError) + } + + XCTAssertThrowsError(try U256Wrapper.fromLimbs(limbs: [])) { error in + XCTAssertTrue(error is WalletKitError) + } + } + + func testU256WrapperToHexString() { + let u256 = U256Wrapper.fromU64(value: 42) + let hexString = u256.toHexString() + // Should be padded to 66 characters (0x + 64 hex digits) + XCTAssertEqual(hexString.count, 66) + XCTAssertTrue(hexString.hasPrefix("0x")) + XCTAssertTrue(hexString.hasSuffix("2a")) + } + + func testU256WrapperToHexStringPadding() { + // Test that small values are properly padded + let testCases: [(UInt64, String)] = [ + (1, "0x0000000000000000000000000000000000000000000000000000000000000001"), + (2, "0x0000000000000000000000000000000000000000000000000000000000000002"), + (255, "0x00000000000000000000000000000000000000000000000000000000000000ff"), + ] + + for (value, expectedHex) in testCases { + let u256 = U256Wrapper.fromU64(value: value) + XCTAssertEqual(u256.toHexString(), expectedHex) + } + } + + func testU256WrapperIntoLimbs() { + let u256 = U256Wrapper.fromU64(value: 12345) + let limbs = u256.intoLimbs() + XCTAssertEqual(limbs.count, 4) + XCTAssertEqual(limbs[0], 12345) + XCTAssertEqual(limbs[1], 0) + XCTAssertEqual(limbs[2], 0) + XCTAssertEqual(limbs[3], 0) + } + + func testU256WrapperLimbsRoundTrip() throws { + // Test that converting to/from limbs round-trips correctly + let originalLimbs: [UInt64] = [12345, 67890, 11111, 22222] + let u256 = try U256Wrapper.fromLimbs(limbs: originalLimbs) + let resultLimbs = u256.intoLimbs() + + XCTAssertEqual(resultLimbs, originalLimbs) + } + + func testU256WrapperZeroValue() { + let u256 = U256Wrapper.fromU64(value: 0) + XCTAssertEqual(u256.toDecimalString(), "0") + XCTAssertEqual(u256.toHexString(), "0x0000000000000000000000000000000000000000000000000000000000000000") + + let limbs = u256.intoLimbs() + XCTAssertEqual(limbs, [0, 0, 0, 0]) + } + + func testU256WrapperMultipleConversions() throws { + // Test creating U256 from different sources and verifying consistency + let value: UInt64 = 999999 + + let fromU64 = U256Wrapper.fromU64(value: value) + let fromHex = try U256Wrapper.tryFromHexString( + hexString: "0x00000000000000000000000000000000000000000000000000000000000f423f" + ) + let fromLimbs = try U256Wrapper.fromLimbs(limbs: [999999, 0, 0, 0]) + + // All should produce the same decimal string + XCTAssertEqual(fromU64.toDecimalString(), "999999") + XCTAssertEqual(fromHex.toDecimalString(), "999999") + XCTAssertEqual(fromLimbs.toDecimalString(), "999999") + + // All should produce the same hex string + let expectedHex = "0x00000000000000000000000000000000000000000000000000000000000f423f" + XCTAssertEqual(fromU64.toHexString(), expectedHex) + XCTAssertEqual(fromHex.toHexString(), expectedHex) + XCTAssertEqual(fromLimbs.toHexString(), expectedHex) + } + + // MARK: - Authenticator Initialization Tests + + func testInvalidSeedEmpty() async { + let emptySeed = Data() + + await XCTAssertThrowsErrorAsync( + try await Authenticator.initWithDefaults( + seed: emptySeed, + rpcUrl: testRpcUrl, + environment: .staging + ) + ) { error in + if let walletError = error as? WalletKitError, + case .InvalidInput(let attribute, _) = walletError { + XCTAssertEqual(attribute, "seed") + } else { + XCTFail("Expected InvalidInput for seed, got \(error)") + } + } + } + + func testInvalidSeedTooShort() async { + let shortSeed = Data(repeating: 0, count: 16) + + await XCTAssertThrowsErrorAsync( + try await Authenticator.initWithDefaults( + seed: shortSeed, + rpcUrl: testRpcUrl, + environment: .staging + ) + ) { error in + if let walletError = error as? WalletKitError, + case .InvalidInput(let attribute, _) = walletError { + XCTAssertEqual(attribute, "seed") + } else { + XCTFail("Expected InvalidInput for seed, got \(error)") + } + } + } + + func testInvalidSeedTooLong() async { + let longSeed = Data(repeating: 0, count: 64) + + await XCTAssertThrowsErrorAsync( + try await Authenticator.initWithDefaults( + seed: longSeed, + rpcUrl: testRpcUrl, + environment: .staging + ) + ) { error in + if let walletError = error as? WalletKitError, + case .InvalidInput(let attribute, _) = walletError { + XCTAssertEqual(attribute, "seed") + } else { + XCTFail("Expected InvalidInput for seed, got \(error)") + } + } + } + + func testInvalidRpcUrlEmpty() async { + let seed = generateRandomSeed() + + await XCTAssertThrowsErrorAsync( + try await Authenticator.initWithDefaults( + seed: seed, + rpcUrl: "", + environment: .staging + ) + ) { error in + if let walletError = error as? WalletKitError, + case .InvalidInput(let attribute, _) = walletError { + XCTAssertEqual(attribute, "rpc_url") + } else { + XCTFail("Expected InvalidInput for rpc_url, got \(error)") + } + } + } + + func testMultipleEnvironments() async { + let seed = generateRandomSeed() + let environments: [Environment] = [.staging, .production] + + for environment in environments { + await XCTAssertThrowsErrorAsync( + try await Authenticator.initWithDefaults( + seed: seed, + rpcUrl: testRpcUrl, + environment: environment + ) + ) { error in + // Should throw an error for non-existent account in any environment + XCTAssertTrue(error is WalletKitError, "Should throw WalletKitError for \(environment)") + } + } + } + + func testValidSeedLength() { + let validSeed = Data(repeating: 0, count: 32) + XCTAssertEqual(validSeed.count, 32, "Valid seed should be 32 bytes") + } + + func testGenerateRandomSeedLength() { + let seed = generateRandomSeed() + XCTAssertEqual(seed.count, 32, "Generated seed should be 32 bytes") + } + + func testGenerateRandomSeedRandomness() { + // Generate multiple seeds and verify they're different + let seed1 = generateRandomSeed() + let seed2 = generateRandomSeed() + let seed3 = generateRandomSeed() + + XCTAssertNotEqual(seed1, seed2, "Seeds should be random and different") + XCTAssertNotEqual(seed2, seed3, "Seeds should be random and different") + XCTAssertNotEqual(seed1, seed3, "Seeds should be random and different") + } + + // MARK: - Helper for async error assertions + + func XCTAssertThrowsErrorAsync( + _ expression: @autoclosure () async throws -> T, + _ message: @autoclosure () -> String = "", + file: StaticString = #filePath, + line: UInt = #line, + _ errorHandler: (_ error: Error) -> Void = { _ in } + ) async { + do { + _ = try await expression() + XCTFail(message(), file: file, line: line) + } catch { + errorHandler(error) + } + } + + // MARK: - Environment Tests + + func testEnvironmentValues() { + // Just verify environments exist and can be created + let staging = Environment.staging + let production = Environment.production + + XCTAssertNotNil(staging) + XCTAssertNotNil(production) + } +} \ No newline at end of file From 215ca5e832453513c0ee5bddbbd93907bdea7261 Mon Sep 17 00:00:00 2001 From: Luke Mann Date: Sun, 25 Jan 2026 06:12:20 -0800 Subject: [PATCH 03/10] fix swift tests --- swift/tests/Package.swift | 4 +- .../WalletKitTests/AuthenticatorTests.swift | 70 +++++++++++-------- swift/tests/WalletKitTests/SimpleTest.swift | 8 --- 3 files changed, 44 insertions(+), 38 deletions(-) delete mode 100644 swift/tests/WalletKitTests/SimpleTest.swift diff --git a/swift/tests/Package.swift b/swift/tests/Package.swift index 419c6b7ec..b042303ff 100644 --- a/swift/tests/Package.swift +++ b/swift/tests/Package.swift @@ -6,7 +6,7 @@ let package = Package( name: "WalletKitForeignTestPackage", platforms: [ .iOS(.v13), - .macOS(.v12), + .macOS(.v12) ], products: [ .library( @@ -27,6 +27,6 @@ let package = Package( name: "WalletKitTests", dependencies: ["WalletKit"], path: "WalletKitTests" - ), + ) ] ) diff --git a/swift/tests/WalletKitTests/AuthenticatorTests.swift b/swift/tests/WalletKitTests/AuthenticatorTests.swift index 969ea8014..c370cdb05 100644 --- a/swift/tests/WalletKitTests/AuthenticatorTests.swift +++ b/swift/tests/WalletKitTests/AuthenticatorTests.swift @@ -7,10 +7,16 @@ final class AuthenticatorTests: XCTestCase { // MARK: - Helper Functions + struct U256HexTestCase { + let hexInput: String + let expectedDecimal: String + let expectedHex: String + } + func generateRandomSeed() -> Data { var bytes = [UInt8](repeating: 0, count: 32) - for i in 0..<32 { - bytes[i] = UInt8.random(in: 0...255) + for index in 0..<32 { + bytes[index] = UInt8.random(in: 0...255) } return Data(bytes) } @@ -60,33 +66,41 @@ final class AuthenticatorTests: XCTestCase { func testU256WrapperDeterministicHexParsing() throws { // Test with known values from Rust tests - let testCases: [(String, String, String)] = [ - ( - "0x0000000000000000000000000000000000000000000000000000000000000001", - "1", - "0x0000000000000000000000000000000000000000000000000000000000000001" - ), - ( - "0x000000000000000000000000000000000000000000000000000000000000002a", - "42", - "0x000000000000000000000000000000000000000000000000000000000000002a" + let testCases: [U256HexTestCase] = [ + U256HexTestCase( + hexInput: "0x0000000000000000000000000000000000000000000000000000000000000001", + expectedDecimal: "1", + expectedHex: "0x0000000000000000000000000000000000000000000000000000000000000001" ), - ( - "0x00000000000000000000000000000000000000000000000000000000000f423f", - "999999", - "0x00000000000000000000000000000000000000000000000000000000000f423f" + U256HexTestCase( + hexInput: "0x000000000000000000000000000000000000000000000000000000000000002a", + expectedDecimal: "42", + expectedHex: "0x000000000000000000000000000000000000000000000000000000000000002a" ), - ( - "0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6", - "80084422859880547211683076133703299733277748156566366325829078699459944778998", - "0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6" + U256HexTestCase( + hexInput: "0x00000000000000000000000000000000000000000000000000000000000f423f", + expectedDecimal: "999999", + expectedHex: "0x00000000000000000000000000000000000000000000000000000000000f423f" ), + U256HexTestCase( + hexInput: "0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6", + expectedDecimal: "80084422859880547211683076133703299733277748156566366325829078699459944778998", + expectedHex: "0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6" + ) ] - for (hexInput, expectedDecimal, expectedHex) in testCases { - let u256 = try U256Wrapper.tryFromHexString(hexString: hexInput) - XCTAssertEqual(u256.toDecimalString(), expectedDecimal, "Decimal mismatch for \(hexInput)") - XCTAssertEqual(u256.toHexString(), expectedHex, "Hex mismatch for \(hexInput)") + for testCase in testCases { + let u256 = try U256Wrapper.tryFromHexString(hexString: testCase.hexInput) + XCTAssertEqual( + u256.toDecimalString(), + testCase.expectedDecimal, + "Decimal mismatch for \(testCase.hexInput)" + ) + XCTAssertEqual( + u256.toHexString(), + testCase.expectedHex, + "Hex mismatch for \(testCase.hexInput)" + ) } } @@ -96,7 +110,7 @@ final class AuthenticatorTests: XCTestCase { "0x0000000000000000000000000000000000000000000000000000000000000001", "0x00000000000000000000000000000000000000000000000000000000000000ff", "0x0000000000000000000000000000000000000000000000000000000000001234", - "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" ] for hexString in hexStrings { @@ -117,7 +131,7 @@ final class AuthenticatorTests: XCTestCase { "0xZZZZ", "1g", "not a hex string", - "0xGGGG", + "0xGGGG" ] for invalidInput in invalidInputs { @@ -180,7 +194,7 @@ final class AuthenticatorTests: XCTestCase { let testCases: [(UInt64, String)] = [ (1, "0x0000000000000000000000000000000000000000000000000000000000000001"), (2, "0x0000000000000000000000000000000000000000000000000000000000000002"), - (255, "0x00000000000000000000000000000000000000000000000000000000000000ff"), + (255, "0x00000000000000000000000000000000000000000000000000000000000000ff") ] for (value, expectedHex) in testCases { @@ -383,4 +397,4 @@ final class AuthenticatorTests: XCTestCase { XCTAssertNotNil(staging) XCTAssertNotNil(production) } -} \ No newline at end of file +} diff --git a/swift/tests/WalletKitTests/SimpleTest.swift b/swift/tests/WalletKitTests/SimpleTest.swift deleted file mode 100644 index fb30acd50..000000000 --- a/swift/tests/WalletKitTests/SimpleTest.swift +++ /dev/null @@ -1,8 +0,0 @@ -import XCTest -@testable import WalletKit - -final class SimpleTest: XCTestCase { - func simpleTest() { - XCTAssertEqual(1, 1) - } -} From d85a77af26115a5233277c1bcb7c04012b5c70f7 Mon Sep 17 00:00:00 2001 From: Luke Mann Date: Sun, 25 Jan 2026 06:13:49 -0800 Subject: [PATCH 04/10] fix kotlin tests --- kotlin/settings.gradle.kts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/kotlin/settings.gradle.kts b/kotlin/settings.gradle.kts index 73668b1b0..f16770349 100644 --- a/kotlin/settings.gradle.kts +++ b/kotlin/settings.gradle.kts @@ -14,6 +14,7 @@ pluginManagement { plugins { id("com.android.library") version "8.3.0" id("org.jetbrains.kotlin.android") version "1.9.22" + id("org.jetbrains.kotlin.jvm") version "1.9.22" } } @@ -25,4 +26,5 @@ dependencyResolutionManagement { } rootProject.name = "walletkit" -include("lib") +include("walletkit-android") +include("walletkit-tests") From 907d74e90ce69e9d5763444db2d68592cc916a9f Mon Sep 17 00:00:00 2001 From: Luke Mann Date: Sun, 25 Jan 2026 06:24:48 -0800 Subject: [PATCH 05/10] walletkit-android -> walletkit --- .github/workflows/release.yml | 6 +- .gitignore | 2 +- README.md | 4 +- build_android_local.sh | 6 +- kotlin/README.md | 2 +- kotlin/build.sh | 14 +- kotlin/build_kotlin.sh | 2 +- kotlin/lib/build.gradle.kts | 2 +- kotlin/settings.gradle.kts | 2 +- .../uniffi/walletkit_core/walletkit_core.kt | 7375 +++++++++++++++++ kotlin/walletkit-tests/build.gradle.kts | 2 +- .../build.gradle.kts | 2 +- .../consumer-rules.pro | 0 13 files changed, 7397 insertions(+), 22 deletions(-) create mode 100644 kotlin/walletkit-android/src/main/java/uniffi/walletkit_core/walletkit_core.kt rename kotlin/{walletkit-android => walletkit}/build.gradle.kts (97%) rename kotlin/{walletkit-android => walletkit}/consumer-rules.pro (100%) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d1c2893d2..ccd43e9dc 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -210,7 +210,7 @@ jobs: - name: Move artifacts run: | - mkdir -p kotlin/walletkit-android/src/main/jniLibs && cd kotlin/walletkit-android/src/main/jniLibs + mkdir -p kotlin/walletkit/src/main/jniLibs && cd kotlin/walletkit/src/main/jniLibs mkdir armeabi-v7a arm64-v8a x86 x86_64 mv /home/runner/work/walletkit/walletkit/android-armv7-linux-androideabi/libwalletkit.so ./armeabi-v7a/libwalletkit.so mv /home/runner/work/walletkit/walletkit/android-aarch64-linux-android/libwalletkit.so ./arm64-v8a/libwalletkit.so @@ -219,11 +219,11 @@ jobs: - name: Generate bindings working-directory: kotlin - run: cargo run -p uniffi-bindgen generate ./walletkit-android/src/main/jniLibs/arm64-v8a/libwalletkit.so --library --language kotlin --no-format --out-dir walletkit-android/src/main/java + run: cargo run -p uniffi-bindgen generate ./walletkit/src/main/jniLibs/arm64-v8a/libwalletkit.so --library --language kotlin --no-format --out-dir walletkit/src/main/java - name: Publish working-directory: kotlin - run: ./gradlew walletkit-android:publish + run: ./gradlew walletkit:publish env: GITHUB_ACTOR: wld-walletkit-bot GITHUB_TOKEN: ${{ github.token }} diff --git a/.gitignore b/.gitignore index 4f74359d5..ba3c1b70a 100644 --- a/.gitignore +++ b/.gitignore @@ -21,7 +21,7 @@ swift/tests/.build/ # Kotlin bindings and native libs kotlin/libs/ -kotlin/walletkit-android/src/main/java/uniffi/ +kotlin/walletkit/src/main/java/uniffi/ kotlin/walletkit-tests/build/ .build/ diff --git a/README.md b/README.md index b137f4d6a..9751b242e 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ WalletKit's bindings for Kotlin are distributed through GitHub packages. ```kotlin dependencies { /// ... - implementation "org.world:walletkit-android:VERSION" + implementation "org.world:walletkit:VERSION" } ``` @@ -70,7 +70,7 @@ To test local changes before publishing a release, use the build script to compi This will: 1. Build the Rust library for all Android architectures (arm64-v8a, armeabi-v7a, x86_64, x86) 2. Generate Kotlin UniFFI bindings -3. Publish to `~/.m2/repository/org/world/walletkit-android/` +3. Publish to `~/.m2/repository/org/world/walletkit/` In your consuming project, ensure `mavenLocal()` is included in your repositories and update your dependency version to the SNAPSHOT version (e.g., `0.3.1-SNAPSHOT`). diff --git a/build_android_local.sh b/build_android_local.sh index 7398387fc..7eb4cdbc4 100755 --- a/build_android_local.sh +++ b/build_android_local.sh @@ -25,11 +25,11 @@ cd kotlin # Publish to Maven Local echo "Publishing to Maven Local..." -./gradlew :lib:publishToMavenLocal -PversionName="$VERSION" +./gradlew :walletkit:publishToMavenLocal -PversionName="$VERSION" echo "" echo "โœ… Successfully published $VERSION to Maven Local!" -echo "Published to: ~/.m2/repository/org/world/walletkit-android/$VERSION/" +echo "Published to: ~/.m2/repository/org/world/walletkit/$VERSION/" echo "" echo "To use in your project:" -echo " implementation 'org.world:walletkit-android:$VERSION'" +echo " implementation 'org.world:walletkit:$VERSION'" diff --git a/kotlin/README.md b/kotlin/README.md index 2373f1d6c..9585b3158 100644 --- a/kotlin/README.md +++ b/kotlin/README.md @@ -22,5 +22,5 @@ This folder contains support files for WalletKit to work in Kotlin: ## Kotlin project structure The Kotlin project has two members: -- `walletkit-android`: The main WalletKit library with UniFFI bindings for Kotlin. +- `walletkit`: The main WalletKit library with UniFFI bindings for Kotlin. - `walletkit-tests`: Unit tests to assert the Kotlin bindings behave as intended (foreign tests). diff --git a/kotlin/build.sh b/kotlin/build.sh index ecbc71b37..779431c94 100755 --- a/kotlin/build.sh +++ b/kotlin/build.sh @@ -4,7 +4,7 @@ set -e echo "Building WalletKit Android SDK..." # Create jniLibs directories -mkdir -p ./lib/src/main/jniLibs/{arm64-v8a,armeabi-v7a,x86_64,x86} +mkdir -p ./walletkit/src/main/jniLibs/{arm64-v8a,armeabi-v7a,x86_64,x86} # Build for all Android architectures echo "Building for aarch64-linux-android..." @@ -21,18 +21,18 @@ cross build -p walletkit --release --target=i686-linux-android --features v4 # Move .so files to jniLibs echo "Moving native libraries..." -mv ../target/aarch64-linux-android/release/libwalletkit.so ./lib/src/main/jniLibs/arm64-v8a/libwalletkit.so -mv ../target/armv7-linux-androideabi/release/libwalletkit.so ./lib/src/main/jniLibs/armeabi-v7a/libwalletkit.so -mv ../target/x86_64-linux-android/release/libwalletkit.so ./lib/src/main/jniLibs/x86_64/libwalletkit.so -mv ../target/i686-linux-android/release/libwalletkit.so ./lib/src/main/jniLibs/x86/libwalletkit.so +mv ../target/aarch64-linux-android/release/libwalletkit.so ./walletkit/src/main/jniLibs/arm64-v8a/libwalletkit.so +mv ../target/armv7-linux-androideabi/release/libwalletkit.so ./walletkit/src/main/jniLibs/armeabi-v7a/libwalletkit.so +mv ../target/x86_64-linux-android/release/libwalletkit.so ./walletkit/src/main/jniLibs/x86_64/libwalletkit.so +mv ../target/i686-linux-android/release/libwalletkit.so ./walletkit/src/main/jniLibs/x86/libwalletkit.so # Generate Kotlin bindings echo "Generating Kotlin bindings..." cargo run -p uniffi-bindgen generate \ - ./lib/src/main/jniLibs/arm64-v8a/libwalletkit.so \ + ./walletkit/src/main/jniLibs/arm64-v8a/libwalletkit.so \ --library \ --language kotlin \ --no-format \ - --out-dir lib/src/main/java + --out-dir walletkit/src/main/java echo "โœ… Build complete!" diff --git a/kotlin/build_kotlin.sh b/kotlin/build_kotlin.sh index 73c19dcdd..bd517cfff 100755 --- a/kotlin/build_kotlin.sh +++ b/kotlin/build_kotlin.sh @@ -6,7 +6,7 @@ set -euo pipefail PROJECT_ROOT_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" KOTLIN_DIR="$PROJECT_ROOT_PATH/kotlin" -JAVA_SRC_DIR="$KOTLIN_DIR/walletkit-android/src/main/java" +JAVA_SRC_DIR="$KOTLIN_DIR/walletkit/src/main/java" LIBS_DIR="$KOTLIN_DIR/libs" # Clean previous artifacts diff --git a/kotlin/lib/build.gradle.kts b/kotlin/lib/build.gradle.kts index eddfce6f9..734522be3 100644 --- a/kotlin/lib/build.gradle.kts +++ b/kotlin/lib/build.gradle.kts @@ -46,7 +46,7 @@ afterEvaluate { publications { create("maven") { groupId = "org.world" - artifactId = "walletkit-android" + artifactId = "walletkit" version = if (project.hasProperty("versionName")) { project.property("versionName") as String diff --git a/kotlin/settings.gradle.kts b/kotlin/settings.gradle.kts index f16770349..e3860c3bb 100644 --- a/kotlin/settings.gradle.kts +++ b/kotlin/settings.gradle.kts @@ -26,5 +26,5 @@ dependencyResolutionManagement { } rootProject.name = "walletkit" -include("walletkit-android") +include("walletkit") include("walletkit-tests") diff --git a/kotlin/walletkit-android/src/main/java/uniffi/walletkit_core/walletkit_core.kt b/kotlin/walletkit-android/src/main/java/uniffi/walletkit_core/walletkit_core.kt new file mode 100644 index 000000000..a516e08e8 --- /dev/null +++ b/kotlin/walletkit-android/src/main/java/uniffi/walletkit_core/walletkit_core.kt @@ -0,0 +1,7375 @@ +// This file was autogenerated by some hot garbage in the `uniffi` crate. +// Trust me, you don't want to mess with it! + +@file:Suppress("NAME_SHADOWING") + +package uniffi.walletkit_core + +// Common helper code. +// +// Ideally this would live in a separate .kt file where it can be unittested etc +// in isolation, and perhaps even published as a re-useable package. +// +// However, it's important that the details of how this helper code works (e.g. the +// way that different builtin types are passed across the FFI) exactly match what's +// expected by the Rust code on the other side of the interface. In practice right +// now that means coming from the exact some version of `uniffi` that was used to +// compile the Rust component. The easiest way to ensure this is to bundle the Kotlin +// helpers directly inline like we're doing here. + +import com.sun.jna.Library +import com.sun.jna.IntegerType +import com.sun.jna.Native +import com.sun.jna.Pointer +import com.sun.jna.Structure +import com.sun.jna.Callback +import com.sun.jna.ptr.* +import java.nio.ByteBuffer +import java.nio.ByteOrder +import java.nio.CharBuffer +import java.nio.charset.CodingErrorAction +import java.util.concurrent.atomic.AtomicLong +import java.util.concurrent.ConcurrentHashMap +import java.util.concurrent.atomic.AtomicBoolean +import kotlin.coroutines.resume +import kotlinx.coroutines.CancellableContinuation +import kotlinx.coroutines.DelicateCoroutinesApi +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.Job +import kotlinx.coroutines.launch +import kotlinx.coroutines.suspendCancellableCoroutine + +// This is a helper for safely working with byte buffers returned from the Rust code. +// A rust-owned buffer is represented by its capacity, its current length, and a +// pointer to the underlying data. + +/** + * @suppress + */ +@Structure.FieldOrder("capacity", "len", "data") +open class RustBuffer : Structure() { + // Note: `capacity` and `len` are actually `ULong` values, but JVM only supports signed values. + // When dealing with these fields, make sure to call `toULong()`. + @JvmField var capacity: Long = 0 + @JvmField var len: Long = 0 + @JvmField var data: Pointer? = null + + class ByValue: RustBuffer(), Structure.ByValue + class ByReference: RustBuffer(), Structure.ByReference + + internal fun setValue(other: RustBuffer) { + capacity = other.capacity + len = other.len + data = other.data + } + + companion object { + internal fun alloc(size: ULong = 0UL) = uniffiRustCall() { status -> + // Note: need to convert the size to a `Long` value to make this work with JVM. + UniffiLib.INSTANCE.ffi_walletkit_core_rustbuffer_alloc(size.toLong(), status) + }.also { + if(it.data == null) { + throw RuntimeException("RustBuffer.alloc() returned null data pointer (size=${size})") + } + } + + internal fun create(capacity: ULong, len: ULong, data: Pointer?): RustBuffer.ByValue { + var buf = RustBuffer.ByValue() + buf.capacity = capacity.toLong() + buf.len = len.toLong() + buf.data = data + return buf + } + + internal fun free(buf: RustBuffer.ByValue) = uniffiRustCall() { status -> + UniffiLib.INSTANCE.ffi_walletkit_core_rustbuffer_free(buf, status) + } + } + + @Suppress("TooGenericExceptionThrown") + fun asByteBuffer() = + this.data?.getByteBuffer(0, this.len.toLong())?.also { + it.order(ByteOrder.BIG_ENDIAN) + } +} + +/** + * The equivalent of the `*mut RustBuffer` type. + * Required for callbacks taking in an out pointer. + * + * Size is the sum of all values in the struct. + * + * @suppress + */ +class RustBufferByReference : ByReference(16) { + /** + * Set the pointed-to `RustBuffer` to the given value. + */ + fun setValue(value: RustBuffer.ByValue) { + // NOTE: The offsets are as they are in the C-like struct. + val pointer = getPointer() + pointer.setLong(0, value.capacity) + pointer.setLong(8, value.len) + pointer.setPointer(16, value.data) + } + + /** + * Get a `RustBuffer.ByValue` from this reference. + */ + fun getValue(): RustBuffer.ByValue { + val pointer = getPointer() + val value = RustBuffer.ByValue() + value.writeField("capacity", pointer.getLong(0)) + value.writeField("len", pointer.getLong(8)) + value.writeField("data", pointer.getLong(16)) + + return value + } +} + +// This is a helper for safely passing byte references into the rust code. +// It's not actually used at the moment, because there aren't many things that you +// can take a direct pointer to in the JVM, and if we're going to copy something +// then we might as well copy it into a `RustBuffer`. But it's here for API +// completeness. + +@Structure.FieldOrder("len", "data") +internal open class ForeignBytes : Structure() { + @JvmField var len: Int = 0 + @JvmField var data: Pointer? = null + + class ByValue : ForeignBytes(), Structure.ByValue +} +/** + * The FfiConverter interface handles converter types to and from the FFI + * + * All implementing objects should be public to support external types. When a + * type is external we need to import it's FfiConverter. + * + * @suppress + */ +public interface FfiConverter { + // Convert an FFI type to a Kotlin type + fun lift(value: FfiType): KotlinType + + // Convert an Kotlin type to an FFI type + fun lower(value: KotlinType): FfiType + + // Read a Kotlin type from a `ByteBuffer` + fun read(buf: ByteBuffer): KotlinType + + // Calculate bytes to allocate when creating a `RustBuffer` + // + // This must return at least as many bytes as the write() function will + // write. It can return more bytes than needed, for example when writing + // Strings we can't know the exact bytes needed until we the UTF-8 + // encoding, so we pessimistically allocate the largest size possible (3 + // bytes per codepoint). Allocating extra bytes is not really a big deal + // because the `RustBuffer` is short-lived. + fun allocationSize(value: KotlinType): ULong + + // Write a Kotlin type to a `ByteBuffer` + fun write(value: KotlinType, buf: ByteBuffer) + + // Lower a value into a `RustBuffer` + // + // This method lowers a value into a `RustBuffer` rather than the normal + // FfiType. It's used by the callback interface code. Callback interface + // returns are always serialized into a `RustBuffer` regardless of their + // normal FFI type. + fun lowerIntoRustBuffer(value: KotlinType): RustBuffer.ByValue { + val rbuf = RustBuffer.alloc(allocationSize(value)) + try { + val bbuf = rbuf.data!!.getByteBuffer(0, rbuf.capacity).also { + it.order(ByteOrder.BIG_ENDIAN) + } + write(value, bbuf) + rbuf.writeField("len", bbuf.position().toLong()) + return rbuf + } catch (e: Throwable) { + RustBuffer.free(rbuf) + throw e + } + } + + // Lift a value from a `RustBuffer`. + // + // This here mostly because of the symmetry with `lowerIntoRustBuffer()`. + // It's currently only used by the `FfiConverterRustBuffer` class below. + fun liftFromRustBuffer(rbuf: RustBuffer.ByValue): KotlinType { + val byteBuf = rbuf.asByteBuffer()!! + try { + val item = read(byteBuf) + if (byteBuf.hasRemaining()) { + throw RuntimeException("junk remaining in buffer after lifting, something is very wrong!!") + } + return item + } finally { + RustBuffer.free(rbuf) + } + } +} + +/** + * FfiConverter that uses `RustBuffer` as the FfiType + * + * @suppress + */ +public interface FfiConverterRustBuffer: FfiConverter { + override fun lift(value: RustBuffer.ByValue) = liftFromRustBuffer(value) + override fun lower(value: KotlinType) = lowerIntoRustBuffer(value) +} +// A handful of classes and functions to support the generated data structures. +// This would be a good candidate for isolating in its own ffi-support lib. + +internal const val UNIFFI_CALL_SUCCESS = 0.toByte() +internal const val UNIFFI_CALL_ERROR = 1.toByte() +internal const val UNIFFI_CALL_UNEXPECTED_ERROR = 2.toByte() + +@Structure.FieldOrder("code", "error_buf") +internal open class UniffiRustCallStatus : Structure() { + @JvmField var code: Byte = 0 + @JvmField var error_buf: RustBuffer.ByValue = RustBuffer.ByValue() + + class ByValue: UniffiRustCallStatus(), Structure.ByValue + + fun isSuccess(): Boolean { + return code == UNIFFI_CALL_SUCCESS + } + + fun isError(): Boolean { + return code == UNIFFI_CALL_ERROR + } + + fun isPanic(): Boolean { + return code == UNIFFI_CALL_UNEXPECTED_ERROR + } + + companion object { + fun create(code: Byte, errorBuf: RustBuffer.ByValue): UniffiRustCallStatus.ByValue { + val callStatus = UniffiRustCallStatus.ByValue() + callStatus.code = code + callStatus.error_buf = errorBuf + return callStatus + } + } +} + +class InternalException(message: String) : kotlin.Exception(message) + +/** + * Each top-level error class has a companion object that can lift the error from the call status's rust buffer + * + * @suppress + */ +interface UniffiRustCallStatusErrorHandler { + fun lift(error_buf: RustBuffer.ByValue): E; +} + +// Helpers for calling Rust +// In practice we usually need to be synchronized to call this safely, so it doesn't +// synchronize itself + +// Call a rust function that returns a Result<>. Pass in the Error class companion that corresponds to the Err +private inline fun uniffiRustCallWithError(errorHandler: UniffiRustCallStatusErrorHandler, callback: (UniffiRustCallStatus) -> U): U { + var status = UniffiRustCallStatus() + val return_value = callback(status) + uniffiCheckCallStatus(errorHandler, status) + return return_value +} + +// Check UniffiRustCallStatus and throw an error if the call wasn't successful +private fun uniffiCheckCallStatus(errorHandler: UniffiRustCallStatusErrorHandler, status: UniffiRustCallStatus) { + if (status.isSuccess()) { + return + } else if (status.isError()) { + throw errorHandler.lift(status.error_buf) + } else if (status.isPanic()) { + // when the rust code sees a panic, it tries to construct a rustbuffer + // with the message. but if that code panics, then it just sends back + // an empty buffer. + if (status.error_buf.len > 0) { + throw InternalException(FfiConverterString.lift(status.error_buf)) + } else { + throw InternalException("Rust panic") + } + } else { + throw InternalException("Unknown rust call status: $status.code") + } +} + +/** + * UniffiRustCallStatusErrorHandler implementation for times when we don't expect a CALL_ERROR + * + * @suppress + */ +object UniffiNullRustCallStatusErrorHandler: UniffiRustCallStatusErrorHandler { + override fun lift(error_buf: RustBuffer.ByValue): InternalException { + RustBuffer.free(error_buf) + return InternalException("Unexpected CALL_ERROR") + } +} + +// Call a rust function that returns a plain value +private inline fun uniffiRustCall(callback: (UniffiRustCallStatus) -> U): U { + return uniffiRustCallWithError(UniffiNullRustCallStatusErrorHandler, callback) +} + +internal inline fun uniffiTraitInterfaceCall( + callStatus: UniffiRustCallStatus, + makeCall: () -> T, + writeReturn: (T) -> Unit, +) { + try { + writeReturn(makeCall()) + } catch(e: kotlin.Exception) { + callStatus.code = UNIFFI_CALL_UNEXPECTED_ERROR + callStatus.error_buf = FfiConverterString.lower(e.toString()) + } +} + +internal inline fun uniffiTraitInterfaceCallWithError( + callStatus: UniffiRustCallStatus, + makeCall: () -> T, + writeReturn: (T) -> Unit, + lowerError: (E) -> RustBuffer.ByValue +) { + try { + writeReturn(makeCall()) + } catch(e: kotlin.Exception) { + if (e is E) { + callStatus.code = UNIFFI_CALL_ERROR + callStatus.error_buf = lowerError(e) + } else { + callStatus.code = UNIFFI_CALL_UNEXPECTED_ERROR + callStatus.error_buf = FfiConverterString.lower(e.toString()) + } + } +} +// Map handles to objects +// +// This is used pass an opaque 64-bit handle representing a foreign object to the Rust code. +internal class UniffiHandleMap { + private val map = ConcurrentHashMap() + private val counter = java.util.concurrent.atomic.AtomicLong(0) + + val size: Int + get() = map.size + + // Insert a new object into the handle map and get a handle for it + fun insert(obj: T): Long { + val handle = counter.getAndAdd(1) + map.put(handle, obj) + return handle + } + + // Get an object from the handle map + fun get(handle: Long): T { + return map.get(handle) ?: throw InternalException("UniffiHandleMap.get: Invalid handle") + } + + // Remove an entry from the handlemap and get the Kotlin object back + fun remove(handle: Long): T { + return map.remove(handle) ?: throw InternalException("UniffiHandleMap: Invalid handle") + } +} + +// Contains loading, initialization code, +// and the FFI Function declarations in a com.sun.jna.Library. +@Synchronized +private fun findLibraryName(componentName: String): String { + val libOverride = System.getProperty("uniffi.component.$componentName.libraryOverride") + if (libOverride != null) { + return libOverride + } + return "walletkit" +} + +private inline fun loadIndirect( + componentName: String +): Lib { + return Native.load(findLibraryName(componentName), Lib::class.java) +} + +// Define FFI callback types +internal interface UniffiRustFutureContinuationCallback : com.sun.jna.Callback { + fun callback(`data`: Long,`pollResult`: Byte,) +} +internal interface UniffiForeignFutureFree : com.sun.jna.Callback { + fun callback(`handle`: Long,) +} +internal interface UniffiCallbackInterfaceFree : com.sun.jna.Callback { + fun callback(`handle`: Long,) +} +@Structure.FieldOrder("handle", "free") +internal open class UniffiForeignFuture( + @JvmField internal var `handle`: Long = 0.toLong(), + @JvmField internal var `free`: UniffiForeignFutureFree? = null, +) : Structure() { + class UniffiByValue( + `handle`: Long = 0.toLong(), + `free`: UniffiForeignFutureFree? = null, + ): UniffiForeignFuture(`handle`,`free`,), Structure.ByValue + + internal fun uniffiSetValue(other: UniffiForeignFuture) { + `handle` = other.`handle` + `free` = other.`free` + } + +} +@Structure.FieldOrder("returnValue", "callStatus") +internal open class UniffiForeignFutureStructU8( + @JvmField internal var `returnValue`: Byte = 0.toByte(), + @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), +) : Structure() { + class UniffiByValue( + `returnValue`: Byte = 0.toByte(), + `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), + ): UniffiForeignFutureStructU8(`returnValue`,`callStatus`,), Structure.ByValue + + internal fun uniffiSetValue(other: UniffiForeignFutureStructU8) { + `returnValue` = other.`returnValue` + `callStatus` = other.`callStatus` + } + +} +internal interface UniffiForeignFutureCompleteU8 : com.sun.jna.Callback { + fun callback(`callbackData`: Long,`result`: UniffiForeignFutureStructU8.UniffiByValue,) +} +@Structure.FieldOrder("returnValue", "callStatus") +internal open class UniffiForeignFutureStructI8( + @JvmField internal var `returnValue`: Byte = 0.toByte(), + @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), +) : Structure() { + class UniffiByValue( + `returnValue`: Byte = 0.toByte(), + `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), + ): UniffiForeignFutureStructI8(`returnValue`,`callStatus`,), Structure.ByValue + + internal fun uniffiSetValue(other: UniffiForeignFutureStructI8) { + `returnValue` = other.`returnValue` + `callStatus` = other.`callStatus` + } + +} +internal interface UniffiForeignFutureCompleteI8 : com.sun.jna.Callback { + fun callback(`callbackData`: Long,`result`: UniffiForeignFutureStructI8.UniffiByValue,) +} +@Structure.FieldOrder("returnValue", "callStatus") +internal open class UniffiForeignFutureStructU16( + @JvmField internal var `returnValue`: Short = 0.toShort(), + @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), +) : Structure() { + class UniffiByValue( + `returnValue`: Short = 0.toShort(), + `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), + ): UniffiForeignFutureStructU16(`returnValue`,`callStatus`,), Structure.ByValue + + internal fun uniffiSetValue(other: UniffiForeignFutureStructU16) { + `returnValue` = other.`returnValue` + `callStatus` = other.`callStatus` + } + +} +internal interface UniffiForeignFutureCompleteU16 : com.sun.jna.Callback { + fun callback(`callbackData`: Long,`result`: UniffiForeignFutureStructU16.UniffiByValue,) +} +@Structure.FieldOrder("returnValue", "callStatus") +internal open class UniffiForeignFutureStructI16( + @JvmField internal var `returnValue`: Short = 0.toShort(), + @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), +) : Structure() { + class UniffiByValue( + `returnValue`: Short = 0.toShort(), + `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), + ): UniffiForeignFutureStructI16(`returnValue`,`callStatus`,), Structure.ByValue + + internal fun uniffiSetValue(other: UniffiForeignFutureStructI16) { + `returnValue` = other.`returnValue` + `callStatus` = other.`callStatus` + } + +} +internal interface UniffiForeignFutureCompleteI16 : com.sun.jna.Callback { + fun callback(`callbackData`: Long,`result`: UniffiForeignFutureStructI16.UniffiByValue,) +} +@Structure.FieldOrder("returnValue", "callStatus") +internal open class UniffiForeignFutureStructU32( + @JvmField internal var `returnValue`: Int = 0, + @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), +) : Structure() { + class UniffiByValue( + `returnValue`: Int = 0, + `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), + ): UniffiForeignFutureStructU32(`returnValue`,`callStatus`,), Structure.ByValue + + internal fun uniffiSetValue(other: UniffiForeignFutureStructU32) { + `returnValue` = other.`returnValue` + `callStatus` = other.`callStatus` + } + +} +internal interface UniffiForeignFutureCompleteU32 : com.sun.jna.Callback { + fun callback(`callbackData`: Long,`result`: UniffiForeignFutureStructU32.UniffiByValue,) +} +@Structure.FieldOrder("returnValue", "callStatus") +internal open class UniffiForeignFutureStructI32( + @JvmField internal var `returnValue`: Int = 0, + @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), +) : Structure() { + class UniffiByValue( + `returnValue`: Int = 0, + `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), + ): UniffiForeignFutureStructI32(`returnValue`,`callStatus`,), Structure.ByValue + + internal fun uniffiSetValue(other: UniffiForeignFutureStructI32) { + `returnValue` = other.`returnValue` + `callStatus` = other.`callStatus` + } + +} +internal interface UniffiForeignFutureCompleteI32 : com.sun.jna.Callback { + fun callback(`callbackData`: Long,`result`: UniffiForeignFutureStructI32.UniffiByValue,) +} +@Structure.FieldOrder("returnValue", "callStatus") +internal open class UniffiForeignFutureStructU64( + @JvmField internal var `returnValue`: Long = 0.toLong(), + @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), +) : Structure() { + class UniffiByValue( + `returnValue`: Long = 0.toLong(), + `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), + ): UniffiForeignFutureStructU64(`returnValue`,`callStatus`,), Structure.ByValue + + internal fun uniffiSetValue(other: UniffiForeignFutureStructU64) { + `returnValue` = other.`returnValue` + `callStatus` = other.`callStatus` + } + +} +internal interface UniffiForeignFutureCompleteU64 : com.sun.jna.Callback { + fun callback(`callbackData`: Long,`result`: UniffiForeignFutureStructU64.UniffiByValue,) +} +@Structure.FieldOrder("returnValue", "callStatus") +internal open class UniffiForeignFutureStructI64( + @JvmField internal var `returnValue`: Long = 0.toLong(), + @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), +) : Structure() { + class UniffiByValue( + `returnValue`: Long = 0.toLong(), + `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), + ): UniffiForeignFutureStructI64(`returnValue`,`callStatus`,), Structure.ByValue + + internal fun uniffiSetValue(other: UniffiForeignFutureStructI64) { + `returnValue` = other.`returnValue` + `callStatus` = other.`callStatus` + } + +} +internal interface UniffiForeignFutureCompleteI64 : com.sun.jna.Callback { + fun callback(`callbackData`: Long,`result`: UniffiForeignFutureStructI64.UniffiByValue,) +} +@Structure.FieldOrder("returnValue", "callStatus") +internal open class UniffiForeignFutureStructF32( + @JvmField internal var `returnValue`: Float = 0.0f, + @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), +) : Structure() { + class UniffiByValue( + `returnValue`: Float = 0.0f, + `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), + ): UniffiForeignFutureStructF32(`returnValue`,`callStatus`,), Structure.ByValue + + internal fun uniffiSetValue(other: UniffiForeignFutureStructF32) { + `returnValue` = other.`returnValue` + `callStatus` = other.`callStatus` + } + +} +internal interface UniffiForeignFutureCompleteF32 : com.sun.jna.Callback { + fun callback(`callbackData`: Long,`result`: UniffiForeignFutureStructF32.UniffiByValue,) +} +@Structure.FieldOrder("returnValue", "callStatus") +internal open class UniffiForeignFutureStructF64( + @JvmField internal var `returnValue`: Double = 0.0, + @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), +) : Structure() { + class UniffiByValue( + `returnValue`: Double = 0.0, + `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), + ): UniffiForeignFutureStructF64(`returnValue`,`callStatus`,), Structure.ByValue + + internal fun uniffiSetValue(other: UniffiForeignFutureStructF64) { + `returnValue` = other.`returnValue` + `callStatus` = other.`callStatus` + } + +} +internal interface UniffiForeignFutureCompleteF64 : com.sun.jna.Callback { + fun callback(`callbackData`: Long,`result`: UniffiForeignFutureStructF64.UniffiByValue,) +} +@Structure.FieldOrder("returnValue", "callStatus") +internal open class UniffiForeignFutureStructPointer( + @JvmField internal var `returnValue`: Pointer = Pointer.NULL, + @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), +) : Structure() { + class UniffiByValue( + `returnValue`: Pointer = Pointer.NULL, + `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), + ): UniffiForeignFutureStructPointer(`returnValue`,`callStatus`,), Structure.ByValue + + internal fun uniffiSetValue(other: UniffiForeignFutureStructPointer) { + `returnValue` = other.`returnValue` + `callStatus` = other.`callStatus` + } + +} +internal interface UniffiForeignFutureCompletePointer : com.sun.jna.Callback { + fun callback(`callbackData`: Long,`result`: UniffiForeignFutureStructPointer.UniffiByValue,) +} +@Structure.FieldOrder("returnValue", "callStatus") +internal open class UniffiForeignFutureStructRustBuffer( + @JvmField internal var `returnValue`: RustBuffer.ByValue = RustBuffer.ByValue(), + @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), +) : Structure() { + class UniffiByValue( + `returnValue`: RustBuffer.ByValue = RustBuffer.ByValue(), + `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), + ): UniffiForeignFutureStructRustBuffer(`returnValue`,`callStatus`,), Structure.ByValue + + internal fun uniffiSetValue(other: UniffiForeignFutureStructRustBuffer) { + `returnValue` = other.`returnValue` + `callStatus` = other.`callStatus` + } + +} +internal interface UniffiForeignFutureCompleteRustBuffer : com.sun.jna.Callback { + fun callback(`callbackData`: Long,`result`: UniffiForeignFutureStructRustBuffer.UniffiByValue,) +} +@Structure.FieldOrder("callStatus") +internal open class UniffiForeignFutureStructVoid( + @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), +) : Structure() { + class UniffiByValue( + `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), + ): UniffiForeignFutureStructVoid(`callStatus`,), Structure.ByValue + + internal fun uniffiSetValue(other: UniffiForeignFutureStructVoid) { + `callStatus` = other.`callStatus` + } + +} +internal interface UniffiForeignFutureCompleteVoid : com.sun.jna.Callback { + fun callback(`callbackData`: Long,`result`: UniffiForeignFutureStructVoid.UniffiByValue,) +} +internal interface UniffiCallbackInterfaceAtomicBlobStoreMethod0 : com.sun.jna.Callback { + fun callback(`uniffiHandle`: Long,`path`: RustBuffer.ByValue,`uniffiOutReturn`: RustBuffer,uniffiCallStatus: UniffiRustCallStatus,) +} +internal interface UniffiCallbackInterfaceAtomicBlobStoreMethod1 : com.sun.jna.Callback { + fun callback(`uniffiHandle`: Long,`path`: RustBuffer.ByValue,`bytes`: RustBuffer.ByValue,`uniffiOutReturn`: Pointer,uniffiCallStatus: UniffiRustCallStatus,) +} +internal interface UniffiCallbackInterfaceAtomicBlobStoreMethod2 : com.sun.jna.Callback { + fun callback(`uniffiHandle`: Long,`path`: RustBuffer.ByValue,`uniffiOutReturn`: Pointer,uniffiCallStatus: UniffiRustCallStatus,) +} +internal interface UniffiCallbackInterfaceDeviceKeystoreMethod0 : com.sun.jna.Callback { + fun callback(`uniffiHandle`: Long,`associatedData`: RustBuffer.ByValue,`plaintext`: RustBuffer.ByValue,`uniffiOutReturn`: RustBuffer,uniffiCallStatus: UniffiRustCallStatus,) +} +internal interface UniffiCallbackInterfaceDeviceKeystoreMethod1 : com.sun.jna.Callback { + fun callback(`uniffiHandle`: Long,`associatedData`: RustBuffer.ByValue,`ciphertext`: RustBuffer.ByValue,`uniffiOutReturn`: RustBuffer,uniffiCallStatus: UniffiRustCallStatus,) +} +internal interface UniffiCallbackInterfaceLoggerMethod0 : com.sun.jna.Callback { + fun callback(`uniffiHandle`: Long,`level`: RustBuffer.ByValue,`message`: RustBuffer.ByValue,`uniffiOutReturn`: Pointer,uniffiCallStatus: UniffiRustCallStatus,) +} +internal interface UniffiCallbackInterfaceStorageProviderMethod0 : com.sun.jna.Callback { + fun callback(`uniffiHandle`: Long,`uniffiOutReturn`: PointerByReference,uniffiCallStatus: UniffiRustCallStatus,) +} +internal interface UniffiCallbackInterfaceStorageProviderMethod1 : com.sun.jna.Callback { + fun callback(`uniffiHandle`: Long,`uniffiOutReturn`: PointerByReference,uniffiCallStatus: UniffiRustCallStatus,) +} +internal interface UniffiCallbackInterfaceStorageProviderMethod2 : com.sun.jna.Callback { + fun callback(`uniffiHandle`: Long,`uniffiOutReturn`: PointerByReference,uniffiCallStatus: UniffiRustCallStatus,) +} +@Structure.FieldOrder("read", "writeAtomic", "delete", "uniffiFree") +internal open class UniffiVTableCallbackInterfaceAtomicBlobStore( + @JvmField internal var `read`: UniffiCallbackInterfaceAtomicBlobStoreMethod0? = null, + @JvmField internal var `writeAtomic`: UniffiCallbackInterfaceAtomicBlobStoreMethod1? = null, + @JvmField internal var `delete`: UniffiCallbackInterfaceAtomicBlobStoreMethod2? = null, + @JvmField internal var `uniffiFree`: UniffiCallbackInterfaceFree? = null, +) : Structure() { + class UniffiByValue( + `read`: UniffiCallbackInterfaceAtomicBlobStoreMethod0? = null, + `writeAtomic`: UniffiCallbackInterfaceAtomicBlobStoreMethod1? = null, + `delete`: UniffiCallbackInterfaceAtomicBlobStoreMethod2? = null, + `uniffiFree`: UniffiCallbackInterfaceFree? = null, + ): UniffiVTableCallbackInterfaceAtomicBlobStore(`read`,`writeAtomic`,`delete`,`uniffiFree`,), Structure.ByValue + + internal fun uniffiSetValue(other: UniffiVTableCallbackInterfaceAtomicBlobStore) { + `read` = other.`read` + `writeAtomic` = other.`writeAtomic` + `delete` = other.`delete` + `uniffiFree` = other.`uniffiFree` + } + +} +@Structure.FieldOrder("seal", "openSealed", "uniffiFree") +internal open class UniffiVTableCallbackInterfaceDeviceKeystore( + @JvmField internal var `seal`: UniffiCallbackInterfaceDeviceKeystoreMethod0? = null, + @JvmField internal var `openSealed`: UniffiCallbackInterfaceDeviceKeystoreMethod1? = null, + @JvmField internal var `uniffiFree`: UniffiCallbackInterfaceFree? = null, +) : Structure() { + class UniffiByValue( + `seal`: UniffiCallbackInterfaceDeviceKeystoreMethod0? = null, + `openSealed`: UniffiCallbackInterfaceDeviceKeystoreMethod1? = null, + `uniffiFree`: UniffiCallbackInterfaceFree? = null, + ): UniffiVTableCallbackInterfaceDeviceKeystore(`seal`,`openSealed`,`uniffiFree`,), Structure.ByValue + + internal fun uniffiSetValue(other: UniffiVTableCallbackInterfaceDeviceKeystore) { + `seal` = other.`seal` + `openSealed` = other.`openSealed` + `uniffiFree` = other.`uniffiFree` + } + +} +@Structure.FieldOrder("log", "uniffiFree") +internal open class UniffiVTableCallbackInterfaceLogger( + @JvmField internal var `log`: UniffiCallbackInterfaceLoggerMethod0? = null, + @JvmField internal var `uniffiFree`: UniffiCallbackInterfaceFree? = null, +) : Structure() { + class UniffiByValue( + `log`: UniffiCallbackInterfaceLoggerMethod0? = null, + `uniffiFree`: UniffiCallbackInterfaceFree? = null, + ): UniffiVTableCallbackInterfaceLogger(`log`,`uniffiFree`,), Structure.ByValue + + internal fun uniffiSetValue(other: UniffiVTableCallbackInterfaceLogger) { + `log` = other.`log` + `uniffiFree` = other.`uniffiFree` + } + +} +@Structure.FieldOrder("keystore", "blobStore", "paths", "uniffiFree") +internal open class UniffiVTableCallbackInterfaceStorageProvider( + @JvmField internal var `keystore`: UniffiCallbackInterfaceStorageProviderMethod0? = null, + @JvmField internal var `blobStore`: UniffiCallbackInterfaceStorageProviderMethod1? = null, + @JvmField internal var `paths`: UniffiCallbackInterfaceStorageProviderMethod2? = null, + @JvmField internal var `uniffiFree`: UniffiCallbackInterfaceFree? = null, +) : Structure() { + class UniffiByValue( + `keystore`: UniffiCallbackInterfaceStorageProviderMethod0? = null, + `blobStore`: UniffiCallbackInterfaceStorageProviderMethod1? = null, + `paths`: UniffiCallbackInterfaceStorageProviderMethod2? = null, + `uniffiFree`: UniffiCallbackInterfaceFree? = null, + ): UniffiVTableCallbackInterfaceStorageProvider(`keystore`,`blobStore`,`paths`,`uniffiFree`,), Structure.ByValue + + internal fun uniffiSetValue(other: UniffiVTableCallbackInterfaceStorageProvider) { + `keystore` = other.`keystore` + `blobStore` = other.`blobStore` + `paths` = other.`paths` + `uniffiFree` = other.`uniffiFree` + } + +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +// For large crates we prevent `MethodTooLargeException` (see #2340) +// N.B. the name of the extension is very misleading, since it is +// rather `InterfaceTooLargeException`, caused by too many methods +// in the interface for large crates. +// +// By splitting the otherwise huge interface into two parts +// * UniffiLib +// * IntegrityCheckingUniffiLib (this) +// we allow for ~2x as many methods in the UniffiLib interface. +// +// The `ffi_uniffi_contract_version` method and all checksum methods are put +// into `IntegrityCheckingUniffiLib` and these methods are called only once, +// when the library is loaded. +internal interface IntegrityCheckingUniffiLib : Library { + // Integrity check functions only + fun uniffi_walletkit_core_checksum_func_set_logger( +): Short +fun uniffi_walletkit_core_checksum_method_atomicblobstore_read( +): Short +fun uniffi_walletkit_core_checksum_method_atomicblobstore_write_atomic( +): Short +fun uniffi_walletkit_core_checksum_method_atomicblobstore_delete( +): Short +fun uniffi_walletkit_core_checksum_method_credentialstore_begin_proof_disclosure( +): Short +fun uniffi_walletkit_core_checksum_method_credentialstore_init( +): Short +fun uniffi_walletkit_core_checksum_method_credentialstore_list_credentials( +): Short +fun uniffi_walletkit_core_checksum_method_credentialstore_merkle_cache_get( +): Short +fun uniffi_walletkit_core_checksum_method_credentialstore_merkle_cache_put( +): Short +fun uniffi_walletkit_core_checksum_method_credentialstore_storage_paths( +): Short +fun uniffi_walletkit_core_checksum_method_credentialstore_store_credential( +): Short +fun uniffi_walletkit_core_checksum_method_devicekeystore_seal( +): Short +fun uniffi_walletkit_core_checksum_method_devicekeystore_open_sealed( +): Short +fun uniffi_walletkit_core_checksum_method_logger_log( +): Short +fun uniffi_walletkit_core_checksum_method_proofcontext_get_credential_type( +): Short +fun uniffi_walletkit_core_checksum_method_proofcontext_get_external_nullifier( +): Short +fun uniffi_walletkit_core_checksum_method_proofcontext_get_signal_hash( +): Short +fun uniffi_walletkit_core_checksum_method_proofoutput_get_credential_type( +): Short +fun uniffi_walletkit_core_checksum_method_proofoutput_get_merkle_root( +): Short +fun uniffi_walletkit_core_checksum_method_proofoutput_get_nullifier_hash( +): Short +fun uniffi_walletkit_core_checksum_method_proofoutput_get_proof_as_string( +): Short +fun uniffi_walletkit_core_checksum_method_proofoutput_to_json( +): Short +fun uniffi_walletkit_core_checksum_method_storagepaths_cache_db_path_string( +): Short +fun uniffi_walletkit_core_checksum_method_storagepaths_lock_path_string( +): Short +fun uniffi_walletkit_core_checksum_method_storagepaths_root_path_string( +): Short +fun uniffi_walletkit_core_checksum_method_storagepaths_vault_db_path_string( +): Short +fun uniffi_walletkit_core_checksum_method_storagepaths_worldid_dir_path_string( +): Short +fun uniffi_walletkit_core_checksum_method_storageprovider_keystore( +): Short +fun uniffi_walletkit_core_checksum_method_storageprovider_blob_store( +): Short +fun uniffi_walletkit_core_checksum_method_storageprovider_paths( +): Short +fun uniffi_walletkit_core_checksum_method_u256wrapper_into_limbs( +): Short +fun uniffi_walletkit_core_checksum_method_u256wrapper_to_decimal_string( +): Short +fun uniffi_walletkit_core_checksum_method_u256wrapper_to_hex_string( +): Short +fun uniffi_walletkit_core_checksum_method_worldid_generate_nullifier_hash( +): Short +fun uniffi_walletkit_core_checksum_method_worldid_generate_proof( +): Short +fun uniffi_walletkit_core_checksum_method_worldid_get_identity_commitment( +): Short +fun uniffi_walletkit_core_checksum_method_worldid_is_equal_to( +): Short +fun uniffi_walletkit_core_checksum_constructor_credentialstore_from_provider_arc( +): Short +fun uniffi_walletkit_core_checksum_constructor_credentialstore_new_with_components( +): Short +fun uniffi_walletkit_core_checksum_constructor_merkletreeproof_from_identity_commitment( +): Short +fun uniffi_walletkit_core_checksum_constructor_merkletreeproof_from_json_proof( +): Short +fun uniffi_walletkit_core_checksum_constructor_proofcontext_new( +): Short +fun uniffi_walletkit_core_checksum_constructor_proofcontext_new_from_bytes( +): Short +fun uniffi_walletkit_core_checksum_constructor_proofcontext_new_from_signal_hash( +): Short +fun uniffi_walletkit_core_checksum_constructor_storagepaths_from_root( +): Short +fun uniffi_walletkit_core_checksum_constructor_u256wrapper_from_limbs( +): Short +fun uniffi_walletkit_core_checksum_constructor_u256wrapper_from_u32( +): Short +fun uniffi_walletkit_core_checksum_constructor_u256wrapper_from_u64( +): Short +fun uniffi_walletkit_core_checksum_constructor_u256wrapper_try_from_hex_string( +): Short +fun uniffi_walletkit_core_checksum_constructor_worldid_new( +): Short +fun ffi_walletkit_core_uniffi_contract_version( +): Int + +} + +// A JNA Library to expose the extern-C FFI definitions. +// This is an implementation detail which will be called internally by the public API. +internal interface UniffiLib : Library { + companion object { + internal val INSTANCE: UniffiLib by lazy { + val componentName = "walletkit_core" + // For large crates we prevent `MethodTooLargeException` (see #2340) + // N.B. the name of the extension is very misleading, since it is + // rather `InterfaceTooLargeException`, caused by too many methods + // in the interface for large crates. + // + // By splitting the otherwise huge interface into two parts + // * UniffiLib (this) + // * IntegrityCheckingUniffiLib + // And all checksum methods are put into `IntegrityCheckingUniffiLib` + // we allow for ~2x as many methods in the UniffiLib interface. + // + // Thus we first load the library with `loadIndirect` as `IntegrityCheckingUniffiLib` + // so that we can (optionally!) call `uniffiCheckApiChecksums`... + loadIndirect(componentName) + .also { lib: IntegrityCheckingUniffiLib -> + uniffiCheckContractApiVersion(lib) + uniffiCheckApiChecksums(lib) + } + // ... and then we load the library as `UniffiLib` + // N.B. we cannot use `loadIndirect` once and then try to cast it to `UniffiLib` + // => results in `java.lang.ClassCastException: com.sun.proxy.$Proxy cannot be cast to ...` + // error. So we must call `loadIndirect` twice. For crates large enough + // to trigger this issue, the performance impact is negligible, running on + // a macOS M1 machine the `loadIndirect` call takes ~50ms. + val lib = loadIndirect(componentName) + // No need to check the contract version and checksums, since + // we already did that with `IntegrityCheckingUniffiLib` above. + uniffiCallbackInterfaceAtomicBlobStore.register(lib) + uniffiCallbackInterfaceDeviceKeystore.register(lib) + uniffiCallbackInterfaceLogger.register(lib) + uniffiCallbackInterfaceStorageProvider.register(lib) + // Loading of library with integrity check done. + lib + } + + // The Cleaner for the whole library + internal val CLEANER: UniffiCleaner by lazy { + UniffiCleaner.create() + } + } + + // FFI functions + fun uniffi_walletkit_core_fn_clone_atomicblobstore(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, +): Pointer +fun uniffi_walletkit_core_fn_free_atomicblobstore(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, +): Unit +fun uniffi_walletkit_core_fn_init_callback_vtable_atomicblobstore(`vtable`: UniffiVTableCallbackInterfaceAtomicBlobStore, +): Unit +fun uniffi_walletkit_core_fn_method_atomicblobstore_read(`ptr`: Pointer,`path`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, +): RustBuffer.ByValue +fun uniffi_walletkit_core_fn_method_atomicblobstore_write_atomic(`ptr`: Pointer,`path`: RustBuffer.ByValue,`bytes`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, +): Unit +fun uniffi_walletkit_core_fn_method_atomicblobstore_delete(`ptr`: Pointer,`path`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, +): Unit +fun uniffi_walletkit_core_fn_clone_credentialstore(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, +): Pointer +fun uniffi_walletkit_core_fn_free_credentialstore(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, +): Unit +fun uniffi_walletkit_core_fn_constructor_credentialstore_from_provider_arc(`provider`: Pointer,uniffi_out_err: UniffiRustCallStatus, +): Pointer +fun uniffi_walletkit_core_fn_constructor_credentialstore_new_with_components(`paths`: Pointer,`keystore`: Pointer,`blobStore`: Pointer,uniffi_out_err: UniffiRustCallStatus, +): Pointer +fun uniffi_walletkit_core_fn_method_credentialstore_begin_proof_disclosure(`ptr`: Pointer,`requestId`: RustBuffer.ByValue,`nullifier`: RustBuffer.ByValue,`proofBytes`: RustBuffer.ByValue,`now`: Long,`ttlSeconds`: Long,uniffi_out_err: UniffiRustCallStatus, +): RustBuffer.ByValue +fun uniffi_walletkit_core_fn_method_credentialstore_init(`ptr`: Pointer,`leafIndex`: Long,`now`: Long,uniffi_out_err: UniffiRustCallStatus, +): Unit +fun uniffi_walletkit_core_fn_method_credentialstore_list_credentials(`ptr`: Pointer,`issuerSchemaId`: RustBuffer.ByValue,`now`: Long,uniffi_out_err: UniffiRustCallStatus, +): RustBuffer.ByValue +fun uniffi_walletkit_core_fn_method_credentialstore_merkle_cache_get(`ptr`: Pointer,`registryKind`: Byte,`root`: RustBuffer.ByValue,`now`: Long,uniffi_out_err: UniffiRustCallStatus, +): RustBuffer.ByValue +fun uniffi_walletkit_core_fn_method_credentialstore_merkle_cache_put(`ptr`: Pointer,`registryKind`: Byte,`root`: RustBuffer.ByValue,`proofBytes`: RustBuffer.ByValue,`now`: Long,`ttlSeconds`: Long,uniffi_out_err: UniffiRustCallStatus, +): Unit +fun uniffi_walletkit_core_fn_method_credentialstore_storage_paths(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, +): Pointer +fun uniffi_walletkit_core_fn_method_credentialstore_store_credential(`ptr`: Pointer,`issuerSchemaId`: Long,`status`: RustBuffer.ByValue,`subjectBlindingFactor`: RustBuffer.ByValue,`genesisIssuedAt`: Long,`expiresAt`: RustBuffer.ByValue,`credentialBlob`: RustBuffer.ByValue,`associatedData`: RustBuffer.ByValue,`now`: Long,uniffi_out_err: UniffiRustCallStatus, +): RustBuffer.ByValue +fun uniffi_walletkit_core_fn_clone_devicekeystore(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, +): Pointer +fun uniffi_walletkit_core_fn_free_devicekeystore(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, +): Unit +fun uniffi_walletkit_core_fn_init_callback_vtable_devicekeystore(`vtable`: UniffiVTableCallbackInterfaceDeviceKeystore, +): Unit +fun uniffi_walletkit_core_fn_method_devicekeystore_seal(`ptr`: Pointer,`associatedData`: RustBuffer.ByValue,`plaintext`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, +): RustBuffer.ByValue +fun uniffi_walletkit_core_fn_method_devicekeystore_open_sealed(`ptr`: Pointer,`associatedData`: RustBuffer.ByValue,`ciphertext`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, +): RustBuffer.ByValue +fun uniffi_walletkit_core_fn_clone_logger(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, +): Pointer +fun uniffi_walletkit_core_fn_free_logger(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, +): Unit +fun uniffi_walletkit_core_fn_init_callback_vtable_logger(`vtable`: UniffiVTableCallbackInterfaceLogger, +): Unit +fun uniffi_walletkit_core_fn_method_logger_log(`ptr`: Pointer,`level`: RustBuffer.ByValue,`message`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, +): Unit +fun uniffi_walletkit_core_fn_clone_merkletreeproof(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, +): Pointer +fun uniffi_walletkit_core_fn_free_merkletreeproof(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, +): Unit +fun uniffi_walletkit_core_fn_constructor_merkletreeproof_from_identity_commitment(`identityCommitment`: Pointer,`sequencerHost`: RustBuffer.ByValue,`requireMinedProof`: Byte, +): Long +fun uniffi_walletkit_core_fn_constructor_merkletreeproof_from_json_proof(`jsonProof`: RustBuffer.ByValue,`merkleRoot`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, +): Pointer +fun uniffi_walletkit_core_fn_clone_proofcontext(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, +): Pointer +fun uniffi_walletkit_core_fn_free_proofcontext(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, +): Unit +fun uniffi_walletkit_core_fn_constructor_proofcontext_new(`appId`: RustBuffer.ByValue,`action`: RustBuffer.ByValue,`signal`: RustBuffer.ByValue,`credentialType`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, +): Pointer +fun uniffi_walletkit_core_fn_constructor_proofcontext_new_from_bytes(`appId`: RustBuffer.ByValue,`action`: RustBuffer.ByValue,`signal`: RustBuffer.ByValue,`credentialType`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, +): Pointer +fun uniffi_walletkit_core_fn_constructor_proofcontext_new_from_signal_hash(`appId`: RustBuffer.ByValue,`action`: RustBuffer.ByValue,`credentialType`: RustBuffer.ByValue,`signalHash`: Pointer,uniffi_out_err: UniffiRustCallStatus, +): Pointer +fun uniffi_walletkit_core_fn_method_proofcontext_get_credential_type(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, +): RustBuffer.ByValue +fun uniffi_walletkit_core_fn_method_proofcontext_get_external_nullifier(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, +): Pointer +fun uniffi_walletkit_core_fn_method_proofcontext_get_signal_hash(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, +): Pointer +fun uniffi_walletkit_core_fn_clone_proofoutput(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, +): Pointer +fun uniffi_walletkit_core_fn_free_proofoutput(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, +): Unit +fun uniffi_walletkit_core_fn_method_proofoutput_get_credential_type(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, +): RustBuffer.ByValue +fun uniffi_walletkit_core_fn_method_proofoutput_get_merkle_root(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, +): Pointer +fun uniffi_walletkit_core_fn_method_proofoutput_get_nullifier_hash(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, +): Pointer +fun uniffi_walletkit_core_fn_method_proofoutput_get_proof_as_string(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, +): RustBuffer.ByValue +fun uniffi_walletkit_core_fn_method_proofoutput_to_json(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, +): RustBuffer.ByValue +fun uniffi_walletkit_core_fn_clone_storagepaths(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, +): Pointer +fun uniffi_walletkit_core_fn_free_storagepaths(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, +): Unit +fun uniffi_walletkit_core_fn_constructor_storagepaths_from_root(`root`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, +): Pointer +fun uniffi_walletkit_core_fn_method_storagepaths_cache_db_path_string(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, +): RustBuffer.ByValue +fun uniffi_walletkit_core_fn_method_storagepaths_lock_path_string(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, +): RustBuffer.ByValue +fun uniffi_walletkit_core_fn_method_storagepaths_root_path_string(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, +): RustBuffer.ByValue +fun uniffi_walletkit_core_fn_method_storagepaths_vault_db_path_string(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, +): RustBuffer.ByValue +fun uniffi_walletkit_core_fn_method_storagepaths_worldid_dir_path_string(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, +): RustBuffer.ByValue +fun uniffi_walletkit_core_fn_clone_storageprovider(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, +): Pointer +fun uniffi_walletkit_core_fn_free_storageprovider(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, +): Unit +fun uniffi_walletkit_core_fn_init_callback_vtable_storageprovider(`vtable`: UniffiVTableCallbackInterfaceStorageProvider, +): Unit +fun uniffi_walletkit_core_fn_method_storageprovider_keystore(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, +): Pointer +fun uniffi_walletkit_core_fn_method_storageprovider_blob_store(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, +): Pointer +fun uniffi_walletkit_core_fn_method_storageprovider_paths(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, +): Pointer +fun uniffi_walletkit_core_fn_clone_u256wrapper(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, +): Pointer +fun uniffi_walletkit_core_fn_free_u256wrapper(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, +): Unit +fun uniffi_walletkit_core_fn_constructor_u256wrapper_from_limbs(`limbs`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, +): Pointer +fun uniffi_walletkit_core_fn_constructor_u256wrapper_from_u32(`value`: Int,uniffi_out_err: UniffiRustCallStatus, +): Pointer +fun uniffi_walletkit_core_fn_constructor_u256wrapper_from_u64(`value`: Long,uniffi_out_err: UniffiRustCallStatus, +): Pointer +fun uniffi_walletkit_core_fn_constructor_u256wrapper_try_from_hex_string(`hexString`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, +): Pointer +fun uniffi_walletkit_core_fn_method_u256wrapper_into_limbs(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, +): RustBuffer.ByValue +fun uniffi_walletkit_core_fn_method_u256wrapper_to_decimal_string(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, +): RustBuffer.ByValue +fun uniffi_walletkit_core_fn_method_u256wrapper_to_hex_string(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, +): RustBuffer.ByValue +fun uniffi_walletkit_core_fn_clone_worldid(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, +): Pointer +fun uniffi_walletkit_core_fn_free_worldid(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, +): Unit +fun uniffi_walletkit_core_fn_constructor_worldid_new(`secret`: RustBuffer.ByValue,`environment`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, +): Pointer +fun uniffi_walletkit_core_fn_method_worldid_generate_nullifier_hash(`ptr`: Pointer,`context`: Pointer,uniffi_out_err: UniffiRustCallStatus, +): Pointer +fun uniffi_walletkit_core_fn_method_worldid_generate_proof(`ptr`: Pointer,`context`: Pointer, +): Long +fun uniffi_walletkit_core_fn_method_worldid_get_identity_commitment(`ptr`: Pointer,`credentialType`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, +): Pointer +fun uniffi_walletkit_core_fn_method_worldid_is_equal_to(`ptr`: Pointer,`other`: Pointer,uniffi_out_err: UniffiRustCallStatus, +): Byte +fun uniffi_walletkit_core_fn_func_set_logger(`logger`: Pointer,uniffi_out_err: UniffiRustCallStatus, +): Unit +fun ffi_walletkit_core_rustbuffer_alloc(`size`: Long,uniffi_out_err: UniffiRustCallStatus, +): RustBuffer.ByValue +fun ffi_walletkit_core_rustbuffer_from_bytes(`bytes`: ForeignBytes.ByValue,uniffi_out_err: UniffiRustCallStatus, +): RustBuffer.ByValue +fun ffi_walletkit_core_rustbuffer_free(`buf`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, +): Unit +fun ffi_walletkit_core_rustbuffer_reserve(`buf`: RustBuffer.ByValue,`additional`: Long,uniffi_out_err: UniffiRustCallStatus, +): RustBuffer.ByValue +fun ffi_walletkit_core_rust_future_poll_u8(`handle`: Long,`callback`: UniffiRustFutureContinuationCallback,`callbackData`: Long, +): Unit +fun ffi_walletkit_core_rust_future_cancel_u8(`handle`: Long, +): Unit +fun ffi_walletkit_core_rust_future_free_u8(`handle`: Long, +): Unit +fun ffi_walletkit_core_rust_future_complete_u8(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, +): Byte +fun ffi_walletkit_core_rust_future_poll_i8(`handle`: Long,`callback`: UniffiRustFutureContinuationCallback,`callbackData`: Long, +): Unit +fun ffi_walletkit_core_rust_future_cancel_i8(`handle`: Long, +): Unit +fun ffi_walletkit_core_rust_future_free_i8(`handle`: Long, +): Unit +fun ffi_walletkit_core_rust_future_complete_i8(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, +): Byte +fun ffi_walletkit_core_rust_future_poll_u16(`handle`: Long,`callback`: UniffiRustFutureContinuationCallback,`callbackData`: Long, +): Unit +fun ffi_walletkit_core_rust_future_cancel_u16(`handle`: Long, +): Unit +fun ffi_walletkit_core_rust_future_free_u16(`handle`: Long, +): Unit +fun ffi_walletkit_core_rust_future_complete_u16(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, +): Short +fun ffi_walletkit_core_rust_future_poll_i16(`handle`: Long,`callback`: UniffiRustFutureContinuationCallback,`callbackData`: Long, +): Unit +fun ffi_walletkit_core_rust_future_cancel_i16(`handle`: Long, +): Unit +fun ffi_walletkit_core_rust_future_free_i16(`handle`: Long, +): Unit +fun ffi_walletkit_core_rust_future_complete_i16(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, +): Short +fun ffi_walletkit_core_rust_future_poll_u32(`handle`: Long,`callback`: UniffiRustFutureContinuationCallback,`callbackData`: Long, +): Unit +fun ffi_walletkit_core_rust_future_cancel_u32(`handle`: Long, +): Unit +fun ffi_walletkit_core_rust_future_free_u32(`handle`: Long, +): Unit +fun ffi_walletkit_core_rust_future_complete_u32(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, +): Int +fun ffi_walletkit_core_rust_future_poll_i32(`handle`: Long,`callback`: UniffiRustFutureContinuationCallback,`callbackData`: Long, +): Unit +fun ffi_walletkit_core_rust_future_cancel_i32(`handle`: Long, +): Unit +fun ffi_walletkit_core_rust_future_free_i32(`handle`: Long, +): Unit +fun ffi_walletkit_core_rust_future_complete_i32(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, +): Int +fun ffi_walletkit_core_rust_future_poll_u64(`handle`: Long,`callback`: UniffiRustFutureContinuationCallback,`callbackData`: Long, +): Unit +fun ffi_walletkit_core_rust_future_cancel_u64(`handle`: Long, +): Unit +fun ffi_walletkit_core_rust_future_free_u64(`handle`: Long, +): Unit +fun ffi_walletkit_core_rust_future_complete_u64(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, +): Long +fun ffi_walletkit_core_rust_future_poll_i64(`handle`: Long,`callback`: UniffiRustFutureContinuationCallback,`callbackData`: Long, +): Unit +fun ffi_walletkit_core_rust_future_cancel_i64(`handle`: Long, +): Unit +fun ffi_walletkit_core_rust_future_free_i64(`handle`: Long, +): Unit +fun ffi_walletkit_core_rust_future_complete_i64(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, +): Long +fun ffi_walletkit_core_rust_future_poll_f32(`handle`: Long,`callback`: UniffiRustFutureContinuationCallback,`callbackData`: Long, +): Unit +fun ffi_walletkit_core_rust_future_cancel_f32(`handle`: Long, +): Unit +fun ffi_walletkit_core_rust_future_free_f32(`handle`: Long, +): Unit +fun ffi_walletkit_core_rust_future_complete_f32(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, +): Float +fun ffi_walletkit_core_rust_future_poll_f64(`handle`: Long,`callback`: UniffiRustFutureContinuationCallback,`callbackData`: Long, +): Unit +fun ffi_walletkit_core_rust_future_cancel_f64(`handle`: Long, +): Unit +fun ffi_walletkit_core_rust_future_free_f64(`handle`: Long, +): Unit +fun ffi_walletkit_core_rust_future_complete_f64(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, +): Double +fun ffi_walletkit_core_rust_future_poll_pointer(`handle`: Long,`callback`: UniffiRustFutureContinuationCallback,`callbackData`: Long, +): Unit +fun ffi_walletkit_core_rust_future_cancel_pointer(`handle`: Long, +): Unit +fun ffi_walletkit_core_rust_future_free_pointer(`handle`: Long, +): Unit +fun ffi_walletkit_core_rust_future_complete_pointer(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, +): Pointer +fun ffi_walletkit_core_rust_future_poll_rust_buffer(`handle`: Long,`callback`: UniffiRustFutureContinuationCallback,`callbackData`: Long, +): Unit +fun ffi_walletkit_core_rust_future_cancel_rust_buffer(`handle`: Long, +): Unit +fun ffi_walletkit_core_rust_future_free_rust_buffer(`handle`: Long, +): Unit +fun ffi_walletkit_core_rust_future_complete_rust_buffer(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, +): RustBuffer.ByValue +fun ffi_walletkit_core_rust_future_poll_void(`handle`: Long,`callback`: UniffiRustFutureContinuationCallback,`callbackData`: Long, +): Unit +fun ffi_walletkit_core_rust_future_cancel_void(`handle`: Long, +): Unit +fun ffi_walletkit_core_rust_future_free_void(`handle`: Long, +): Unit +fun ffi_walletkit_core_rust_future_complete_void(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, +): Unit + +} + +private fun uniffiCheckContractApiVersion(lib: IntegrityCheckingUniffiLib) { + // Get the bindings contract version from our ComponentInterface + val bindings_contract_version = 29 + // Get the scaffolding contract version by calling the into the dylib + val scaffolding_contract_version = lib.ffi_walletkit_core_uniffi_contract_version() + if (bindings_contract_version != scaffolding_contract_version) { + throw RuntimeException("UniFFI contract version mismatch: try cleaning and rebuilding your project") + } +} +@Suppress("UNUSED_PARAMETER") +private fun uniffiCheckApiChecksums(lib: IntegrityCheckingUniffiLib) { + if (lib.uniffi_walletkit_core_checksum_func_set_logger() != 57797.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_walletkit_core_checksum_method_atomicblobstore_read() != 44189.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_walletkit_core_checksum_method_atomicblobstore_write_atomic() != 57047.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_walletkit_core_checksum_method_atomicblobstore_delete() != 36326.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_walletkit_core_checksum_method_credentialstore_begin_proof_disclosure() != 18377.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_walletkit_core_checksum_method_credentialstore_init() != 25655.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_walletkit_core_checksum_method_credentialstore_list_credentials() != 37014.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_walletkit_core_checksum_method_credentialstore_merkle_cache_get() != 47785.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_walletkit_core_checksum_method_credentialstore_merkle_cache_put() != 8522.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_walletkit_core_checksum_method_credentialstore_storage_paths() != 31883.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_walletkit_core_checksum_method_credentialstore_store_credential() != 36573.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_walletkit_core_checksum_method_devicekeystore_seal() != 38896.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_walletkit_core_checksum_method_devicekeystore_open_sealed() != 59369.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_walletkit_core_checksum_method_logger_log() != 32531.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_walletkit_core_checksum_method_proofcontext_get_credential_type() != 23302.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_walletkit_core_checksum_method_proofcontext_get_external_nullifier() != 2388.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_walletkit_core_checksum_method_proofcontext_get_signal_hash() != 57766.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_walletkit_core_checksum_method_proofoutput_get_credential_type() != 23311.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_walletkit_core_checksum_method_proofoutput_get_merkle_root() != 19502.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_walletkit_core_checksum_method_proofoutput_get_nullifier_hash() != 43933.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_walletkit_core_checksum_method_proofoutput_get_proof_as_string() != 3072.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_walletkit_core_checksum_method_proofoutput_to_json() != 57737.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_walletkit_core_checksum_method_storagepaths_cache_db_path_string() != 3315.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_walletkit_core_checksum_method_storagepaths_lock_path_string() != 47091.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_walletkit_core_checksum_method_storagepaths_root_path_string() != 15513.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_walletkit_core_checksum_method_storagepaths_vault_db_path_string() != 48541.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_walletkit_core_checksum_method_storagepaths_worldid_dir_path_string() != 45505.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_walletkit_core_checksum_method_storageprovider_keystore() != 16820.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_walletkit_core_checksum_method_storageprovider_blob_store() != 9190.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_walletkit_core_checksum_method_storageprovider_paths() != 57665.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_walletkit_core_checksum_method_u256wrapper_into_limbs() != 40163.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_walletkit_core_checksum_method_u256wrapper_to_decimal_string() != 33044.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_walletkit_core_checksum_method_u256wrapper_to_hex_string() != 54248.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_walletkit_core_checksum_method_worldid_generate_nullifier_hash() != 17853.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_walletkit_core_checksum_method_worldid_generate_proof() != 46586.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_walletkit_core_checksum_method_worldid_get_identity_commitment() != 46113.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_walletkit_core_checksum_method_worldid_is_equal_to() != 12875.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_walletkit_core_checksum_constructor_credentialstore_from_provider_arc() != 15088.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_walletkit_core_checksum_constructor_credentialstore_new_with_components() != 49336.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_walletkit_core_checksum_constructor_merkletreeproof_from_identity_commitment() != 33610.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_walletkit_core_checksum_constructor_merkletreeproof_from_json_proof() != 37954.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_walletkit_core_checksum_constructor_proofcontext_new() != 53136.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_walletkit_core_checksum_constructor_proofcontext_new_from_bytes() != 40283.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_walletkit_core_checksum_constructor_proofcontext_new_from_signal_hash() != 45451.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_walletkit_core_checksum_constructor_storagepaths_from_root() != 12903.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_walletkit_core_checksum_constructor_u256wrapper_from_limbs() != 55436.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_walletkit_core_checksum_constructor_u256wrapper_from_u32() != 23290.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_walletkit_core_checksum_constructor_u256wrapper_from_u64() != 34597.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_walletkit_core_checksum_constructor_u256wrapper_try_from_hex_string() != 7382.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_walletkit_core_checksum_constructor_worldid_new() != 28488.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } +} + +/** + * @suppress + */ +public fun uniffiEnsureInitialized() { + UniffiLib.INSTANCE +} + +// Async support +// Async return type handlers + +internal const val UNIFFI_RUST_FUTURE_POLL_READY = 0.toByte() +internal const val UNIFFI_RUST_FUTURE_POLL_MAYBE_READY = 1.toByte() + +internal val uniffiContinuationHandleMap = UniffiHandleMap>() + +// FFI type for Rust future continuations +internal object uniffiRustFutureContinuationCallbackImpl: UniffiRustFutureContinuationCallback { + override fun callback(data: Long, pollResult: Byte) { + uniffiContinuationHandleMap.remove(data).resume(pollResult) + } +} + +internal suspend fun uniffiRustCallAsync( + rustFuture: Long, + pollFunc: (Long, UniffiRustFutureContinuationCallback, Long) -> Unit, + completeFunc: (Long, UniffiRustCallStatus) -> F, + freeFunc: (Long) -> Unit, + liftFunc: (F) -> T, + errorHandler: UniffiRustCallStatusErrorHandler +): T { + try { + do { + val pollResult = suspendCancellableCoroutine { continuation -> + pollFunc( + rustFuture, + uniffiRustFutureContinuationCallbackImpl, + uniffiContinuationHandleMap.insert(continuation) + ) + } + } while (pollResult != UNIFFI_RUST_FUTURE_POLL_READY); + + return liftFunc( + uniffiRustCallWithError(errorHandler, { status -> completeFunc(rustFuture, status) }) + ) + } finally { + freeFunc(rustFuture) + } +} + +// Public interface members begin here. + + +// Interface implemented by anything that can contain an object reference. +// +// Such types expose a `destroy()` method that must be called to cleanly +// dispose of the contained objects. Failure to call this method may result +// in memory leaks. +// +// The easiest way to ensure this method is called is to use the `.use` +// helper method to execute a block and destroy the object at the end. +interface Disposable { + fun destroy() + companion object { + fun destroy(vararg args: Any?) { + for (arg in args) { + when (arg) { + is Disposable -> arg.destroy() + is ArrayList<*> -> { + for (idx in arg.indices) { + val element = arg[idx] + if (element is Disposable) { + element.destroy() + } + } + } + is Map<*, *> -> { + for (element in arg.values) { + if (element is Disposable) { + element.destroy() + } + } + } + is Iterable<*> -> { + for (element in arg) { + if (element is Disposable) { + element.destroy() + } + } + } + } + } + } + } +} + +/** + * @suppress + */ +inline fun T.use(block: (T) -> R) = + try { + block(this) + } finally { + try { + // N.B. our implementation is on the nullable type `Disposable?`. + this?.destroy() + } catch (e: Throwable) { + // swallow + } + } + +/** + * Used to instantiate an interface without an actual pointer, for fakes in tests, mostly. + * + * @suppress + * */ +object NoPointer// Magic number for the Rust proxy to call using the same mechanism as every other method, +// to free the callback once it's dropped by Rust. +internal const val IDX_CALLBACK_FREE = 0 +// Callback return codes +internal const val UNIFFI_CALLBACK_SUCCESS = 0 +internal const val UNIFFI_CALLBACK_ERROR = 1 +internal const val UNIFFI_CALLBACK_UNEXPECTED_ERROR = 2 + +/** + * @suppress + */ +public abstract class FfiConverterCallbackInterface: FfiConverter { + internal val handleMap = UniffiHandleMap() + + internal fun drop(handle: Long) { + handleMap.remove(handle) + } + + override fun lift(value: Long): CallbackInterface { + return handleMap.get(value) + } + + override fun read(buf: ByteBuffer) = lift(buf.getLong()) + + override fun lower(value: CallbackInterface) = handleMap.insert(value) + + override fun allocationSize(value: CallbackInterface) = 8UL + + override fun write(value: CallbackInterface, buf: ByteBuffer) { + buf.putLong(lower(value)) + } +} +/** + * The cleaner interface for Object finalization code to run. + * This is the entry point to any implementation that we're using. + * + * The cleaner registers objects and returns cleanables, so now we are + * defining a `UniffiCleaner` with a `UniffiClenaer.Cleanable` to abstract the + * different implmentations available at compile time. + * + * @suppress + */ +interface UniffiCleaner { + interface Cleanable { + fun clean() + } + + fun register(value: Any, cleanUpTask: Runnable): UniffiCleaner.Cleanable + + companion object +} + +// The fallback Jna cleaner, which is available for both Android, and the JVM. +private class UniffiJnaCleaner : UniffiCleaner { + private val cleaner = com.sun.jna.internal.Cleaner.getCleaner() + + override fun register(value: Any, cleanUpTask: Runnable): UniffiCleaner.Cleanable = + UniffiJnaCleanable(cleaner.register(value, cleanUpTask)) +} + +private class UniffiJnaCleanable( + private val cleanable: com.sun.jna.internal.Cleaner.Cleanable, +) : UniffiCleaner.Cleanable { + override fun clean() = cleanable.clean() +} + + +// We decide at uniffi binding generation time whether we were +// using Android or not. +// There are further runtime checks to chose the correct implementation +// of the cleaner. +private fun UniffiCleaner.Companion.create(): UniffiCleaner = + try { + // For safety's sake: if the library hasn't been run in android_cleaner = true + // mode, but is being run on Android, then we still need to think about + // Android API versions. + // So we check if java.lang.ref.Cleaner is there, and use thatโ€ฆ + java.lang.Class.forName("java.lang.ref.Cleaner") + JavaLangRefCleaner() + } catch (e: ClassNotFoundException) { + // โ€ฆ otherwise, fallback to the JNA cleaner. + UniffiJnaCleaner() + } + +private class JavaLangRefCleaner : UniffiCleaner { + val cleaner = java.lang.ref.Cleaner.create() + + override fun register(value: Any, cleanUpTask: Runnable): UniffiCleaner.Cleanable = + JavaLangRefCleanable(cleaner.register(value, cleanUpTask)) +} + +private class JavaLangRefCleanable( + val cleanable: java.lang.ref.Cleaner.Cleanable +) : UniffiCleaner.Cleanable { + override fun clean() = cleanable.clean() +} + +/** + * @suppress + */ +public object FfiConverterUByte: FfiConverter { + override fun lift(value: Byte): UByte { + return value.toUByte() + } + + override fun read(buf: ByteBuffer): UByte { + return lift(buf.get()) + } + + override fun lower(value: UByte): Byte { + return value.toByte() + } + + override fun allocationSize(value: UByte) = 1UL + + override fun write(value: UByte, buf: ByteBuffer) { + buf.put(value.toByte()) + } +} + +/** + * @suppress + */ +public object FfiConverterUShort: FfiConverter { + override fun lift(value: Short): UShort { + return value.toUShort() + } + + override fun read(buf: ByteBuffer): UShort { + return lift(buf.getShort()) + } + + override fun lower(value: UShort): Short { + return value.toShort() + } + + override fun allocationSize(value: UShort) = 2UL + + override fun write(value: UShort, buf: ByteBuffer) { + buf.putShort(value.toShort()) + } +} + +/** + * @suppress + */ +public object FfiConverterUInt: FfiConverter { + override fun lift(value: Int): UInt { + return value.toUInt() + } + + override fun read(buf: ByteBuffer): UInt { + return lift(buf.getInt()) + } + + override fun lower(value: UInt): Int { + return value.toInt() + } + + override fun allocationSize(value: UInt) = 4UL + + override fun write(value: UInt, buf: ByteBuffer) { + buf.putInt(value.toInt()) + } +} + +/** + * @suppress + */ +public object FfiConverterULong: FfiConverter { + override fun lift(value: Long): ULong { + return value.toULong() + } + + override fun read(buf: ByteBuffer): ULong { + return lift(buf.getLong()) + } + + override fun lower(value: ULong): Long { + return value.toLong() + } + + override fun allocationSize(value: ULong) = 8UL + + override fun write(value: ULong, buf: ByteBuffer) { + buf.putLong(value.toLong()) + } +} + +/** + * @suppress + */ +public object FfiConverterBoolean: FfiConverter { + override fun lift(value: Byte): Boolean { + return value.toInt() != 0 + } + + override fun read(buf: ByteBuffer): Boolean { + return lift(buf.get()) + } + + override fun lower(value: Boolean): Byte { + return if (value) 1.toByte() else 0.toByte() + } + + override fun allocationSize(value: Boolean) = 1UL + + override fun write(value: Boolean, buf: ByteBuffer) { + buf.put(lower(value)) + } +} + +/** + * @suppress + */ +public object FfiConverterString: FfiConverter { + // Note: we don't inherit from FfiConverterRustBuffer, because we use a + // special encoding when lowering/lifting. We can use `RustBuffer.len` to + // store our length and avoid writing it out to the buffer. + override fun lift(value: RustBuffer.ByValue): String { + try { + val byteArr = ByteArray(value.len.toInt()) + value.asByteBuffer()!!.get(byteArr) + return byteArr.toString(Charsets.UTF_8) + } finally { + RustBuffer.free(value) + } + } + + override fun read(buf: ByteBuffer): String { + val len = buf.getInt() + val byteArr = ByteArray(len) + buf.get(byteArr) + return byteArr.toString(Charsets.UTF_8) + } + + fun toUtf8(value: String): ByteBuffer { + // Make sure we don't have invalid UTF-16, check for lone surrogates. + return Charsets.UTF_8.newEncoder().run { + onMalformedInput(CodingErrorAction.REPORT) + encode(CharBuffer.wrap(value)) + } + } + + override fun lower(value: String): RustBuffer.ByValue { + val byteBuf = toUtf8(value) + // Ideally we'd pass these bytes to `ffi_bytebuffer_from_bytes`, but doing so would require us + // to copy them into a JNA `Memory`. So we might as well directly copy them into a `RustBuffer`. + val rbuf = RustBuffer.alloc(byteBuf.limit().toULong()) + rbuf.asByteBuffer()!!.put(byteBuf) + return rbuf + } + + // We aren't sure exactly how many bytes our string will be once it's UTF-8 + // encoded. Allocate 3 bytes per UTF-16 code unit which will always be + // enough. + override fun allocationSize(value: String): ULong { + val sizeForLength = 4UL + val sizeForString = value.length.toULong() * 3UL + return sizeForLength + sizeForString + } + + override fun write(value: String, buf: ByteBuffer) { + val byteBuf = toUtf8(value) + buf.putInt(byteBuf.limit()) + buf.put(byteBuf) + } +} + +/** + * @suppress + */ +public object FfiConverterByteArray: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): ByteArray { + val len = buf.getInt() + val byteArr = ByteArray(len) + buf.get(byteArr) + return byteArr + } + override fun allocationSize(value: ByteArray): ULong { + return 4UL + value.size.toULong() + } + override fun write(value: ByteArray, buf: ByteBuffer) { + buf.putInt(value.size) + buf.put(value) + } +} + + +// This template implements a class for working with a Rust struct via a Pointer/Arc +// to the live Rust struct on the other side of the FFI. +// +// Each instance implements core operations for working with the Rust `Arc` and the +// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. +// +// There's some subtlety here, because we have to be careful not to operate on a Rust +// struct after it has been dropped, and because we must expose a public API for freeing +// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: +// +// * Each instance holds an opaque pointer to the underlying Rust struct. +// Method calls need to read this pointer from the object's state and pass it in to +// the Rust FFI. +// +// * When an instance is no longer needed, its pointer should be passed to a +// special destructor function provided by the Rust FFI, which will drop the +// underlying Rust struct. +// +// * Given an instance, calling code is expected to call the special +// `destroy` method in order to free it after use, either by calling it explicitly +// or by using a higher-level helper like the `use` method. Failing to do so risks +// leaking the underlying Rust struct. +// +// * We can't assume that calling code will do the right thing, and must be prepared +// to handle Kotlin method calls executing concurrently with or even after a call to +// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. +// +// * We must never allow Rust code to operate on the underlying Rust struct after +// the destructor has been called, and must never call the destructor more than once. +// Doing so may trigger memory unsafety. +// +// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` +// is implemented to call the destructor when the Kotlin object becomes unreachable. +// This is done in a background thread. This is not a panacea, and client code should be aware that +// 1. the thread may starve if some there are objects that have poorly performing +// `drop` methods or do significant work in their `drop` methods. +// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, +// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). +// +// If we try to implement this with mutual exclusion on access to the pointer, there is the +// possibility of a race between a method call and a concurrent call to `destroy`: +// +// * Thread A starts a method call, reads the value of the pointer, but is interrupted +// before it can pass the pointer over the FFI to Rust. +// * Thread B calls `destroy` and frees the underlying Rust struct. +// * Thread A resumes, passing the already-read pointer value to Rust and triggering +// a use-after-free. +// +// One possible solution would be to use a `ReadWriteLock`, with each method call taking +// a read lock (and thus allowed to run concurrently) and the special `destroy` method +// taking a write lock (and thus blocking on live method calls). However, we aim not to +// generate methods with any hidden blocking semantics, and a `destroy` method that might +// block if called incorrectly seems to meet that bar. +// +// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track +// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` +// has been called. These are updated according to the following rules: +// +// * The initial value of the counter is 1, indicating a live object with no in-flight calls. +// The initial value for the flag is false. +// +// * At the start of each method call, we atomically check the counter. +// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. +// If it is nonzero them we atomically increment it by 1 and proceed with the method call. +// +// * At the end of each method call, we atomically decrement and check the counter. +// If it has reached zero then we destroy the underlying Rust struct. +// +// * When `destroy` is called, we atomically flip the flag from false to true. +// If the flag was already true we silently fail. +// Otherwise we atomically decrement and check the counter. +// If it has reached zero then we destroy the underlying Rust struct. +// +// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, +// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. +// +// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been +// called *and* all in-flight method calls have completed, avoiding violating any of the expectations +// of the underlying Rust code. +// +// This makes a cleaner a better alternative to _not_ calling `destroy()` as +// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` +// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner +// thread may be starved, and the app will leak memory. +// +// In this case, `destroy`ing manually may be a better solution. +// +// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects +// with Rust peers are reclaimed: +// +// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: +// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: +// 3. The memory is reclaimed when the process terminates. +// +// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 +// + + +/** + * Atomic blob store for small binary files (e.g., `account_keys.bin`). + */ +public interface AtomicBlobStore { + + /** + * Reads the blob at `path`, if present. + * + * # Errors + * + * Returns an error if the read fails. + */ + fun `read`(`path`: kotlin.String): kotlin.ByteArray? + + /** + * Writes bytes atomically to `path`. + * + * # Errors + * + * Returns an error if the write fails. + */ + fun `writeAtomic`(`path`: kotlin.String, `bytes`: kotlin.ByteArray) + + /** + * Deletes the blob at `path`. + * + * # Errors + * + * Returns an error if the delete fails. + */ + fun `delete`(`path`: kotlin.String) + + companion object +} + +/** + * Atomic blob store for small binary files (e.g., `account_keys.bin`). + */ +open class AtomicBlobStoreImpl: Disposable, AutoCloseable, AtomicBlobStore +{ + + constructor(pointer: Pointer) { + this.pointer = pointer + this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) + } + + /** + * This constructor can be used to instantiate a fake object. Only used for tests. Any + * attempt to actually use an object constructed this way will fail as there is no + * connected Rust object. + */ + @Suppress("UNUSED_PARAMETER") + constructor(noPointer: NoPointer) { + this.pointer = null + this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) + } + + protected val pointer: Pointer? + protected val cleanable: UniffiCleaner.Cleanable + + private val wasDestroyed = AtomicBoolean(false) + private val callCounter = AtomicLong(1) + + override fun destroy() { + // Only allow a single call to this method. + // TODO: maybe we should log a warning if called more than once? + if (this.wasDestroyed.compareAndSet(false, true)) { + // This decrement always matches the initial count of 1 given at creation time. + if (this.callCounter.decrementAndGet() == 0L) { + cleanable.clean() + } + } + } + + @Synchronized + override fun close() { + this.destroy() + } + + internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { + // Check and increment the call counter, to keep the object alive. + // This needs a compare-and-set retry loop in case of concurrent updates. + do { + val c = this.callCounter.get() + if (c == 0L) { + throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") + } + if (c == Long.MAX_VALUE) { + throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") + } + } while (! this.callCounter.compareAndSet(c, c + 1L)) + // Now we can safely do the method call without the pointer being freed concurrently. + try { + return block(this.uniffiClonePointer()) + } finally { + // This decrement always matches the increment we performed above. + if (this.callCounter.decrementAndGet() == 0L) { + cleanable.clean() + } + } + } + + // Use a static inner class instead of a closure so as not to accidentally + // capture `this` as part of the cleanable's action. + private class UniffiCleanAction(private val pointer: Pointer?) : Runnable { + override fun run() { + pointer?.let { ptr -> + uniffiRustCall { status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_free_atomicblobstore(ptr, status) + } + } + } + } + + fun uniffiClonePointer(): Pointer { + return uniffiRustCall() { status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_clone_atomicblobstore(pointer!!, status) + } + } + + + /** + * Reads the blob at `path`, if present. + * + * # Errors + * + * Returns an error if the read fails. + */ + @Throws(StorageException::class)override fun `read`(`path`: kotlin.String): kotlin.ByteArray? { + return FfiConverterOptionalByteArray.lift( + callWithPointer { + uniffiRustCallWithError(StorageException) { _status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_atomicblobstore_read( + it, FfiConverterString.lower(`path`),_status) +} + } + ) + } + + + + /** + * Writes bytes atomically to `path`. + * + * # Errors + * + * Returns an error if the write fails. + */ + @Throws(StorageException::class)override fun `writeAtomic`(`path`: kotlin.String, `bytes`: kotlin.ByteArray) + = + callWithPointer { + uniffiRustCallWithError(StorageException) { _status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_atomicblobstore_write_atomic( + it, FfiConverterString.lower(`path`),FfiConverterByteArray.lower(`bytes`),_status) +} + } + + + + + /** + * Deletes the blob at `path`. + * + * # Errors + * + * Returns an error if the delete fails. + */ + @Throws(StorageException::class)override fun `delete`(`path`: kotlin.String) + = + callWithPointer { + uniffiRustCallWithError(StorageException) { _status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_atomicblobstore_delete( + it, FfiConverterString.lower(`path`),_status) +} + } + + + + + + + + companion object + +} + + +// Put the implementation in an object so we don't pollute the top-level namespace +internal object uniffiCallbackInterfaceAtomicBlobStore { + internal object `read`: UniffiCallbackInterfaceAtomicBlobStoreMethod0 { + override fun callback(`uniffiHandle`: Long,`path`: RustBuffer.ByValue,`uniffiOutReturn`: RustBuffer,uniffiCallStatus: UniffiRustCallStatus,) { + val uniffiObj = FfiConverterTypeAtomicBlobStore.handleMap.get(uniffiHandle) + val makeCall = { -> + uniffiObj.`read`( + FfiConverterString.lift(`path`), + ) + } + val writeReturn = { value: kotlin.ByteArray? -> uniffiOutReturn.setValue(FfiConverterOptionalByteArray.lower(value)) } + uniffiTraitInterfaceCallWithError( + uniffiCallStatus, + makeCall, + writeReturn, + { e: StorageException -> FfiConverterTypeStorageError.lower(e) } + ) + } + } + internal object `writeAtomic`: UniffiCallbackInterfaceAtomicBlobStoreMethod1 { + override fun callback(`uniffiHandle`: Long,`path`: RustBuffer.ByValue,`bytes`: RustBuffer.ByValue,`uniffiOutReturn`: Pointer,uniffiCallStatus: UniffiRustCallStatus,) { + val uniffiObj = FfiConverterTypeAtomicBlobStore.handleMap.get(uniffiHandle) + val makeCall = { -> + uniffiObj.`writeAtomic`( + FfiConverterString.lift(`path`), + FfiConverterByteArray.lift(`bytes`), + ) + } + val writeReturn = { _: Unit -> Unit } + uniffiTraitInterfaceCallWithError( + uniffiCallStatus, + makeCall, + writeReturn, + { e: StorageException -> FfiConverterTypeStorageError.lower(e) } + ) + } + } + internal object `delete`: UniffiCallbackInterfaceAtomicBlobStoreMethod2 { + override fun callback(`uniffiHandle`: Long,`path`: RustBuffer.ByValue,`uniffiOutReturn`: Pointer,uniffiCallStatus: UniffiRustCallStatus,) { + val uniffiObj = FfiConverterTypeAtomicBlobStore.handleMap.get(uniffiHandle) + val makeCall = { -> + uniffiObj.`delete`( + FfiConverterString.lift(`path`), + ) + } + val writeReturn = { _: Unit -> Unit } + uniffiTraitInterfaceCallWithError( + uniffiCallStatus, + makeCall, + writeReturn, + { e: StorageException -> FfiConverterTypeStorageError.lower(e) } + ) + } + } + + internal object uniffiFree: UniffiCallbackInterfaceFree { + override fun callback(handle: Long) { + FfiConverterTypeAtomicBlobStore.handleMap.remove(handle) + } + } + + internal var vtable = UniffiVTableCallbackInterfaceAtomicBlobStore.UniffiByValue( + `read`, + `writeAtomic`, + `delete`, + uniffiFree, + ) + + // Registers the foreign callback with the Rust side. + // This method is generated for each callback interface. + internal fun register(lib: UniffiLib) { + lib.uniffi_walletkit_core_fn_init_callback_vtable_atomicblobstore(vtable) + } +} + +/** + * @suppress + */ +public object FfiConverterTypeAtomicBlobStore: FfiConverter { + internal val handleMap = UniffiHandleMap() + + override fun lower(value: AtomicBlobStore): Pointer { + return Pointer(handleMap.insert(value)) + } + + override fun lift(value: Pointer): AtomicBlobStore { + return AtomicBlobStoreImpl(value) + } + + override fun read(buf: ByteBuffer): AtomicBlobStore { + // The Rust code always writes pointers as 8 bytes, and will + // fail to compile if they don't fit. + return lift(Pointer(buf.getLong())) + } + + override fun allocationSize(value: AtomicBlobStore) = 8UL + + override fun write(value: AtomicBlobStore, buf: ByteBuffer) { + // The Rust code always expects pointers written as 8 bytes, + // and will fail to compile if they don't fit. + buf.putLong(Pointer.nativeValue(lower(value))) + } +} + + +// This template implements a class for working with a Rust struct via a Pointer/Arc +// to the live Rust struct on the other side of the FFI. +// +// Each instance implements core operations for working with the Rust `Arc` and the +// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. +// +// There's some subtlety here, because we have to be careful not to operate on a Rust +// struct after it has been dropped, and because we must expose a public API for freeing +// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: +// +// * Each instance holds an opaque pointer to the underlying Rust struct. +// Method calls need to read this pointer from the object's state and pass it in to +// the Rust FFI. +// +// * When an instance is no longer needed, its pointer should be passed to a +// special destructor function provided by the Rust FFI, which will drop the +// underlying Rust struct. +// +// * Given an instance, calling code is expected to call the special +// `destroy` method in order to free it after use, either by calling it explicitly +// or by using a higher-level helper like the `use` method. Failing to do so risks +// leaking the underlying Rust struct. +// +// * We can't assume that calling code will do the right thing, and must be prepared +// to handle Kotlin method calls executing concurrently with or even after a call to +// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. +// +// * We must never allow Rust code to operate on the underlying Rust struct after +// the destructor has been called, and must never call the destructor more than once. +// Doing so may trigger memory unsafety. +// +// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` +// is implemented to call the destructor when the Kotlin object becomes unreachable. +// This is done in a background thread. This is not a panacea, and client code should be aware that +// 1. the thread may starve if some there are objects that have poorly performing +// `drop` methods or do significant work in their `drop` methods. +// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, +// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). +// +// If we try to implement this with mutual exclusion on access to the pointer, there is the +// possibility of a race between a method call and a concurrent call to `destroy`: +// +// * Thread A starts a method call, reads the value of the pointer, but is interrupted +// before it can pass the pointer over the FFI to Rust. +// * Thread B calls `destroy` and frees the underlying Rust struct. +// * Thread A resumes, passing the already-read pointer value to Rust and triggering +// a use-after-free. +// +// One possible solution would be to use a `ReadWriteLock`, with each method call taking +// a read lock (and thus allowed to run concurrently) and the special `destroy` method +// taking a write lock (and thus blocking on live method calls). However, we aim not to +// generate methods with any hidden blocking semantics, and a `destroy` method that might +// block if called incorrectly seems to meet that bar. +// +// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track +// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` +// has been called. These are updated according to the following rules: +// +// * The initial value of the counter is 1, indicating a live object with no in-flight calls. +// The initial value for the flag is false. +// +// * At the start of each method call, we atomically check the counter. +// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. +// If it is nonzero them we atomically increment it by 1 and proceed with the method call. +// +// * At the end of each method call, we atomically decrement and check the counter. +// If it has reached zero then we destroy the underlying Rust struct. +// +// * When `destroy` is called, we atomically flip the flag from false to true. +// If the flag was already true we silently fail. +// Otherwise we atomically decrement and check the counter. +// If it has reached zero then we destroy the underlying Rust struct. +// +// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, +// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. +// +// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been +// called *and* all in-flight method calls have completed, avoiding violating any of the expectations +// of the underlying Rust code. +// +// This makes a cleaner a better alternative to _not_ calling `destroy()` as +// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` +// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner +// thread may be starved, and the app will leak memory. +// +// In this case, `destroy`ing manually may be a better solution. +// +// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects +// with Rust peers are reclaimed: +// +// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: +// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: +// 3. The memory is reclaimed when the process terminates. +// +// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 +// + + +/** + * Concrete storage implementation backed by `SQLCipher` databases. + */ +public interface CredentialStoreInterface { + + /** + * Enforces replay safety for proof disclosure. + * + * # Errors + * + * Returns an error if the disclosure conflicts or storage fails. + */ + fun `beginProofDisclosure`(`requestId`: kotlin.ByteArray, `nullifier`: kotlin.ByteArray, `proofBytes`: kotlin.ByteArray, `now`: kotlin.ULong, `ttlSeconds`: kotlin.ULong): ProofDisclosureResultFfi + + /** + * Initializes storage and validates the account leaf index. + * + * # Errors + * + * Returns an error if initialization fails or the leaf index mismatches. + */ + fun `init`(`leafIndex`: kotlin.ULong, `now`: kotlin.ULong) + + /** + * Lists active credentials, optionally filtered by issuer schema ID. + * + * # Errors + * + * Returns an error if the credential query fails. + */ + fun `listCredentials`(`issuerSchemaId`: kotlin.ULong?, `now`: kotlin.ULong): List + + /** + * Fetches a cached Merkle proof if available. + * + * # Errors + * + * Returns an error if the cache lookup fails. + */ + fun `merkleCacheGet`(`registryKind`: kotlin.UByte, `root`: kotlin.ByteArray, `now`: kotlin.ULong): kotlin.ByteArray? + + /** + * Inserts a cached Merkle proof with a TTL. + * + * # Errors + * + * Returns an error if the cache insert fails. + */ + fun `merkleCachePut`(`registryKind`: kotlin.UByte, `root`: kotlin.ByteArray, `proofBytes`: kotlin.ByteArray, `now`: kotlin.ULong, `ttlSeconds`: kotlin.ULong) + + /** + * Returns the storage paths used by this handle. + * + * # Errors + * + * Returns an error if the storage mutex is poisoned. + */ + fun `storagePaths`(): StoragePaths + + /** + * Stores a credential and optional associated data. + * + * # Errors + * + * Returns an error if the credential cannot be stored. + */ + fun `storeCredential`(`issuerSchemaId`: kotlin.ULong, `status`: CredentialStatus, `subjectBlindingFactor`: kotlin.ByteArray, `genesisIssuedAt`: kotlin.ULong, `expiresAt`: kotlin.ULong?, `credentialBlob`: kotlin.ByteArray, `associatedData`: kotlin.ByteArray?, `now`: kotlin.ULong): kotlin.ByteArray + + companion object +} + +/** + * Concrete storage implementation backed by `SQLCipher` databases. + */ +open class CredentialStore: Disposable, AutoCloseable, CredentialStoreInterface +{ + + constructor(pointer: Pointer) { + this.pointer = pointer + this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) + } + + /** + * This constructor can be used to instantiate a fake object. Only used for tests. Any + * attempt to actually use an object constructed this way will fail as there is no + * connected Rust object. + */ + @Suppress("UNUSED_PARAMETER") + constructor(noPointer: NoPointer) { + this.pointer = null + this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) + } + + protected val pointer: Pointer? + protected val cleanable: UniffiCleaner.Cleanable + + private val wasDestroyed = AtomicBoolean(false) + private val callCounter = AtomicLong(1) + + override fun destroy() { + // Only allow a single call to this method. + // TODO: maybe we should log a warning if called more than once? + if (this.wasDestroyed.compareAndSet(false, true)) { + // This decrement always matches the initial count of 1 given at creation time. + if (this.callCounter.decrementAndGet() == 0L) { + cleanable.clean() + } + } + } + + @Synchronized + override fun close() { + this.destroy() + } + + internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { + // Check and increment the call counter, to keep the object alive. + // This needs a compare-and-set retry loop in case of concurrent updates. + do { + val c = this.callCounter.get() + if (c == 0L) { + throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") + } + if (c == Long.MAX_VALUE) { + throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") + } + } while (! this.callCounter.compareAndSet(c, c + 1L)) + // Now we can safely do the method call without the pointer being freed concurrently. + try { + return block(this.uniffiClonePointer()) + } finally { + // This decrement always matches the increment we performed above. + if (this.callCounter.decrementAndGet() == 0L) { + cleanable.clean() + } + } + } + + // Use a static inner class instead of a closure so as not to accidentally + // capture `this` as part of the cleanable's action. + private class UniffiCleanAction(private val pointer: Pointer?) : Runnable { + override fun run() { + pointer?.let { ptr -> + uniffiRustCall { status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_free_credentialstore(ptr, status) + } + } + } + } + + fun uniffiClonePointer(): Pointer { + return uniffiRustCall() { status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_clone_credentialstore(pointer!!, status) + } + } + + + /** + * Enforces replay safety for proof disclosure. + * + * # Errors + * + * Returns an error if the disclosure conflicts or storage fails. + */ + @Throws(StorageException::class)override fun `beginProofDisclosure`(`requestId`: kotlin.ByteArray, `nullifier`: kotlin.ByteArray, `proofBytes`: kotlin.ByteArray, `now`: kotlin.ULong, `ttlSeconds`: kotlin.ULong): ProofDisclosureResultFfi { + return FfiConverterTypeProofDisclosureResultFfi.lift( + callWithPointer { + uniffiRustCallWithError(StorageException) { _status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_credentialstore_begin_proof_disclosure( + it, FfiConverterByteArray.lower(`requestId`),FfiConverterByteArray.lower(`nullifier`),FfiConverterByteArray.lower(`proofBytes`),FfiConverterULong.lower(`now`),FfiConverterULong.lower(`ttlSeconds`),_status) +} + } + ) + } + + + + /** + * Initializes storage and validates the account leaf index. + * + * # Errors + * + * Returns an error if initialization fails or the leaf index mismatches. + */ + @Throws(StorageException::class)override fun `init`(`leafIndex`: kotlin.ULong, `now`: kotlin.ULong) + = + callWithPointer { + uniffiRustCallWithError(StorageException) { _status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_credentialstore_init( + it, FfiConverterULong.lower(`leafIndex`),FfiConverterULong.lower(`now`),_status) +} + } + + + + + /** + * Lists active credentials, optionally filtered by issuer schema ID. + * + * # Errors + * + * Returns an error if the credential query fails. + */ + @Throws(StorageException::class)override fun `listCredentials`(`issuerSchemaId`: kotlin.ULong?, `now`: kotlin.ULong): List { + return FfiConverterSequenceTypeCredentialRecordFfi.lift( + callWithPointer { + uniffiRustCallWithError(StorageException) { _status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_credentialstore_list_credentials( + it, FfiConverterOptionalULong.lower(`issuerSchemaId`),FfiConverterULong.lower(`now`),_status) +} + } + ) + } + + + + /** + * Fetches a cached Merkle proof if available. + * + * # Errors + * + * Returns an error if the cache lookup fails. + */ + @Throws(StorageException::class)override fun `merkleCacheGet`(`registryKind`: kotlin.UByte, `root`: kotlin.ByteArray, `now`: kotlin.ULong): kotlin.ByteArray? { + return FfiConverterOptionalByteArray.lift( + callWithPointer { + uniffiRustCallWithError(StorageException) { _status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_credentialstore_merkle_cache_get( + it, FfiConverterUByte.lower(`registryKind`),FfiConverterByteArray.lower(`root`),FfiConverterULong.lower(`now`),_status) +} + } + ) + } + + + + /** + * Inserts a cached Merkle proof with a TTL. + * + * # Errors + * + * Returns an error if the cache insert fails. + */ + @Throws(StorageException::class)override fun `merkleCachePut`(`registryKind`: kotlin.UByte, `root`: kotlin.ByteArray, `proofBytes`: kotlin.ByteArray, `now`: kotlin.ULong, `ttlSeconds`: kotlin.ULong) + = + callWithPointer { + uniffiRustCallWithError(StorageException) { _status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_credentialstore_merkle_cache_put( + it, FfiConverterUByte.lower(`registryKind`),FfiConverterByteArray.lower(`root`),FfiConverterByteArray.lower(`proofBytes`),FfiConverterULong.lower(`now`),FfiConverterULong.lower(`ttlSeconds`),_status) +} + } + + + + + /** + * Returns the storage paths used by this handle. + * + * # Errors + * + * Returns an error if the storage mutex is poisoned. + */ + @Throws(StorageException::class)override fun `storagePaths`(): StoragePaths { + return FfiConverterTypeStoragePaths.lift( + callWithPointer { + uniffiRustCallWithError(StorageException) { _status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_credentialstore_storage_paths( + it, _status) +} + } + ) + } + + + + /** + * Stores a credential and optional associated data. + * + * # Errors + * + * Returns an error if the credential cannot be stored. + */ + @Throws(StorageException::class)override fun `storeCredential`(`issuerSchemaId`: kotlin.ULong, `status`: CredentialStatus, `subjectBlindingFactor`: kotlin.ByteArray, `genesisIssuedAt`: kotlin.ULong, `expiresAt`: kotlin.ULong?, `credentialBlob`: kotlin.ByteArray, `associatedData`: kotlin.ByteArray?, `now`: kotlin.ULong): kotlin.ByteArray { + return FfiConverterByteArray.lift( + callWithPointer { + uniffiRustCallWithError(StorageException) { _status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_credentialstore_store_credential( + it, FfiConverterULong.lower(`issuerSchemaId`),FfiConverterTypeCredentialStatus.lower(`status`),FfiConverterByteArray.lower(`subjectBlindingFactor`),FfiConverterULong.lower(`genesisIssuedAt`),FfiConverterOptionalULong.lower(`expiresAt`),FfiConverterByteArray.lower(`credentialBlob`),FfiConverterOptionalByteArray.lower(`associatedData`),FfiConverterULong.lower(`now`),_status) +} + } + ) + } + + + + + + companion object { + + /** + * Creates a new storage handle from a platform provider. + * + * # Errors + * + * Returns an error if the storage lock cannot be opened. + */ + @Throws(StorageException::class) fun `fromProviderArc`(`provider`: StorageProvider): CredentialStore { + return FfiConverterTypeCredentialStore.lift( + uniffiRustCallWithError(StorageException) { _status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_constructor_credentialstore_from_provider_arc( + FfiConverterTypeStorageProvider.lower(`provider`),_status) +} + ) + } + + + + /** + * Creates a new storage handle from explicit components. + * + * # Errors + * + * Returns an error if the storage lock cannot be opened. + */ + @Throws(StorageException::class) fun `newWithComponents`(`paths`: StoragePaths, `keystore`: DeviceKeystore, `blobStore`: AtomicBlobStore): CredentialStore { + return FfiConverterTypeCredentialStore.lift( + uniffiRustCallWithError(StorageException) { _status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_constructor_credentialstore_new_with_components( + FfiConverterTypeStoragePaths.lower(`paths`),FfiConverterTypeDeviceKeystore.lower(`keystore`),FfiConverterTypeAtomicBlobStore.lower(`blobStore`),_status) +} + ) + } + + + + } + +} + +/** + * @suppress + */ +public object FfiConverterTypeCredentialStore: FfiConverter { + + override fun lower(value: CredentialStore): Pointer { + return value.uniffiClonePointer() + } + + override fun lift(value: Pointer): CredentialStore { + return CredentialStore(value) + } + + override fun read(buf: ByteBuffer): CredentialStore { + // The Rust code always writes pointers as 8 bytes, and will + // fail to compile if they don't fit. + return lift(Pointer(buf.getLong())) + } + + override fun allocationSize(value: CredentialStore) = 8UL + + override fun write(value: CredentialStore, buf: ByteBuffer) { + // The Rust code always expects pointers written as 8 bytes, + // and will fail to compile if they don't fit. + buf.putLong(Pointer.nativeValue(lower(value))) + } +} + + +// This template implements a class for working with a Rust struct via a Pointer/Arc +// to the live Rust struct on the other side of the FFI. +// +// Each instance implements core operations for working with the Rust `Arc` and the +// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. +// +// There's some subtlety here, because we have to be careful not to operate on a Rust +// struct after it has been dropped, and because we must expose a public API for freeing +// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: +// +// * Each instance holds an opaque pointer to the underlying Rust struct. +// Method calls need to read this pointer from the object's state and pass it in to +// the Rust FFI. +// +// * When an instance is no longer needed, its pointer should be passed to a +// special destructor function provided by the Rust FFI, which will drop the +// underlying Rust struct. +// +// * Given an instance, calling code is expected to call the special +// `destroy` method in order to free it after use, either by calling it explicitly +// or by using a higher-level helper like the `use` method. Failing to do so risks +// leaking the underlying Rust struct. +// +// * We can't assume that calling code will do the right thing, and must be prepared +// to handle Kotlin method calls executing concurrently with or even after a call to +// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. +// +// * We must never allow Rust code to operate on the underlying Rust struct after +// the destructor has been called, and must never call the destructor more than once. +// Doing so may trigger memory unsafety. +// +// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` +// is implemented to call the destructor when the Kotlin object becomes unreachable. +// This is done in a background thread. This is not a panacea, and client code should be aware that +// 1. the thread may starve if some there are objects that have poorly performing +// `drop` methods or do significant work in their `drop` methods. +// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, +// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). +// +// If we try to implement this with mutual exclusion on access to the pointer, there is the +// possibility of a race between a method call and a concurrent call to `destroy`: +// +// * Thread A starts a method call, reads the value of the pointer, but is interrupted +// before it can pass the pointer over the FFI to Rust. +// * Thread B calls `destroy` and frees the underlying Rust struct. +// * Thread A resumes, passing the already-read pointer value to Rust and triggering +// a use-after-free. +// +// One possible solution would be to use a `ReadWriteLock`, with each method call taking +// a read lock (and thus allowed to run concurrently) and the special `destroy` method +// taking a write lock (and thus blocking on live method calls). However, we aim not to +// generate methods with any hidden blocking semantics, and a `destroy` method that might +// block if called incorrectly seems to meet that bar. +// +// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track +// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` +// has been called. These are updated according to the following rules: +// +// * The initial value of the counter is 1, indicating a live object with no in-flight calls. +// The initial value for the flag is false. +// +// * At the start of each method call, we atomically check the counter. +// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. +// If it is nonzero them we atomically increment it by 1 and proceed with the method call. +// +// * At the end of each method call, we atomically decrement and check the counter. +// If it has reached zero then we destroy the underlying Rust struct. +// +// * When `destroy` is called, we atomically flip the flag from false to true. +// If the flag was already true we silently fail. +// Otherwise we atomically decrement and check the counter. +// If it has reached zero then we destroy the underlying Rust struct. +// +// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, +// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. +// +// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been +// called *and* all in-flight method calls have completed, avoiding violating any of the expectations +// of the underlying Rust code. +// +// This makes a cleaner a better alternative to _not_ calling `destroy()` as +// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` +// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner +// thread may be starved, and the app will leak memory. +// +// In this case, `destroy`ing manually may be a better solution. +// +// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects +// with Rust peers are reclaimed: +// +// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: +// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: +// 3. The memory is reclaimed when the process terminates. +// +// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 +// + + +/** + * Device keystore interface used to seal and open account keys. + */ +public interface DeviceKeystore { + + /** + * Seals plaintext under the device-bound key, binding `associated_data`. + * + * # Errors + * + * Returns an error if the keystore refuses the operation or the seal fails. + */ + fun `seal`(`associatedData`: kotlin.ByteArray, `plaintext`: kotlin.ByteArray): kotlin.ByteArray + + /** + * Opens ciphertext under the device-bound key, verifying `associated_data`. + * + * # Errors + * + * Returns an error if authentication fails or the keystore cannot open. + */ + fun `openSealed`(`associatedData`: kotlin.ByteArray, `ciphertext`: kotlin.ByteArray): kotlin.ByteArray + + companion object +} + +/** + * Device keystore interface used to seal and open account keys. + */ +open class DeviceKeystoreImpl: Disposable, AutoCloseable, DeviceKeystore +{ + + constructor(pointer: Pointer) { + this.pointer = pointer + this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) + } + + /** + * This constructor can be used to instantiate a fake object. Only used for tests. Any + * attempt to actually use an object constructed this way will fail as there is no + * connected Rust object. + */ + @Suppress("UNUSED_PARAMETER") + constructor(noPointer: NoPointer) { + this.pointer = null + this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) + } + + protected val pointer: Pointer? + protected val cleanable: UniffiCleaner.Cleanable + + private val wasDestroyed = AtomicBoolean(false) + private val callCounter = AtomicLong(1) + + override fun destroy() { + // Only allow a single call to this method. + // TODO: maybe we should log a warning if called more than once? + if (this.wasDestroyed.compareAndSet(false, true)) { + // This decrement always matches the initial count of 1 given at creation time. + if (this.callCounter.decrementAndGet() == 0L) { + cleanable.clean() + } + } + } + + @Synchronized + override fun close() { + this.destroy() + } + + internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { + // Check and increment the call counter, to keep the object alive. + // This needs a compare-and-set retry loop in case of concurrent updates. + do { + val c = this.callCounter.get() + if (c == 0L) { + throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") + } + if (c == Long.MAX_VALUE) { + throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") + } + } while (! this.callCounter.compareAndSet(c, c + 1L)) + // Now we can safely do the method call without the pointer being freed concurrently. + try { + return block(this.uniffiClonePointer()) + } finally { + // This decrement always matches the increment we performed above. + if (this.callCounter.decrementAndGet() == 0L) { + cleanable.clean() + } + } + } + + // Use a static inner class instead of a closure so as not to accidentally + // capture `this` as part of the cleanable's action. + private class UniffiCleanAction(private val pointer: Pointer?) : Runnable { + override fun run() { + pointer?.let { ptr -> + uniffiRustCall { status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_free_devicekeystore(ptr, status) + } + } + } + } + + fun uniffiClonePointer(): Pointer { + return uniffiRustCall() { status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_clone_devicekeystore(pointer!!, status) + } + } + + + /** + * Seals plaintext under the device-bound key, binding `associated_data`. + * + * # Errors + * + * Returns an error if the keystore refuses the operation or the seal fails. + */ + @Throws(StorageException::class)override fun `seal`(`associatedData`: kotlin.ByteArray, `plaintext`: kotlin.ByteArray): kotlin.ByteArray { + return FfiConverterByteArray.lift( + callWithPointer { + uniffiRustCallWithError(StorageException) { _status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_devicekeystore_seal( + it, FfiConverterByteArray.lower(`associatedData`),FfiConverterByteArray.lower(`plaintext`),_status) +} + } + ) + } + + + + /** + * Opens ciphertext under the device-bound key, verifying `associated_data`. + * + * # Errors + * + * Returns an error if authentication fails or the keystore cannot open. + */ + @Throws(StorageException::class)override fun `openSealed`(`associatedData`: kotlin.ByteArray, `ciphertext`: kotlin.ByteArray): kotlin.ByteArray { + return FfiConverterByteArray.lift( + callWithPointer { + uniffiRustCallWithError(StorageException) { _status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_devicekeystore_open_sealed( + it, FfiConverterByteArray.lower(`associatedData`),FfiConverterByteArray.lower(`ciphertext`),_status) +} + } + ) + } + + + + + + + companion object + +} + + +// Put the implementation in an object so we don't pollute the top-level namespace +internal object uniffiCallbackInterfaceDeviceKeystore { + internal object `seal`: UniffiCallbackInterfaceDeviceKeystoreMethod0 { + override fun callback(`uniffiHandle`: Long,`associatedData`: RustBuffer.ByValue,`plaintext`: RustBuffer.ByValue,`uniffiOutReturn`: RustBuffer,uniffiCallStatus: UniffiRustCallStatus,) { + val uniffiObj = FfiConverterTypeDeviceKeystore.handleMap.get(uniffiHandle) + val makeCall = { -> + uniffiObj.`seal`( + FfiConverterByteArray.lift(`associatedData`), + FfiConverterByteArray.lift(`plaintext`), + ) + } + val writeReturn = { value: kotlin.ByteArray -> uniffiOutReturn.setValue(FfiConverterByteArray.lower(value)) } + uniffiTraitInterfaceCallWithError( + uniffiCallStatus, + makeCall, + writeReturn, + { e: StorageException -> FfiConverterTypeStorageError.lower(e) } + ) + } + } + internal object `openSealed`: UniffiCallbackInterfaceDeviceKeystoreMethod1 { + override fun callback(`uniffiHandle`: Long,`associatedData`: RustBuffer.ByValue,`ciphertext`: RustBuffer.ByValue,`uniffiOutReturn`: RustBuffer,uniffiCallStatus: UniffiRustCallStatus,) { + val uniffiObj = FfiConverterTypeDeviceKeystore.handleMap.get(uniffiHandle) + val makeCall = { -> + uniffiObj.`openSealed`( + FfiConverterByteArray.lift(`associatedData`), + FfiConverterByteArray.lift(`ciphertext`), + ) + } + val writeReturn = { value: kotlin.ByteArray -> uniffiOutReturn.setValue(FfiConverterByteArray.lower(value)) } + uniffiTraitInterfaceCallWithError( + uniffiCallStatus, + makeCall, + writeReturn, + { e: StorageException -> FfiConverterTypeStorageError.lower(e) } + ) + } + } + + internal object uniffiFree: UniffiCallbackInterfaceFree { + override fun callback(handle: Long) { + FfiConverterTypeDeviceKeystore.handleMap.remove(handle) + } + } + + internal var vtable = UniffiVTableCallbackInterfaceDeviceKeystore.UniffiByValue( + `seal`, + `openSealed`, + uniffiFree, + ) + + // Registers the foreign callback with the Rust side. + // This method is generated for each callback interface. + internal fun register(lib: UniffiLib) { + lib.uniffi_walletkit_core_fn_init_callback_vtable_devicekeystore(vtable) + } +} + +/** + * @suppress + */ +public object FfiConverterTypeDeviceKeystore: FfiConverter { + internal val handleMap = UniffiHandleMap() + + override fun lower(value: DeviceKeystore): Pointer { + return Pointer(handleMap.insert(value)) + } + + override fun lift(value: Pointer): DeviceKeystore { + return DeviceKeystoreImpl(value) + } + + override fun read(buf: ByteBuffer): DeviceKeystore { + // The Rust code always writes pointers as 8 bytes, and will + // fail to compile if they don't fit. + return lift(Pointer(buf.getLong())) + } + + override fun allocationSize(value: DeviceKeystore) = 8UL + + override fun write(value: DeviceKeystore, buf: ByteBuffer) { + // The Rust code always expects pointers written as 8 bytes, + // and will fail to compile if they don't fit. + buf.putLong(Pointer.nativeValue(lower(value))) + } +} + + +// This template implements a class for working with a Rust struct via a Pointer/Arc +// to the live Rust struct on the other side of the FFI. +// +// Each instance implements core operations for working with the Rust `Arc` and the +// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. +// +// There's some subtlety here, because we have to be careful not to operate on a Rust +// struct after it has been dropped, and because we must expose a public API for freeing +// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: +// +// * Each instance holds an opaque pointer to the underlying Rust struct. +// Method calls need to read this pointer from the object's state and pass it in to +// the Rust FFI. +// +// * When an instance is no longer needed, its pointer should be passed to a +// special destructor function provided by the Rust FFI, which will drop the +// underlying Rust struct. +// +// * Given an instance, calling code is expected to call the special +// `destroy` method in order to free it after use, either by calling it explicitly +// or by using a higher-level helper like the `use` method. Failing to do so risks +// leaking the underlying Rust struct. +// +// * We can't assume that calling code will do the right thing, and must be prepared +// to handle Kotlin method calls executing concurrently with or even after a call to +// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. +// +// * We must never allow Rust code to operate on the underlying Rust struct after +// the destructor has been called, and must never call the destructor more than once. +// Doing so may trigger memory unsafety. +// +// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` +// is implemented to call the destructor when the Kotlin object becomes unreachable. +// This is done in a background thread. This is not a panacea, and client code should be aware that +// 1. the thread may starve if some there are objects that have poorly performing +// `drop` methods or do significant work in their `drop` methods. +// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, +// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). +// +// If we try to implement this with mutual exclusion on access to the pointer, there is the +// possibility of a race between a method call and a concurrent call to `destroy`: +// +// * Thread A starts a method call, reads the value of the pointer, but is interrupted +// before it can pass the pointer over the FFI to Rust. +// * Thread B calls `destroy` and frees the underlying Rust struct. +// * Thread A resumes, passing the already-read pointer value to Rust and triggering +// a use-after-free. +// +// One possible solution would be to use a `ReadWriteLock`, with each method call taking +// a read lock (and thus allowed to run concurrently) and the special `destroy` method +// taking a write lock (and thus blocking on live method calls). However, we aim not to +// generate methods with any hidden blocking semantics, and a `destroy` method that might +// block if called incorrectly seems to meet that bar. +// +// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track +// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` +// has been called. These are updated according to the following rules: +// +// * The initial value of the counter is 1, indicating a live object with no in-flight calls. +// The initial value for the flag is false. +// +// * At the start of each method call, we atomically check the counter. +// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. +// If it is nonzero them we atomically increment it by 1 and proceed with the method call. +// +// * At the end of each method call, we atomically decrement and check the counter. +// If it has reached zero then we destroy the underlying Rust struct. +// +// * When `destroy` is called, we atomically flip the flag from false to true. +// If the flag was already true we silently fail. +// Otherwise we atomically decrement and check the counter. +// If it has reached zero then we destroy the underlying Rust struct. +// +// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, +// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. +// +// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been +// called *and* all in-flight method calls have completed, avoiding violating any of the expectations +// of the underlying Rust code. +// +// This makes a cleaner a better alternative to _not_ calling `destroy()` as +// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` +// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner +// thread may be starved, and the app will leak memory. +// +// In this case, `destroy`ing manually may be a better solution. +// +// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects +// with Rust peers are reclaimed: +// +// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: +// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: +// 3. The memory is reclaimed when the process terminates. +// +// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 +// + + +/** + * Trait representing a logger that can log messages at various levels. + * + * This trait should be implemented by any logger that wants to receive log messages. + * It is exported via `UniFFI` for use in foreign languages. + * + * # Examples + * + * Implementing the `Logger` trait: + * + * ```rust + * use walletkit_core::logger::{Logger, LogLevel}; + * + * struct MyLogger; + * + * impl Logger for MyLogger { + * fn log(&self, level: LogLevel, message: String) { + * println!("[{:?}] {}", level, message); + * } + * } + * ``` + * + * ## Swift + * + * ```swift + * class WalletKitLoggerBridge: WalletKit.Logger { + * static let shared = WalletKitLoggerBridge() + * + * func log(level: WalletKit.LogLevel, message: String) { + * Log.log(level.toCoreLevel(), message) + * } + * } + * + * public func setupWalletKitLogger() { + * WalletKit.setLogger(logger: WalletKitLoggerBridge.shared) + * } + * ``` + * + * ### In app delegate + * + * ```swift + * setupWalletKitLogger() // Call this only once!!! + * ``` + */ +public interface Logger { + + /** + * Logs a message at the specified log level. + * + * # Arguments + * + * * `level` - The severity level of the log message. + * * `message` - The log message to be recorded. + */ + fun `log`(`level`: LogLevel, `message`: kotlin.String) + + companion object +} + +/** + * Trait representing a logger that can log messages at various levels. + * + * This trait should be implemented by any logger that wants to receive log messages. + * It is exported via `UniFFI` for use in foreign languages. + * + * # Examples + * + * Implementing the `Logger` trait: + * + * ```rust + * use walletkit_core::logger::{Logger, LogLevel}; + * + * struct MyLogger; + * + * impl Logger for MyLogger { + * fn log(&self, level: LogLevel, message: String) { + * println!("[{:?}] {}", level, message); + * } + * } + * ``` + * + * ## Swift + * + * ```swift + * class WalletKitLoggerBridge: WalletKit.Logger { + * static let shared = WalletKitLoggerBridge() + * + * func log(level: WalletKit.LogLevel, message: String) { + * Log.log(level.toCoreLevel(), message) + * } + * } + * + * public func setupWalletKitLogger() { + * WalletKit.setLogger(logger: WalletKitLoggerBridge.shared) + * } + * ``` + * + * ### In app delegate + * + * ```swift + * setupWalletKitLogger() // Call this only once!!! + * ``` + */ +open class LoggerImpl: Disposable, AutoCloseable, Logger +{ + + constructor(pointer: Pointer) { + this.pointer = pointer + this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) + } + + /** + * This constructor can be used to instantiate a fake object. Only used for tests. Any + * attempt to actually use an object constructed this way will fail as there is no + * connected Rust object. + */ + @Suppress("UNUSED_PARAMETER") + constructor(noPointer: NoPointer) { + this.pointer = null + this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) + } + + protected val pointer: Pointer? + protected val cleanable: UniffiCleaner.Cleanable + + private val wasDestroyed = AtomicBoolean(false) + private val callCounter = AtomicLong(1) + + override fun destroy() { + // Only allow a single call to this method. + // TODO: maybe we should log a warning if called more than once? + if (this.wasDestroyed.compareAndSet(false, true)) { + // This decrement always matches the initial count of 1 given at creation time. + if (this.callCounter.decrementAndGet() == 0L) { + cleanable.clean() + } + } + } + + @Synchronized + override fun close() { + this.destroy() + } + + internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { + // Check and increment the call counter, to keep the object alive. + // This needs a compare-and-set retry loop in case of concurrent updates. + do { + val c = this.callCounter.get() + if (c == 0L) { + throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") + } + if (c == Long.MAX_VALUE) { + throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") + } + } while (! this.callCounter.compareAndSet(c, c + 1L)) + // Now we can safely do the method call without the pointer being freed concurrently. + try { + return block(this.uniffiClonePointer()) + } finally { + // This decrement always matches the increment we performed above. + if (this.callCounter.decrementAndGet() == 0L) { + cleanable.clean() + } + } + } + + // Use a static inner class instead of a closure so as not to accidentally + // capture `this` as part of the cleanable's action. + private class UniffiCleanAction(private val pointer: Pointer?) : Runnable { + override fun run() { + pointer?.let { ptr -> + uniffiRustCall { status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_free_logger(ptr, status) + } + } + } + } + + fun uniffiClonePointer(): Pointer { + return uniffiRustCall() { status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_clone_logger(pointer!!, status) + } + } + + + /** + * Logs a message at the specified log level. + * + * # Arguments + * + * * `level` - The severity level of the log message. + * * `message` - The log message to be recorded. + */override fun `log`(`level`: LogLevel, `message`: kotlin.String) + = + callWithPointer { + uniffiRustCall() { _status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_logger_log( + it, FfiConverterTypeLogLevel.lower(`level`),FfiConverterString.lower(`message`),_status) +} + } + + + + + + + + companion object + +} + + +// Put the implementation in an object so we don't pollute the top-level namespace +internal object uniffiCallbackInterfaceLogger { + internal object `log`: UniffiCallbackInterfaceLoggerMethod0 { + override fun callback(`uniffiHandle`: Long,`level`: RustBuffer.ByValue,`message`: RustBuffer.ByValue,`uniffiOutReturn`: Pointer,uniffiCallStatus: UniffiRustCallStatus,) { + val uniffiObj = FfiConverterTypeLogger.handleMap.get(uniffiHandle) + val makeCall = { -> + uniffiObj.`log`( + FfiConverterTypeLogLevel.lift(`level`), + FfiConverterString.lift(`message`), + ) + } + val writeReturn = { _: Unit -> Unit } + uniffiTraitInterfaceCall(uniffiCallStatus, makeCall, writeReturn) + } + } + + internal object uniffiFree: UniffiCallbackInterfaceFree { + override fun callback(handle: Long) { + FfiConverterTypeLogger.handleMap.remove(handle) + } + } + + internal var vtable = UniffiVTableCallbackInterfaceLogger.UniffiByValue( + `log`, + uniffiFree, + ) + + // Registers the foreign callback with the Rust side. + // This method is generated for each callback interface. + internal fun register(lib: UniffiLib) { + lib.uniffi_walletkit_core_fn_init_callback_vtable_logger(vtable) + } +} + +/** + * @suppress + */ +public object FfiConverterTypeLogger: FfiConverter { + internal val handleMap = UniffiHandleMap() + + override fun lower(value: Logger): Pointer { + return Pointer(handleMap.insert(value)) + } + + override fun lift(value: Pointer): Logger { + return LoggerImpl(value) + } + + override fun read(buf: ByteBuffer): Logger { + // The Rust code always writes pointers as 8 bytes, and will + // fail to compile if they don't fit. + return lift(Pointer(buf.getLong())) + } + + override fun allocationSize(value: Logger) = 8UL + + override fun write(value: Logger, buf: ByteBuffer) { + // The Rust code always expects pointers written as 8 bytes, + // and will fail to compile if they don't fit. + buf.putLong(Pointer.nativeValue(lower(value))) + } +} + + +// This template implements a class for working with a Rust struct via a Pointer/Arc +// to the live Rust struct on the other side of the FFI. +// +// Each instance implements core operations for working with the Rust `Arc` and the +// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. +// +// There's some subtlety here, because we have to be careful not to operate on a Rust +// struct after it has been dropped, and because we must expose a public API for freeing +// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: +// +// * Each instance holds an opaque pointer to the underlying Rust struct. +// Method calls need to read this pointer from the object's state and pass it in to +// the Rust FFI. +// +// * When an instance is no longer needed, its pointer should be passed to a +// special destructor function provided by the Rust FFI, which will drop the +// underlying Rust struct. +// +// * Given an instance, calling code is expected to call the special +// `destroy` method in order to free it after use, either by calling it explicitly +// or by using a higher-level helper like the `use` method. Failing to do so risks +// leaking the underlying Rust struct. +// +// * We can't assume that calling code will do the right thing, and must be prepared +// to handle Kotlin method calls executing concurrently with or even after a call to +// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. +// +// * We must never allow Rust code to operate on the underlying Rust struct after +// the destructor has been called, and must never call the destructor more than once. +// Doing so may trigger memory unsafety. +// +// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` +// is implemented to call the destructor when the Kotlin object becomes unreachable. +// This is done in a background thread. This is not a panacea, and client code should be aware that +// 1. the thread may starve if some there are objects that have poorly performing +// `drop` methods or do significant work in their `drop` methods. +// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, +// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). +// +// If we try to implement this with mutual exclusion on access to the pointer, there is the +// possibility of a race between a method call and a concurrent call to `destroy`: +// +// * Thread A starts a method call, reads the value of the pointer, but is interrupted +// before it can pass the pointer over the FFI to Rust. +// * Thread B calls `destroy` and frees the underlying Rust struct. +// * Thread A resumes, passing the already-read pointer value to Rust and triggering +// a use-after-free. +// +// One possible solution would be to use a `ReadWriteLock`, with each method call taking +// a read lock (and thus allowed to run concurrently) and the special `destroy` method +// taking a write lock (and thus blocking on live method calls). However, we aim not to +// generate methods with any hidden blocking semantics, and a `destroy` method that might +// block if called incorrectly seems to meet that bar. +// +// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track +// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` +// has been called. These are updated according to the following rules: +// +// * The initial value of the counter is 1, indicating a live object with no in-flight calls. +// The initial value for the flag is false. +// +// * At the start of each method call, we atomically check the counter. +// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. +// If it is nonzero them we atomically increment it by 1 and proceed with the method call. +// +// * At the end of each method call, we atomically decrement and check the counter. +// If it has reached zero then we destroy the underlying Rust struct. +// +// * When `destroy` is called, we atomically flip the flag from false to true. +// If the flag was already true we silently fail. +// Otherwise we atomically decrement and check the counter. +// If it has reached zero then we destroy the underlying Rust struct. +// +// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, +// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. +// +// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been +// called *and* all in-flight method calls have completed, avoiding violating any of the expectations +// of the underlying Rust code. +// +// This makes a cleaner a better alternative to _not_ calling `destroy()` as +// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` +// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner +// thread may be starved, and the app will leak memory. +// +// In this case, `destroy`ing manually may be a better solution. +// +// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects +// with Rust peers are reclaimed: +// +// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: +// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: +// 3. The memory is reclaimed when the process terminates. +// +// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 +// + + +public interface MerkleTreeProofInterface { + + companion object +} + +open class MerkleTreeProof: Disposable, AutoCloseable, MerkleTreeProofInterface +{ + + constructor(pointer: Pointer) { + this.pointer = pointer + this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) + } + + /** + * This constructor can be used to instantiate a fake object. Only used for tests. Any + * attempt to actually use an object constructed this way will fail as there is no + * connected Rust object. + */ + @Suppress("UNUSED_PARAMETER") + constructor(noPointer: NoPointer) { + this.pointer = null + this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) + } + + protected val pointer: Pointer? + protected val cleanable: UniffiCleaner.Cleanable + + private val wasDestroyed = AtomicBoolean(false) + private val callCounter = AtomicLong(1) + + override fun destroy() { + // Only allow a single call to this method. + // TODO: maybe we should log a warning if called more than once? + if (this.wasDestroyed.compareAndSet(false, true)) { + // This decrement always matches the initial count of 1 given at creation time. + if (this.callCounter.decrementAndGet() == 0L) { + cleanable.clean() + } + } + } + + @Synchronized + override fun close() { + this.destroy() + } + + internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { + // Check and increment the call counter, to keep the object alive. + // This needs a compare-and-set retry loop in case of concurrent updates. + do { + val c = this.callCounter.get() + if (c == 0L) { + throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") + } + if (c == Long.MAX_VALUE) { + throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") + } + } while (! this.callCounter.compareAndSet(c, c + 1L)) + // Now we can safely do the method call without the pointer being freed concurrently. + try { + return block(this.uniffiClonePointer()) + } finally { + // This decrement always matches the increment we performed above. + if (this.callCounter.decrementAndGet() == 0L) { + cleanable.clean() + } + } + } + + // Use a static inner class instead of a closure so as not to accidentally + // capture `this` as part of the cleanable's action. + private class UniffiCleanAction(private val pointer: Pointer?) : Runnable { + override fun run() { + pointer?.let { ptr -> + uniffiRustCall { status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_free_merkletreeproof(ptr, status) + } + } + } + } + + fun uniffiClonePointer(): Pointer { + return uniffiRustCall() { status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_clone_merkletreeproof(pointer!!, status) + } + } + + + + + companion object { + + /** + * Retrieves a Merkle inclusion proof from the sign up sequencer for a given identity commitment. + * Each credential/environment pair uses a different sign up sequencer. + * + * # Errors + * Will throw an error if the request fails or parsing the response fails. + */ + @Throws(WalletKitException::class) + @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") + suspend fun `fromIdentityCommitment`(`identityCommitment`: U256Wrapper, `sequencerHost`: kotlin.String, `requireMinedProof`: kotlin.Boolean) : MerkleTreeProof { + return uniffiRustCallAsync( + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_constructor_merkletreeproof_from_identity_commitment(FfiConverterTypeU256Wrapper.lower(`identityCommitment`),FfiConverterString.lower(`sequencerHost`),FfiConverterBoolean.lower(`requireMinedProof`),), + { future, callback, continuation -> UniffiLib.INSTANCE.ffi_walletkit_core_rust_future_poll_pointer(future, callback, continuation) }, + { future, continuation -> UniffiLib.INSTANCE.ffi_walletkit_core_rust_future_complete_pointer(future, continuation) }, + { future -> UniffiLib.INSTANCE.ffi_walletkit_core_rust_future_free_pointer(future) }, + // lift function + { FfiConverterTypeMerkleTreeProof.lift(it) }, + // Error FFI converter + WalletKitException.ErrorHandler, + ) + } + + + @Throws(WalletKitException::class) fun `fromJsonProof`(`jsonProof`: kotlin.String, `merkleRoot`: kotlin.String): MerkleTreeProof { + return FfiConverterTypeMerkleTreeProof.lift( + uniffiRustCallWithError(WalletKitException) { _status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_constructor_merkletreeproof_from_json_proof( + FfiConverterString.lower(`jsonProof`),FfiConverterString.lower(`merkleRoot`),_status) +} + ) + } + + + + } + +} + +/** + * @suppress + */ +public object FfiConverterTypeMerkleTreeProof: FfiConverter { + + override fun lower(value: MerkleTreeProof): Pointer { + return value.uniffiClonePointer() + } + + override fun lift(value: Pointer): MerkleTreeProof { + return MerkleTreeProof(value) + } + + override fun read(buf: ByteBuffer): MerkleTreeProof { + // The Rust code always writes pointers as 8 bytes, and will + // fail to compile if they don't fit. + return lift(Pointer(buf.getLong())) + } + + override fun allocationSize(value: MerkleTreeProof) = 8UL + + override fun write(value: MerkleTreeProof, buf: ByteBuffer) { + // The Rust code always expects pointers written as 8 bytes, + // and will fail to compile if they don't fit. + buf.putLong(Pointer.nativeValue(lower(value))) + } +} + + +// This template implements a class for working with a Rust struct via a Pointer/Arc +// to the live Rust struct on the other side of the FFI. +// +// Each instance implements core operations for working with the Rust `Arc` and the +// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. +// +// There's some subtlety here, because we have to be careful not to operate on a Rust +// struct after it has been dropped, and because we must expose a public API for freeing +// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: +// +// * Each instance holds an opaque pointer to the underlying Rust struct. +// Method calls need to read this pointer from the object's state and pass it in to +// the Rust FFI. +// +// * When an instance is no longer needed, its pointer should be passed to a +// special destructor function provided by the Rust FFI, which will drop the +// underlying Rust struct. +// +// * Given an instance, calling code is expected to call the special +// `destroy` method in order to free it after use, either by calling it explicitly +// or by using a higher-level helper like the `use` method. Failing to do so risks +// leaking the underlying Rust struct. +// +// * We can't assume that calling code will do the right thing, and must be prepared +// to handle Kotlin method calls executing concurrently with or even after a call to +// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. +// +// * We must never allow Rust code to operate on the underlying Rust struct after +// the destructor has been called, and must never call the destructor more than once. +// Doing so may trigger memory unsafety. +// +// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` +// is implemented to call the destructor when the Kotlin object becomes unreachable. +// This is done in a background thread. This is not a panacea, and client code should be aware that +// 1. the thread may starve if some there are objects that have poorly performing +// `drop` methods or do significant work in their `drop` methods. +// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, +// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). +// +// If we try to implement this with mutual exclusion on access to the pointer, there is the +// possibility of a race between a method call and a concurrent call to `destroy`: +// +// * Thread A starts a method call, reads the value of the pointer, but is interrupted +// before it can pass the pointer over the FFI to Rust. +// * Thread B calls `destroy` and frees the underlying Rust struct. +// * Thread A resumes, passing the already-read pointer value to Rust and triggering +// a use-after-free. +// +// One possible solution would be to use a `ReadWriteLock`, with each method call taking +// a read lock (and thus allowed to run concurrently) and the special `destroy` method +// taking a write lock (and thus blocking on live method calls). However, we aim not to +// generate methods with any hidden blocking semantics, and a `destroy` method that might +// block if called incorrectly seems to meet that bar. +// +// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track +// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` +// has been called. These are updated according to the following rules: +// +// * The initial value of the counter is 1, indicating a live object with no in-flight calls. +// The initial value for the flag is false. +// +// * At the start of each method call, we atomically check the counter. +// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. +// If it is nonzero them we atomically increment it by 1 and proceed with the method call. +// +// * At the end of each method call, we atomically decrement and check the counter. +// If it has reached zero then we destroy the underlying Rust struct. +// +// * When `destroy` is called, we atomically flip the flag from false to true. +// If the flag was already true we silently fail. +// Otherwise we atomically decrement and check the counter. +// If it has reached zero then we destroy the underlying Rust struct. +// +// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, +// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. +// +// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been +// called *and* all in-flight method calls have completed, avoiding violating any of the expectations +// of the underlying Rust code. +// +// This makes a cleaner a better alternative to _not_ calling `destroy()` as +// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` +// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner +// thread may be starved, and the app will leak memory. +// +// In this case, `destroy`ing manually may be a better solution. +// +// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects +// with Rust peers are reclaimed: +// +// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: +// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: +// 3. The memory is reclaimed when the process terminates. +// +// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 +// + + +/** + * A `ProofContext` contains the basic information on the verifier and the specific action a user will be proving. + * + * It is required to generate a `Proof` and will generally be initialized from an `app_id` and `action`. + * + * Note on naming: `ProofContext` is used to make it clear in FFIs which may not respect the module structure. + */ +public interface ProofContextInterface { + + /** + * Get the credential type for this context. + */ + fun `getCredentialType`(): CredentialType + + /** + * Get the raw external nullifier for this context. + */ + fun `getExternalNullifier`(): U256Wrapper + + /** + * Get the signal hash for this context. + */ + fun `getSignalHash`(): U256Wrapper + + companion object +} + +/** + * A `ProofContext` contains the basic information on the verifier and the specific action a user will be proving. + * + * It is required to generate a `Proof` and will generally be initialized from an `app_id` and `action`. + * + * Note on naming: `ProofContext` is used to make it clear in FFIs which may not respect the module structure. + */ +open class ProofContext: Disposable, AutoCloseable, ProofContextInterface +{ + + constructor(pointer: Pointer) { + this.pointer = pointer + this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) + } + + /** + * This constructor can be used to instantiate a fake object. Only used for tests. Any + * attempt to actually use an object constructed this way will fail as there is no + * connected Rust object. + */ + @Suppress("UNUSED_PARAMETER") + constructor(noPointer: NoPointer) { + this.pointer = null + this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) + } + /** + * Initializes a `ProofContext`. + * + * Will compute the relevant external nullifier from the provided `app_id` and `action` as defined by the + * World ID Protocol. The external nullifier generation matches the logic in the + * [Developer Portal](https://github.com/worldcoin/developer-portal/blob/main/web/lib/hashing.ts). + * + * # Arguments + * + * * `app_id` - The ID of the application requesting proofs. This can be obtained from the [Developer Portal](https://developer.world.org). + * * `action` - Optional. Custom incognito action being requested. + * * `signal` - Optional. The signal is included in the ZKP and is committed to in the proof. When verifying the proof, the + * same signal must be provided to ensure the proof is valid. The signal can be used to prevent replay attacks, MITM or other cases. + * More details available in the [docs](https://docs.world.org/world-id/further-reading/zero-knowledge-proofs). + * * `credential_type` - The type of credential being requested. + + */ + constructor(`appId`: kotlin.String, `action`: kotlin.String?, `signal`: kotlin.String?, `credentialType`: CredentialType) : + this( + uniffiRustCall() { _status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_constructor_proofcontext_new( + FfiConverterString.lower(`appId`),FfiConverterOptionalString.lower(`action`),FfiConverterOptionalString.lower(`signal`),FfiConverterTypeCredentialType.lower(`credentialType`),_status) +} + ) + + protected val pointer: Pointer? + protected val cleanable: UniffiCleaner.Cleanable + + private val wasDestroyed = AtomicBoolean(false) + private val callCounter = AtomicLong(1) + + override fun destroy() { + // Only allow a single call to this method. + // TODO: maybe we should log a warning if called more than once? + if (this.wasDestroyed.compareAndSet(false, true)) { + // This decrement always matches the initial count of 1 given at creation time. + if (this.callCounter.decrementAndGet() == 0L) { + cleanable.clean() + } + } + } + + @Synchronized + override fun close() { + this.destroy() + } + + internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { + // Check and increment the call counter, to keep the object alive. + // This needs a compare-and-set retry loop in case of concurrent updates. + do { + val c = this.callCounter.get() + if (c == 0L) { + throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") + } + if (c == Long.MAX_VALUE) { + throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") + } + } while (! this.callCounter.compareAndSet(c, c + 1L)) + // Now we can safely do the method call without the pointer being freed concurrently. + try { + return block(this.uniffiClonePointer()) + } finally { + // This decrement always matches the increment we performed above. + if (this.callCounter.decrementAndGet() == 0L) { + cleanable.clean() + } + } + } + + // Use a static inner class instead of a closure so as not to accidentally + // capture `this` as part of the cleanable's action. + private class UniffiCleanAction(private val pointer: Pointer?) : Runnable { + override fun run() { + pointer?.let { ptr -> + uniffiRustCall { status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_free_proofcontext(ptr, status) + } + } + } + } + + fun uniffiClonePointer(): Pointer { + return uniffiRustCall() { status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_clone_proofcontext(pointer!!, status) + } + } + + + /** + * Get the credential type for this context. + */override fun `getCredentialType`(): CredentialType { + return FfiConverterTypeCredentialType.lift( + callWithPointer { + uniffiRustCall() { _status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_proofcontext_get_credential_type( + it, _status) +} + } + ) + } + + + + /** + * Get the raw external nullifier for this context. + */override fun `getExternalNullifier`(): U256Wrapper { + return FfiConverterTypeU256Wrapper.lift( + callWithPointer { + uniffiRustCall() { _status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_proofcontext_get_external_nullifier( + it, _status) +} + } + ) + } + + + + /** + * Get the signal hash for this context. + */override fun `getSignalHash`(): U256Wrapper { + return FfiConverterTypeU256Wrapper.lift( + callWithPointer { + uniffiRustCall() { _status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_proofcontext_get_signal_hash( + it, _status) +} + } + ) + } + + + + + + companion object { + + /** + * Initializes a `Proof::ProofContext` where the `action` is provided as raw bytes. This is useful for advanced cases + * where the `action` is an already ABI encoded value for on-chain usage. + * See _walletkit-core/tests/solidity.rs_ for an example. + * + * Will compute the relevant external nullifier from the provided `app_id` and `action`. + * + * # Arguments + * + * See `ProofContext::new` for reference. The `action` and `signal` need to be provided as raw bytes. + + */ fun `newFromBytes`(`appId`: kotlin.String, `action`: kotlin.ByteArray?, `signal`: kotlin.ByteArray?, `credentialType`: CredentialType): ProofContext { + return FfiConverterTypeProofContext.lift( + uniffiRustCall() { _status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_constructor_proofcontext_new_from_bytes( + FfiConverterString.lower(`appId`),FfiConverterOptionalByteArray.lower(`action`),FfiConverterOptionalByteArray.lower(`signal`),FfiConverterTypeCredentialType.lower(`credentialType`),_status) +} + ) + } + + + + /** + * Initializes a `ProofContext` from an already hashed signal. + * + * Please note it is imperative to hash into the Semaphore field. Not all U256 are part of the field. + * Use the `hash_to_field` function to hash into the field. + * + * # Usage + * - This may be used when the hash of the signal is computed externally. + * - For example, this is used for support of legacy `MiniKit` v1 commands in World App where `minikit-js` hashed the signal. + * + * # Arguments + * + * * `app_id` - The ID of the application requesting proofs. This can be obtained from the [Developer Portal](https://developer.world.org). + * * `action` - Optional. Custom incognito action being requested as bytes. + * * `credential_type` - The type of credential being requested. + * * `signal` - The already hashed signal as a field element. + * + * # Errors + * + * - Returns an error if the signal is not a valid number in the field. + */ + @Throws(WalletKitException::class) fun `newFromSignalHash`(`appId`: kotlin.String, `action`: kotlin.ByteArray?, `credentialType`: CredentialType, `signalHash`: U256Wrapper): ProofContext { + return FfiConverterTypeProofContext.lift( + uniffiRustCallWithError(WalletKitException) { _status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_constructor_proofcontext_new_from_signal_hash( + FfiConverterString.lower(`appId`),FfiConverterOptionalByteArray.lower(`action`),FfiConverterTypeCredentialType.lower(`credentialType`),FfiConverterTypeU256Wrapper.lower(`signalHash`),_status) +} + ) + } + + + + } + +} + +/** + * @suppress + */ +public object FfiConverterTypeProofContext: FfiConverter { + + override fun lower(value: ProofContext): Pointer { + return value.uniffiClonePointer() + } + + override fun lift(value: Pointer): ProofContext { + return ProofContext(value) + } + + override fun read(buf: ByteBuffer): ProofContext { + // The Rust code always writes pointers as 8 bytes, and will + // fail to compile if they don't fit. + return lift(Pointer(buf.getLong())) + } + + override fun allocationSize(value: ProofContext) = 8UL + + override fun write(value: ProofContext, buf: ByteBuffer) { + // The Rust code always expects pointers written as 8 bytes, + // and will fail to compile if they don't fit. + buf.putLong(Pointer.nativeValue(lower(value))) + } +} + + +// This template implements a class for working with a Rust struct via a Pointer/Arc +// to the live Rust struct on the other side of the FFI. +// +// Each instance implements core operations for working with the Rust `Arc` and the +// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. +// +// There's some subtlety here, because we have to be careful not to operate on a Rust +// struct after it has been dropped, and because we must expose a public API for freeing +// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: +// +// * Each instance holds an opaque pointer to the underlying Rust struct. +// Method calls need to read this pointer from the object's state and pass it in to +// the Rust FFI. +// +// * When an instance is no longer needed, its pointer should be passed to a +// special destructor function provided by the Rust FFI, which will drop the +// underlying Rust struct. +// +// * Given an instance, calling code is expected to call the special +// `destroy` method in order to free it after use, either by calling it explicitly +// or by using a higher-level helper like the `use` method. Failing to do so risks +// leaking the underlying Rust struct. +// +// * We can't assume that calling code will do the right thing, and must be prepared +// to handle Kotlin method calls executing concurrently with or even after a call to +// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. +// +// * We must never allow Rust code to operate on the underlying Rust struct after +// the destructor has been called, and must never call the destructor more than once. +// Doing so may trigger memory unsafety. +// +// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` +// is implemented to call the destructor when the Kotlin object becomes unreachable. +// This is done in a background thread. This is not a panacea, and client code should be aware that +// 1. the thread may starve if some there are objects that have poorly performing +// `drop` methods or do significant work in their `drop` methods. +// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, +// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). +// +// If we try to implement this with mutual exclusion on access to the pointer, there is the +// possibility of a race between a method call and a concurrent call to `destroy`: +// +// * Thread A starts a method call, reads the value of the pointer, but is interrupted +// before it can pass the pointer over the FFI to Rust. +// * Thread B calls `destroy` and frees the underlying Rust struct. +// * Thread A resumes, passing the already-read pointer value to Rust and triggering +// a use-after-free. +// +// One possible solution would be to use a `ReadWriteLock`, with each method call taking +// a read lock (and thus allowed to run concurrently) and the special `destroy` method +// taking a write lock (and thus blocking on live method calls). However, we aim not to +// generate methods with any hidden blocking semantics, and a `destroy` method that might +// block if called incorrectly seems to meet that bar. +// +// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track +// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` +// has been called. These are updated according to the following rules: +// +// * The initial value of the counter is 1, indicating a live object with no in-flight calls. +// The initial value for the flag is false. +// +// * At the start of each method call, we atomically check the counter. +// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. +// If it is nonzero them we atomically increment it by 1 and proceed with the method call. +// +// * At the end of each method call, we atomically decrement and check the counter. +// If it has reached zero then we destroy the underlying Rust struct. +// +// * When `destroy` is called, we atomically flip the flag from false to true. +// If the flag was already true we silently fail. +// Otherwise we atomically decrement and check the counter. +// If it has reached zero then we destroy the underlying Rust struct. +// +// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, +// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. +// +// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been +// called *and* all in-flight method calls have completed, avoiding violating any of the expectations +// of the underlying Rust code. +// +// This makes a cleaner a better alternative to _not_ calling `destroy()` as +// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` +// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner +// thread may be starved, and the app will leak memory. +// +// In this case, `destroy`ing manually may be a better solution. +// +// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects +// with Rust peers are reclaimed: +// +// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: +// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: +// 3. The memory is reclaimed when the process terminates. +// +// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 +// + + +/** + * Represents the complete output of a World ID Proof (i.e. a credential persentation). This output + * can be serialized to JSON and can be verified easily with the Developer Portal or Sign up Sequencer. + * + * For on-chain verification, the `proof` (which is packed) should generally be deserialized into `uint256[8]`. + * + * More information on: [On-Chain Verification](https://docs.world.org/world-id/id/on-chain) + */ +public interface ProofOutputInterface { + + /** + * Exposes the credential type to foreign code. Struct fields are not directly exposed to foreign code. + */ + fun `getCredentialType`(): CredentialType + + /** + * Exposes the merkle root to foreign code. Struct fields are not directly exposed to foreign code. + */ + fun `getMerkleRoot`(): U256Wrapper + + /** + * Exposes the nullifier hash to foreign code. Struct fields are not directly exposed to foreign code. + */ + fun `getNullifierHash`(): U256Wrapper + + /** + * Exposes the proof as a string to foreign code. Struct fields are not directly exposed to foreign code. + */ + fun `getProofAsString`(): kotlin.String + + /** + * Converts the entire proof output to a JSON string with standard attribute names. + * + * # Errors + * Will error if serialization fails. + */ + fun `toJson`(): kotlin.String + + companion object +} + +/** + * Represents the complete output of a World ID Proof (i.e. a credential persentation). This output + * can be serialized to JSON and can be verified easily with the Developer Portal or Sign up Sequencer. + * + * For on-chain verification, the `proof` (which is packed) should generally be deserialized into `uint256[8]`. + * + * More information on: [On-Chain Verification](https://docs.world.org/world-id/id/on-chain) + */ +open class ProofOutput: Disposable, AutoCloseable, ProofOutputInterface +{ + + constructor(pointer: Pointer) { + this.pointer = pointer + this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) + } + + /** + * This constructor can be used to instantiate a fake object. Only used for tests. Any + * attempt to actually use an object constructed this way will fail as there is no + * connected Rust object. + */ + @Suppress("UNUSED_PARAMETER") + constructor(noPointer: NoPointer) { + this.pointer = null + this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) + } + + protected val pointer: Pointer? + protected val cleanable: UniffiCleaner.Cleanable + + private val wasDestroyed = AtomicBoolean(false) + private val callCounter = AtomicLong(1) + + override fun destroy() { + // Only allow a single call to this method. + // TODO: maybe we should log a warning if called more than once? + if (this.wasDestroyed.compareAndSet(false, true)) { + // This decrement always matches the initial count of 1 given at creation time. + if (this.callCounter.decrementAndGet() == 0L) { + cleanable.clean() + } + } + } + + @Synchronized + override fun close() { + this.destroy() + } + + internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { + // Check and increment the call counter, to keep the object alive. + // This needs a compare-and-set retry loop in case of concurrent updates. + do { + val c = this.callCounter.get() + if (c == 0L) { + throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") + } + if (c == Long.MAX_VALUE) { + throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") + } + } while (! this.callCounter.compareAndSet(c, c + 1L)) + // Now we can safely do the method call without the pointer being freed concurrently. + try { + return block(this.uniffiClonePointer()) + } finally { + // This decrement always matches the increment we performed above. + if (this.callCounter.decrementAndGet() == 0L) { + cleanable.clean() + } + } + } + + // Use a static inner class instead of a closure so as not to accidentally + // capture `this` as part of the cleanable's action. + private class UniffiCleanAction(private val pointer: Pointer?) : Runnable { + override fun run() { + pointer?.let { ptr -> + uniffiRustCall { status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_free_proofoutput(ptr, status) + } + } + } + } + + fun uniffiClonePointer(): Pointer { + return uniffiRustCall() { status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_clone_proofoutput(pointer!!, status) + } + } + + + /** + * Exposes the credential type to foreign code. Struct fields are not directly exposed to foreign code. + */override fun `getCredentialType`(): CredentialType { + return FfiConverterTypeCredentialType.lift( + callWithPointer { + uniffiRustCall() { _status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_proofoutput_get_credential_type( + it, _status) +} + } + ) + } + + + + /** + * Exposes the merkle root to foreign code. Struct fields are not directly exposed to foreign code. + */override fun `getMerkleRoot`(): U256Wrapper { + return FfiConverterTypeU256Wrapper.lift( + callWithPointer { + uniffiRustCall() { _status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_proofoutput_get_merkle_root( + it, _status) +} + } + ) + } + + + + /** + * Exposes the nullifier hash to foreign code. Struct fields are not directly exposed to foreign code. + */override fun `getNullifierHash`(): U256Wrapper { + return FfiConverterTypeU256Wrapper.lift( + callWithPointer { + uniffiRustCall() { _status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_proofoutput_get_nullifier_hash( + it, _status) +} + } + ) + } + + + + /** + * Exposes the proof as a string to foreign code. Struct fields are not directly exposed to foreign code. + */override fun `getProofAsString`(): kotlin.String { + return FfiConverterString.lift( + callWithPointer { + uniffiRustCall() { _status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_proofoutput_get_proof_as_string( + it, _status) +} + } + ) + } + + + + /** + * Converts the entire proof output to a JSON string with standard attribute names. + * + * # Errors + * Will error if serialization fails. + */ + @Throws(WalletKitException::class)override fun `toJson`(): kotlin.String { + return FfiConverterString.lift( + callWithPointer { + uniffiRustCallWithError(WalletKitException) { _status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_proofoutput_to_json( + it, _status) +} + } + ) + } + + + + + + + companion object + +} + +/** + * @suppress + */ +public object FfiConverterTypeProofOutput: FfiConverter { + + override fun lower(value: ProofOutput): Pointer { + return value.uniffiClonePointer() + } + + override fun lift(value: Pointer): ProofOutput { + return ProofOutput(value) + } + + override fun read(buf: ByteBuffer): ProofOutput { + // The Rust code always writes pointers as 8 bytes, and will + // fail to compile if they don't fit. + return lift(Pointer(buf.getLong())) + } + + override fun allocationSize(value: ProofOutput) = 8UL + + override fun write(value: ProofOutput, buf: ByteBuffer) { + // The Rust code always expects pointers written as 8 bytes, + // and will fail to compile if they don't fit. + buf.putLong(Pointer.nativeValue(lower(value))) + } +} + + +// This template implements a class for working with a Rust struct via a Pointer/Arc +// to the live Rust struct on the other side of the FFI. +// +// Each instance implements core operations for working with the Rust `Arc` and the +// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. +// +// There's some subtlety here, because we have to be careful not to operate on a Rust +// struct after it has been dropped, and because we must expose a public API for freeing +// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: +// +// * Each instance holds an opaque pointer to the underlying Rust struct. +// Method calls need to read this pointer from the object's state and pass it in to +// the Rust FFI. +// +// * When an instance is no longer needed, its pointer should be passed to a +// special destructor function provided by the Rust FFI, which will drop the +// underlying Rust struct. +// +// * Given an instance, calling code is expected to call the special +// `destroy` method in order to free it after use, either by calling it explicitly +// or by using a higher-level helper like the `use` method. Failing to do so risks +// leaking the underlying Rust struct. +// +// * We can't assume that calling code will do the right thing, and must be prepared +// to handle Kotlin method calls executing concurrently with or even after a call to +// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. +// +// * We must never allow Rust code to operate on the underlying Rust struct after +// the destructor has been called, and must never call the destructor more than once. +// Doing so may trigger memory unsafety. +// +// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` +// is implemented to call the destructor when the Kotlin object becomes unreachable. +// This is done in a background thread. This is not a panacea, and client code should be aware that +// 1. the thread may starve if some there are objects that have poorly performing +// `drop` methods or do significant work in their `drop` methods. +// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, +// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). +// +// If we try to implement this with mutual exclusion on access to the pointer, there is the +// possibility of a race between a method call and a concurrent call to `destroy`: +// +// * Thread A starts a method call, reads the value of the pointer, but is interrupted +// before it can pass the pointer over the FFI to Rust. +// * Thread B calls `destroy` and frees the underlying Rust struct. +// * Thread A resumes, passing the already-read pointer value to Rust and triggering +// a use-after-free. +// +// One possible solution would be to use a `ReadWriteLock`, with each method call taking +// a read lock (and thus allowed to run concurrently) and the special `destroy` method +// taking a write lock (and thus blocking on live method calls). However, we aim not to +// generate methods with any hidden blocking semantics, and a `destroy` method that might +// block if called incorrectly seems to meet that bar. +// +// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track +// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` +// has been called. These are updated according to the following rules: +// +// * The initial value of the counter is 1, indicating a live object with no in-flight calls. +// The initial value for the flag is false. +// +// * At the start of each method call, we atomically check the counter. +// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. +// If it is nonzero them we atomically increment it by 1 and proceed with the method call. +// +// * At the end of each method call, we atomically decrement and check the counter. +// If it has reached zero then we destroy the underlying Rust struct. +// +// * When `destroy` is called, we atomically flip the flag from false to true. +// If the flag was already true we silently fail. +// Otherwise we atomically decrement and check the counter. +// If it has reached zero then we destroy the underlying Rust struct. +// +// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, +// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. +// +// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been +// called *and* all in-flight method calls have completed, avoiding violating any of the expectations +// of the underlying Rust code. +// +// This makes a cleaner a better alternative to _not_ calling `destroy()` as +// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` +// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner +// thread may be starved, and the app will leak memory. +// +// In this case, `destroy`ing manually may be a better solution. +// +// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects +// with Rust peers are reclaimed: +// +// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: +// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: +// 3. The memory is reclaimed when the process terminates. +// +// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 +// + + +/** + * Paths for credential storage artifacts under `/worldid`. + */ +public interface StoragePathsInterface { + + /** + * Returns the path to the cache database as a string. + */ + fun `cacheDbPathString`(): kotlin.String + + /** + * Returns the path to the lock file as a string. + */ + fun `lockPathString`(): kotlin.String + + /** + * Returns the storage root directory as a string. + */ + fun `rootPathString`(): kotlin.String + + /** + * Returns the path to the vault database as a string. + */ + fun `vaultDbPathString`(): kotlin.String + + /** + * Returns the World ID storage directory as a string. + */ + fun `worldidDirPathString`(): kotlin.String + + companion object +} + +/** + * Paths for credential storage artifacts under `/worldid`. + */ +open class StoragePaths: Disposable, AutoCloseable, StoragePathsInterface +{ + + constructor(pointer: Pointer) { + this.pointer = pointer + this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) + } + + /** + * This constructor can be used to instantiate a fake object. Only used for tests. Any + * attempt to actually use an object constructed this way will fail as there is no + * connected Rust object. + */ + @Suppress("UNUSED_PARAMETER") + constructor(noPointer: NoPointer) { + this.pointer = null + this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) + } + + protected val pointer: Pointer? + protected val cleanable: UniffiCleaner.Cleanable + + private val wasDestroyed = AtomicBoolean(false) + private val callCounter = AtomicLong(1) + + override fun destroy() { + // Only allow a single call to this method. + // TODO: maybe we should log a warning if called more than once? + if (this.wasDestroyed.compareAndSet(false, true)) { + // This decrement always matches the initial count of 1 given at creation time. + if (this.callCounter.decrementAndGet() == 0L) { + cleanable.clean() + } + } + } + + @Synchronized + override fun close() { + this.destroy() + } + + internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { + // Check and increment the call counter, to keep the object alive. + // This needs a compare-and-set retry loop in case of concurrent updates. + do { + val c = this.callCounter.get() + if (c == 0L) { + throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") + } + if (c == Long.MAX_VALUE) { + throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") + } + } while (! this.callCounter.compareAndSet(c, c + 1L)) + // Now we can safely do the method call without the pointer being freed concurrently. + try { + return block(this.uniffiClonePointer()) + } finally { + // This decrement always matches the increment we performed above. + if (this.callCounter.decrementAndGet() == 0L) { + cleanable.clean() + } + } + } + + // Use a static inner class instead of a closure so as not to accidentally + // capture `this` as part of the cleanable's action. + private class UniffiCleanAction(private val pointer: Pointer?) : Runnable { + override fun run() { + pointer?.let { ptr -> + uniffiRustCall { status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_free_storagepaths(ptr, status) + } + } + } + } + + fun uniffiClonePointer(): Pointer { + return uniffiRustCall() { status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_clone_storagepaths(pointer!!, status) + } + } + + + /** + * Returns the path to the cache database as a string. + */override fun `cacheDbPathString`(): kotlin.String { + return FfiConverterString.lift( + callWithPointer { + uniffiRustCall() { _status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_storagepaths_cache_db_path_string( + it, _status) +} + } + ) + } + + + + /** + * Returns the path to the lock file as a string. + */override fun `lockPathString`(): kotlin.String { + return FfiConverterString.lift( + callWithPointer { + uniffiRustCall() { _status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_storagepaths_lock_path_string( + it, _status) +} + } + ) + } + + + + /** + * Returns the storage root directory as a string. + */override fun `rootPathString`(): kotlin.String { + return FfiConverterString.lift( + callWithPointer { + uniffiRustCall() { _status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_storagepaths_root_path_string( + it, _status) +} + } + ) + } + + + + /** + * Returns the path to the vault database as a string. + */override fun `vaultDbPathString`(): kotlin.String { + return FfiConverterString.lift( + callWithPointer { + uniffiRustCall() { _status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_storagepaths_vault_db_path_string( + it, _status) +} + } + ) + } + + + + /** + * Returns the World ID storage directory as a string. + */override fun `worldidDirPathString`(): kotlin.String { + return FfiConverterString.lift( + callWithPointer { + uniffiRustCall() { _status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_storagepaths_worldid_dir_path_string( + it, _status) +} + } + ) + } + + + + + + companion object { + + /** + * Builds storage paths rooted at `root`. + */ fun `fromRoot`(`root`: kotlin.String): StoragePaths { + return FfiConverterTypeStoragePaths.lift( + uniffiRustCall() { _status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_constructor_storagepaths_from_root( + FfiConverterString.lower(`root`),_status) +} + ) + } + + + + } + +} + +/** + * @suppress + */ +public object FfiConverterTypeStoragePaths: FfiConverter { + + override fun lower(value: StoragePaths): Pointer { + return value.uniffiClonePointer() + } + + override fun lift(value: Pointer): StoragePaths { + return StoragePaths(value) + } + + override fun read(buf: ByteBuffer): StoragePaths { + // The Rust code always writes pointers as 8 bytes, and will + // fail to compile if they don't fit. + return lift(Pointer(buf.getLong())) + } + + override fun allocationSize(value: StoragePaths) = 8UL + + override fun write(value: StoragePaths, buf: ByteBuffer) { + // The Rust code always expects pointers written as 8 bytes, + // and will fail to compile if they don't fit. + buf.putLong(Pointer.nativeValue(lower(value))) + } +} + + +// This template implements a class for working with a Rust struct via a Pointer/Arc +// to the live Rust struct on the other side of the FFI. +// +// Each instance implements core operations for working with the Rust `Arc` and the +// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. +// +// There's some subtlety here, because we have to be careful not to operate on a Rust +// struct after it has been dropped, and because we must expose a public API for freeing +// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: +// +// * Each instance holds an opaque pointer to the underlying Rust struct. +// Method calls need to read this pointer from the object's state and pass it in to +// the Rust FFI. +// +// * When an instance is no longer needed, its pointer should be passed to a +// special destructor function provided by the Rust FFI, which will drop the +// underlying Rust struct. +// +// * Given an instance, calling code is expected to call the special +// `destroy` method in order to free it after use, either by calling it explicitly +// or by using a higher-level helper like the `use` method. Failing to do so risks +// leaking the underlying Rust struct. +// +// * We can't assume that calling code will do the right thing, and must be prepared +// to handle Kotlin method calls executing concurrently with or even after a call to +// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. +// +// * We must never allow Rust code to operate on the underlying Rust struct after +// the destructor has been called, and must never call the destructor more than once. +// Doing so may trigger memory unsafety. +// +// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` +// is implemented to call the destructor when the Kotlin object becomes unreachable. +// This is done in a background thread. This is not a panacea, and client code should be aware that +// 1. the thread may starve if some there are objects that have poorly performing +// `drop` methods or do significant work in their `drop` methods. +// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, +// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). +// +// If we try to implement this with mutual exclusion on access to the pointer, there is the +// possibility of a race between a method call and a concurrent call to `destroy`: +// +// * Thread A starts a method call, reads the value of the pointer, but is interrupted +// before it can pass the pointer over the FFI to Rust. +// * Thread B calls `destroy` and frees the underlying Rust struct. +// * Thread A resumes, passing the already-read pointer value to Rust and triggering +// a use-after-free. +// +// One possible solution would be to use a `ReadWriteLock`, with each method call taking +// a read lock (and thus allowed to run concurrently) and the special `destroy` method +// taking a write lock (and thus blocking on live method calls). However, we aim not to +// generate methods with any hidden blocking semantics, and a `destroy` method that might +// block if called incorrectly seems to meet that bar. +// +// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track +// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` +// has been called. These are updated according to the following rules: +// +// * The initial value of the counter is 1, indicating a live object with no in-flight calls. +// The initial value for the flag is false. +// +// * At the start of each method call, we atomically check the counter. +// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. +// If it is nonzero them we atomically increment it by 1 and proceed with the method call. +// +// * At the end of each method call, we atomically decrement and check the counter. +// If it has reached zero then we destroy the underlying Rust struct. +// +// * When `destroy` is called, we atomically flip the flag from false to true. +// If the flag was already true we silently fail. +// Otherwise we atomically decrement and check the counter. +// If it has reached zero then we destroy the underlying Rust struct. +// +// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, +// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. +// +// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been +// called *and* all in-flight method calls have completed, avoiding violating any of the expectations +// of the underlying Rust code. +// +// This makes a cleaner a better alternative to _not_ calling `destroy()` as +// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` +// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner +// thread may be starved, and the app will leak memory. +// +// In this case, `destroy`ing manually may be a better solution. +// +// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects +// with Rust peers are reclaimed: +// +// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: +// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: +// 3. The memory is reclaimed when the process terminates. +// +// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 +// + + +/** + * Provider responsible for platform-specific storage components and paths. + */ +public interface StorageProvider { + + /** + * Returns the device keystore implementation. + */ + fun `keystore`(): DeviceKeystore + + /** + * Returns the blob store implementation. + */ + fun `blobStore`(): AtomicBlobStore + + /** + * Returns the storage paths selected by the platform. + */ + fun `paths`(): StoragePaths + + companion object +} + +/** + * Provider responsible for platform-specific storage components and paths. + */ +open class StorageProviderImpl: Disposable, AutoCloseable, StorageProvider +{ + + constructor(pointer: Pointer) { + this.pointer = pointer + this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) + } + + /** + * This constructor can be used to instantiate a fake object. Only used for tests. Any + * attempt to actually use an object constructed this way will fail as there is no + * connected Rust object. + */ + @Suppress("UNUSED_PARAMETER") + constructor(noPointer: NoPointer) { + this.pointer = null + this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) + } + + protected val pointer: Pointer? + protected val cleanable: UniffiCleaner.Cleanable + + private val wasDestroyed = AtomicBoolean(false) + private val callCounter = AtomicLong(1) + + override fun destroy() { + // Only allow a single call to this method. + // TODO: maybe we should log a warning if called more than once? + if (this.wasDestroyed.compareAndSet(false, true)) { + // This decrement always matches the initial count of 1 given at creation time. + if (this.callCounter.decrementAndGet() == 0L) { + cleanable.clean() + } + } + } + + @Synchronized + override fun close() { + this.destroy() + } + + internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { + // Check and increment the call counter, to keep the object alive. + // This needs a compare-and-set retry loop in case of concurrent updates. + do { + val c = this.callCounter.get() + if (c == 0L) { + throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") + } + if (c == Long.MAX_VALUE) { + throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") + } + } while (! this.callCounter.compareAndSet(c, c + 1L)) + // Now we can safely do the method call without the pointer being freed concurrently. + try { + return block(this.uniffiClonePointer()) + } finally { + // This decrement always matches the increment we performed above. + if (this.callCounter.decrementAndGet() == 0L) { + cleanable.clean() + } + } + } + + // Use a static inner class instead of a closure so as not to accidentally + // capture `this` as part of the cleanable's action. + private class UniffiCleanAction(private val pointer: Pointer?) : Runnable { + override fun run() { + pointer?.let { ptr -> + uniffiRustCall { status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_free_storageprovider(ptr, status) + } + } + } + } + + fun uniffiClonePointer(): Pointer { + return uniffiRustCall() { status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_clone_storageprovider(pointer!!, status) + } + } + + + /** + * Returns the device keystore implementation. + */override fun `keystore`(): DeviceKeystore { + return FfiConverterTypeDeviceKeystore.lift( + callWithPointer { + uniffiRustCall() { _status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_storageprovider_keystore( + it, _status) +} + } + ) + } + + + + /** + * Returns the blob store implementation. + */override fun `blobStore`(): AtomicBlobStore { + return FfiConverterTypeAtomicBlobStore.lift( + callWithPointer { + uniffiRustCall() { _status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_storageprovider_blob_store( + it, _status) +} + } + ) + } + + + + /** + * Returns the storage paths selected by the platform. + */override fun `paths`(): StoragePaths { + return FfiConverterTypeStoragePaths.lift( + callWithPointer { + uniffiRustCall() { _status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_storageprovider_paths( + it, _status) +} + } + ) + } + + + + + + + companion object + +} + + +// Put the implementation in an object so we don't pollute the top-level namespace +internal object uniffiCallbackInterfaceStorageProvider { + internal object `keystore`: UniffiCallbackInterfaceStorageProviderMethod0 { + override fun callback(`uniffiHandle`: Long,`uniffiOutReturn`: PointerByReference,uniffiCallStatus: UniffiRustCallStatus,) { + val uniffiObj = FfiConverterTypeStorageProvider.handleMap.get(uniffiHandle) + val makeCall = { -> + uniffiObj.`keystore`( + ) + } + val writeReturn = { value: DeviceKeystore -> uniffiOutReturn.setValue(FfiConverterTypeDeviceKeystore.lower(value)) } + uniffiTraitInterfaceCall(uniffiCallStatus, makeCall, writeReturn) + } + } + internal object `blobStore`: UniffiCallbackInterfaceStorageProviderMethod1 { + override fun callback(`uniffiHandle`: Long,`uniffiOutReturn`: PointerByReference,uniffiCallStatus: UniffiRustCallStatus,) { + val uniffiObj = FfiConverterTypeStorageProvider.handleMap.get(uniffiHandle) + val makeCall = { -> + uniffiObj.`blobStore`( + ) + } + val writeReturn = { value: AtomicBlobStore -> uniffiOutReturn.setValue(FfiConverterTypeAtomicBlobStore.lower(value)) } + uniffiTraitInterfaceCall(uniffiCallStatus, makeCall, writeReturn) + } + } + internal object `paths`: UniffiCallbackInterfaceStorageProviderMethod2 { + override fun callback(`uniffiHandle`: Long,`uniffiOutReturn`: PointerByReference,uniffiCallStatus: UniffiRustCallStatus,) { + val uniffiObj = FfiConverterTypeStorageProvider.handleMap.get(uniffiHandle) + val makeCall = { -> + uniffiObj.`paths`( + ) + } + val writeReturn = { value: StoragePaths -> uniffiOutReturn.setValue(FfiConverterTypeStoragePaths.lower(value)) } + uniffiTraitInterfaceCall(uniffiCallStatus, makeCall, writeReturn) + } + } + + internal object uniffiFree: UniffiCallbackInterfaceFree { + override fun callback(handle: Long) { + FfiConverterTypeStorageProvider.handleMap.remove(handle) + } + } + + internal var vtable = UniffiVTableCallbackInterfaceStorageProvider.UniffiByValue( + `keystore`, + `blobStore`, + `paths`, + uniffiFree, + ) + + // Registers the foreign callback with the Rust side. + // This method is generated for each callback interface. + internal fun register(lib: UniffiLib) { + lib.uniffi_walletkit_core_fn_init_callback_vtable_storageprovider(vtable) + } +} + +/** + * @suppress + */ +public object FfiConverterTypeStorageProvider: FfiConverter { + internal val handleMap = UniffiHandleMap() + + override fun lower(value: StorageProvider): Pointer { + return Pointer(handleMap.insert(value)) + } + + override fun lift(value: Pointer): StorageProvider { + return StorageProviderImpl(value) + } + + override fun read(buf: ByteBuffer): StorageProvider { + // The Rust code always writes pointers as 8 bytes, and will + // fail to compile if they don't fit. + return lift(Pointer(buf.getLong())) + } + + override fun allocationSize(value: StorageProvider) = 8UL + + override fun write(value: StorageProvider, buf: ByteBuffer) { + // The Rust code always expects pointers written as 8 bytes, + // and will fail to compile if they don't fit. + buf.putLong(Pointer.nativeValue(lower(value))) + } +} + + +// This template implements a class for working with a Rust struct via a Pointer/Arc +// to the live Rust struct on the other side of the FFI. +// +// Each instance implements core operations for working with the Rust `Arc` and the +// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. +// +// There's some subtlety here, because we have to be careful not to operate on a Rust +// struct after it has been dropped, and because we must expose a public API for freeing +// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: +// +// * Each instance holds an opaque pointer to the underlying Rust struct. +// Method calls need to read this pointer from the object's state and pass it in to +// the Rust FFI. +// +// * When an instance is no longer needed, its pointer should be passed to a +// special destructor function provided by the Rust FFI, which will drop the +// underlying Rust struct. +// +// * Given an instance, calling code is expected to call the special +// `destroy` method in order to free it after use, either by calling it explicitly +// or by using a higher-level helper like the `use` method. Failing to do so risks +// leaking the underlying Rust struct. +// +// * We can't assume that calling code will do the right thing, and must be prepared +// to handle Kotlin method calls executing concurrently with or even after a call to +// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. +// +// * We must never allow Rust code to operate on the underlying Rust struct after +// the destructor has been called, and must never call the destructor more than once. +// Doing so may trigger memory unsafety. +// +// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` +// is implemented to call the destructor when the Kotlin object becomes unreachable. +// This is done in a background thread. This is not a panacea, and client code should be aware that +// 1. the thread may starve if some there are objects that have poorly performing +// `drop` methods or do significant work in their `drop` methods. +// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, +// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). +// +// If we try to implement this with mutual exclusion on access to the pointer, there is the +// possibility of a race between a method call and a concurrent call to `destroy`: +// +// * Thread A starts a method call, reads the value of the pointer, but is interrupted +// before it can pass the pointer over the FFI to Rust. +// * Thread B calls `destroy` and frees the underlying Rust struct. +// * Thread A resumes, passing the already-read pointer value to Rust and triggering +// a use-after-free. +// +// One possible solution would be to use a `ReadWriteLock`, with each method call taking +// a read lock (and thus allowed to run concurrently) and the special `destroy` method +// taking a write lock (and thus blocking on live method calls). However, we aim not to +// generate methods with any hidden blocking semantics, and a `destroy` method that might +// block if called incorrectly seems to meet that bar. +// +// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track +// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` +// has been called. These are updated according to the following rules: +// +// * The initial value of the counter is 1, indicating a live object with no in-flight calls. +// The initial value for the flag is false. +// +// * At the start of each method call, we atomically check the counter. +// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. +// If it is nonzero them we atomically increment it by 1 and proceed with the method call. +// +// * At the end of each method call, we atomically decrement and check the counter. +// If it has reached zero then we destroy the underlying Rust struct. +// +// * When `destroy` is called, we atomically flip the flag from false to true. +// If the flag was already true we silently fail. +// Otherwise we atomically decrement and check the counter. +// If it has reached zero then we destroy the underlying Rust struct. +// +// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, +// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. +// +// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been +// called *and* all in-flight method calls have completed, avoiding violating any of the expectations +// of the underlying Rust code. +// +// This makes a cleaner a better alternative to _not_ calling `destroy()` as +// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` +// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner +// thread may be starved, and the app will leak memory. +// +// In this case, `destroy`ing manually may be a better solution. +// +// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects +// with Rust peers are reclaimed: +// +// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: +// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: +// 3. The memory is reclaimed when the process terminates. +// +// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 +// + + +/** + * A wrapper around `U256` to represent a field element in the protocol. Wrapper enables FFI interoperability. + * + * Most inputs and outputs from the zero-knowledge proofs are `U256` values. + * While using `U256` directly is convenient and recommended when working with the proofs, particularly in Rust, + * it is not a user-friendly type for interactions or communications in other languages / systems. + * + * Particularly, when sending proof inputs/outputs as JSON on HTTP requests, the values SHOULD + * be represented as padded hex strings from Big Endian bytes. + */ +public interface U256WrapperInterface { + + /** + * Converts the `U256` value into a vector of 4 `u64` values (little-endian). Least significant limb first. + * + * Using a vector as an array cannot be lowered to foreign bindings. + */ + fun `intoLimbs`(): List + + /** + * Outputs the decimal string representation of the `U256` value. + */ + fun `toDecimalString`(): kotlin.String + + /** + * Outputs a hex string representation of the `U256` value padded to 32 bytes (plus two bytes for the `0x` prefix). + */ + fun `toHexString`(): kotlin.String + + companion object +} + +/** + * A wrapper around `U256` to represent a field element in the protocol. Wrapper enables FFI interoperability. + * + * Most inputs and outputs from the zero-knowledge proofs are `U256` values. + * While using `U256` directly is convenient and recommended when working with the proofs, particularly in Rust, + * it is not a user-friendly type for interactions or communications in other languages / systems. + * + * Particularly, when sending proof inputs/outputs as JSON on HTTP requests, the values SHOULD + * be represented as padded hex strings from Big Endian bytes. + */ +open class U256Wrapper: Disposable, AutoCloseable, U256WrapperInterface +{ + + constructor(pointer: Pointer) { + this.pointer = pointer + this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) + } + + /** + * This constructor can be used to instantiate a fake object. Only used for tests. Any + * attempt to actually use an object constructed this way will fail as there is no + * connected Rust object. + */ + @Suppress("UNUSED_PARAMETER") + constructor(noPointer: NoPointer) { + this.pointer = null + this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) + } + + protected val pointer: Pointer? + protected val cleanable: UniffiCleaner.Cleanable + + private val wasDestroyed = AtomicBoolean(false) + private val callCounter = AtomicLong(1) + + override fun destroy() { + // Only allow a single call to this method. + // TODO: maybe we should log a warning if called more than once? + if (this.wasDestroyed.compareAndSet(false, true)) { + // This decrement always matches the initial count of 1 given at creation time. + if (this.callCounter.decrementAndGet() == 0L) { + cleanable.clean() + } + } + } + + @Synchronized + override fun close() { + this.destroy() + } + + internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { + // Check and increment the call counter, to keep the object alive. + // This needs a compare-and-set retry loop in case of concurrent updates. + do { + val c = this.callCounter.get() + if (c == 0L) { + throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") + } + if (c == Long.MAX_VALUE) { + throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") + } + } while (! this.callCounter.compareAndSet(c, c + 1L)) + // Now we can safely do the method call without the pointer being freed concurrently. + try { + return block(this.uniffiClonePointer()) + } finally { + // This decrement always matches the increment we performed above. + if (this.callCounter.decrementAndGet() == 0L) { + cleanable.clean() + } + } + } + + // Use a static inner class instead of a closure so as not to accidentally + // capture `this` as part of the cleanable's action. + private class UniffiCleanAction(private val pointer: Pointer?) : Runnable { + override fun run() { + pointer?.let { ptr -> + uniffiRustCall { status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_free_u256wrapper(ptr, status) + } + } + } + } + + fun uniffiClonePointer(): Pointer { + return uniffiRustCall() { status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_clone_u256wrapper(pointer!!, status) + } + } + + + /** + * Converts the `U256` value into a vector of 4 `u64` values (little-endian). Least significant limb first. + * + * Using a vector as an array cannot be lowered to foreign bindings. + */override fun `intoLimbs`(): List { + return FfiConverterSequenceULong.lift( + callWithPointer { + uniffiRustCall() { _status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_u256wrapper_into_limbs( + it, _status) +} + } + ) + } + + + + /** + * Outputs the decimal string representation of the `U256` value. + */override fun `toDecimalString`(): kotlin.String { + return FfiConverterString.lift( + callWithPointer { + uniffiRustCall() { _status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_u256wrapper_to_decimal_string( + it, _status) +} + } + ) + } + + + + /** + * Outputs a hex string representation of the `U256` value padded to 32 bytes (plus two bytes for the `0x` prefix). + */override fun `toHexString`(): kotlin.String { + return FfiConverterString.lift( + callWithPointer { + uniffiRustCall() { _status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_u256wrapper_to_hex_string( + it, _status) +} + } + ) + } + + + + + + companion object { + + /** + * Creates a `U256` value from an array of 4 `u64` values (little-endian). Least significant limb first. + * + * This is the same as the `U256::from_limbs` method, but exposed to foreign bindings. + * + * # Errors + * + * Will return an `Error::InvalidNumber` if the input is not a valid `U256` value. + */ + @Throws(WalletKitException::class) fun `fromLimbs`(`limbs`: List): U256Wrapper { + return FfiConverterTypeU256Wrapper.lift( + uniffiRustCallWithError(WalletKitException) { _status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_constructor_u256wrapper_from_limbs( + FfiConverterSequenceULong.lower(`limbs`),_status) +} + ) + } + + + + /** + * Creates a `U256` value from a `u32` value. + * + * Logically this will only support values up to 32 bits. For larger values a different initialization should be used. + */ fun `fromU32`(`value`: kotlin.UInt): U256Wrapper { + return FfiConverterTypeU256Wrapper.lift( + uniffiRustCall() { _status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_constructor_u256wrapper_from_u32( + FfiConverterUInt.lower(`value`),_status) +} + ) + } + + + + /** + * Creates a `U256` value from a `u64` value. + * + * Logically this will only support values up to 64 bits. For larger values a different initialization should be used. + */ fun `fromU64`(`value`: kotlin.ULong): U256Wrapper { + return FfiConverterTypeU256Wrapper.lift( + uniffiRustCall() { _status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_constructor_u256wrapper_from_u64( + FfiConverterULong.lower(`value`),_status) +} + ) + } + + + + /** + * Attempts to parse a hex string as a `U256` value (wrapped). + * + * # Errors + * Will return an `Error::InvalidNumber` if the input is not a valid hex-string-presented number up to 256 bits. + */ + @Throws(WalletKitException::class) fun `tryFromHexString`(`hexString`: kotlin.String): U256Wrapper { + return FfiConverterTypeU256Wrapper.lift( + uniffiRustCallWithError(WalletKitException) { _status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_constructor_u256wrapper_try_from_hex_string( + FfiConverterString.lower(`hexString`),_status) +} + ) + } + + + + } + +} + +/** + * @suppress + */ +public object FfiConverterTypeU256Wrapper: FfiConverter { + + override fun lower(value: U256Wrapper): Pointer { + return value.uniffiClonePointer() + } + + override fun lift(value: Pointer): U256Wrapper { + return U256Wrapper(value) + } + + override fun read(buf: ByteBuffer): U256Wrapper { + // The Rust code always writes pointers as 8 bytes, and will + // fail to compile if they don't fit. + return lift(Pointer(buf.getLong())) + } + + override fun allocationSize(value: U256Wrapper) = 8UL + + override fun write(value: U256Wrapper, buf: ByteBuffer) { + // The Rust code always expects pointers written as 8 bytes, + // and will fail to compile if they don't fit. + buf.putLong(Pointer.nativeValue(lower(value))) + } +} + + +// This template implements a class for working with a Rust struct via a Pointer/Arc +// to the live Rust struct on the other side of the FFI. +// +// Each instance implements core operations for working with the Rust `Arc` and the +// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. +// +// There's some subtlety here, because we have to be careful not to operate on a Rust +// struct after it has been dropped, and because we must expose a public API for freeing +// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: +// +// * Each instance holds an opaque pointer to the underlying Rust struct. +// Method calls need to read this pointer from the object's state and pass it in to +// the Rust FFI. +// +// * When an instance is no longer needed, its pointer should be passed to a +// special destructor function provided by the Rust FFI, which will drop the +// underlying Rust struct. +// +// * Given an instance, calling code is expected to call the special +// `destroy` method in order to free it after use, either by calling it explicitly +// or by using a higher-level helper like the `use` method. Failing to do so risks +// leaking the underlying Rust struct. +// +// * We can't assume that calling code will do the right thing, and must be prepared +// to handle Kotlin method calls executing concurrently with or even after a call to +// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. +// +// * We must never allow Rust code to operate on the underlying Rust struct after +// the destructor has been called, and must never call the destructor more than once. +// Doing so may trigger memory unsafety. +// +// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` +// is implemented to call the destructor when the Kotlin object becomes unreachable. +// This is done in a background thread. This is not a panacea, and client code should be aware that +// 1. the thread may starve if some there are objects that have poorly performing +// `drop` methods or do significant work in their `drop` methods. +// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, +// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). +// +// If we try to implement this with mutual exclusion on access to the pointer, there is the +// possibility of a race between a method call and a concurrent call to `destroy`: +// +// * Thread A starts a method call, reads the value of the pointer, but is interrupted +// before it can pass the pointer over the FFI to Rust. +// * Thread B calls `destroy` and frees the underlying Rust struct. +// * Thread A resumes, passing the already-read pointer value to Rust and triggering +// a use-after-free. +// +// One possible solution would be to use a `ReadWriteLock`, with each method call taking +// a read lock (and thus allowed to run concurrently) and the special `destroy` method +// taking a write lock (and thus blocking on live method calls). However, we aim not to +// generate methods with any hidden blocking semantics, and a `destroy` method that might +// block if called incorrectly seems to meet that bar. +// +// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track +// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` +// has been called. These are updated according to the following rules: +// +// * The initial value of the counter is 1, indicating a live object with no in-flight calls. +// The initial value for the flag is false. +// +// * At the start of each method call, we atomically check the counter. +// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. +// If it is nonzero them we atomically increment it by 1 and proceed with the method call. +// +// * At the end of each method call, we atomically decrement and check the counter. +// If it has reached zero then we destroy the underlying Rust struct. +// +// * When `destroy` is called, we atomically flip the flag from false to true. +// If the flag was already true we silently fail. +// Otherwise we atomically decrement and check the counter. +// If it has reached zero then we destroy the underlying Rust struct. +// +// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, +// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. +// +// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been +// called *and* all in-flight method calls have completed, avoiding violating any of the expectations +// of the underlying Rust code. +// +// This makes a cleaner a better alternative to _not_ calling `destroy()` as +// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` +// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner +// thread may be starved, and the app will leak memory. +// +// In this case, `destroy`ing manually may be a better solution. +// +// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects +// with Rust peers are reclaimed: +// +// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: +// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: +// 3. The memory is reclaimed when the process terminates. +// +// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 +// + + +/** + * A base World ID identity which can be used to generate World ID Proofs for different credentials. + * + * Most essential primitive for World ID. + */ +public interface WorldIdInterface { + + /** + * Generates a nullifier hash for a particular context (i.e. app + action) and the identity. + * The nullifier hash is a unique pseudo-random number for the particular identity and context. + * More information can be found [here](https://docs.world.org/world-id/concepts#vocabulary) + * + * [Protocol Reference](https://docs.semaphore.pse.dev/V2/technical-reference/circuits#nullifier-hash). + */ + fun `generateNullifierHash`(`context`: ProofContext): U256Wrapper + + /** + * Generates a World ID Zero-knowledge proof (ZKP) for a specific context (i.e. app + action) and the identity. + * This is equivalent to the user presenting their credential to a verifying party. + * + * **Requires the `semaphore` feature flag.** + * + * # Errors + * Will error if the Merkle Tree inclusion proof cannot be retrieved from the sign up sequencer or if + * something fails with the proof generation. + * + * # Example + * ```rust + * use walletkit_core::{proof::ProofContext, CredentialType, Environment, world_id::WorldId}; + * use std::sync::Arc; + * + * # tokio_test::block_on(async { + * let world_id = WorldId::new(b"not_a_real_secret", &Environment::Staging); + * let context = ProofContext::new("app_ce4cb73cb75fc3b73b71ffb4de178410", Some("my_action".to_string()), None, CredentialType::Device); + * let proof = world_id.generate_proof(&context).await.unwrap(); + * assert_eq!(proof.nullifier_hash.to_hex_string(), "0x302e253346d2b41a0fd71562ffc6e5ddcbab6d8ea3dd6d68e6a695b5639b1c37") + * # }) + * ``` + * note: running the doctest example above requires an HTTP connection to the sequencer. + */ + suspend fun `generateProof`(`context`: ProofContext): ProofOutput + + /** + * Generates the `identity_commitment` for a specific World ID identity and for a specific credential. + * For the same World ID, each credential will generate a different `identity_commitment` for privacy reasons. This is + * accomplished by using a different `identity_trapdoor` internally. + * + * The identity commitment is the public part of a World ID. It is what gets inserted into the membership set on-chain. Identity commitments + * are not directly used in proof verification. + */ + fun `getIdentityCommitment`(`credentialType`: CredentialType): U256Wrapper + + /** + * Compares two `WorldId`s for equality. + * + * This function uses constant-time comparison to prevent timing attacks, but should be performant enough. + * + * Exposed for foreign use. Use `PartialEq` if comparing within Rust. + * + * # Returns + * + * `true` if the two `WorldId`s are equal, `false` otherwise. + */ + fun `isEqualTo`(`other`: WorldId): kotlin.Boolean + + companion object +} + +/** + * A base World ID identity which can be used to generate World ID Proofs for different credentials. + * + * Most essential primitive for World ID. + */ +open class WorldId: Disposable, AutoCloseable, WorldIdInterface +{ + + constructor(pointer: Pointer) { + this.pointer = pointer + this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) + } + + /** + * This constructor can be used to instantiate a fake object. Only used for tests. Any + * attempt to actually use an object constructed this way will fail as there is no + * connected Rust object. + */ + @Suppress("UNUSED_PARAMETER") + constructor(noPointer: NoPointer) { + this.pointer = null + this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) + } + /** + * Initializes a new `Identity` from a World ID secret. The identity is initialized for a specific environment. + */ + constructor(`secret`: kotlin.ByteArray, `environment`: Environment) : + this( + uniffiRustCall() { _status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_constructor_worldid_new( + FfiConverterByteArray.lower(`secret`),FfiConverterTypeEnvironment.lower(`environment`),_status) +} + ) + + protected val pointer: Pointer? + protected val cleanable: UniffiCleaner.Cleanable + + private val wasDestroyed = AtomicBoolean(false) + private val callCounter = AtomicLong(1) + + override fun destroy() { + // Only allow a single call to this method. + // TODO: maybe we should log a warning if called more than once? + if (this.wasDestroyed.compareAndSet(false, true)) { + // This decrement always matches the initial count of 1 given at creation time. + if (this.callCounter.decrementAndGet() == 0L) { + cleanable.clean() + } + } + } + + @Synchronized + override fun close() { + this.destroy() + } + + internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { + // Check and increment the call counter, to keep the object alive. + // This needs a compare-and-set retry loop in case of concurrent updates. + do { + val c = this.callCounter.get() + if (c == 0L) { + throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") + } + if (c == Long.MAX_VALUE) { + throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") + } + } while (! this.callCounter.compareAndSet(c, c + 1L)) + // Now we can safely do the method call without the pointer being freed concurrently. + try { + return block(this.uniffiClonePointer()) + } finally { + // This decrement always matches the increment we performed above. + if (this.callCounter.decrementAndGet() == 0L) { + cleanable.clean() + } + } + } + + // Use a static inner class instead of a closure so as not to accidentally + // capture `this` as part of the cleanable's action. + private class UniffiCleanAction(private val pointer: Pointer?) : Runnable { + override fun run() { + pointer?.let { ptr -> + uniffiRustCall { status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_free_worldid(ptr, status) + } + } + } + } + + fun uniffiClonePointer(): Pointer { + return uniffiRustCall() { status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_clone_worldid(pointer!!, status) + } + } + + + /** + * Generates a nullifier hash for a particular context (i.e. app + action) and the identity. + * The nullifier hash is a unique pseudo-random number for the particular identity and context. + * More information can be found [here](https://docs.world.org/world-id/concepts#vocabulary) + * + * [Protocol Reference](https://docs.semaphore.pse.dev/V2/technical-reference/circuits#nullifier-hash). + */override fun `generateNullifierHash`(`context`: ProofContext): U256Wrapper { + return FfiConverterTypeU256Wrapper.lift( + callWithPointer { + uniffiRustCall() { _status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_worldid_generate_nullifier_hash( + it, FfiConverterTypeProofContext.lower(`context`),_status) +} + } + ) + } + + + + /** + * Generates a World ID Zero-knowledge proof (ZKP) for a specific context (i.e. app + action) and the identity. + * This is equivalent to the user presenting their credential to a verifying party. + * + * **Requires the `semaphore` feature flag.** + * + * # Errors + * Will error if the Merkle Tree inclusion proof cannot be retrieved from the sign up sequencer or if + * something fails with the proof generation. + * + * # Example + * ```rust + * use walletkit_core::{proof::ProofContext, CredentialType, Environment, world_id::WorldId}; + * use std::sync::Arc; + * + * # tokio_test::block_on(async { + * let world_id = WorldId::new(b"not_a_real_secret", &Environment::Staging); + * let context = ProofContext::new("app_ce4cb73cb75fc3b73b71ffb4de178410", Some("my_action".to_string()), None, CredentialType::Device); + * let proof = world_id.generate_proof(&context).await.unwrap(); + * assert_eq!(proof.nullifier_hash.to_hex_string(), "0x302e253346d2b41a0fd71562ffc6e5ddcbab6d8ea3dd6d68e6a695b5639b1c37") + * # }) + * ``` + * note: running the doctest example above requires an HTTP connection to the sequencer. + */ + @Throws(WalletKitException::class) + @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") + override suspend fun `generateProof`(`context`: ProofContext) : ProofOutput { + return uniffiRustCallAsync( + callWithPointer { thisPtr -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_worldid_generate_proof( + thisPtr, + FfiConverterTypeProofContext.lower(`context`), + ) + }, + { future, callback, continuation -> UniffiLib.INSTANCE.ffi_walletkit_core_rust_future_poll_pointer(future, callback, continuation) }, + { future, continuation -> UniffiLib.INSTANCE.ffi_walletkit_core_rust_future_complete_pointer(future, continuation) }, + { future -> UniffiLib.INSTANCE.ffi_walletkit_core_rust_future_free_pointer(future) }, + // lift function + { FfiConverterTypeProofOutput.lift(it) }, + // Error FFI converter + WalletKitException.ErrorHandler, + ) + } + + + /** + * Generates the `identity_commitment` for a specific World ID identity and for a specific credential. + * For the same World ID, each credential will generate a different `identity_commitment` for privacy reasons. This is + * accomplished by using a different `identity_trapdoor` internally. + * + * The identity commitment is the public part of a World ID. It is what gets inserted into the membership set on-chain. Identity commitments + * are not directly used in proof verification. + */override fun `getIdentityCommitment`(`credentialType`: CredentialType): U256Wrapper { + return FfiConverterTypeU256Wrapper.lift( + callWithPointer { + uniffiRustCall() { _status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_worldid_get_identity_commitment( + it, FfiConverterTypeCredentialType.lower(`credentialType`),_status) +} + } + ) + } + + + + /** + * Compares two `WorldId`s for equality. + * + * This function uses constant-time comparison to prevent timing attacks, but should be performant enough. + * + * Exposed for foreign use. Use `PartialEq` if comparing within Rust. + * + * # Returns + * + * `true` if the two `WorldId`s are equal, `false` otherwise. + */override fun `isEqualTo`(`other`: WorldId): kotlin.Boolean { + return FfiConverterBoolean.lift( + callWithPointer { + uniffiRustCall() { _status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_worldid_is_equal_to( + it, FfiConverterTypeWorldId.lower(`other`),_status) +} + } + ) + } + + + + + + + companion object + +} + +/** + * @suppress + */ +public object FfiConverterTypeWorldId: FfiConverter { + + override fun lower(value: WorldId): Pointer { + return value.uniffiClonePointer() + } + + override fun lift(value: Pointer): WorldId { + return WorldId(value) + } + + override fun read(buf: ByteBuffer): WorldId { + // The Rust code always writes pointers as 8 bytes, and will + // fail to compile if they don't fit. + return lift(Pointer(buf.getLong())) + } + + override fun allocationSize(value: WorldId) = 8UL + + override fun write(value: WorldId, buf: ByteBuffer) { + // The Rust code always expects pointers written as 8 bytes, + // and will fail to compile if they don't fit. + buf.putLong(Pointer.nativeValue(lower(value))) + } +} + + + +/** + * FFI-friendly credential record. + */ +data class CredentialRecordFfi ( + /** + * Credential identifier. + */ + var `credentialId`: kotlin.ByteArray, + /** + * Issuer schema identifier. + */ + var `issuerSchemaId`: kotlin.ULong, + /** + * Current credential status. + */ + var `status`: CredentialStatus, + /** + * Subject blinding factor tied to the credential subject. + */ + var `subjectBlindingFactor`: kotlin.ByteArray, + /** + * Genesis issuance timestamp (seconds). + */ + var `genesisIssuedAt`: kotlin.ULong, + /** + * Optional expiry timestamp (seconds). + */ + var `expiresAt`: kotlin.ULong?, + /** + * Last updated timestamp (seconds). + */ + var `updatedAt`: kotlin.ULong, + /** + * Raw credential blob bytes. + */ + var `credentialBlob`: kotlin.ByteArray, + /** + * Optional associated data blob bytes. + */ + var `associatedData`: kotlin.ByteArray? +) { + + companion object +} + +/** + * @suppress + */ +public object FfiConverterTypeCredentialRecordFfi: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): CredentialRecordFfi { + return CredentialRecordFfi( + FfiConverterByteArray.read(buf), + FfiConverterULong.read(buf), + FfiConverterTypeCredentialStatus.read(buf), + FfiConverterByteArray.read(buf), + FfiConverterULong.read(buf), + FfiConverterOptionalULong.read(buf), + FfiConverterULong.read(buf), + FfiConverterByteArray.read(buf), + FfiConverterOptionalByteArray.read(buf), + ) + } + + override fun allocationSize(value: CredentialRecordFfi) = ( + FfiConverterByteArray.allocationSize(value.`credentialId`) + + FfiConverterULong.allocationSize(value.`issuerSchemaId`) + + FfiConverterTypeCredentialStatus.allocationSize(value.`status`) + + FfiConverterByteArray.allocationSize(value.`subjectBlindingFactor`) + + FfiConverterULong.allocationSize(value.`genesisIssuedAt`) + + FfiConverterOptionalULong.allocationSize(value.`expiresAt`) + + FfiConverterULong.allocationSize(value.`updatedAt`) + + FfiConverterByteArray.allocationSize(value.`credentialBlob`) + + FfiConverterOptionalByteArray.allocationSize(value.`associatedData`) + ) + + override fun write(value: CredentialRecordFfi, buf: ByteBuffer) { + FfiConverterByteArray.write(value.`credentialId`, buf) + FfiConverterULong.write(value.`issuerSchemaId`, buf) + FfiConverterTypeCredentialStatus.write(value.`status`, buf) + FfiConverterByteArray.write(value.`subjectBlindingFactor`, buf) + FfiConverterULong.write(value.`genesisIssuedAt`, buf) + FfiConverterOptionalULong.write(value.`expiresAt`, buf) + FfiConverterULong.write(value.`updatedAt`, buf) + FfiConverterByteArray.write(value.`credentialBlob`, buf) + FfiConverterOptionalByteArray.write(value.`associatedData`, buf) + } +} + + + +/** + * FFI-friendly proof disclosure result. + */ +data class ProofDisclosureResultFfi ( + /** + * Result kind. + */ + var `kind`: ProofDisclosureKind, + /** + * Stored proof package bytes. + */ + var `bytes`: kotlin.ByteArray +) { + + companion object +} + +/** + * @suppress + */ +public object FfiConverterTypeProofDisclosureResultFfi: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): ProofDisclosureResultFfi { + return ProofDisclosureResultFfi( + FfiConverterTypeProofDisclosureKind.read(buf), + FfiConverterByteArray.read(buf), + ) + } + + override fun allocationSize(value: ProofDisclosureResultFfi) = ( + FfiConverterTypeProofDisclosureKind.allocationSize(value.`kind`) + + FfiConverterByteArray.allocationSize(value.`bytes`) + ) + + override fun write(value: ProofDisclosureResultFfi, buf: ByteBuffer) { + FfiConverterTypeProofDisclosureKind.write(value.`kind`, buf) + FfiConverterByteArray.write(value.`bytes`, buf) + } +} + + + +/** + * Kind of blob stored in the vault. + */ + +enum class BlobKind(val value: kotlin.UByte) { + + /** + * Credential blob payload. + */ + CREDENTIAL_BLOB(1u), + /** + * Associated data payload. + */ + ASSOCIATED_DATA(2u); + companion object +} + + +/** + * @suppress + */ +public object FfiConverterTypeBlobKind: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer) = try { + BlobKind.values()[buf.getInt() - 1] + } catch (e: IndexOutOfBoundsException) { + throw RuntimeException("invalid enum value, something is very wrong!!", e) + } + + override fun allocationSize(value: BlobKind) = 4UL + + override fun write(value: BlobKind, buf: ByteBuffer) { + buf.putInt(value.ordinal + 1) + } +} + + + + + +/** + * Status of a stored credential. + */ + +enum class CredentialStatus(val value: kotlin.UByte) { + + /** + * Credential is active and can be used. + */ + ACTIVE(1u), + /** + * Credential has been revoked. + */ + REVOKED(2u), + /** + * Credential has expired. + */ + EXPIRED(3u); + companion object +} + + +/** + * @suppress + */ +public object FfiConverterTypeCredentialStatus: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer) = try { + CredentialStatus.values()[buf.getInt() - 1] + } catch (e: IndexOutOfBoundsException) { + throw RuntimeException("invalid enum value, something is very wrong!!", e) + } + + override fun allocationSize(value: CredentialStatus) = 4UL + + override fun write(value: CredentialStatus, buf: ByteBuffer) { + buf.putInt(value.ordinal + 1) + } +} + + + + + +/** + * A `CredentialType` represents a specific credential which can be presented by a World ID holder. + * + * For example, if a World ID is Orb-verified, the holder can use their `Orb` credential to prove they have a + * valid Orb-verified credential. + * + * More details in `https://docs.world.org/world-id/concepts#proof-of-personhood` + */ + +enum class CredentialType { + + /** + * Represents persons who have been biometrically verified at an Orb. Highest level of proof of personhood verification. + */ + ORB, + /** + * Verified biometric ICAO-9303 government-issued document holder + */ + DOCUMENT, + /** + * Verified biometric ICAO-9303 government-issued document holder with additional presence checks + * such as Chip Authentication or Active Authentication. + * + * + * The identity trapdoor is `secure_passport` but it's serialized as `secure_document` to match `idkit-js` and the Developer Portal. + * Reference: + */ + SECURE_DOCUMENT, + /** + * Represents a semi-unique device + */ + DEVICE; + companion object +} + + +/** + * @suppress + */ +public object FfiConverterTypeCredentialType: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer) = try { + CredentialType.values()[buf.getInt() - 1] + } catch (e: IndexOutOfBoundsException) { + throw RuntimeException("invalid enum value, something is very wrong!!", e) + } + + override fun allocationSize(value: CredentialType) = 4UL + + override fun write(value: CredentialType, buf: ByteBuffer) { + buf.putInt(value.ordinal + 1) + } +} + + + + + +/** + * Represents the environment in which a World ID is being presented and used. + * + * Each environment uses different sources of truth for the World ID credentials. + * + * More information on testing for the World ID Protocol can be found in: `https://docs.world.org/world-id/quick-start/testing` + */ + +enum class Environment { + + /** + * For testing purposes ONLY. + */ + STAGING, + /** + * Live production environment. World ID Tree: `id.worldcoin.eth` + */ + PRODUCTION; + companion object +} + + +/** + * @suppress + */ +public object FfiConverterTypeEnvironment: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer) = try { + Environment.values()[buf.getInt() - 1] + } catch (e: IndexOutOfBoundsException) { + throw RuntimeException("invalid enum value, something is very wrong!!", e) + } + + override fun allocationSize(value: Environment) = 4UL + + override fun write(value: Environment, buf: ByteBuffer) { + buf.putInt(value.ordinal + 1) + } +} + + + + + +/** + * Enumeration of possible log levels. + * + * This enum represents the severity levels that can be used when logging messages. + */ + +enum class LogLevel { + + /** + * Designates very low priority, often extremely detailed messages. + */ + TRACE, + /** + * Designates lower priority debugging information. + */ + DEBUG, + /** + * Designates informational messages that highlight the progress of the application. + */ + INFO, + /** + * Designates potentially harmful situations. + */ + WARN, + /** + * Designates error events that might still allow the application to continue running. + */ + ERROR; + companion object +} + + +/** + * @suppress + */ +public object FfiConverterTypeLogLevel: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer) = try { + LogLevel.values()[buf.getInt() - 1] + } catch (e: IndexOutOfBoundsException) { + throw RuntimeException("invalid enum value, something is very wrong!!", e) + } + + override fun allocationSize(value: LogLevel) = 4UL + + override fun write(value: LogLevel, buf: ByteBuffer) { + buf.putInt(value.ordinal + 1) + } +} + + + + + +/** + * FFI-friendly proof disclosure result kind. + */ + +enum class ProofDisclosureKind { + + /** + * Stored bytes for the first disclosure of a request. + */ + FRESH, + /** + * Stored bytes replayed for an existing request. + */ + REPLAY; + companion object +} + + +/** + * @suppress + */ +public object FfiConverterTypeProofDisclosureKind: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer) = try { + ProofDisclosureKind.values()[buf.getInt() - 1] + } catch (e: IndexOutOfBoundsException) { + throw RuntimeException("invalid enum value, something is very wrong!!", e) + } + + override fun allocationSize(value: ProofDisclosureKind) = 4UL + + override fun write(value: ProofDisclosureKind, buf: ByteBuffer) { + buf.putInt(value.ordinal + 1) + } +} + + + + + + + +/** + * Errors raised by credential storage primitives. + */ +sealed class StorageException: kotlin.Exception() { + + /** + * Errors coming from the device keystore. + */ + class Keystore( + + val v1: kotlin.String + ) : StorageException() { + override val message + get() = "v1=${ v1 }" + } + + /** + * Errors coming from the blob store. + */ + class BlobStore( + + val v1: kotlin.String + ) : StorageException() { + override val message + get() = "v1=${ v1 }" + } + + /** + * Errors coming from the storage lock. + */ + class Lock( + + val v1: kotlin.String + ) : StorageException() { + override val message + get() = "v1=${ v1 }" + } + + /** + * Serialization/deserialization failures. + */ + class Serialization( + + val v1: kotlin.String + ) : StorageException() { + override val message + get() = "v1=${ v1 }" + } + + /** + * Cryptographic failures (AEAD, HKDF, etc.). + */ + class Crypto( + + val v1: kotlin.String + ) : StorageException() { + override val message + get() = "v1=${ v1 }" + } + + /** + * Invalid or malformed account key envelope. + */ + class InvalidEnvelope( + + val v1: kotlin.String + ) : StorageException() { + override val message + get() = "v1=${ v1 }" + } + + /** + * Unsupported envelope version. + */ + class UnsupportedEnvelopeVersion( + + val v1: kotlin.UInt + ) : StorageException() { + override val message + get() = "v1=${ v1 }" + } + + /** + * Errors coming from the vault database. + */ + class VaultDb( + + val v1: kotlin.String + ) : StorageException() { + override val message + get() = "v1=${ v1 }" + } + + /** + * Errors coming from the cache database. + */ + class CacheDb( + + val v1: kotlin.String + ) : StorageException() { + override val message + get() = "v1=${ v1 }" + } + + /** + * Leaf index mismatch during initialization. + */ + class InvalidLeafIndex( + + /** + * Leaf index stored in the vault. + */ + val `expected`: kotlin.ULong, + + /** + * Leaf index provided by the caller. + */ + val `provided`: kotlin.ULong + ) : StorageException() { + override val message + get() = "expected=${ `expected` }, provided=${ `provided` }" + } + + /** + * Vault database integrity check failed. + */ + class CorruptedVault( + + val v1: kotlin.String + ) : StorageException() { + override val message + get() = "v1=${ v1 }" + } + + /** + * Storage has not been initialized yet. + */ + class NotInitialized( + ) : StorageException() { + override val message + get() = "" + } + + /** + * Nullifier already disclosed for a different request. + */ + class NullifierAlreadyDisclosed( + ) : StorageException() { + override val message + get() = "" + } + + /** + * Credential not found in the vault. + */ + class CredentialNotFound( + ) : StorageException() { + override val message + get() = "" + } + + /** + * Unexpected `UniFFI` callback error. + */ + class UnexpectedUniFfiCallbackException( + + val v1: kotlin.String + ) : StorageException() { + override val message + get() = "v1=${ v1 }" + } + + + companion object ErrorHandler : UniffiRustCallStatusErrorHandler { + override fun lift(error_buf: RustBuffer.ByValue): StorageException = FfiConverterTypeStorageError.lift(error_buf) + } + + +} + +/** + * @suppress + */ +public object FfiConverterTypeStorageError : FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): StorageException { + + + return when(buf.getInt()) { + 1 -> StorageException.Keystore( + FfiConverterString.read(buf), + ) + 2 -> StorageException.BlobStore( + FfiConverterString.read(buf), + ) + 3 -> StorageException.Lock( + FfiConverterString.read(buf), + ) + 4 -> StorageException.Serialization( + FfiConverterString.read(buf), + ) + 5 -> StorageException.Crypto( + FfiConverterString.read(buf), + ) + 6 -> StorageException.InvalidEnvelope( + FfiConverterString.read(buf), + ) + 7 -> StorageException.UnsupportedEnvelopeVersion( + FfiConverterUInt.read(buf), + ) + 8 -> StorageException.VaultDb( + FfiConverterString.read(buf), + ) + 9 -> StorageException.CacheDb( + FfiConverterString.read(buf), + ) + 10 -> StorageException.InvalidLeafIndex( + FfiConverterULong.read(buf), + FfiConverterULong.read(buf), + ) + 11 -> StorageException.CorruptedVault( + FfiConverterString.read(buf), + ) + 12 -> StorageException.NotInitialized() + 13 -> StorageException.NullifierAlreadyDisclosed() + 14 -> StorageException.CredentialNotFound() + 15 -> StorageException.UnexpectedUniFfiCallbackException( + FfiConverterString.read(buf), + ) + else -> throw RuntimeException("invalid error enum value, something is very wrong!!") + } + } + + override fun allocationSize(value: StorageException): ULong { + return when(value) { + is StorageException.Keystore -> ( + // Add the size for the Int that specifies the variant plus the size needed for all fields + 4UL + + FfiConverterString.allocationSize(value.v1) + ) + is StorageException.BlobStore -> ( + // Add the size for the Int that specifies the variant plus the size needed for all fields + 4UL + + FfiConverterString.allocationSize(value.v1) + ) + is StorageException.Lock -> ( + // Add the size for the Int that specifies the variant plus the size needed for all fields + 4UL + + FfiConverterString.allocationSize(value.v1) + ) + is StorageException.Serialization -> ( + // Add the size for the Int that specifies the variant plus the size needed for all fields + 4UL + + FfiConverterString.allocationSize(value.v1) + ) + is StorageException.Crypto -> ( + // Add the size for the Int that specifies the variant plus the size needed for all fields + 4UL + + FfiConverterString.allocationSize(value.v1) + ) + is StorageException.InvalidEnvelope -> ( + // Add the size for the Int that specifies the variant plus the size needed for all fields + 4UL + + FfiConverterString.allocationSize(value.v1) + ) + is StorageException.UnsupportedEnvelopeVersion -> ( + // Add the size for the Int that specifies the variant plus the size needed for all fields + 4UL + + FfiConverterUInt.allocationSize(value.v1) + ) + is StorageException.VaultDb -> ( + // Add the size for the Int that specifies the variant plus the size needed for all fields + 4UL + + FfiConverterString.allocationSize(value.v1) + ) + is StorageException.CacheDb -> ( + // Add the size for the Int that specifies the variant plus the size needed for all fields + 4UL + + FfiConverterString.allocationSize(value.v1) + ) + is StorageException.InvalidLeafIndex -> ( + // Add the size for the Int that specifies the variant plus the size needed for all fields + 4UL + + FfiConverterULong.allocationSize(value.`expected`) + + FfiConverterULong.allocationSize(value.`provided`) + ) + is StorageException.CorruptedVault -> ( + // Add the size for the Int that specifies the variant plus the size needed for all fields + 4UL + + FfiConverterString.allocationSize(value.v1) + ) + is StorageException.NotInitialized -> ( + // Add the size for the Int that specifies the variant plus the size needed for all fields + 4UL + ) + is StorageException.NullifierAlreadyDisclosed -> ( + // Add the size for the Int that specifies the variant plus the size needed for all fields + 4UL + ) + is StorageException.CredentialNotFound -> ( + // Add the size for the Int that specifies the variant plus the size needed for all fields + 4UL + ) + is StorageException.UnexpectedUniFfiCallbackException -> ( + // Add the size for the Int that specifies the variant plus the size needed for all fields + 4UL + + FfiConverterString.allocationSize(value.v1) + ) + } + } + + override fun write(value: StorageException, buf: ByteBuffer) { + when(value) { + is StorageException.Keystore -> { + buf.putInt(1) + FfiConverterString.write(value.v1, buf) + Unit + } + is StorageException.BlobStore -> { + buf.putInt(2) + FfiConverterString.write(value.v1, buf) + Unit + } + is StorageException.Lock -> { + buf.putInt(3) + FfiConverterString.write(value.v1, buf) + Unit + } + is StorageException.Serialization -> { + buf.putInt(4) + FfiConverterString.write(value.v1, buf) + Unit + } + is StorageException.Crypto -> { + buf.putInt(5) + FfiConverterString.write(value.v1, buf) + Unit + } + is StorageException.InvalidEnvelope -> { + buf.putInt(6) + FfiConverterString.write(value.v1, buf) + Unit + } + is StorageException.UnsupportedEnvelopeVersion -> { + buf.putInt(7) + FfiConverterUInt.write(value.v1, buf) + Unit + } + is StorageException.VaultDb -> { + buf.putInt(8) + FfiConverterString.write(value.v1, buf) + Unit + } + is StorageException.CacheDb -> { + buf.putInt(9) + FfiConverterString.write(value.v1, buf) + Unit + } + is StorageException.InvalidLeafIndex -> { + buf.putInt(10) + FfiConverterULong.write(value.`expected`, buf) + FfiConverterULong.write(value.`provided`, buf) + Unit + } + is StorageException.CorruptedVault -> { + buf.putInt(11) + FfiConverterString.write(value.v1, buf) + Unit + } + is StorageException.NotInitialized -> { + buf.putInt(12) + Unit + } + is StorageException.NullifierAlreadyDisclosed -> { + buf.putInt(13) + Unit + } + is StorageException.CredentialNotFound -> { + buf.putInt(14) + Unit + } + is StorageException.UnexpectedUniFfiCallbackException -> { + buf.putInt(15) + FfiConverterString.write(value.v1, buf) + Unit + } + }.let { /* this makes the `when` an expression, which ensures it is exhaustive */ } + } + +} + + + + + +/** + * Error outputs from `WalletKit` + */ +sealed class WalletKitException: kotlin.Exception() { + + /** + * Invalid input provided (e.g., incorrect length, format, etc.) + */ + class InvalidInput( + + /** + * The attribute that is invalid + */ + val `attribute`: kotlin.String, + + /** + * The reason the input is invalid + */ + val `reason`: kotlin.String + ) : WalletKitException() { + override val message + get() = "attribute=${ `attribute` }, reason=${ `reason` }" + } + + /** + * The presented data is not a valid U256 integer + */ + class InvalidNumber( + ) : WalletKitException() { + override val message + get() = "" + } + + /** + * Unexpected error serializing information + */ + class SerializationException( + + /** + * The error message from the serialization + */ + val `error`: kotlin.String + ) : WalletKitException() { + override val message + get() = "error=${ `error` }" + } + + /** + * Network connection error with details + */ + class NetworkException( + + /** + * The URL of the request + */ + val `url`: kotlin.String, + + /** + * The error message from the request + */ + val `error`: kotlin.String, + + /** + * The HTTP status code of the request, if available + */ + val `status`: kotlin.UShort? + ) : WalletKitException() { + override val message + get() = "url=${ `url` }, error=${ `error` }, status=${ `status` }" + } + + /** + * HTTP request failure + */ + class Reqwest( + + /** + * The error message from the request + */ + val `error`: kotlin.String + ) : WalletKitException() { + override val message + get() = "error=${ `error` }" + } + + /** + * Unhandled error generating a Zero-Knowledge Proof + */ + class ProofGeneration( + + /** + * The error message from the proof generation + */ + val `error`: kotlin.String + ) : WalletKitException() { + override val message + get() = "error=${ `error` }" + } + + /** + * The `semaphore` feature flag is not enabled + */ + class SemaphoreNotEnabled( + ) : WalletKitException() { + override val message + get() = "" + } + + /** + * The requested credential is not issued for this World ID + */ + class CredentialNotIssued( + ) : WalletKitException() { + override val message + get() = "" + } + + /** + * The requested credential has not been submitted on-chain + */ + class CredentialNotMined( + ) : WalletKitException() { + override val message + get() = "" + } + + /** + * This operation requires a registered account and an account is not registered + * for this authenticator. Call `create_account` first to register it. + */ + class AccountDoesNotExist( + ) : WalletKitException() { + override val message + get() = "" + } + + /** + * The account already exists for this authenticator. Call `account_index` to get the account index. + */ + class AccountAlreadyExists( + ) : WalletKitException() { + override val message + get() = "" + } + + /** + * The public key was not found in the batch, i.e. the authenticator is not authorized to sign for this action + */ + class UnauthorizedAuthenticator( + ) : WalletKitException() { + override val message + get() = "" + } + + /** + * An unexpected error occurred with the Authenticator + */ + class AuthenticatorException( + + /** + * The error message from the authenticator + */ + val `error`: kotlin.String + ) : WalletKitException() { + override val message + get() = "error=${ `error` }" + } + + /** + * An unexpected error occurred + */ + class Generic( + + /** + * The details of the error + */ + val `error`: kotlin.String + ) : WalletKitException() { + override val message + get() = "error=${ `error` }" + } + + + companion object ErrorHandler : UniffiRustCallStatusErrorHandler { + override fun lift(error_buf: RustBuffer.ByValue): WalletKitException = FfiConverterTypeWalletKitError.lift(error_buf) + } + + +} + +/** + * @suppress + */ +public object FfiConverterTypeWalletKitError : FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): WalletKitException { + + + return when(buf.getInt()) { + 1 -> WalletKitException.InvalidInput( + FfiConverterString.read(buf), + FfiConverterString.read(buf), + ) + 2 -> WalletKitException.InvalidNumber() + 3 -> WalletKitException.SerializationException( + FfiConverterString.read(buf), + ) + 4 -> WalletKitException.NetworkException( + FfiConverterString.read(buf), + FfiConverterString.read(buf), + FfiConverterOptionalUShort.read(buf), + ) + 5 -> WalletKitException.Reqwest( + FfiConverterString.read(buf), + ) + 6 -> WalletKitException.ProofGeneration( + FfiConverterString.read(buf), + ) + 7 -> WalletKitException.SemaphoreNotEnabled() + 8 -> WalletKitException.CredentialNotIssued() + 9 -> WalletKitException.CredentialNotMined() + 10 -> WalletKitException.AccountDoesNotExist() + 11 -> WalletKitException.AccountAlreadyExists() + 12 -> WalletKitException.UnauthorizedAuthenticator() + 13 -> WalletKitException.AuthenticatorException( + FfiConverterString.read(buf), + ) + 14 -> WalletKitException.Generic( + FfiConverterString.read(buf), + ) + else -> throw RuntimeException("invalid error enum value, something is very wrong!!") + } + } + + override fun allocationSize(value: WalletKitException): ULong { + return when(value) { + is WalletKitException.InvalidInput -> ( + // Add the size for the Int that specifies the variant plus the size needed for all fields + 4UL + + FfiConverterString.allocationSize(value.`attribute`) + + FfiConverterString.allocationSize(value.`reason`) + ) + is WalletKitException.InvalidNumber -> ( + // Add the size for the Int that specifies the variant plus the size needed for all fields + 4UL + ) + is WalletKitException.SerializationException -> ( + // Add the size for the Int that specifies the variant plus the size needed for all fields + 4UL + + FfiConverterString.allocationSize(value.`error`) + ) + is WalletKitException.NetworkException -> ( + // Add the size for the Int that specifies the variant plus the size needed for all fields + 4UL + + FfiConverterString.allocationSize(value.`url`) + + FfiConverterString.allocationSize(value.`error`) + + FfiConverterOptionalUShort.allocationSize(value.`status`) + ) + is WalletKitException.Reqwest -> ( + // Add the size for the Int that specifies the variant plus the size needed for all fields + 4UL + + FfiConverterString.allocationSize(value.`error`) + ) + is WalletKitException.ProofGeneration -> ( + // Add the size for the Int that specifies the variant plus the size needed for all fields + 4UL + + FfiConverterString.allocationSize(value.`error`) + ) + is WalletKitException.SemaphoreNotEnabled -> ( + // Add the size for the Int that specifies the variant plus the size needed for all fields + 4UL + ) + is WalletKitException.CredentialNotIssued -> ( + // Add the size for the Int that specifies the variant plus the size needed for all fields + 4UL + ) + is WalletKitException.CredentialNotMined -> ( + // Add the size for the Int that specifies the variant plus the size needed for all fields + 4UL + ) + is WalletKitException.AccountDoesNotExist -> ( + // Add the size for the Int that specifies the variant plus the size needed for all fields + 4UL + ) + is WalletKitException.AccountAlreadyExists -> ( + // Add the size for the Int that specifies the variant plus the size needed for all fields + 4UL + ) + is WalletKitException.UnauthorizedAuthenticator -> ( + // Add the size for the Int that specifies the variant plus the size needed for all fields + 4UL + ) + is WalletKitException.AuthenticatorException -> ( + // Add the size for the Int that specifies the variant plus the size needed for all fields + 4UL + + FfiConverterString.allocationSize(value.`error`) + ) + is WalletKitException.Generic -> ( + // Add the size for the Int that specifies the variant plus the size needed for all fields + 4UL + + FfiConverterString.allocationSize(value.`error`) + ) + } + } + + override fun write(value: WalletKitException, buf: ByteBuffer) { + when(value) { + is WalletKitException.InvalidInput -> { + buf.putInt(1) + FfiConverterString.write(value.`attribute`, buf) + FfiConverterString.write(value.`reason`, buf) + Unit + } + is WalletKitException.InvalidNumber -> { + buf.putInt(2) + Unit + } + is WalletKitException.SerializationException -> { + buf.putInt(3) + FfiConverterString.write(value.`error`, buf) + Unit + } + is WalletKitException.NetworkException -> { + buf.putInt(4) + FfiConverterString.write(value.`url`, buf) + FfiConverterString.write(value.`error`, buf) + FfiConverterOptionalUShort.write(value.`status`, buf) + Unit + } + is WalletKitException.Reqwest -> { + buf.putInt(5) + FfiConverterString.write(value.`error`, buf) + Unit + } + is WalletKitException.ProofGeneration -> { + buf.putInt(6) + FfiConverterString.write(value.`error`, buf) + Unit + } + is WalletKitException.SemaphoreNotEnabled -> { + buf.putInt(7) + Unit + } + is WalletKitException.CredentialNotIssued -> { + buf.putInt(8) + Unit + } + is WalletKitException.CredentialNotMined -> { + buf.putInt(9) + Unit + } + is WalletKitException.AccountDoesNotExist -> { + buf.putInt(10) + Unit + } + is WalletKitException.AccountAlreadyExists -> { + buf.putInt(11) + Unit + } + is WalletKitException.UnauthorizedAuthenticator -> { + buf.putInt(12) + Unit + } + is WalletKitException.AuthenticatorException -> { + buf.putInt(13) + FfiConverterString.write(value.`error`, buf) + Unit + } + is WalletKitException.Generic -> { + buf.putInt(14) + FfiConverterString.write(value.`error`, buf) + Unit + } + }.let { /* this makes the `when` an expression, which ensures it is exhaustive */ } + } + +} + + + + +/** + * @suppress + */ +public object FfiConverterOptionalUShort: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): kotlin.UShort? { + if (buf.get().toInt() == 0) { + return null + } + return FfiConverterUShort.read(buf) + } + + override fun allocationSize(value: kotlin.UShort?): ULong { + if (value == null) { + return 1UL + } else { + return 1UL + FfiConverterUShort.allocationSize(value) + } + } + + override fun write(value: kotlin.UShort?, buf: ByteBuffer) { + if (value == null) { + buf.put(0) + } else { + buf.put(1) + FfiConverterUShort.write(value, buf) + } + } +} + + + + +/** + * @suppress + */ +public object FfiConverterOptionalULong: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): kotlin.ULong? { + if (buf.get().toInt() == 0) { + return null + } + return FfiConverterULong.read(buf) + } + + override fun allocationSize(value: kotlin.ULong?): ULong { + if (value == null) { + return 1UL + } else { + return 1UL + FfiConverterULong.allocationSize(value) + } + } + + override fun write(value: kotlin.ULong?, buf: ByteBuffer) { + if (value == null) { + buf.put(0) + } else { + buf.put(1) + FfiConverterULong.write(value, buf) + } + } +} + + + + +/** + * @suppress + */ +public object FfiConverterOptionalString: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): kotlin.String? { + if (buf.get().toInt() == 0) { + return null + } + return FfiConverterString.read(buf) + } + + override fun allocationSize(value: kotlin.String?): ULong { + if (value == null) { + return 1UL + } else { + return 1UL + FfiConverterString.allocationSize(value) + } + } + + override fun write(value: kotlin.String?, buf: ByteBuffer) { + if (value == null) { + buf.put(0) + } else { + buf.put(1) + FfiConverterString.write(value, buf) + } + } +} + + + + +/** + * @suppress + */ +public object FfiConverterOptionalByteArray: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): kotlin.ByteArray? { + if (buf.get().toInt() == 0) { + return null + } + return FfiConverterByteArray.read(buf) + } + + override fun allocationSize(value: kotlin.ByteArray?): ULong { + if (value == null) { + return 1UL + } else { + return 1UL + FfiConverterByteArray.allocationSize(value) + } + } + + override fun write(value: kotlin.ByteArray?, buf: ByteBuffer) { + if (value == null) { + buf.put(0) + } else { + buf.put(1) + FfiConverterByteArray.write(value, buf) + } + } +} + + + + +/** + * @suppress + */ +public object FfiConverterSequenceULong: FfiConverterRustBuffer> { + override fun read(buf: ByteBuffer): List { + val len = buf.getInt() + return List(len) { + FfiConverterULong.read(buf) + } + } + + override fun allocationSize(value: List): ULong { + val sizeForLength = 4UL + val sizeForItems = value.map { FfiConverterULong.allocationSize(it) }.sum() + return sizeForLength + sizeForItems + } + + override fun write(value: List, buf: ByteBuffer) { + buf.putInt(value.size) + value.iterator().forEach { + FfiConverterULong.write(it, buf) + } + } +} + + + + +/** + * @suppress + */ +public object FfiConverterSequenceTypeCredentialRecordFfi: FfiConverterRustBuffer> { + override fun read(buf: ByteBuffer): List { + val len = buf.getInt() + return List(len) { + FfiConverterTypeCredentialRecordFfi.read(buf) + } + } + + override fun allocationSize(value: List): ULong { + val sizeForLength = 4UL + val sizeForItems = value.map { FfiConverterTypeCredentialRecordFfi.allocationSize(it) }.sum() + return sizeForLength + sizeForItems + } + + override fun write(value: List, buf: ByteBuffer) { + buf.putInt(value.size) + value.iterator().forEach { + FfiConverterTypeCredentialRecordFfi.write(it, buf) + } + } +} + + + + + + + + + /** + * Sets the global logger. + * + * This function allows you to provide your own implementation of the `Logger` trait. + * It initializes the logging system and should be called before any logging occurs. + * + * # Arguments + * + * * `logger` - An `Arc` containing your logger implementation. + * + * # Panics + * + * Panics if the logger has already been set. + * + * # Note + * + * If the logger has already been set, this function will print a message and do nothing. + */ fun `setLogger`(`logger`: Logger) + = + uniffiRustCall() { _status -> + UniffiLib.INSTANCE.uniffi_walletkit_core_fn_func_set_logger( + FfiConverterTypeLogger.lower(`logger`),_status) +} + + + + diff --git a/kotlin/walletkit-tests/build.gradle.kts b/kotlin/walletkit-tests/build.gradle.kts index d3a5f433c..06c9fce69 100644 --- a/kotlin/walletkit-tests/build.gradle.kts +++ b/kotlin/walletkit-tests/build.gradle.kts @@ -19,7 +19,7 @@ dependencies { sourceSets { test { kotlin.srcDirs( - "$rootDir/walletkit-android/src/main/java/uniffi/walletkit_core" + "$rootDir/walletkit/src/main/java/uniffi/walletkit_core" ) } } diff --git a/kotlin/walletkit-android/build.gradle.kts b/kotlin/walletkit/build.gradle.kts similarity index 97% rename from kotlin/walletkit-android/build.gradle.kts rename to kotlin/walletkit/build.gradle.kts index e6f047448..0df6f72ce 100644 --- a/kotlin/walletkit-android/build.gradle.kts +++ b/kotlin/walletkit/build.gradle.kts @@ -42,7 +42,7 @@ afterEvaluate { publications { create("maven") { groupId = "org.world" - artifactId = "walletkit-android" + artifactId = "walletkit" // Read version from Cargo.toml val cargoToml = file("../../Cargo.toml") diff --git a/kotlin/walletkit-android/consumer-rules.pro b/kotlin/walletkit/consumer-rules.pro similarity index 100% rename from kotlin/walletkit-android/consumer-rules.pro rename to kotlin/walletkit/consumer-rules.pro From c3c0bb796074d1c2c95e946c0381837484331064 Mon Sep 17 00:00:00 2001 From: Luke Mann Date: Sun, 25 Jan 2026 06:33:04 -0800 Subject: [PATCH 06/10] SimpleTest rename --- .../src/test/kotlin/org/world/walletkit/SimpleTest.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kotlin/walletkit-tests/src/test/kotlin/org/world/walletkit/SimpleTest.kt b/kotlin/walletkit-tests/src/test/kotlin/org/world/walletkit/SimpleTest.kt index f0efbfac9..962cd3082 100644 --- a/kotlin/walletkit-tests/src/test/kotlin/org/world/walletkit/SimpleTest.kt +++ b/kotlin/walletkit-tests/src/test/kotlin/org/world/walletkit/SimpleTest.kt @@ -5,9 +5,9 @@ import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertNull -class SimpleTests { +class SimpleTest { @Test fun simpleTest() { assertEquals(1, 1) } -} +} \ No newline at end of file From dbd4b2e525ec6eb28ac7cccf0b3a258bccba4462 Mon Sep 17 00:00:00 2001 From: Luke Mann Date: Sun, 25 Jan 2026 06:33:41 -0800 Subject: [PATCH 07/10] ktlint --- .../src/test/kotlin/org/world/walletkit/SimpleTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kotlin/walletkit-tests/src/test/kotlin/org/world/walletkit/SimpleTest.kt b/kotlin/walletkit-tests/src/test/kotlin/org/world/walletkit/SimpleTest.kt index 962cd3082..735329300 100644 --- a/kotlin/walletkit-tests/src/test/kotlin/org/world/walletkit/SimpleTest.kt +++ b/kotlin/walletkit-tests/src/test/kotlin/org/world/walletkit/SimpleTest.kt @@ -10,4 +10,4 @@ class SimpleTest { fun simpleTest() { assertEquals(1, 1) } -} \ No newline at end of file +} From f74caaacc7744d56b298b86a72040f8a843f95d2 Mon Sep 17 00:00:00 2001 From: Luke Mann Date: Sun, 25 Jan 2026 13:17:33 -0800 Subject: [PATCH 08/10] remove build file --- .../uniffi/walletkit_core/walletkit_core.kt | 7375 ----------------- 1 file changed, 7375 deletions(-) delete mode 100644 kotlin/walletkit-android/src/main/java/uniffi/walletkit_core/walletkit_core.kt diff --git a/kotlin/walletkit-android/src/main/java/uniffi/walletkit_core/walletkit_core.kt b/kotlin/walletkit-android/src/main/java/uniffi/walletkit_core/walletkit_core.kt deleted file mode 100644 index a516e08e8..000000000 --- a/kotlin/walletkit-android/src/main/java/uniffi/walletkit_core/walletkit_core.kt +++ /dev/null @@ -1,7375 +0,0 @@ -// This file was autogenerated by some hot garbage in the `uniffi` crate. -// Trust me, you don't want to mess with it! - -@file:Suppress("NAME_SHADOWING") - -package uniffi.walletkit_core - -// Common helper code. -// -// Ideally this would live in a separate .kt file where it can be unittested etc -// in isolation, and perhaps even published as a re-useable package. -// -// However, it's important that the details of how this helper code works (e.g. the -// way that different builtin types are passed across the FFI) exactly match what's -// expected by the Rust code on the other side of the interface. In practice right -// now that means coming from the exact some version of `uniffi` that was used to -// compile the Rust component. The easiest way to ensure this is to bundle the Kotlin -// helpers directly inline like we're doing here. - -import com.sun.jna.Library -import com.sun.jna.IntegerType -import com.sun.jna.Native -import com.sun.jna.Pointer -import com.sun.jna.Structure -import com.sun.jna.Callback -import com.sun.jna.ptr.* -import java.nio.ByteBuffer -import java.nio.ByteOrder -import java.nio.CharBuffer -import java.nio.charset.CodingErrorAction -import java.util.concurrent.atomic.AtomicLong -import java.util.concurrent.ConcurrentHashMap -import java.util.concurrent.atomic.AtomicBoolean -import kotlin.coroutines.resume -import kotlinx.coroutines.CancellableContinuation -import kotlinx.coroutines.DelicateCoroutinesApi -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.Job -import kotlinx.coroutines.launch -import kotlinx.coroutines.suspendCancellableCoroutine - -// This is a helper for safely working with byte buffers returned from the Rust code. -// A rust-owned buffer is represented by its capacity, its current length, and a -// pointer to the underlying data. - -/** - * @suppress - */ -@Structure.FieldOrder("capacity", "len", "data") -open class RustBuffer : Structure() { - // Note: `capacity` and `len` are actually `ULong` values, but JVM only supports signed values. - // When dealing with these fields, make sure to call `toULong()`. - @JvmField var capacity: Long = 0 - @JvmField var len: Long = 0 - @JvmField var data: Pointer? = null - - class ByValue: RustBuffer(), Structure.ByValue - class ByReference: RustBuffer(), Structure.ByReference - - internal fun setValue(other: RustBuffer) { - capacity = other.capacity - len = other.len - data = other.data - } - - companion object { - internal fun alloc(size: ULong = 0UL) = uniffiRustCall() { status -> - // Note: need to convert the size to a `Long` value to make this work with JVM. - UniffiLib.INSTANCE.ffi_walletkit_core_rustbuffer_alloc(size.toLong(), status) - }.also { - if(it.data == null) { - throw RuntimeException("RustBuffer.alloc() returned null data pointer (size=${size})") - } - } - - internal fun create(capacity: ULong, len: ULong, data: Pointer?): RustBuffer.ByValue { - var buf = RustBuffer.ByValue() - buf.capacity = capacity.toLong() - buf.len = len.toLong() - buf.data = data - return buf - } - - internal fun free(buf: RustBuffer.ByValue) = uniffiRustCall() { status -> - UniffiLib.INSTANCE.ffi_walletkit_core_rustbuffer_free(buf, status) - } - } - - @Suppress("TooGenericExceptionThrown") - fun asByteBuffer() = - this.data?.getByteBuffer(0, this.len.toLong())?.also { - it.order(ByteOrder.BIG_ENDIAN) - } -} - -/** - * The equivalent of the `*mut RustBuffer` type. - * Required for callbacks taking in an out pointer. - * - * Size is the sum of all values in the struct. - * - * @suppress - */ -class RustBufferByReference : ByReference(16) { - /** - * Set the pointed-to `RustBuffer` to the given value. - */ - fun setValue(value: RustBuffer.ByValue) { - // NOTE: The offsets are as they are in the C-like struct. - val pointer = getPointer() - pointer.setLong(0, value.capacity) - pointer.setLong(8, value.len) - pointer.setPointer(16, value.data) - } - - /** - * Get a `RustBuffer.ByValue` from this reference. - */ - fun getValue(): RustBuffer.ByValue { - val pointer = getPointer() - val value = RustBuffer.ByValue() - value.writeField("capacity", pointer.getLong(0)) - value.writeField("len", pointer.getLong(8)) - value.writeField("data", pointer.getLong(16)) - - return value - } -} - -// This is a helper for safely passing byte references into the rust code. -// It's not actually used at the moment, because there aren't many things that you -// can take a direct pointer to in the JVM, and if we're going to copy something -// then we might as well copy it into a `RustBuffer`. But it's here for API -// completeness. - -@Structure.FieldOrder("len", "data") -internal open class ForeignBytes : Structure() { - @JvmField var len: Int = 0 - @JvmField var data: Pointer? = null - - class ByValue : ForeignBytes(), Structure.ByValue -} -/** - * The FfiConverter interface handles converter types to and from the FFI - * - * All implementing objects should be public to support external types. When a - * type is external we need to import it's FfiConverter. - * - * @suppress - */ -public interface FfiConverter { - // Convert an FFI type to a Kotlin type - fun lift(value: FfiType): KotlinType - - // Convert an Kotlin type to an FFI type - fun lower(value: KotlinType): FfiType - - // Read a Kotlin type from a `ByteBuffer` - fun read(buf: ByteBuffer): KotlinType - - // Calculate bytes to allocate when creating a `RustBuffer` - // - // This must return at least as many bytes as the write() function will - // write. It can return more bytes than needed, for example when writing - // Strings we can't know the exact bytes needed until we the UTF-8 - // encoding, so we pessimistically allocate the largest size possible (3 - // bytes per codepoint). Allocating extra bytes is not really a big deal - // because the `RustBuffer` is short-lived. - fun allocationSize(value: KotlinType): ULong - - // Write a Kotlin type to a `ByteBuffer` - fun write(value: KotlinType, buf: ByteBuffer) - - // Lower a value into a `RustBuffer` - // - // This method lowers a value into a `RustBuffer` rather than the normal - // FfiType. It's used by the callback interface code. Callback interface - // returns are always serialized into a `RustBuffer` regardless of their - // normal FFI type. - fun lowerIntoRustBuffer(value: KotlinType): RustBuffer.ByValue { - val rbuf = RustBuffer.alloc(allocationSize(value)) - try { - val bbuf = rbuf.data!!.getByteBuffer(0, rbuf.capacity).also { - it.order(ByteOrder.BIG_ENDIAN) - } - write(value, bbuf) - rbuf.writeField("len", bbuf.position().toLong()) - return rbuf - } catch (e: Throwable) { - RustBuffer.free(rbuf) - throw e - } - } - - // Lift a value from a `RustBuffer`. - // - // This here mostly because of the symmetry with `lowerIntoRustBuffer()`. - // It's currently only used by the `FfiConverterRustBuffer` class below. - fun liftFromRustBuffer(rbuf: RustBuffer.ByValue): KotlinType { - val byteBuf = rbuf.asByteBuffer()!! - try { - val item = read(byteBuf) - if (byteBuf.hasRemaining()) { - throw RuntimeException("junk remaining in buffer after lifting, something is very wrong!!") - } - return item - } finally { - RustBuffer.free(rbuf) - } - } -} - -/** - * FfiConverter that uses `RustBuffer` as the FfiType - * - * @suppress - */ -public interface FfiConverterRustBuffer: FfiConverter { - override fun lift(value: RustBuffer.ByValue) = liftFromRustBuffer(value) - override fun lower(value: KotlinType) = lowerIntoRustBuffer(value) -} -// A handful of classes and functions to support the generated data structures. -// This would be a good candidate for isolating in its own ffi-support lib. - -internal const val UNIFFI_CALL_SUCCESS = 0.toByte() -internal const val UNIFFI_CALL_ERROR = 1.toByte() -internal const val UNIFFI_CALL_UNEXPECTED_ERROR = 2.toByte() - -@Structure.FieldOrder("code", "error_buf") -internal open class UniffiRustCallStatus : Structure() { - @JvmField var code: Byte = 0 - @JvmField var error_buf: RustBuffer.ByValue = RustBuffer.ByValue() - - class ByValue: UniffiRustCallStatus(), Structure.ByValue - - fun isSuccess(): Boolean { - return code == UNIFFI_CALL_SUCCESS - } - - fun isError(): Boolean { - return code == UNIFFI_CALL_ERROR - } - - fun isPanic(): Boolean { - return code == UNIFFI_CALL_UNEXPECTED_ERROR - } - - companion object { - fun create(code: Byte, errorBuf: RustBuffer.ByValue): UniffiRustCallStatus.ByValue { - val callStatus = UniffiRustCallStatus.ByValue() - callStatus.code = code - callStatus.error_buf = errorBuf - return callStatus - } - } -} - -class InternalException(message: String) : kotlin.Exception(message) - -/** - * Each top-level error class has a companion object that can lift the error from the call status's rust buffer - * - * @suppress - */ -interface UniffiRustCallStatusErrorHandler { - fun lift(error_buf: RustBuffer.ByValue): E; -} - -// Helpers for calling Rust -// In practice we usually need to be synchronized to call this safely, so it doesn't -// synchronize itself - -// Call a rust function that returns a Result<>. Pass in the Error class companion that corresponds to the Err -private inline fun uniffiRustCallWithError(errorHandler: UniffiRustCallStatusErrorHandler, callback: (UniffiRustCallStatus) -> U): U { - var status = UniffiRustCallStatus() - val return_value = callback(status) - uniffiCheckCallStatus(errorHandler, status) - return return_value -} - -// Check UniffiRustCallStatus and throw an error if the call wasn't successful -private fun uniffiCheckCallStatus(errorHandler: UniffiRustCallStatusErrorHandler, status: UniffiRustCallStatus) { - if (status.isSuccess()) { - return - } else if (status.isError()) { - throw errorHandler.lift(status.error_buf) - } else if (status.isPanic()) { - // when the rust code sees a panic, it tries to construct a rustbuffer - // with the message. but if that code panics, then it just sends back - // an empty buffer. - if (status.error_buf.len > 0) { - throw InternalException(FfiConverterString.lift(status.error_buf)) - } else { - throw InternalException("Rust panic") - } - } else { - throw InternalException("Unknown rust call status: $status.code") - } -} - -/** - * UniffiRustCallStatusErrorHandler implementation for times when we don't expect a CALL_ERROR - * - * @suppress - */ -object UniffiNullRustCallStatusErrorHandler: UniffiRustCallStatusErrorHandler { - override fun lift(error_buf: RustBuffer.ByValue): InternalException { - RustBuffer.free(error_buf) - return InternalException("Unexpected CALL_ERROR") - } -} - -// Call a rust function that returns a plain value -private inline fun uniffiRustCall(callback: (UniffiRustCallStatus) -> U): U { - return uniffiRustCallWithError(UniffiNullRustCallStatusErrorHandler, callback) -} - -internal inline fun uniffiTraitInterfaceCall( - callStatus: UniffiRustCallStatus, - makeCall: () -> T, - writeReturn: (T) -> Unit, -) { - try { - writeReturn(makeCall()) - } catch(e: kotlin.Exception) { - callStatus.code = UNIFFI_CALL_UNEXPECTED_ERROR - callStatus.error_buf = FfiConverterString.lower(e.toString()) - } -} - -internal inline fun uniffiTraitInterfaceCallWithError( - callStatus: UniffiRustCallStatus, - makeCall: () -> T, - writeReturn: (T) -> Unit, - lowerError: (E) -> RustBuffer.ByValue -) { - try { - writeReturn(makeCall()) - } catch(e: kotlin.Exception) { - if (e is E) { - callStatus.code = UNIFFI_CALL_ERROR - callStatus.error_buf = lowerError(e) - } else { - callStatus.code = UNIFFI_CALL_UNEXPECTED_ERROR - callStatus.error_buf = FfiConverterString.lower(e.toString()) - } - } -} -// Map handles to objects -// -// This is used pass an opaque 64-bit handle representing a foreign object to the Rust code. -internal class UniffiHandleMap { - private val map = ConcurrentHashMap() - private val counter = java.util.concurrent.atomic.AtomicLong(0) - - val size: Int - get() = map.size - - // Insert a new object into the handle map and get a handle for it - fun insert(obj: T): Long { - val handle = counter.getAndAdd(1) - map.put(handle, obj) - return handle - } - - // Get an object from the handle map - fun get(handle: Long): T { - return map.get(handle) ?: throw InternalException("UniffiHandleMap.get: Invalid handle") - } - - // Remove an entry from the handlemap and get the Kotlin object back - fun remove(handle: Long): T { - return map.remove(handle) ?: throw InternalException("UniffiHandleMap: Invalid handle") - } -} - -// Contains loading, initialization code, -// and the FFI Function declarations in a com.sun.jna.Library. -@Synchronized -private fun findLibraryName(componentName: String): String { - val libOverride = System.getProperty("uniffi.component.$componentName.libraryOverride") - if (libOverride != null) { - return libOverride - } - return "walletkit" -} - -private inline fun loadIndirect( - componentName: String -): Lib { - return Native.load(findLibraryName(componentName), Lib::class.java) -} - -// Define FFI callback types -internal interface UniffiRustFutureContinuationCallback : com.sun.jna.Callback { - fun callback(`data`: Long,`pollResult`: Byte,) -} -internal interface UniffiForeignFutureFree : com.sun.jna.Callback { - fun callback(`handle`: Long,) -} -internal interface UniffiCallbackInterfaceFree : com.sun.jna.Callback { - fun callback(`handle`: Long,) -} -@Structure.FieldOrder("handle", "free") -internal open class UniffiForeignFuture( - @JvmField internal var `handle`: Long = 0.toLong(), - @JvmField internal var `free`: UniffiForeignFutureFree? = null, -) : Structure() { - class UniffiByValue( - `handle`: Long = 0.toLong(), - `free`: UniffiForeignFutureFree? = null, - ): UniffiForeignFuture(`handle`,`free`,), Structure.ByValue - - internal fun uniffiSetValue(other: UniffiForeignFuture) { - `handle` = other.`handle` - `free` = other.`free` - } - -} -@Structure.FieldOrder("returnValue", "callStatus") -internal open class UniffiForeignFutureStructU8( - @JvmField internal var `returnValue`: Byte = 0.toByte(), - @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), -) : Structure() { - class UniffiByValue( - `returnValue`: Byte = 0.toByte(), - `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), - ): UniffiForeignFutureStructU8(`returnValue`,`callStatus`,), Structure.ByValue - - internal fun uniffiSetValue(other: UniffiForeignFutureStructU8) { - `returnValue` = other.`returnValue` - `callStatus` = other.`callStatus` - } - -} -internal interface UniffiForeignFutureCompleteU8 : com.sun.jna.Callback { - fun callback(`callbackData`: Long,`result`: UniffiForeignFutureStructU8.UniffiByValue,) -} -@Structure.FieldOrder("returnValue", "callStatus") -internal open class UniffiForeignFutureStructI8( - @JvmField internal var `returnValue`: Byte = 0.toByte(), - @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), -) : Structure() { - class UniffiByValue( - `returnValue`: Byte = 0.toByte(), - `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), - ): UniffiForeignFutureStructI8(`returnValue`,`callStatus`,), Structure.ByValue - - internal fun uniffiSetValue(other: UniffiForeignFutureStructI8) { - `returnValue` = other.`returnValue` - `callStatus` = other.`callStatus` - } - -} -internal interface UniffiForeignFutureCompleteI8 : com.sun.jna.Callback { - fun callback(`callbackData`: Long,`result`: UniffiForeignFutureStructI8.UniffiByValue,) -} -@Structure.FieldOrder("returnValue", "callStatus") -internal open class UniffiForeignFutureStructU16( - @JvmField internal var `returnValue`: Short = 0.toShort(), - @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), -) : Structure() { - class UniffiByValue( - `returnValue`: Short = 0.toShort(), - `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), - ): UniffiForeignFutureStructU16(`returnValue`,`callStatus`,), Structure.ByValue - - internal fun uniffiSetValue(other: UniffiForeignFutureStructU16) { - `returnValue` = other.`returnValue` - `callStatus` = other.`callStatus` - } - -} -internal interface UniffiForeignFutureCompleteU16 : com.sun.jna.Callback { - fun callback(`callbackData`: Long,`result`: UniffiForeignFutureStructU16.UniffiByValue,) -} -@Structure.FieldOrder("returnValue", "callStatus") -internal open class UniffiForeignFutureStructI16( - @JvmField internal var `returnValue`: Short = 0.toShort(), - @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), -) : Structure() { - class UniffiByValue( - `returnValue`: Short = 0.toShort(), - `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), - ): UniffiForeignFutureStructI16(`returnValue`,`callStatus`,), Structure.ByValue - - internal fun uniffiSetValue(other: UniffiForeignFutureStructI16) { - `returnValue` = other.`returnValue` - `callStatus` = other.`callStatus` - } - -} -internal interface UniffiForeignFutureCompleteI16 : com.sun.jna.Callback { - fun callback(`callbackData`: Long,`result`: UniffiForeignFutureStructI16.UniffiByValue,) -} -@Structure.FieldOrder("returnValue", "callStatus") -internal open class UniffiForeignFutureStructU32( - @JvmField internal var `returnValue`: Int = 0, - @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), -) : Structure() { - class UniffiByValue( - `returnValue`: Int = 0, - `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), - ): UniffiForeignFutureStructU32(`returnValue`,`callStatus`,), Structure.ByValue - - internal fun uniffiSetValue(other: UniffiForeignFutureStructU32) { - `returnValue` = other.`returnValue` - `callStatus` = other.`callStatus` - } - -} -internal interface UniffiForeignFutureCompleteU32 : com.sun.jna.Callback { - fun callback(`callbackData`: Long,`result`: UniffiForeignFutureStructU32.UniffiByValue,) -} -@Structure.FieldOrder("returnValue", "callStatus") -internal open class UniffiForeignFutureStructI32( - @JvmField internal var `returnValue`: Int = 0, - @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), -) : Structure() { - class UniffiByValue( - `returnValue`: Int = 0, - `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), - ): UniffiForeignFutureStructI32(`returnValue`,`callStatus`,), Structure.ByValue - - internal fun uniffiSetValue(other: UniffiForeignFutureStructI32) { - `returnValue` = other.`returnValue` - `callStatus` = other.`callStatus` - } - -} -internal interface UniffiForeignFutureCompleteI32 : com.sun.jna.Callback { - fun callback(`callbackData`: Long,`result`: UniffiForeignFutureStructI32.UniffiByValue,) -} -@Structure.FieldOrder("returnValue", "callStatus") -internal open class UniffiForeignFutureStructU64( - @JvmField internal var `returnValue`: Long = 0.toLong(), - @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), -) : Structure() { - class UniffiByValue( - `returnValue`: Long = 0.toLong(), - `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), - ): UniffiForeignFutureStructU64(`returnValue`,`callStatus`,), Structure.ByValue - - internal fun uniffiSetValue(other: UniffiForeignFutureStructU64) { - `returnValue` = other.`returnValue` - `callStatus` = other.`callStatus` - } - -} -internal interface UniffiForeignFutureCompleteU64 : com.sun.jna.Callback { - fun callback(`callbackData`: Long,`result`: UniffiForeignFutureStructU64.UniffiByValue,) -} -@Structure.FieldOrder("returnValue", "callStatus") -internal open class UniffiForeignFutureStructI64( - @JvmField internal var `returnValue`: Long = 0.toLong(), - @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), -) : Structure() { - class UniffiByValue( - `returnValue`: Long = 0.toLong(), - `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), - ): UniffiForeignFutureStructI64(`returnValue`,`callStatus`,), Structure.ByValue - - internal fun uniffiSetValue(other: UniffiForeignFutureStructI64) { - `returnValue` = other.`returnValue` - `callStatus` = other.`callStatus` - } - -} -internal interface UniffiForeignFutureCompleteI64 : com.sun.jna.Callback { - fun callback(`callbackData`: Long,`result`: UniffiForeignFutureStructI64.UniffiByValue,) -} -@Structure.FieldOrder("returnValue", "callStatus") -internal open class UniffiForeignFutureStructF32( - @JvmField internal var `returnValue`: Float = 0.0f, - @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), -) : Structure() { - class UniffiByValue( - `returnValue`: Float = 0.0f, - `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), - ): UniffiForeignFutureStructF32(`returnValue`,`callStatus`,), Structure.ByValue - - internal fun uniffiSetValue(other: UniffiForeignFutureStructF32) { - `returnValue` = other.`returnValue` - `callStatus` = other.`callStatus` - } - -} -internal interface UniffiForeignFutureCompleteF32 : com.sun.jna.Callback { - fun callback(`callbackData`: Long,`result`: UniffiForeignFutureStructF32.UniffiByValue,) -} -@Structure.FieldOrder("returnValue", "callStatus") -internal open class UniffiForeignFutureStructF64( - @JvmField internal var `returnValue`: Double = 0.0, - @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), -) : Structure() { - class UniffiByValue( - `returnValue`: Double = 0.0, - `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), - ): UniffiForeignFutureStructF64(`returnValue`,`callStatus`,), Structure.ByValue - - internal fun uniffiSetValue(other: UniffiForeignFutureStructF64) { - `returnValue` = other.`returnValue` - `callStatus` = other.`callStatus` - } - -} -internal interface UniffiForeignFutureCompleteF64 : com.sun.jna.Callback { - fun callback(`callbackData`: Long,`result`: UniffiForeignFutureStructF64.UniffiByValue,) -} -@Structure.FieldOrder("returnValue", "callStatus") -internal open class UniffiForeignFutureStructPointer( - @JvmField internal var `returnValue`: Pointer = Pointer.NULL, - @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), -) : Structure() { - class UniffiByValue( - `returnValue`: Pointer = Pointer.NULL, - `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), - ): UniffiForeignFutureStructPointer(`returnValue`,`callStatus`,), Structure.ByValue - - internal fun uniffiSetValue(other: UniffiForeignFutureStructPointer) { - `returnValue` = other.`returnValue` - `callStatus` = other.`callStatus` - } - -} -internal interface UniffiForeignFutureCompletePointer : com.sun.jna.Callback { - fun callback(`callbackData`: Long,`result`: UniffiForeignFutureStructPointer.UniffiByValue,) -} -@Structure.FieldOrder("returnValue", "callStatus") -internal open class UniffiForeignFutureStructRustBuffer( - @JvmField internal var `returnValue`: RustBuffer.ByValue = RustBuffer.ByValue(), - @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), -) : Structure() { - class UniffiByValue( - `returnValue`: RustBuffer.ByValue = RustBuffer.ByValue(), - `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), - ): UniffiForeignFutureStructRustBuffer(`returnValue`,`callStatus`,), Structure.ByValue - - internal fun uniffiSetValue(other: UniffiForeignFutureStructRustBuffer) { - `returnValue` = other.`returnValue` - `callStatus` = other.`callStatus` - } - -} -internal interface UniffiForeignFutureCompleteRustBuffer : com.sun.jna.Callback { - fun callback(`callbackData`: Long,`result`: UniffiForeignFutureStructRustBuffer.UniffiByValue,) -} -@Structure.FieldOrder("callStatus") -internal open class UniffiForeignFutureStructVoid( - @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), -) : Structure() { - class UniffiByValue( - `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), - ): UniffiForeignFutureStructVoid(`callStatus`,), Structure.ByValue - - internal fun uniffiSetValue(other: UniffiForeignFutureStructVoid) { - `callStatus` = other.`callStatus` - } - -} -internal interface UniffiForeignFutureCompleteVoid : com.sun.jna.Callback { - fun callback(`callbackData`: Long,`result`: UniffiForeignFutureStructVoid.UniffiByValue,) -} -internal interface UniffiCallbackInterfaceAtomicBlobStoreMethod0 : com.sun.jna.Callback { - fun callback(`uniffiHandle`: Long,`path`: RustBuffer.ByValue,`uniffiOutReturn`: RustBuffer,uniffiCallStatus: UniffiRustCallStatus,) -} -internal interface UniffiCallbackInterfaceAtomicBlobStoreMethod1 : com.sun.jna.Callback { - fun callback(`uniffiHandle`: Long,`path`: RustBuffer.ByValue,`bytes`: RustBuffer.ByValue,`uniffiOutReturn`: Pointer,uniffiCallStatus: UniffiRustCallStatus,) -} -internal interface UniffiCallbackInterfaceAtomicBlobStoreMethod2 : com.sun.jna.Callback { - fun callback(`uniffiHandle`: Long,`path`: RustBuffer.ByValue,`uniffiOutReturn`: Pointer,uniffiCallStatus: UniffiRustCallStatus,) -} -internal interface UniffiCallbackInterfaceDeviceKeystoreMethod0 : com.sun.jna.Callback { - fun callback(`uniffiHandle`: Long,`associatedData`: RustBuffer.ByValue,`plaintext`: RustBuffer.ByValue,`uniffiOutReturn`: RustBuffer,uniffiCallStatus: UniffiRustCallStatus,) -} -internal interface UniffiCallbackInterfaceDeviceKeystoreMethod1 : com.sun.jna.Callback { - fun callback(`uniffiHandle`: Long,`associatedData`: RustBuffer.ByValue,`ciphertext`: RustBuffer.ByValue,`uniffiOutReturn`: RustBuffer,uniffiCallStatus: UniffiRustCallStatus,) -} -internal interface UniffiCallbackInterfaceLoggerMethod0 : com.sun.jna.Callback { - fun callback(`uniffiHandle`: Long,`level`: RustBuffer.ByValue,`message`: RustBuffer.ByValue,`uniffiOutReturn`: Pointer,uniffiCallStatus: UniffiRustCallStatus,) -} -internal interface UniffiCallbackInterfaceStorageProviderMethod0 : com.sun.jna.Callback { - fun callback(`uniffiHandle`: Long,`uniffiOutReturn`: PointerByReference,uniffiCallStatus: UniffiRustCallStatus,) -} -internal interface UniffiCallbackInterfaceStorageProviderMethod1 : com.sun.jna.Callback { - fun callback(`uniffiHandle`: Long,`uniffiOutReturn`: PointerByReference,uniffiCallStatus: UniffiRustCallStatus,) -} -internal interface UniffiCallbackInterfaceStorageProviderMethod2 : com.sun.jna.Callback { - fun callback(`uniffiHandle`: Long,`uniffiOutReturn`: PointerByReference,uniffiCallStatus: UniffiRustCallStatus,) -} -@Structure.FieldOrder("read", "writeAtomic", "delete", "uniffiFree") -internal open class UniffiVTableCallbackInterfaceAtomicBlobStore( - @JvmField internal var `read`: UniffiCallbackInterfaceAtomicBlobStoreMethod0? = null, - @JvmField internal var `writeAtomic`: UniffiCallbackInterfaceAtomicBlobStoreMethod1? = null, - @JvmField internal var `delete`: UniffiCallbackInterfaceAtomicBlobStoreMethod2? = null, - @JvmField internal var `uniffiFree`: UniffiCallbackInterfaceFree? = null, -) : Structure() { - class UniffiByValue( - `read`: UniffiCallbackInterfaceAtomicBlobStoreMethod0? = null, - `writeAtomic`: UniffiCallbackInterfaceAtomicBlobStoreMethod1? = null, - `delete`: UniffiCallbackInterfaceAtomicBlobStoreMethod2? = null, - `uniffiFree`: UniffiCallbackInterfaceFree? = null, - ): UniffiVTableCallbackInterfaceAtomicBlobStore(`read`,`writeAtomic`,`delete`,`uniffiFree`,), Structure.ByValue - - internal fun uniffiSetValue(other: UniffiVTableCallbackInterfaceAtomicBlobStore) { - `read` = other.`read` - `writeAtomic` = other.`writeAtomic` - `delete` = other.`delete` - `uniffiFree` = other.`uniffiFree` - } - -} -@Structure.FieldOrder("seal", "openSealed", "uniffiFree") -internal open class UniffiVTableCallbackInterfaceDeviceKeystore( - @JvmField internal var `seal`: UniffiCallbackInterfaceDeviceKeystoreMethod0? = null, - @JvmField internal var `openSealed`: UniffiCallbackInterfaceDeviceKeystoreMethod1? = null, - @JvmField internal var `uniffiFree`: UniffiCallbackInterfaceFree? = null, -) : Structure() { - class UniffiByValue( - `seal`: UniffiCallbackInterfaceDeviceKeystoreMethod0? = null, - `openSealed`: UniffiCallbackInterfaceDeviceKeystoreMethod1? = null, - `uniffiFree`: UniffiCallbackInterfaceFree? = null, - ): UniffiVTableCallbackInterfaceDeviceKeystore(`seal`,`openSealed`,`uniffiFree`,), Structure.ByValue - - internal fun uniffiSetValue(other: UniffiVTableCallbackInterfaceDeviceKeystore) { - `seal` = other.`seal` - `openSealed` = other.`openSealed` - `uniffiFree` = other.`uniffiFree` - } - -} -@Structure.FieldOrder("log", "uniffiFree") -internal open class UniffiVTableCallbackInterfaceLogger( - @JvmField internal var `log`: UniffiCallbackInterfaceLoggerMethod0? = null, - @JvmField internal var `uniffiFree`: UniffiCallbackInterfaceFree? = null, -) : Structure() { - class UniffiByValue( - `log`: UniffiCallbackInterfaceLoggerMethod0? = null, - `uniffiFree`: UniffiCallbackInterfaceFree? = null, - ): UniffiVTableCallbackInterfaceLogger(`log`,`uniffiFree`,), Structure.ByValue - - internal fun uniffiSetValue(other: UniffiVTableCallbackInterfaceLogger) { - `log` = other.`log` - `uniffiFree` = other.`uniffiFree` - } - -} -@Structure.FieldOrder("keystore", "blobStore", "paths", "uniffiFree") -internal open class UniffiVTableCallbackInterfaceStorageProvider( - @JvmField internal var `keystore`: UniffiCallbackInterfaceStorageProviderMethod0? = null, - @JvmField internal var `blobStore`: UniffiCallbackInterfaceStorageProviderMethod1? = null, - @JvmField internal var `paths`: UniffiCallbackInterfaceStorageProviderMethod2? = null, - @JvmField internal var `uniffiFree`: UniffiCallbackInterfaceFree? = null, -) : Structure() { - class UniffiByValue( - `keystore`: UniffiCallbackInterfaceStorageProviderMethod0? = null, - `blobStore`: UniffiCallbackInterfaceStorageProviderMethod1? = null, - `paths`: UniffiCallbackInterfaceStorageProviderMethod2? = null, - `uniffiFree`: UniffiCallbackInterfaceFree? = null, - ): UniffiVTableCallbackInterfaceStorageProvider(`keystore`,`blobStore`,`paths`,`uniffiFree`,), Structure.ByValue - - internal fun uniffiSetValue(other: UniffiVTableCallbackInterfaceStorageProvider) { - `keystore` = other.`keystore` - `blobStore` = other.`blobStore` - `paths` = other.`paths` - `uniffiFree` = other.`uniffiFree` - } - -} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// For large crates we prevent `MethodTooLargeException` (see #2340) -// N.B. the name of the extension is very misleading, since it is -// rather `InterfaceTooLargeException`, caused by too many methods -// in the interface for large crates. -// -// By splitting the otherwise huge interface into two parts -// * UniffiLib -// * IntegrityCheckingUniffiLib (this) -// we allow for ~2x as many methods in the UniffiLib interface. -// -// The `ffi_uniffi_contract_version` method and all checksum methods are put -// into `IntegrityCheckingUniffiLib` and these methods are called only once, -// when the library is loaded. -internal interface IntegrityCheckingUniffiLib : Library { - // Integrity check functions only - fun uniffi_walletkit_core_checksum_func_set_logger( -): Short -fun uniffi_walletkit_core_checksum_method_atomicblobstore_read( -): Short -fun uniffi_walletkit_core_checksum_method_atomicblobstore_write_atomic( -): Short -fun uniffi_walletkit_core_checksum_method_atomicblobstore_delete( -): Short -fun uniffi_walletkit_core_checksum_method_credentialstore_begin_proof_disclosure( -): Short -fun uniffi_walletkit_core_checksum_method_credentialstore_init( -): Short -fun uniffi_walletkit_core_checksum_method_credentialstore_list_credentials( -): Short -fun uniffi_walletkit_core_checksum_method_credentialstore_merkle_cache_get( -): Short -fun uniffi_walletkit_core_checksum_method_credentialstore_merkle_cache_put( -): Short -fun uniffi_walletkit_core_checksum_method_credentialstore_storage_paths( -): Short -fun uniffi_walletkit_core_checksum_method_credentialstore_store_credential( -): Short -fun uniffi_walletkit_core_checksum_method_devicekeystore_seal( -): Short -fun uniffi_walletkit_core_checksum_method_devicekeystore_open_sealed( -): Short -fun uniffi_walletkit_core_checksum_method_logger_log( -): Short -fun uniffi_walletkit_core_checksum_method_proofcontext_get_credential_type( -): Short -fun uniffi_walletkit_core_checksum_method_proofcontext_get_external_nullifier( -): Short -fun uniffi_walletkit_core_checksum_method_proofcontext_get_signal_hash( -): Short -fun uniffi_walletkit_core_checksum_method_proofoutput_get_credential_type( -): Short -fun uniffi_walletkit_core_checksum_method_proofoutput_get_merkle_root( -): Short -fun uniffi_walletkit_core_checksum_method_proofoutput_get_nullifier_hash( -): Short -fun uniffi_walletkit_core_checksum_method_proofoutput_get_proof_as_string( -): Short -fun uniffi_walletkit_core_checksum_method_proofoutput_to_json( -): Short -fun uniffi_walletkit_core_checksum_method_storagepaths_cache_db_path_string( -): Short -fun uniffi_walletkit_core_checksum_method_storagepaths_lock_path_string( -): Short -fun uniffi_walletkit_core_checksum_method_storagepaths_root_path_string( -): Short -fun uniffi_walletkit_core_checksum_method_storagepaths_vault_db_path_string( -): Short -fun uniffi_walletkit_core_checksum_method_storagepaths_worldid_dir_path_string( -): Short -fun uniffi_walletkit_core_checksum_method_storageprovider_keystore( -): Short -fun uniffi_walletkit_core_checksum_method_storageprovider_blob_store( -): Short -fun uniffi_walletkit_core_checksum_method_storageprovider_paths( -): Short -fun uniffi_walletkit_core_checksum_method_u256wrapper_into_limbs( -): Short -fun uniffi_walletkit_core_checksum_method_u256wrapper_to_decimal_string( -): Short -fun uniffi_walletkit_core_checksum_method_u256wrapper_to_hex_string( -): Short -fun uniffi_walletkit_core_checksum_method_worldid_generate_nullifier_hash( -): Short -fun uniffi_walletkit_core_checksum_method_worldid_generate_proof( -): Short -fun uniffi_walletkit_core_checksum_method_worldid_get_identity_commitment( -): Short -fun uniffi_walletkit_core_checksum_method_worldid_is_equal_to( -): Short -fun uniffi_walletkit_core_checksum_constructor_credentialstore_from_provider_arc( -): Short -fun uniffi_walletkit_core_checksum_constructor_credentialstore_new_with_components( -): Short -fun uniffi_walletkit_core_checksum_constructor_merkletreeproof_from_identity_commitment( -): Short -fun uniffi_walletkit_core_checksum_constructor_merkletreeproof_from_json_proof( -): Short -fun uniffi_walletkit_core_checksum_constructor_proofcontext_new( -): Short -fun uniffi_walletkit_core_checksum_constructor_proofcontext_new_from_bytes( -): Short -fun uniffi_walletkit_core_checksum_constructor_proofcontext_new_from_signal_hash( -): Short -fun uniffi_walletkit_core_checksum_constructor_storagepaths_from_root( -): Short -fun uniffi_walletkit_core_checksum_constructor_u256wrapper_from_limbs( -): Short -fun uniffi_walletkit_core_checksum_constructor_u256wrapper_from_u32( -): Short -fun uniffi_walletkit_core_checksum_constructor_u256wrapper_from_u64( -): Short -fun uniffi_walletkit_core_checksum_constructor_u256wrapper_try_from_hex_string( -): Short -fun uniffi_walletkit_core_checksum_constructor_worldid_new( -): Short -fun ffi_walletkit_core_uniffi_contract_version( -): Int - -} - -// A JNA Library to expose the extern-C FFI definitions. -// This is an implementation detail which will be called internally by the public API. -internal interface UniffiLib : Library { - companion object { - internal val INSTANCE: UniffiLib by lazy { - val componentName = "walletkit_core" - // For large crates we prevent `MethodTooLargeException` (see #2340) - // N.B. the name of the extension is very misleading, since it is - // rather `InterfaceTooLargeException`, caused by too many methods - // in the interface for large crates. - // - // By splitting the otherwise huge interface into two parts - // * UniffiLib (this) - // * IntegrityCheckingUniffiLib - // And all checksum methods are put into `IntegrityCheckingUniffiLib` - // we allow for ~2x as many methods in the UniffiLib interface. - // - // Thus we first load the library with `loadIndirect` as `IntegrityCheckingUniffiLib` - // so that we can (optionally!) call `uniffiCheckApiChecksums`... - loadIndirect(componentName) - .also { lib: IntegrityCheckingUniffiLib -> - uniffiCheckContractApiVersion(lib) - uniffiCheckApiChecksums(lib) - } - // ... and then we load the library as `UniffiLib` - // N.B. we cannot use `loadIndirect` once and then try to cast it to `UniffiLib` - // => results in `java.lang.ClassCastException: com.sun.proxy.$Proxy cannot be cast to ...` - // error. So we must call `loadIndirect` twice. For crates large enough - // to trigger this issue, the performance impact is negligible, running on - // a macOS M1 machine the `loadIndirect` call takes ~50ms. - val lib = loadIndirect(componentName) - // No need to check the contract version and checksums, since - // we already did that with `IntegrityCheckingUniffiLib` above. - uniffiCallbackInterfaceAtomicBlobStore.register(lib) - uniffiCallbackInterfaceDeviceKeystore.register(lib) - uniffiCallbackInterfaceLogger.register(lib) - uniffiCallbackInterfaceStorageProvider.register(lib) - // Loading of library with integrity check done. - lib - } - - // The Cleaner for the whole library - internal val CLEANER: UniffiCleaner by lazy { - UniffiCleaner.create() - } - } - - // FFI functions - fun uniffi_walletkit_core_fn_clone_atomicblobstore(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, -): Pointer -fun uniffi_walletkit_core_fn_free_atomicblobstore(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, -): Unit -fun uniffi_walletkit_core_fn_init_callback_vtable_atomicblobstore(`vtable`: UniffiVTableCallbackInterfaceAtomicBlobStore, -): Unit -fun uniffi_walletkit_core_fn_method_atomicblobstore_read(`ptr`: Pointer,`path`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, -): RustBuffer.ByValue -fun uniffi_walletkit_core_fn_method_atomicblobstore_write_atomic(`ptr`: Pointer,`path`: RustBuffer.ByValue,`bytes`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, -): Unit -fun uniffi_walletkit_core_fn_method_atomicblobstore_delete(`ptr`: Pointer,`path`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, -): Unit -fun uniffi_walletkit_core_fn_clone_credentialstore(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, -): Pointer -fun uniffi_walletkit_core_fn_free_credentialstore(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, -): Unit -fun uniffi_walletkit_core_fn_constructor_credentialstore_from_provider_arc(`provider`: Pointer,uniffi_out_err: UniffiRustCallStatus, -): Pointer -fun uniffi_walletkit_core_fn_constructor_credentialstore_new_with_components(`paths`: Pointer,`keystore`: Pointer,`blobStore`: Pointer,uniffi_out_err: UniffiRustCallStatus, -): Pointer -fun uniffi_walletkit_core_fn_method_credentialstore_begin_proof_disclosure(`ptr`: Pointer,`requestId`: RustBuffer.ByValue,`nullifier`: RustBuffer.ByValue,`proofBytes`: RustBuffer.ByValue,`now`: Long,`ttlSeconds`: Long,uniffi_out_err: UniffiRustCallStatus, -): RustBuffer.ByValue -fun uniffi_walletkit_core_fn_method_credentialstore_init(`ptr`: Pointer,`leafIndex`: Long,`now`: Long,uniffi_out_err: UniffiRustCallStatus, -): Unit -fun uniffi_walletkit_core_fn_method_credentialstore_list_credentials(`ptr`: Pointer,`issuerSchemaId`: RustBuffer.ByValue,`now`: Long,uniffi_out_err: UniffiRustCallStatus, -): RustBuffer.ByValue -fun uniffi_walletkit_core_fn_method_credentialstore_merkle_cache_get(`ptr`: Pointer,`registryKind`: Byte,`root`: RustBuffer.ByValue,`now`: Long,uniffi_out_err: UniffiRustCallStatus, -): RustBuffer.ByValue -fun uniffi_walletkit_core_fn_method_credentialstore_merkle_cache_put(`ptr`: Pointer,`registryKind`: Byte,`root`: RustBuffer.ByValue,`proofBytes`: RustBuffer.ByValue,`now`: Long,`ttlSeconds`: Long,uniffi_out_err: UniffiRustCallStatus, -): Unit -fun uniffi_walletkit_core_fn_method_credentialstore_storage_paths(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, -): Pointer -fun uniffi_walletkit_core_fn_method_credentialstore_store_credential(`ptr`: Pointer,`issuerSchemaId`: Long,`status`: RustBuffer.ByValue,`subjectBlindingFactor`: RustBuffer.ByValue,`genesisIssuedAt`: Long,`expiresAt`: RustBuffer.ByValue,`credentialBlob`: RustBuffer.ByValue,`associatedData`: RustBuffer.ByValue,`now`: Long,uniffi_out_err: UniffiRustCallStatus, -): RustBuffer.ByValue -fun uniffi_walletkit_core_fn_clone_devicekeystore(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, -): Pointer -fun uniffi_walletkit_core_fn_free_devicekeystore(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, -): Unit -fun uniffi_walletkit_core_fn_init_callback_vtable_devicekeystore(`vtable`: UniffiVTableCallbackInterfaceDeviceKeystore, -): Unit -fun uniffi_walletkit_core_fn_method_devicekeystore_seal(`ptr`: Pointer,`associatedData`: RustBuffer.ByValue,`plaintext`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, -): RustBuffer.ByValue -fun uniffi_walletkit_core_fn_method_devicekeystore_open_sealed(`ptr`: Pointer,`associatedData`: RustBuffer.ByValue,`ciphertext`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, -): RustBuffer.ByValue -fun uniffi_walletkit_core_fn_clone_logger(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, -): Pointer -fun uniffi_walletkit_core_fn_free_logger(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, -): Unit -fun uniffi_walletkit_core_fn_init_callback_vtable_logger(`vtable`: UniffiVTableCallbackInterfaceLogger, -): Unit -fun uniffi_walletkit_core_fn_method_logger_log(`ptr`: Pointer,`level`: RustBuffer.ByValue,`message`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, -): Unit -fun uniffi_walletkit_core_fn_clone_merkletreeproof(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, -): Pointer -fun uniffi_walletkit_core_fn_free_merkletreeproof(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, -): Unit -fun uniffi_walletkit_core_fn_constructor_merkletreeproof_from_identity_commitment(`identityCommitment`: Pointer,`sequencerHost`: RustBuffer.ByValue,`requireMinedProof`: Byte, -): Long -fun uniffi_walletkit_core_fn_constructor_merkletreeproof_from_json_proof(`jsonProof`: RustBuffer.ByValue,`merkleRoot`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, -): Pointer -fun uniffi_walletkit_core_fn_clone_proofcontext(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, -): Pointer -fun uniffi_walletkit_core_fn_free_proofcontext(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, -): Unit -fun uniffi_walletkit_core_fn_constructor_proofcontext_new(`appId`: RustBuffer.ByValue,`action`: RustBuffer.ByValue,`signal`: RustBuffer.ByValue,`credentialType`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, -): Pointer -fun uniffi_walletkit_core_fn_constructor_proofcontext_new_from_bytes(`appId`: RustBuffer.ByValue,`action`: RustBuffer.ByValue,`signal`: RustBuffer.ByValue,`credentialType`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, -): Pointer -fun uniffi_walletkit_core_fn_constructor_proofcontext_new_from_signal_hash(`appId`: RustBuffer.ByValue,`action`: RustBuffer.ByValue,`credentialType`: RustBuffer.ByValue,`signalHash`: Pointer,uniffi_out_err: UniffiRustCallStatus, -): Pointer -fun uniffi_walletkit_core_fn_method_proofcontext_get_credential_type(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, -): RustBuffer.ByValue -fun uniffi_walletkit_core_fn_method_proofcontext_get_external_nullifier(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, -): Pointer -fun uniffi_walletkit_core_fn_method_proofcontext_get_signal_hash(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, -): Pointer -fun uniffi_walletkit_core_fn_clone_proofoutput(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, -): Pointer -fun uniffi_walletkit_core_fn_free_proofoutput(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, -): Unit -fun uniffi_walletkit_core_fn_method_proofoutput_get_credential_type(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, -): RustBuffer.ByValue -fun uniffi_walletkit_core_fn_method_proofoutput_get_merkle_root(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, -): Pointer -fun uniffi_walletkit_core_fn_method_proofoutput_get_nullifier_hash(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, -): Pointer -fun uniffi_walletkit_core_fn_method_proofoutput_get_proof_as_string(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, -): RustBuffer.ByValue -fun uniffi_walletkit_core_fn_method_proofoutput_to_json(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, -): RustBuffer.ByValue -fun uniffi_walletkit_core_fn_clone_storagepaths(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, -): Pointer -fun uniffi_walletkit_core_fn_free_storagepaths(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, -): Unit -fun uniffi_walletkit_core_fn_constructor_storagepaths_from_root(`root`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, -): Pointer -fun uniffi_walletkit_core_fn_method_storagepaths_cache_db_path_string(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, -): RustBuffer.ByValue -fun uniffi_walletkit_core_fn_method_storagepaths_lock_path_string(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, -): RustBuffer.ByValue -fun uniffi_walletkit_core_fn_method_storagepaths_root_path_string(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, -): RustBuffer.ByValue -fun uniffi_walletkit_core_fn_method_storagepaths_vault_db_path_string(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, -): RustBuffer.ByValue -fun uniffi_walletkit_core_fn_method_storagepaths_worldid_dir_path_string(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, -): RustBuffer.ByValue -fun uniffi_walletkit_core_fn_clone_storageprovider(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, -): Pointer -fun uniffi_walletkit_core_fn_free_storageprovider(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, -): Unit -fun uniffi_walletkit_core_fn_init_callback_vtable_storageprovider(`vtable`: UniffiVTableCallbackInterfaceStorageProvider, -): Unit -fun uniffi_walletkit_core_fn_method_storageprovider_keystore(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, -): Pointer -fun uniffi_walletkit_core_fn_method_storageprovider_blob_store(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, -): Pointer -fun uniffi_walletkit_core_fn_method_storageprovider_paths(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, -): Pointer -fun uniffi_walletkit_core_fn_clone_u256wrapper(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, -): Pointer -fun uniffi_walletkit_core_fn_free_u256wrapper(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, -): Unit -fun uniffi_walletkit_core_fn_constructor_u256wrapper_from_limbs(`limbs`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, -): Pointer -fun uniffi_walletkit_core_fn_constructor_u256wrapper_from_u32(`value`: Int,uniffi_out_err: UniffiRustCallStatus, -): Pointer -fun uniffi_walletkit_core_fn_constructor_u256wrapper_from_u64(`value`: Long,uniffi_out_err: UniffiRustCallStatus, -): Pointer -fun uniffi_walletkit_core_fn_constructor_u256wrapper_try_from_hex_string(`hexString`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, -): Pointer -fun uniffi_walletkit_core_fn_method_u256wrapper_into_limbs(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, -): RustBuffer.ByValue -fun uniffi_walletkit_core_fn_method_u256wrapper_to_decimal_string(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, -): RustBuffer.ByValue -fun uniffi_walletkit_core_fn_method_u256wrapper_to_hex_string(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, -): RustBuffer.ByValue -fun uniffi_walletkit_core_fn_clone_worldid(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, -): Pointer -fun uniffi_walletkit_core_fn_free_worldid(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, -): Unit -fun uniffi_walletkit_core_fn_constructor_worldid_new(`secret`: RustBuffer.ByValue,`environment`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, -): Pointer -fun uniffi_walletkit_core_fn_method_worldid_generate_nullifier_hash(`ptr`: Pointer,`context`: Pointer,uniffi_out_err: UniffiRustCallStatus, -): Pointer -fun uniffi_walletkit_core_fn_method_worldid_generate_proof(`ptr`: Pointer,`context`: Pointer, -): Long -fun uniffi_walletkit_core_fn_method_worldid_get_identity_commitment(`ptr`: Pointer,`credentialType`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, -): Pointer -fun uniffi_walletkit_core_fn_method_worldid_is_equal_to(`ptr`: Pointer,`other`: Pointer,uniffi_out_err: UniffiRustCallStatus, -): Byte -fun uniffi_walletkit_core_fn_func_set_logger(`logger`: Pointer,uniffi_out_err: UniffiRustCallStatus, -): Unit -fun ffi_walletkit_core_rustbuffer_alloc(`size`: Long,uniffi_out_err: UniffiRustCallStatus, -): RustBuffer.ByValue -fun ffi_walletkit_core_rustbuffer_from_bytes(`bytes`: ForeignBytes.ByValue,uniffi_out_err: UniffiRustCallStatus, -): RustBuffer.ByValue -fun ffi_walletkit_core_rustbuffer_free(`buf`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, -): Unit -fun ffi_walletkit_core_rustbuffer_reserve(`buf`: RustBuffer.ByValue,`additional`: Long,uniffi_out_err: UniffiRustCallStatus, -): RustBuffer.ByValue -fun ffi_walletkit_core_rust_future_poll_u8(`handle`: Long,`callback`: UniffiRustFutureContinuationCallback,`callbackData`: Long, -): Unit -fun ffi_walletkit_core_rust_future_cancel_u8(`handle`: Long, -): Unit -fun ffi_walletkit_core_rust_future_free_u8(`handle`: Long, -): Unit -fun ffi_walletkit_core_rust_future_complete_u8(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, -): Byte -fun ffi_walletkit_core_rust_future_poll_i8(`handle`: Long,`callback`: UniffiRustFutureContinuationCallback,`callbackData`: Long, -): Unit -fun ffi_walletkit_core_rust_future_cancel_i8(`handle`: Long, -): Unit -fun ffi_walletkit_core_rust_future_free_i8(`handle`: Long, -): Unit -fun ffi_walletkit_core_rust_future_complete_i8(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, -): Byte -fun ffi_walletkit_core_rust_future_poll_u16(`handle`: Long,`callback`: UniffiRustFutureContinuationCallback,`callbackData`: Long, -): Unit -fun ffi_walletkit_core_rust_future_cancel_u16(`handle`: Long, -): Unit -fun ffi_walletkit_core_rust_future_free_u16(`handle`: Long, -): Unit -fun ffi_walletkit_core_rust_future_complete_u16(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, -): Short -fun ffi_walletkit_core_rust_future_poll_i16(`handle`: Long,`callback`: UniffiRustFutureContinuationCallback,`callbackData`: Long, -): Unit -fun ffi_walletkit_core_rust_future_cancel_i16(`handle`: Long, -): Unit -fun ffi_walletkit_core_rust_future_free_i16(`handle`: Long, -): Unit -fun ffi_walletkit_core_rust_future_complete_i16(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, -): Short -fun ffi_walletkit_core_rust_future_poll_u32(`handle`: Long,`callback`: UniffiRustFutureContinuationCallback,`callbackData`: Long, -): Unit -fun ffi_walletkit_core_rust_future_cancel_u32(`handle`: Long, -): Unit -fun ffi_walletkit_core_rust_future_free_u32(`handle`: Long, -): Unit -fun ffi_walletkit_core_rust_future_complete_u32(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, -): Int -fun ffi_walletkit_core_rust_future_poll_i32(`handle`: Long,`callback`: UniffiRustFutureContinuationCallback,`callbackData`: Long, -): Unit -fun ffi_walletkit_core_rust_future_cancel_i32(`handle`: Long, -): Unit -fun ffi_walletkit_core_rust_future_free_i32(`handle`: Long, -): Unit -fun ffi_walletkit_core_rust_future_complete_i32(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, -): Int -fun ffi_walletkit_core_rust_future_poll_u64(`handle`: Long,`callback`: UniffiRustFutureContinuationCallback,`callbackData`: Long, -): Unit -fun ffi_walletkit_core_rust_future_cancel_u64(`handle`: Long, -): Unit -fun ffi_walletkit_core_rust_future_free_u64(`handle`: Long, -): Unit -fun ffi_walletkit_core_rust_future_complete_u64(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, -): Long -fun ffi_walletkit_core_rust_future_poll_i64(`handle`: Long,`callback`: UniffiRustFutureContinuationCallback,`callbackData`: Long, -): Unit -fun ffi_walletkit_core_rust_future_cancel_i64(`handle`: Long, -): Unit -fun ffi_walletkit_core_rust_future_free_i64(`handle`: Long, -): Unit -fun ffi_walletkit_core_rust_future_complete_i64(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, -): Long -fun ffi_walletkit_core_rust_future_poll_f32(`handle`: Long,`callback`: UniffiRustFutureContinuationCallback,`callbackData`: Long, -): Unit -fun ffi_walletkit_core_rust_future_cancel_f32(`handle`: Long, -): Unit -fun ffi_walletkit_core_rust_future_free_f32(`handle`: Long, -): Unit -fun ffi_walletkit_core_rust_future_complete_f32(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, -): Float -fun ffi_walletkit_core_rust_future_poll_f64(`handle`: Long,`callback`: UniffiRustFutureContinuationCallback,`callbackData`: Long, -): Unit -fun ffi_walletkit_core_rust_future_cancel_f64(`handle`: Long, -): Unit -fun ffi_walletkit_core_rust_future_free_f64(`handle`: Long, -): Unit -fun ffi_walletkit_core_rust_future_complete_f64(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, -): Double -fun ffi_walletkit_core_rust_future_poll_pointer(`handle`: Long,`callback`: UniffiRustFutureContinuationCallback,`callbackData`: Long, -): Unit -fun ffi_walletkit_core_rust_future_cancel_pointer(`handle`: Long, -): Unit -fun ffi_walletkit_core_rust_future_free_pointer(`handle`: Long, -): Unit -fun ffi_walletkit_core_rust_future_complete_pointer(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, -): Pointer -fun ffi_walletkit_core_rust_future_poll_rust_buffer(`handle`: Long,`callback`: UniffiRustFutureContinuationCallback,`callbackData`: Long, -): Unit -fun ffi_walletkit_core_rust_future_cancel_rust_buffer(`handle`: Long, -): Unit -fun ffi_walletkit_core_rust_future_free_rust_buffer(`handle`: Long, -): Unit -fun ffi_walletkit_core_rust_future_complete_rust_buffer(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, -): RustBuffer.ByValue -fun ffi_walletkit_core_rust_future_poll_void(`handle`: Long,`callback`: UniffiRustFutureContinuationCallback,`callbackData`: Long, -): Unit -fun ffi_walletkit_core_rust_future_cancel_void(`handle`: Long, -): Unit -fun ffi_walletkit_core_rust_future_free_void(`handle`: Long, -): Unit -fun ffi_walletkit_core_rust_future_complete_void(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, -): Unit - -} - -private fun uniffiCheckContractApiVersion(lib: IntegrityCheckingUniffiLib) { - // Get the bindings contract version from our ComponentInterface - val bindings_contract_version = 29 - // Get the scaffolding contract version by calling the into the dylib - val scaffolding_contract_version = lib.ffi_walletkit_core_uniffi_contract_version() - if (bindings_contract_version != scaffolding_contract_version) { - throw RuntimeException("UniFFI contract version mismatch: try cleaning and rebuilding your project") - } -} -@Suppress("UNUSED_PARAMETER") -private fun uniffiCheckApiChecksums(lib: IntegrityCheckingUniffiLib) { - if (lib.uniffi_walletkit_core_checksum_func_set_logger() != 57797.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_walletkit_core_checksum_method_atomicblobstore_read() != 44189.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_walletkit_core_checksum_method_atomicblobstore_write_atomic() != 57047.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_walletkit_core_checksum_method_atomicblobstore_delete() != 36326.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_walletkit_core_checksum_method_credentialstore_begin_proof_disclosure() != 18377.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_walletkit_core_checksum_method_credentialstore_init() != 25655.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_walletkit_core_checksum_method_credentialstore_list_credentials() != 37014.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_walletkit_core_checksum_method_credentialstore_merkle_cache_get() != 47785.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_walletkit_core_checksum_method_credentialstore_merkle_cache_put() != 8522.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_walletkit_core_checksum_method_credentialstore_storage_paths() != 31883.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_walletkit_core_checksum_method_credentialstore_store_credential() != 36573.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_walletkit_core_checksum_method_devicekeystore_seal() != 38896.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_walletkit_core_checksum_method_devicekeystore_open_sealed() != 59369.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_walletkit_core_checksum_method_logger_log() != 32531.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_walletkit_core_checksum_method_proofcontext_get_credential_type() != 23302.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_walletkit_core_checksum_method_proofcontext_get_external_nullifier() != 2388.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_walletkit_core_checksum_method_proofcontext_get_signal_hash() != 57766.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_walletkit_core_checksum_method_proofoutput_get_credential_type() != 23311.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_walletkit_core_checksum_method_proofoutput_get_merkle_root() != 19502.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_walletkit_core_checksum_method_proofoutput_get_nullifier_hash() != 43933.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_walletkit_core_checksum_method_proofoutput_get_proof_as_string() != 3072.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_walletkit_core_checksum_method_proofoutput_to_json() != 57737.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_walletkit_core_checksum_method_storagepaths_cache_db_path_string() != 3315.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_walletkit_core_checksum_method_storagepaths_lock_path_string() != 47091.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_walletkit_core_checksum_method_storagepaths_root_path_string() != 15513.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_walletkit_core_checksum_method_storagepaths_vault_db_path_string() != 48541.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_walletkit_core_checksum_method_storagepaths_worldid_dir_path_string() != 45505.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_walletkit_core_checksum_method_storageprovider_keystore() != 16820.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_walletkit_core_checksum_method_storageprovider_blob_store() != 9190.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_walletkit_core_checksum_method_storageprovider_paths() != 57665.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_walletkit_core_checksum_method_u256wrapper_into_limbs() != 40163.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_walletkit_core_checksum_method_u256wrapper_to_decimal_string() != 33044.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_walletkit_core_checksum_method_u256wrapper_to_hex_string() != 54248.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_walletkit_core_checksum_method_worldid_generate_nullifier_hash() != 17853.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_walletkit_core_checksum_method_worldid_generate_proof() != 46586.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_walletkit_core_checksum_method_worldid_get_identity_commitment() != 46113.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_walletkit_core_checksum_method_worldid_is_equal_to() != 12875.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_walletkit_core_checksum_constructor_credentialstore_from_provider_arc() != 15088.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_walletkit_core_checksum_constructor_credentialstore_new_with_components() != 49336.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_walletkit_core_checksum_constructor_merkletreeproof_from_identity_commitment() != 33610.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_walletkit_core_checksum_constructor_merkletreeproof_from_json_proof() != 37954.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_walletkit_core_checksum_constructor_proofcontext_new() != 53136.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_walletkit_core_checksum_constructor_proofcontext_new_from_bytes() != 40283.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_walletkit_core_checksum_constructor_proofcontext_new_from_signal_hash() != 45451.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_walletkit_core_checksum_constructor_storagepaths_from_root() != 12903.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_walletkit_core_checksum_constructor_u256wrapper_from_limbs() != 55436.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_walletkit_core_checksum_constructor_u256wrapper_from_u32() != 23290.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_walletkit_core_checksum_constructor_u256wrapper_from_u64() != 34597.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_walletkit_core_checksum_constructor_u256wrapper_try_from_hex_string() != 7382.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_walletkit_core_checksum_constructor_worldid_new() != 28488.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } -} - -/** - * @suppress - */ -public fun uniffiEnsureInitialized() { - UniffiLib.INSTANCE -} - -// Async support -// Async return type handlers - -internal const val UNIFFI_RUST_FUTURE_POLL_READY = 0.toByte() -internal const val UNIFFI_RUST_FUTURE_POLL_MAYBE_READY = 1.toByte() - -internal val uniffiContinuationHandleMap = UniffiHandleMap>() - -// FFI type for Rust future continuations -internal object uniffiRustFutureContinuationCallbackImpl: UniffiRustFutureContinuationCallback { - override fun callback(data: Long, pollResult: Byte) { - uniffiContinuationHandleMap.remove(data).resume(pollResult) - } -} - -internal suspend fun uniffiRustCallAsync( - rustFuture: Long, - pollFunc: (Long, UniffiRustFutureContinuationCallback, Long) -> Unit, - completeFunc: (Long, UniffiRustCallStatus) -> F, - freeFunc: (Long) -> Unit, - liftFunc: (F) -> T, - errorHandler: UniffiRustCallStatusErrorHandler -): T { - try { - do { - val pollResult = suspendCancellableCoroutine { continuation -> - pollFunc( - rustFuture, - uniffiRustFutureContinuationCallbackImpl, - uniffiContinuationHandleMap.insert(continuation) - ) - } - } while (pollResult != UNIFFI_RUST_FUTURE_POLL_READY); - - return liftFunc( - uniffiRustCallWithError(errorHandler, { status -> completeFunc(rustFuture, status) }) - ) - } finally { - freeFunc(rustFuture) - } -} - -// Public interface members begin here. - - -// Interface implemented by anything that can contain an object reference. -// -// Such types expose a `destroy()` method that must be called to cleanly -// dispose of the contained objects. Failure to call this method may result -// in memory leaks. -// -// The easiest way to ensure this method is called is to use the `.use` -// helper method to execute a block and destroy the object at the end. -interface Disposable { - fun destroy() - companion object { - fun destroy(vararg args: Any?) { - for (arg in args) { - when (arg) { - is Disposable -> arg.destroy() - is ArrayList<*> -> { - for (idx in arg.indices) { - val element = arg[idx] - if (element is Disposable) { - element.destroy() - } - } - } - is Map<*, *> -> { - for (element in arg.values) { - if (element is Disposable) { - element.destroy() - } - } - } - is Iterable<*> -> { - for (element in arg) { - if (element is Disposable) { - element.destroy() - } - } - } - } - } - } - } -} - -/** - * @suppress - */ -inline fun T.use(block: (T) -> R) = - try { - block(this) - } finally { - try { - // N.B. our implementation is on the nullable type `Disposable?`. - this?.destroy() - } catch (e: Throwable) { - // swallow - } - } - -/** - * Used to instantiate an interface without an actual pointer, for fakes in tests, mostly. - * - * @suppress - * */ -object NoPointer// Magic number for the Rust proxy to call using the same mechanism as every other method, -// to free the callback once it's dropped by Rust. -internal const val IDX_CALLBACK_FREE = 0 -// Callback return codes -internal const val UNIFFI_CALLBACK_SUCCESS = 0 -internal const val UNIFFI_CALLBACK_ERROR = 1 -internal const val UNIFFI_CALLBACK_UNEXPECTED_ERROR = 2 - -/** - * @suppress - */ -public abstract class FfiConverterCallbackInterface: FfiConverter { - internal val handleMap = UniffiHandleMap() - - internal fun drop(handle: Long) { - handleMap.remove(handle) - } - - override fun lift(value: Long): CallbackInterface { - return handleMap.get(value) - } - - override fun read(buf: ByteBuffer) = lift(buf.getLong()) - - override fun lower(value: CallbackInterface) = handleMap.insert(value) - - override fun allocationSize(value: CallbackInterface) = 8UL - - override fun write(value: CallbackInterface, buf: ByteBuffer) { - buf.putLong(lower(value)) - } -} -/** - * The cleaner interface for Object finalization code to run. - * This is the entry point to any implementation that we're using. - * - * The cleaner registers objects and returns cleanables, so now we are - * defining a `UniffiCleaner` with a `UniffiClenaer.Cleanable` to abstract the - * different implmentations available at compile time. - * - * @suppress - */ -interface UniffiCleaner { - interface Cleanable { - fun clean() - } - - fun register(value: Any, cleanUpTask: Runnable): UniffiCleaner.Cleanable - - companion object -} - -// The fallback Jna cleaner, which is available for both Android, and the JVM. -private class UniffiJnaCleaner : UniffiCleaner { - private val cleaner = com.sun.jna.internal.Cleaner.getCleaner() - - override fun register(value: Any, cleanUpTask: Runnable): UniffiCleaner.Cleanable = - UniffiJnaCleanable(cleaner.register(value, cleanUpTask)) -} - -private class UniffiJnaCleanable( - private val cleanable: com.sun.jna.internal.Cleaner.Cleanable, -) : UniffiCleaner.Cleanable { - override fun clean() = cleanable.clean() -} - - -// We decide at uniffi binding generation time whether we were -// using Android or not. -// There are further runtime checks to chose the correct implementation -// of the cleaner. -private fun UniffiCleaner.Companion.create(): UniffiCleaner = - try { - // For safety's sake: if the library hasn't been run in android_cleaner = true - // mode, but is being run on Android, then we still need to think about - // Android API versions. - // So we check if java.lang.ref.Cleaner is there, and use thatโ€ฆ - java.lang.Class.forName("java.lang.ref.Cleaner") - JavaLangRefCleaner() - } catch (e: ClassNotFoundException) { - // โ€ฆ otherwise, fallback to the JNA cleaner. - UniffiJnaCleaner() - } - -private class JavaLangRefCleaner : UniffiCleaner { - val cleaner = java.lang.ref.Cleaner.create() - - override fun register(value: Any, cleanUpTask: Runnable): UniffiCleaner.Cleanable = - JavaLangRefCleanable(cleaner.register(value, cleanUpTask)) -} - -private class JavaLangRefCleanable( - val cleanable: java.lang.ref.Cleaner.Cleanable -) : UniffiCleaner.Cleanable { - override fun clean() = cleanable.clean() -} - -/** - * @suppress - */ -public object FfiConverterUByte: FfiConverter { - override fun lift(value: Byte): UByte { - return value.toUByte() - } - - override fun read(buf: ByteBuffer): UByte { - return lift(buf.get()) - } - - override fun lower(value: UByte): Byte { - return value.toByte() - } - - override fun allocationSize(value: UByte) = 1UL - - override fun write(value: UByte, buf: ByteBuffer) { - buf.put(value.toByte()) - } -} - -/** - * @suppress - */ -public object FfiConverterUShort: FfiConverter { - override fun lift(value: Short): UShort { - return value.toUShort() - } - - override fun read(buf: ByteBuffer): UShort { - return lift(buf.getShort()) - } - - override fun lower(value: UShort): Short { - return value.toShort() - } - - override fun allocationSize(value: UShort) = 2UL - - override fun write(value: UShort, buf: ByteBuffer) { - buf.putShort(value.toShort()) - } -} - -/** - * @suppress - */ -public object FfiConverterUInt: FfiConverter { - override fun lift(value: Int): UInt { - return value.toUInt() - } - - override fun read(buf: ByteBuffer): UInt { - return lift(buf.getInt()) - } - - override fun lower(value: UInt): Int { - return value.toInt() - } - - override fun allocationSize(value: UInt) = 4UL - - override fun write(value: UInt, buf: ByteBuffer) { - buf.putInt(value.toInt()) - } -} - -/** - * @suppress - */ -public object FfiConverterULong: FfiConverter { - override fun lift(value: Long): ULong { - return value.toULong() - } - - override fun read(buf: ByteBuffer): ULong { - return lift(buf.getLong()) - } - - override fun lower(value: ULong): Long { - return value.toLong() - } - - override fun allocationSize(value: ULong) = 8UL - - override fun write(value: ULong, buf: ByteBuffer) { - buf.putLong(value.toLong()) - } -} - -/** - * @suppress - */ -public object FfiConverterBoolean: FfiConverter { - override fun lift(value: Byte): Boolean { - return value.toInt() != 0 - } - - override fun read(buf: ByteBuffer): Boolean { - return lift(buf.get()) - } - - override fun lower(value: Boolean): Byte { - return if (value) 1.toByte() else 0.toByte() - } - - override fun allocationSize(value: Boolean) = 1UL - - override fun write(value: Boolean, buf: ByteBuffer) { - buf.put(lower(value)) - } -} - -/** - * @suppress - */ -public object FfiConverterString: FfiConverter { - // Note: we don't inherit from FfiConverterRustBuffer, because we use a - // special encoding when lowering/lifting. We can use `RustBuffer.len` to - // store our length and avoid writing it out to the buffer. - override fun lift(value: RustBuffer.ByValue): String { - try { - val byteArr = ByteArray(value.len.toInt()) - value.asByteBuffer()!!.get(byteArr) - return byteArr.toString(Charsets.UTF_8) - } finally { - RustBuffer.free(value) - } - } - - override fun read(buf: ByteBuffer): String { - val len = buf.getInt() - val byteArr = ByteArray(len) - buf.get(byteArr) - return byteArr.toString(Charsets.UTF_8) - } - - fun toUtf8(value: String): ByteBuffer { - // Make sure we don't have invalid UTF-16, check for lone surrogates. - return Charsets.UTF_8.newEncoder().run { - onMalformedInput(CodingErrorAction.REPORT) - encode(CharBuffer.wrap(value)) - } - } - - override fun lower(value: String): RustBuffer.ByValue { - val byteBuf = toUtf8(value) - // Ideally we'd pass these bytes to `ffi_bytebuffer_from_bytes`, but doing so would require us - // to copy them into a JNA `Memory`. So we might as well directly copy them into a `RustBuffer`. - val rbuf = RustBuffer.alloc(byteBuf.limit().toULong()) - rbuf.asByteBuffer()!!.put(byteBuf) - return rbuf - } - - // We aren't sure exactly how many bytes our string will be once it's UTF-8 - // encoded. Allocate 3 bytes per UTF-16 code unit which will always be - // enough. - override fun allocationSize(value: String): ULong { - val sizeForLength = 4UL - val sizeForString = value.length.toULong() * 3UL - return sizeForLength + sizeForString - } - - override fun write(value: String, buf: ByteBuffer) { - val byteBuf = toUtf8(value) - buf.putInt(byteBuf.limit()) - buf.put(byteBuf) - } -} - -/** - * @suppress - */ -public object FfiConverterByteArray: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): ByteArray { - val len = buf.getInt() - val byteArr = ByteArray(len) - buf.get(byteArr) - return byteArr - } - override fun allocationSize(value: ByteArray): ULong { - return 4UL + value.size.toULong() - } - override fun write(value: ByteArray, buf: ByteBuffer) { - buf.putInt(value.size) - buf.put(value) - } -} - - -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - - -/** - * Atomic blob store for small binary files (e.g., `account_keys.bin`). - */ -public interface AtomicBlobStore { - - /** - * Reads the blob at `path`, if present. - * - * # Errors - * - * Returns an error if the read fails. - */ - fun `read`(`path`: kotlin.String): kotlin.ByteArray? - - /** - * Writes bytes atomically to `path`. - * - * # Errors - * - * Returns an error if the write fails. - */ - fun `writeAtomic`(`path`: kotlin.String, `bytes`: kotlin.ByteArray) - - /** - * Deletes the blob at `path`. - * - * # Errors - * - * Returns an error if the delete fails. - */ - fun `delete`(`path`: kotlin.String) - - companion object -} - -/** - * Atomic blob store for small binary files (e.g., `account_keys.bin`). - */ -open class AtomicBlobStoreImpl: Disposable, AutoCloseable, AtomicBlobStore -{ - - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (! this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction(private val pointer: Pointer?) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_free_atomicblobstore(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer { - return uniffiRustCall() { status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_clone_atomicblobstore(pointer!!, status) - } - } - - - /** - * Reads the blob at `path`, if present. - * - * # Errors - * - * Returns an error if the read fails. - */ - @Throws(StorageException::class)override fun `read`(`path`: kotlin.String): kotlin.ByteArray? { - return FfiConverterOptionalByteArray.lift( - callWithPointer { - uniffiRustCallWithError(StorageException) { _status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_atomicblobstore_read( - it, FfiConverterString.lower(`path`),_status) -} - } - ) - } - - - - /** - * Writes bytes atomically to `path`. - * - * # Errors - * - * Returns an error if the write fails. - */ - @Throws(StorageException::class)override fun `writeAtomic`(`path`: kotlin.String, `bytes`: kotlin.ByteArray) - = - callWithPointer { - uniffiRustCallWithError(StorageException) { _status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_atomicblobstore_write_atomic( - it, FfiConverterString.lower(`path`),FfiConverterByteArray.lower(`bytes`),_status) -} - } - - - - - /** - * Deletes the blob at `path`. - * - * # Errors - * - * Returns an error if the delete fails. - */ - @Throws(StorageException::class)override fun `delete`(`path`: kotlin.String) - = - callWithPointer { - uniffiRustCallWithError(StorageException) { _status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_atomicblobstore_delete( - it, FfiConverterString.lower(`path`),_status) -} - } - - - - - - - - companion object - -} - - -// Put the implementation in an object so we don't pollute the top-level namespace -internal object uniffiCallbackInterfaceAtomicBlobStore { - internal object `read`: UniffiCallbackInterfaceAtomicBlobStoreMethod0 { - override fun callback(`uniffiHandle`: Long,`path`: RustBuffer.ByValue,`uniffiOutReturn`: RustBuffer,uniffiCallStatus: UniffiRustCallStatus,) { - val uniffiObj = FfiConverterTypeAtomicBlobStore.handleMap.get(uniffiHandle) - val makeCall = { -> - uniffiObj.`read`( - FfiConverterString.lift(`path`), - ) - } - val writeReturn = { value: kotlin.ByteArray? -> uniffiOutReturn.setValue(FfiConverterOptionalByteArray.lower(value)) } - uniffiTraitInterfaceCallWithError( - uniffiCallStatus, - makeCall, - writeReturn, - { e: StorageException -> FfiConverterTypeStorageError.lower(e) } - ) - } - } - internal object `writeAtomic`: UniffiCallbackInterfaceAtomicBlobStoreMethod1 { - override fun callback(`uniffiHandle`: Long,`path`: RustBuffer.ByValue,`bytes`: RustBuffer.ByValue,`uniffiOutReturn`: Pointer,uniffiCallStatus: UniffiRustCallStatus,) { - val uniffiObj = FfiConverterTypeAtomicBlobStore.handleMap.get(uniffiHandle) - val makeCall = { -> - uniffiObj.`writeAtomic`( - FfiConverterString.lift(`path`), - FfiConverterByteArray.lift(`bytes`), - ) - } - val writeReturn = { _: Unit -> Unit } - uniffiTraitInterfaceCallWithError( - uniffiCallStatus, - makeCall, - writeReturn, - { e: StorageException -> FfiConverterTypeStorageError.lower(e) } - ) - } - } - internal object `delete`: UniffiCallbackInterfaceAtomicBlobStoreMethod2 { - override fun callback(`uniffiHandle`: Long,`path`: RustBuffer.ByValue,`uniffiOutReturn`: Pointer,uniffiCallStatus: UniffiRustCallStatus,) { - val uniffiObj = FfiConverterTypeAtomicBlobStore.handleMap.get(uniffiHandle) - val makeCall = { -> - uniffiObj.`delete`( - FfiConverterString.lift(`path`), - ) - } - val writeReturn = { _: Unit -> Unit } - uniffiTraitInterfaceCallWithError( - uniffiCallStatus, - makeCall, - writeReturn, - { e: StorageException -> FfiConverterTypeStorageError.lower(e) } - ) - } - } - - internal object uniffiFree: UniffiCallbackInterfaceFree { - override fun callback(handle: Long) { - FfiConverterTypeAtomicBlobStore.handleMap.remove(handle) - } - } - - internal var vtable = UniffiVTableCallbackInterfaceAtomicBlobStore.UniffiByValue( - `read`, - `writeAtomic`, - `delete`, - uniffiFree, - ) - - // Registers the foreign callback with the Rust side. - // This method is generated for each callback interface. - internal fun register(lib: UniffiLib) { - lib.uniffi_walletkit_core_fn_init_callback_vtable_atomicblobstore(vtable) - } -} - -/** - * @suppress - */ -public object FfiConverterTypeAtomicBlobStore: FfiConverter { - internal val handleMap = UniffiHandleMap() - - override fun lower(value: AtomicBlobStore): Pointer { - return Pointer(handleMap.insert(value)) - } - - override fun lift(value: Pointer): AtomicBlobStore { - return AtomicBlobStoreImpl(value) - } - - override fun read(buf: ByteBuffer): AtomicBlobStore { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: AtomicBlobStore) = 8UL - - override fun write(value: AtomicBlobStore, buf: ByteBuffer) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - - -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - - -/** - * Concrete storage implementation backed by `SQLCipher` databases. - */ -public interface CredentialStoreInterface { - - /** - * Enforces replay safety for proof disclosure. - * - * # Errors - * - * Returns an error if the disclosure conflicts or storage fails. - */ - fun `beginProofDisclosure`(`requestId`: kotlin.ByteArray, `nullifier`: kotlin.ByteArray, `proofBytes`: kotlin.ByteArray, `now`: kotlin.ULong, `ttlSeconds`: kotlin.ULong): ProofDisclosureResultFfi - - /** - * Initializes storage and validates the account leaf index. - * - * # Errors - * - * Returns an error if initialization fails or the leaf index mismatches. - */ - fun `init`(`leafIndex`: kotlin.ULong, `now`: kotlin.ULong) - - /** - * Lists active credentials, optionally filtered by issuer schema ID. - * - * # Errors - * - * Returns an error if the credential query fails. - */ - fun `listCredentials`(`issuerSchemaId`: kotlin.ULong?, `now`: kotlin.ULong): List - - /** - * Fetches a cached Merkle proof if available. - * - * # Errors - * - * Returns an error if the cache lookup fails. - */ - fun `merkleCacheGet`(`registryKind`: kotlin.UByte, `root`: kotlin.ByteArray, `now`: kotlin.ULong): kotlin.ByteArray? - - /** - * Inserts a cached Merkle proof with a TTL. - * - * # Errors - * - * Returns an error if the cache insert fails. - */ - fun `merkleCachePut`(`registryKind`: kotlin.UByte, `root`: kotlin.ByteArray, `proofBytes`: kotlin.ByteArray, `now`: kotlin.ULong, `ttlSeconds`: kotlin.ULong) - - /** - * Returns the storage paths used by this handle. - * - * # Errors - * - * Returns an error if the storage mutex is poisoned. - */ - fun `storagePaths`(): StoragePaths - - /** - * Stores a credential and optional associated data. - * - * # Errors - * - * Returns an error if the credential cannot be stored. - */ - fun `storeCredential`(`issuerSchemaId`: kotlin.ULong, `status`: CredentialStatus, `subjectBlindingFactor`: kotlin.ByteArray, `genesisIssuedAt`: kotlin.ULong, `expiresAt`: kotlin.ULong?, `credentialBlob`: kotlin.ByteArray, `associatedData`: kotlin.ByteArray?, `now`: kotlin.ULong): kotlin.ByteArray - - companion object -} - -/** - * Concrete storage implementation backed by `SQLCipher` databases. - */ -open class CredentialStore: Disposable, AutoCloseable, CredentialStoreInterface -{ - - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (! this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction(private val pointer: Pointer?) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_free_credentialstore(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer { - return uniffiRustCall() { status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_clone_credentialstore(pointer!!, status) - } - } - - - /** - * Enforces replay safety for proof disclosure. - * - * # Errors - * - * Returns an error if the disclosure conflicts or storage fails. - */ - @Throws(StorageException::class)override fun `beginProofDisclosure`(`requestId`: kotlin.ByteArray, `nullifier`: kotlin.ByteArray, `proofBytes`: kotlin.ByteArray, `now`: kotlin.ULong, `ttlSeconds`: kotlin.ULong): ProofDisclosureResultFfi { - return FfiConverterTypeProofDisclosureResultFfi.lift( - callWithPointer { - uniffiRustCallWithError(StorageException) { _status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_credentialstore_begin_proof_disclosure( - it, FfiConverterByteArray.lower(`requestId`),FfiConverterByteArray.lower(`nullifier`),FfiConverterByteArray.lower(`proofBytes`),FfiConverterULong.lower(`now`),FfiConverterULong.lower(`ttlSeconds`),_status) -} - } - ) - } - - - - /** - * Initializes storage and validates the account leaf index. - * - * # Errors - * - * Returns an error if initialization fails or the leaf index mismatches. - */ - @Throws(StorageException::class)override fun `init`(`leafIndex`: kotlin.ULong, `now`: kotlin.ULong) - = - callWithPointer { - uniffiRustCallWithError(StorageException) { _status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_credentialstore_init( - it, FfiConverterULong.lower(`leafIndex`),FfiConverterULong.lower(`now`),_status) -} - } - - - - - /** - * Lists active credentials, optionally filtered by issuer schema ID. - * - * # Errors - * - * Returns an error if the credential query fails. - */ - @Throws(StorageException::class)override fun `listCredentials`(`issuerSchemaId`: kotlin.ULong?, `now`: kotlin.ULong): List { - return FfiConverterSequenceTypeCredentialRecordFfi.lift( - callWithPointer { - uniffiRustCallWithError(StorageException) { _status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_credentialstore_list_credentials( - it, FfiConverterOptionalULong.lower(`issuerSchemaId`),FfiConverterULong.lower(`now`),_status) -} - } - ) - } - - - - /** - * Fetches a cached Merkle proof if available. - * - * # Errors - * - * Returns an error if the cache lookup fails. - */ - @Throws(StorageException::class)override fun `merkleCacheGet`(`registryKind`: kotlin.UByte, `root`: kotlin.ByteArray, `now`: kotlin.ULong): kotlin.ByteArray? { - return FfiConverterOptionalByteArray.lift( - callWithPointer { - uniffiRustCallWithError(StorageException) { _status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_credentialstore_merkle_cache_get( - it, FfiConverterUByte.lower(`registryKind`),FfiConverterByteArray.lower(`root`),FfiConverterULong.lower(`now`),_status) -} - } - ) - } - - - - /** - * Inserts a cached Merkle proof with a TTL. - * - * # Errors - * - * Returns an error if the cache insert fails. - */ - @Throws(StorageException::class)override fun `merkleCachePut`(`registryKind`: kotlin.UByte, `root`: kotlin.ByteArray, `proofBytes`: kotlin.ByteArray, `now`: kotlin.ULong, `ttlSeconds`: kotlin.ULong) - = - callWithPointer { - uniffiRustCallWithError(StorageException) { _status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_credentialstore_merkle_cache_put( - it, FfiConverterUByte.lower(`registryKind`),FfiConverterByteArray.lower(`root`),FfiConverterByteArray.lower(`proofBytes`),FfiConverterULong.lower(`now`),FfiConverterULong.lower(`ttlSeconds`),_status) -} - } - - - - - /** - * Returns the storage paths used by this handle. - * - * # Errors - * - * Returns an error if the storage mutex is poisoned. - */ - @Throws(StorageException::class)override fun `storagePaths`(): StoragePaths { - return FfiConverterTypeStoragePaths.lift( - callWithPointer { - uniffiRustCallWithError(StorageException) { _status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_credentialstore_storage_paths( - it, _status) -} - } - ) - } - - - - /** - * Stores a credential and optional associated data. - * - * # Errors - * - * Returns an error if the credential cannot be stored. - */ - @Throws(StorageException::class)override fun `storeCredential`(`issuerSchemaId`: kotlin.ULong, `status`: CredentialStatus, `subjectBlindingFactor`: kotlin.ByteArray, `genesisIssuedAt`: kotlin.ULong, `expiresAt`: kotlin.ULong?, `credentialBlob`: kotlin.ByteArray, `associatedData`: kotlin.ByteArray?, `now`: kotlin.ULong): kotlin.ByteArray { - return FfiConverterByteArray.lift( - callWithPointer { - uniffiRustCallWithError(StorageException) { _status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_credentialstore_store_credential( - it, FfiConverterULong.lower(`issuerSchemaId`),FfiConverterTypeCredentialStatus.lower(`status`),FfiConverterByteArray.lower(`subjectBlindingFactor`),FfiConverterULong.lower(`genesisIssuedAt`),FfiConverterOptionalULong.lower(`expiresAt`),FfiConverterByteArray.lower(`credentialBlob`),FfiConverterOptionalByteArray.lower(`associatedData`),FfiConverterULong.lower(`now`),_status) -} - } - ) - } - - - - - - companion object { - - /** - * Creates a new storage handle from a platform provider. - * - * # Errors - * - * Returns an error if the storage lock cannot be opened. - */ - @Throws(StorageException::class) fun `fromProviderArc`(`provider`: StorageProvider): CredentialStore { - return FfiConverterTypeCredentialStore.lift( - uniffiRustCallWithError(StorageException) { _status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_constructor_credentialstore_from_provider_arc( - FfiConverterTypeStorageProvider.lower(`provider`),_status) -} - ) - } - - - - /** - * Creates a new storage handle from explicit components. - * - * # Errors - * - * Returns an error if the storage lock cannot be opened. - */ - @Throws(StorageException::class) fun `newWithComponents`(`paths`: StoragePaths, `keystore`: DeviceKeystore, `blobStore`: AtomicBlobStore): CredentialStore { - return FfiConverterTypeCredentialStore.lift( - uniffiRustCallWithError(StorageException) { _status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_constructor_credentialstore_new_with_components( - FfiConverterTypeStoragePaths.lower(`paths`),FfiConverterTypeDeviceKeystore.lower(`keystore`),FfiConverterTypeAtomicBlobStore.lower(`blobStore`),_status) -} - ) - } - - - - } - -} - -/** - * @suppress - */ -public object FfiConverterTypeCredentialStore: FfiConverter { - - override fun lower(value: CredentialStore): Pointer { - return value.uniffiClonePointer() - } - - override fun lift(value: Pointer): CredentialStore { - return CredentialStore(value) - } - - override fun read(buf: ByteBuffer): CredentialStore { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: CredentialStore) = 8UL - - override fun write(value: CredentialStore, buf: ByteBuffer) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - - -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - - -/** - * Device keystore interface used to seal and open account keys. - */ -public interface DeviceKeystore { - - /** - * Seals plaintext under the device-bound key, binding `associated_data`. - * - * # Errors - * - * Returns an error if the keystore refuses the operation or the seal fails. - */ - fun `seal`(`associatedData`: kotlin.ByteArray, `plaintext`: kotlin.ByteArray): kotlin.ByteArray - - /** - * Opens ciphertext under the device-bound key, verifying `associated_data`. - * - * # Errors - * - * Returns an error if authentication fails or the keystore cannot open. - */ - fun `openSealed`(`associatedData`: kotlin.ByteArray, `ciphertext`: kotlin.ByteArray): kotlin.ByteArray - - companion object -} - -/** - * Device keystore interface used to seal and open account keys. - */ -open class DeviceKeystoreImpl: Disposable, AutoCloseable, DeviceKeystore -{ - - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (! this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction(private val pointer: Pointer?) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_free_devicekeystore(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer { - return uniffiRustCall() { status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_clone_devicekeystore(pointer!!, status) - } - } - - - /** - * Seals plaintext under the device-bound key, binding `associated_data`. - * - * # Errors - * - * Returns an error if the keystore refuses the operation or the seal fails. - */ - @Throws(StorageException::class)override fun `seal`(`associatedData`: kotlin.ByteArray, `plaintext`: kotlin.ByteArray): kotlin.ByteArray { - return FfiConverterByteArray.lift( - callWithPointer { - uniffiRustCallWithError(StorageException) { _status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_devicekeystore_seal( - it, FfiConverterByteArray.lower(`associatedData`),FfiConverterByteArray.lower(`plaintext`),_status) -} - } - ) - } - - - - /** - * Opens ciphertext under the device-bound key, verifying `associated_data`. - * - * # Errors - * - * Returns an error if authentication fails or the keystore cannot open. - */ - @Throws(StorageException::class)override fun `openSealed`(`associatedData`: kotlin.ByteArray, `ciphertext`: kotlin.ByteArray): kotlin.ByteArray { - return FfiConverterByteArray.lift( - callWithPointer { - uniffiRustCallWithError(StorageException) { _status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_devicekeystore_open_sealed( - it, FfiConverterByteArray.lower(`associatedData`),FfiConverterByteArray.lower(`ciphertext`),_status) -} - } - ) - } - - - - - - - companion object - -} - - -// Put the implementation in an object so we don't pollute the top-level namespace -internal object uniffiCallbackInterfaceDeviceKeystore { - internal object `seal`: UniffiCallbackInterfaceDeviceKeystoreMethod0 { - override fun callback(`uniffiHandle`: Long,`associatedData`: RustBuffer.ByValue,`plaintext`: RustBuffer.ByValue,`uniffiOutReturn`: RustBuffer,uniffiCallStatus: UniffiRustCallStatus,) { - val uniffiObj = FfiConverterTypeDeviceKeystore.handleMap.get(uniffiHandle) - val makeCall = { -> - uniffiObj.`seal`( - FfiConverterByteArray.lift(`associatedData`), - FfiConverterByteArray.lift(`plaintext`), - ) - } - val writeReturn = { value: kotlin.ByteArray -> uniffiOutReturn.setValue(FfiConverterByteArray.lower(value)) } - uniffiTraitInterfaceCallWithError( - uniffiCallStatus, - makeCall, - writeReturn, - { e: StorageException -> FfiConverterTypeStorageError.lower(e) } - ) - } - } - internal object `openSealed`: UniffiCallbackInterfaceDeviceKeystoreMethod1 { - override fun callback(`uniffiHandle`: Long,`associatedData`: RustBuffer.ByValue,`ciphertext`: RustBuffer.ByValue,`uniffiOutReturn`: RustBuffer,uniffiCallStatus: UniffiRustCallStatus,) { - val uniffiObj = FfiConverterTypeDeviceKeystore.handleMap.get(uniffiHandle) - val makeCall = { -> - uniffiObj.`openSealed`( - FfiConverterByteArray.lift(`associatedData`), - FfiConverterByteArray.lift(`ciphertext`), - ) - } - val writeReturn = { value: kotlin.ByteArray -> uniffiOutReturn.setValue(FfiConverterByteArray.lower(value)) } - uniffiTraitInterfaceCallWithError( - uniffiCallStatus, - makeCall, - writeReturn, - { e: StorageException -> FfiConverterTypeStorageError.lower(e) } - ) - } - } - - internal object uniffiFree: UniffiCallbackInterfaceFree { - override fun callback(handle: Long) { - FfiConverterTypeDeviceKeystore.handleMap.remove(handle) - } - } - - internal var vtable = UniffiVTableCallbackInterfaceDeviceKeystore.UniffiByValue( - `seal`, - `openSealed`, - uniffiFree, - ) - - // Registers the foreign callback with the Rust side. - // This method is generated for each callback interface. - internal fun register(lib: UniffiLib) { - lib.uniffi_walletkit_core_fn_init_callback_vtable_devicekeystore(vtable) - } -} - -/** - * @suppress - */ -public object FfiConverterTypeDeviceKeystore: FfiConverter { - internal val handleMap = UniffiHandleMap() - - override fun lower(value: DeviceKeystore): Pointer { - return Pointer(handleMap.insert(value)) - } - - override fun lift(value: Pointer): DeviceKeystore { - return DeviceKeystoreImpl(value) - } - - override fun read(buf: ByteBuffer): DeviceKeystore { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: DeviceKeystore) = 8UL - - override fun write(value: DeviceKeystore, buf: ByteBuffer) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - - -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - - -/** - * Trait representing a logger that can log messages at various levels. - * - * This trait should be implemented by any logger that wants to receive log messages. - * It is exported via `UniFFI` for use in foreign languages. - * - * # Examples - * - * Implementing the `Logger` trait: - * - * ```rust - * use walletkit_core::logger::{Logger, LogLevel}; - * - * struct MyLogger; - * - * impl Logger for MyLogger { - * fn log(&self, level: LogLevel, message: String) { - * println!("[{:?}] {}", level, message); - * } - * } - * ``` - * - * ## Swift - * - * ```swift - * class WalletKitLoggerBridge: WalletKit.Logger { - * static let shared = WalletKitLoggerBridge() - * - * func log(level: WalletKit.LogLevel, message: String) { - * Log.log(level.toCoreLevel(), message) - * } - * } - * - * public func setupWalletKitLogger() { - * WalletKit.setLogger(logger: WalletKitLoggerBridge.shared) - * } - * ``` - * - * ### In app delegate - * - * ```swift - * setupWalletKitLogger() // Call this only once!!! - * ``` - */ -public interface Logger { - - /** - * Logs a message at the specified log level. - * - * # Arguments - * - * * `level` - The severity level of the log message. - * * `message` - The log message to be recorded. - */ - fun `log`(`level`: LogLevel, `message`: kotlin.String) - - companion object -} - -/** - * Trait representing a logger that can log messages at various levels. - * - * This trait should be implemented by any logger that wants to receive log messages. - * It is exported via `UniFFI` for use in foreign languages. - * - * # Examples - * - * Implementing the `Logger` trait: - * - * ```rust - * use walletkit_core::logger::{Logger, LogLevel}; - * - * struct MyLogger; - * - * impl Logger for MyLogger { - * fn log(&self, level: LogLevel, message: String) { - * println!("[{:?}] {}", level, message); - * } - * } - * ``` - * - * ## Swift - * - * ```swift - * class WalletKitLoggerBridge: WalletKit.Logger { - * static let shared = WalletKitLoggerBridge() - * - * func log(level: WalletKit.LogLevel, message: String) { - * Log.log(level.toCoreLevel(), message) - * } - * } - * - * public func setupWalletKitLogger() { - * WalletKit.setLogger(logger: WalletKitLoggerBridge.shared) - * } - * ``` - * - * ### In app delegate - * - * ```swift - * setupWalletKitLogger() // Call this only once!!! - * ``` - */ -open class LoggerImpl: Disposable, AutoCloseable, Logger -{ - - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (! this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction(private val pointer: Pointer?) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_free_logger(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer { - return uniffiRustCall() { status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_clone_logger(pointer!!, status) - } - } - - - /** - * Logs a message at the specified log level. - * - * # Arguments - * - * * `level` - The severity level of the log message. - * * `message` - The log message to be recorded. - */override fun `log`(`level`: LogLevel, `message`: kotlin.String) - = - callWithPointer { - uniffiRustCall() { _status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_logger_log( - it, FfiConverterTypeLogLevel.lower(`level`),FfiConverterString.lower(`message`),_status) -} - } - - - - - - - - companion object - -} - - -// Put the implementation in an object so we don't pollute the top-level namespace -internal object uniffiCallbackInterfaceLogger { - internal object `log`: UniffiCallbackInterfaceLoggerMethod0 { - override fun callback(`uniffiHandle`: Long,`level`: RustBuffer.ByValue,`message`: RustBuffer.ByValue,`uniffiOutReturn`: Pointer,uniffiCallStatus: UniffiRustCallStatus,) { - val uniffiObj = FfiConverterTypeLogger.handleMap.get(uniffiHandle) - val makeCall = { -> - uniffiObj.`log`( - FfiConverterTypeLogLevel.lift(`level`), - FfiConverterString.lift(`message`), - ) - } - val writeReturn = { _: Unit -> Unit } - uniffiTraitInterfaceCall(uniffiCallStatus, makeCall, writeReturn) - } - } - - internal object uniffiFree: UniffiCallbackInterfaceFree { - override fun callback(handle: Long) { - FfiConverterTypeLogger.handleMap.remove(handle) - } - } - - internal var vtable = UniffiVTableCallbackInterfaceLogger.UniffiByValue( - `log`, - uniffiFree, - ) - - // Registers the foreign callback with the Rust side. - // This method is generated for each callback interface. - internal fun register(lib: UniffiLib) { - lib.uniffi_walletkit_core_fn_init_callback_vtable_logger(vtable) - } -} - -/** - * @suppress - */ -public object FfiConverterTypeLogger: FfiConverter { - internal val handleMap = UniffiHandleMap() - - override fun lower(value: Logger): Pointer { - return Pointer(handleMap.insert(value)) - } - - override fun lift(value: Pointer): Logger { - return LoggerImpl(value) - } - - override fun read(buf: ByteBuffer): Logger { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: Logger) = 8UL - - override fun write(value: Logger, buf: ByteBuffer) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - - -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - - -public interface MerkleTreeProofInterface { - - companion object -} - -open class MerkleTreeProof: Disposable, AutoCloseable, MerkleTreeProofInterface -{ - - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (! this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction(private val pointer: Pointer?) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_free_merkletreeproof(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer { - return uniffiRustCall() { status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_clone_merkletreeproof(pointer!!, status) - } - } - - - - - companion object { - - /** - * Retrieves a Merkle inclusion proof from the sign up sequencer for a given identity commitment. - * Each credential/environment pair uses a different sign up sequencer. - * - * # Errors - * Will throw an error if the request fails or parsing the response fails. - */ - @Throws(WalletKitException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - suspend fun `fromIdentityCommitment`(`identityCommitment`: U256Wrapper, `sequencerHost`: kotlin.String, `requireMinedProof`: kotlin.Boolean) : MerkleTreeProof { - return uniffiRustCallAsync( - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_constructor_merkletreeproof_from_identity_commitment(FfiConverterTypeU256Wrapper.lower(`identityCommitment`),FfiConverterString.lower(`sequencerHost`),FfiConverterBoolean.lower(`requireMinedProof`),), - { future, callback, continuation -> UniffiLib.INSTANCE.ffi_walletkit_core_rust_future_poll_pointer(future, callback, continuation) }, - { future, continuation -> UniffiLib.INSTANCE.ffi_walletkit_core_rust_future_complete_pointer(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_walletkit_core_rust_future_free_pointer(future) }, - // lift function - { FfiConverterTypeMerkleTreeProof.lift(it) }, - // Error FFI converter - WalletKitException.ErrorHandler, - ) - } - - - @Throws(WalletKitException::class) fun `fromJsonProof`(`jsonProof`: kotlin.String, `merkleRoot`: kotlin.String): MerkleTreeProof { - return FfiConverterTypeMerkleTreeProof.lift( - uniffiRustCallWithError(WalletKitException) { _status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_constructor_merkletreeproof_from_json_proof( - FfiConverterString.lower(`jsonProof`),FfiConverterString.lower(`merkleRoot`),_status) -} - ) - } - - - - } - -} - -/** - * @suppress - */ -public object FfiConverterTypeMerkleTreeProof: FfiConverter { - - override fun lower(value: MerkleTreeProof): Pointer { - return value.uniffiClonePointer() - } - - override fun lift(value: Pointer): MerkleTreeProof { - return MerkleTreeProof(value) - } - - override fun read(buf: ByteBuffer): MerkleTreeProof { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: MerkleTreeProof) = 8UL - - override fun write(value: MerkleTreeProof, buf: ByteBuffer) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - - -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - - -/** - * A `ProofContext` contains the basic information on the verifier and the specific action a user will be proving. - * - * It is required to generate a `Proof` and will generally be initialized from an `app_id` and `action`. - * - * Note on naming: `ProofContext` is used to make it clear in FFIs which may not respect the module structure. - */ -public interface ProofContextInterface { - - /** - * Get the credential type for this context. - */ - fun `getCredentialType`(): CredentialType - - /** - * Get the raw external nullifier for this context. - */ - fun `getExternalNullifier`(): U256Wrapper - - /** - * Get the signal hash for this context. - */ - fun `getSignalHash`(): U256Wrapper - - companion object -} - -/** - * A `ProofContext` contains the basic information on the verifier and the specific action a user will be proving. - * - * It is required to generate a `Proof` and will generally be initialized from an `app_id` and `action`. - * - * Note on naming: `ProofContext` is used to make it clear in FFIs which may not respect the module structure. - */ -open class ProofContext: Disposable, AutoCloseable, ProofContextInterface -{ - - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - /** - * Initializes a `ProofContext`. - * - * Will compute the relevant external nullifier from the provided `app_id` and `action` as defined by the - * World ID Protocol. The external nullifier generation matches the logic in the - * [Developer Portal](https://github.com/worldcoin/developer-portal/blob/main/web/lib/hashing.ts). - * - * # Arguments - * - * * `app_id` - The ID of the application requesting proofs. This can be obtained from the [Developer Portal](https://developer.world.org). - * * `action` - Optional. Custom incognito action being requested. - * * `signal` - Optional. The signal is included in the ZKP and is committed to in the proof. When verifying the proof, the - * same signal must be provided to ensure the proof is valid. The signal can be used to prevent replay attacks, MITM or other cases. - * More details available in the [docs](https://docs.world.org/world-id/further-reading/zero-knowledge-proofs). - * * `credential_type` - The type of credential being requested. - - */ - constructor(`appId`: kotlin.String, `action`: kotlin.String?, `signal`: kotlin.String?, `credentialType`: CredentialType) : - this( - uniffiRustCall() { _status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_constructor_proofcontext_new( - FfiConverterString.lower(`appId`),FfiConverterOptionalString.lower(`action`),FfiConverterOptionalString.lower(`signal`),FfiConverterTypeCredentialType.lower(`credentialType`),_status) -} - ) - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (! this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction(private val pointer: Pointer?) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_free_proofcontext(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer { - return uniffiRustCall() { status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_clone_proofcontext(pointer!!, status) - } - } - - - /** - * Get the credential type for this context. - */override fun `getCredentialType`(): CredentialType { - return FfiConverterTypeCredentialType.lift( - callWithPointer { - uniffiRustCall() { _status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_proofcontext_get_credential_type( - it, _status) -} - } - ) - } - - - - /** - * Get the raw external nullifier for this context. - */override fun `getExternalNullifier`(): U256Wrapper { - return FfiConverterTypeU256Wrapper.lift( - callWithPointer { - uniffiRustCall() { _status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_proofcontext_get_external_nullifier( - it, _status) -} - } - ) - } - - - - /** - * Get the signal hash for this context. - */override fun `getSignalHash`(): U256Wrapper { - return FfiConverterTypeU256Wrapper.lift( - callWithPointer { - uniffiRustCall() { _status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_proofcontext_get_signal_hash( - it, _status) -} - } - ) - } - - - - - - companion object { - - /** - * Initializes a `Proof::ProofContext` where the `action` is provided as raw bytes. This is useful for advanced cases - * where the `action` is an already ABI encoded value for on-chain usage. - * See _walletkit-core/tests/solidity.rs_ for an example. - * - * Will compute the relevant external nullifier from the provided `app_id` and `action`. - * - * # Arguments - * - * See `ProofContext::new` for reference. The `action` and `signal` need to be provided as raw bytes. - - */ fun `newFromBytes`(`appId`: kotlin.String, `action`: kotlin.ByteArray?, `signal`: kotlin.ByteArray?, `credentialType`: CredentialType): ProofContext { - return FfiConverterTypeProofContext.lift( - uniffiRustCall() { _status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_constructor_proofcontext_new_from_bytes( - FfiConverterString.lower(`appId`),FfiConverterOptionalByteArray.lower(`action`),FfiConverterOptionalByteArray.lower(`signal`),FfiConverterTypeCredentialType.lower(`credentialType`),_status) -} - ) - } - - - - /** - * Initializes a `ProofContext` from an already hashed signal. - * - * Please note it is imperative to hash into the Semaphore field. Not all U256 are part of the field. - * Use the `hash_to_field` function to hash into the field. - * - * # Usage - * - This may be used when the hash of the signal is computed externally. - * - For example, this is used for support of legacy `MiniKit` v1 commands in World App where `minikit-js` hashed the signal. - * - * # Arguments - * - * * `app_id` - The ID of the application requesting proofs. This can be obtained from the [Developer Portal](https://developer.world.org). - * * `action` - Optional. Custom incognito action being requested as bytes. - * * `credential_type` - The type of credential being requested. - * * `signal` - The already hashed signal as a field element. - * - * # Errors - * - * - Returns an error if the signal is not a valid number in the field. - */ - @Throws(WalletKitException::class) fun `newFromSignalHash`(`appId`: kotlin.String, `action`: kotlin.ByteArray?, `credentialType`: CredentialType, `signalHash`: U256Wrapper): ProofContext { - return FfiConverterTypeProofContext.lift( - uniffiRustCallWithError(WalletKitException) { _status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_constructor_proofcontext_new_from_signal_hash( - FfiConverterString.lower(`appId`),FfiConverterOptionalByteArray.lower(`action`),FfiConverterTypeCredentialType.lower(`credentialType`),FfiConverterTypeU256Wrapper.lower(`signalHash`),_status) -} - ) - } - - - - } - -} - -/** - * @suppress - */ -public object FfiConverterTypeProofContext: FfiConverter { - - override fun lower(value: ProofContext): Pointer { - return value.uniffiClonePointer() - } - - override fun lift(value: Pointer): ProofContext { - return ProofContext(value) - } - - override fun read(buf: ByteBuffer): ProofContext { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: ProofContext) = 8UL - - override fun write(value: ProofContext, buf: ByteBuffer) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - - -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - - -/** - * Represents the complete output of a World ID Proof (i.e. a credential persentation). This output - * can be serialized to JSON and can be verified easily with the Developer Portal or Sign up Sequencer. - * - * For on-chain verification, the `proof` (which is packed) should generally be deserialized into `uint256[8]`. - * - * More information on: [On-Chain Verification](https://docs.world.org/world-id/id/on-chain) - */ -public interface ProofOutputInterface { - - /** - * Exposes the credential type to foreign code. Struct fields are not directly exposed to foreign code. - */ - fun `getCredentialType`(): CredentialType - - /** - * Exposes the merkle root to foreign code. Struct fields are not directly exposed to foreign code. - */ - fun `getMerkleRoot`(): U256Wrapper - - /** - * Exposes the nullifier hash to foreign code. Struct fields are not directly exposed to foreign code. - */ - fun `getNullifierHash`(): U256Wrapper - - /** - * Exposes the proof as a string to foreign code. Struct fields are not directly exposed to foreign code. - */ - fun `getProofAsString`(): kotlin.String - - /** - * Converts the entire proof output to a JSON string with standard attribute names. - * - * # Errors - * Will error if serialization fails. - */ - fun `toJson`(): kotlin.String - - companion object -} - -/** - * Represents the complete output of a World ID Proof (i.e. a credential persentation). This output - * can be serialized to JSON and can be verified easily with the Developer Portal or Sign up Sequencer. - * - * For on-chain verification, the `proof` (which is packed) should generally be deserialized into `uint256[8]`. - * - * More information on: [On-Chain Verification](https://docs.world.org/world-id/id/on-chain) - */ -open class ProofOutput: Disposable, AutoCloseable, ProofOutputInterface -{ - - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (! this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction(private val pointer: Pointer?) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_free_proofoutput(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer { - return uniffiRustCall() { status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_clone_proofoutput(pointer!!, status) - } - } - - - /** - * Exposes the credential type to foreign code. Struct fields are not directly exposed to foreign code. - */override fun `getCredentialType`(): CredentialType { - return FfiConverterTypeCredentialType.lift( - callWithPointer { - uniffiRustCall() { _status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_proofoutput_get_credential_type( - it, _status) -} - } - ) - } - - - - /** - * Exposes the merkle root to foreign code. Struct fields are not directly exposed to foreign code. - */override fun `getMerkleRoot`(): U256Wrapper { - return FfiConverterTypeU256Wrapper.lift( - callWithPointer { - uniffiRustCall() { _status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_proofoutput_get_merkle_root( - it, _status) -} - } - ) - } - - - - /** - * Exposes the nullifier hash to foreign code. Struct fields are not directly exposed to foreign code. - */override fun `getNullifierHash`(): U256Wrapper { - return FfiConverterTypeU256Wrapper.lift( - callWithPointer { - uniffiRustCall() { _status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_proofoutput_get_nullifier_hash( - it, _status) -} - } - ) - } - - - - /** - * Exposes the proof as a string to foreign code. Struct fields are not directly exposed to foreign code. - */override fun `getProofAsString`(): kotlin.String { - return FfiConverterString.lift( - callWithPointer { - uniffiRustCall() { _status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_proofoutput_get_proof_as_string( - it, _status) -} - } - ) - } - - - - /** - * Converts the entire proof output to a JSON string with standard attribute names. - * - * # Errors - * Will error if serialization fails. - */ - @Throws(WalletKitException::class)override fun `toJson`(): kotlin.String { - return FfiConverterString.lift( - callWithPointer { - uniffiRustCallWithError(WalletKitException) { _status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_proofoutput_to_json( - it, _status) -} - } - ) - } - - - - - - - companion object - -} - -/** - * @suppress - */ -public object FfiConverterTypeProofOutput: FfiConverter { - - override fun lower(value: ProofOutput): Pointer { - return value.uniffiClonePointer() - } - - override fun lift(value: Pointer): ProofOutput { - return ProofOutput(value) - } - - override fun read(buf: ByteBuffer): ProofOutput { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: ProofOutput) = 8UL - - override fun write(value: ProofOutput, buf: ByteBuffer) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - - -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - - -/** - * Paths for credential storage artifacts under `/worldid`. - */ -public interface StoragePathsInterface { - - /** - * Returns the path to the cache database as a string. - */ - fun `cacheDbPathString`(): kotlin.String - - /** - * Returns the path to the lock file as a string. - */ - fun `lockPathString`(): kotlin.String - - /** - * Returns the storage root directory as a string. - */ - fun `rootPathString`(): kotlin.String - - /** - * Returns the path to the vault database as a string. - */ - fun `vaultDbPathString`(): kotlin.String - - /** - * Returns the World ID storage directory as a string. - */ - fun `worldidDirPathString`(): kotlin.String - - companion object -} - -/** - * Paths for credential storage artifacts under `/worldid`. - */ -open class StoragePaths: Disposable, AutoCloseable, StoragePathsInterface -{ - - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (! this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction(private val pointer: Pointer?) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_free_storagepaths(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer { - return uniffiRustCall() { status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_clone_storagepaths(pointer!!, status) - } - } - - - /** - * Returns the path to the cache database as a string. - */override fun `cacheDbPathString`(): kotlin.String { - return FfiConverterString.lift( - callWithPointer { - uniffiRustCall() { _status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_storagepaths_cache_db_path_string( - it, _status) -} - } - ) - } - - - - /** - * Returns the path to the lock file as a string. - */override fun `lockPathString`(): kotlin.String { - return FfiConverterString.lift( - callWithPointer { - uniffiRustCall() { _status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_storagepaths_lock_path_string( - it, _status) -} - } - ) - } - - - - /** - * Returns the storage root directory as a string. - */override fun `rootPathString`(): kotlin.String { - return FfiConverterString.lift( - callWithPointer { - uniffiRustCall() { _status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_storagepaths_root_path_string( - it, _status) -} - } - ) - } - - - - /** - * Returns the path to the vault database as a string. - */override fun `vaultDbPathString`(): kotlin.String { - return FfiConverterString.lift( - callWithPointer { - uniffiRustCall() { _status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_storagepaths_vault_db_path_string( - it, _status) -} - } - ) - } - - - - /** - * Returns the World ID storage directory as a string. - */override fun `worldidDirPathString`(): kotlin.String { - return FfiConverterString.lift( - callWithPointer { - uniffiRustCall() { _status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_storagepaths_worldid_dir_path_string( - it, _status) -} - } - ) - } - - - - - - companion object { - - /** - * Builds storage paths rooted at `root`. - */ fun `fromRoot`(`root`: kotlin.String): StoragePaths { - return FfiConverterTypeStoragePaths.lift( - uniffiRustCall() { _status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_constructor_storagepaths_from_root( - FfiConverterString.lower(`root`),_status) -} - ) - } - - - - } - -} - -/** - * @suppress - */ -public object FfiConverterTypeStoragePaths: FfiConverter { - - override fun lower(value: StoragePaths): Pointer { - return value.uniffiClonePointer() - } - - override fun lift(value: Pointer): StoragePaths { - return StoragePaths(value) - } - - override fun read(buf: ByteBuffer): StoragePaths { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: StoragePaths) = 8UL - - override fun write(value: StoragePaths, buf: ByteBuffer) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - - -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - - -/** - * Provider responsible for platform-specific storage components and paths. - */ -public interface StorageProvider { - - /** - * Returns the device keystore implementation. - */ - fun `keystore`(): DeviceKeystore - - /** - * Returns the blob store implementation. - */ - fun `blobStore`(): AtomicBlobStore - - /** - * Returns the storage paths selected by the platform. - */ - fun `paths`(): StoragePaths - - companion object -} - -/** - * Provider responsible for platform-specific storage components and paths. - */ -open class StorageProviderImpl: Disposable, AutoCloseable, StorageProvider -{ - - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (! this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction(private val pointer: Pointer?) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_free_storageprovider(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer { - return uniffiRustCall() { status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_clone_storageprovider(pointer!!, status) - } - } - - - /** - * Returns the device keystore implementation. - */override fun `keystore`(): DeviceKeystore { - return FfiConverterTypeDeviceKeystore.lift( - callWithPointer { - uniffiRustCall() { _status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_storageprovider_keystore( - it, _status) -} - } - ) - } - - - - /** - * Returns the blob store implementation. - */override fun `blobStore`(): AtomicBlobStore { - return FfiConverterTypeAtomicBlobStore.lift( - callWithPointer { - uniffiRustCall() { _status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_storageprovider_blob_store( - it, _status) -} - } - ) - } - - - - /** - * Returns the storage paths selected by the platform. - */override fun `paths`(): StoragePaths { - return FfiConverterTypeStoragePaths.lift( - callWithPointer { - uniffiRustCall() { _status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_storageprovider_paths( - it, _status) -} - } - ) - } - - - - - - - companion object - -} - - -// Put the implementation in an object so we don't pollute the top-level namespace -internal object uniffiCallbackInterfaceStorageProvider { - internal object `keystore`: UniffiCallbackInterfaceStorageProviderMethod0 { - override fun callback(`uniffiHandle`: Long,`uniffiOutReturn`: PointerByReference,uniffiCallStatus: UniffiRustCallStatus,) { - val uniffiObj = FfiConverterTypeStorageProvider.handleMap.get(uniffiHandle) - val makeCall = { -> - uniffiObj.`keystore`( - ) - } - val writeReturn = { value: DeviceKeystore -> uniffiOutReturn.setValue(FfiConverterTypeDeviceKeystore.lower(value)) } - uniffiTraitInterfaceCall(uniffiCallStatus, makeCall, writeReturn) - } - } - internal object `blobStore`: UniffiCallbackInterfaceStorageProviderMethod1 { - override fun callback(`uniffiHandle`: Long,`uniffiOutReturn`: PointerByReference,uniffiCallStatus: UniffiRustCallStatus,) { - val uniffiObj = FfiConverterTypeStorageProvider.handleMap.get(uniffiHandle) - val makeCall = { -> - uniffiObj.`blobStore`( - ) - } - val writeReturn = { value: AtomicBlobStore -> uniffiOutReturn.setValue(FfiConverterTypeAtomicBlobStore.lower(value)) } - uniffiTraitInterfaceCall(uniffiCallStatus, makeCall, writeReturn) - } - } - internal object `paths`: UniffiCallbackInterfaceStorageProviderMethod2 { - override fun callback(`uniffiHandle`: Long,`uniffiOutReturn`: PointerByReference,uniffiCallStatus: UniffiRustCallStatus,) { - val uniffiObj = FfiConverterTypeStorageProvider.handleMap.get(uniffiHandle) - val makeCall = { -> - uniffiObj.`paths`( - ) - } - val writeReturn = { value: StoragePaths -> uniffiOutReturn.setValue(FfiConverterTypeStoragePaths.lower(value)) } - uniffiTraitInterfaceCall(uniffiCallStatus, makeCall, writeReturn) - } - } - - internal object uniffiFree: UniffiCallbackInterfaceFree { - override fun callback(handle: Long) { - FfiConverterTypeStorageProvider.handleMap.remove(handle) - } - } - - internal var vtable = UniffiVTableCallbackInterfaceStorageProvider.UniffiByValue( - `keystore`, - `blobStore`, - `paths`, - uniffiFree, - ) - - // Registers the foreign callback with the Rust side. - // This method is generated for each callback interface. - internal fun register(lib: UniffiLib) { - lib.uniffi_walletkit_core_fn_init_callback_vtable_storageprovider(vtable) - } -} - -/** - * @suppress - */ -public object FfiConverterTypeStorageProvider: FfiConverter { - internal val handleMap = UniffiHandleMap() - - override fun lower(value: StorageProvider): Pointer { - return Pointer(handleMap.insert(value)) - } - - override fun lift(value: Pointer): StorageProvider { - return StorageProviderImpl(value) - } - - override fun read(buf: ByteBuffer): StorageProvider { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: StorageProvider) = 8UL - - override fun write(value: StorageProvider, buf: ByteBuffer) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - - -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - - -/** - * A wrapper around `U256` to represent a field element in the protocol. Wrapper enables FFI interoperability. - * - * Most inputs and outputs from the zero-knowledge proofs are `U256` values. - * While using `U256` directly is convenient and recommended when working with the proofs, particularly in Rust, - * it is not a user-friendly type for interactions or communications in other languages / systems. - * - * Particularly, when sending proof inputs/outputs as JSON on HTTP requests, the values SHOULD - * be represented as padded hex strings from Big Endian bytes. - */ -public interface U256WrapperInterface { - - /** - * Converts the `U256` value into a vector of 4 `u64` values (little-endian). Least significant limb first. - * - * Using a vector as an array cannot be lowered to foreign bindings. - */ - fun `intoLimbs`(): List - - /** - * Outputs the decimal string representation of the `U256` value. - */ - fun `toDecimalString`(): kotlin.String - - /** - * Outputs a hex string representation of the `U256` value padded to 32 bytes (plus two bytes for the `0x` prefix). - */ - fun `toHexString`(): kotlin.String - - companion object -} - -/** - * A wrapper around `U256` to represent a field element in the protocol. Wrapper enables FFI interoperability. - * - * Most inputs and outputs from the zero-knowledge proofs are `U256` values. - * While using `U256` directly is convenient and recommended when working with the proofs, particularly in Rust, - * it is not a user-friendly type for interactions or communications in other languages / systems. - * - * Particularly, when sending proof inputs/outputs as JSON on HTTP requests, the values SHOULD - * be represented as padded hex strings from Big Endian bytes. - */ -open class U256Wrapper: Disposable, AutoCloseable, U256WrapperInterface -{ - - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (! this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction(private val pointer: Pointer?) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_free_u256wrapper(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer { - return uniffiRustCall() { status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_clone_u256wrapper(pointer!!, status) - } - } - - - /** - * Converts the `U256` value into a vector of 4 `u64` values (little-endian). Least significant limb first. - * - * Using a vector as an array cannot be lowered to foreign bindings. - */override fun `intoLimbs`(): List { - return FfiConverterSequenceULong.lift( - callWithPointer { - uniffiRustCall() { _status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_u256wrapper_into_limbs( - it, _status) -} - } - ) - } - - - - /** - * Outputs the decimal string representation of the `U256` value. - */override fun `toDecimalString`(): kotlin.String { - return FfiConverterString.lift( - callWithPointer { - uniffiRustCall() { _status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_u256wrapper_to_decimal_string( - it, _status) -} - } - ) - } - - - - /** - * Outputs a hex string representation of the `U256` value padded to 32 bytes (plus two bytes for the `0x` prefix). - */override fun `toHexString`(): kotlin.String { - return FfiConverterString.lift( - callWithPointer { - uniffiRustCall() { _status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_u256wrapper_to_hex_string( - it, _status) -} - } - ) - } - - - - - - companion object { - - /** - * Creates a `U256` value from an array of 4 `u64` values (little-endian). Least significant limb first. - * - * This is the same as the `U256::from_limbs` method, but exposed to foreign bindings. - * - * # Errors - * - * Will return an `Error::InvalidNumber` if the input is not a valid `U256` value. - */ - @Throws(WalletKitException::class) fun `fromLimbs`(`limbs`: List): U256Wrapper { - return FfiConverterTypeU256Wrapper.lift( - uniffiRustCallWithError(WalletKitException) { _status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_constructor_u256wrapper_from_limbs( - FfiConverterSequenceULong.lower(`limbs`),_status) -} - ) - } - - - - /** - * Creates a `U256` value from a `u32` value. - * - * Logically this will only support values up to 32 bits. For larger values a different initialization should be used. - */ fun `fromU32`(`value`: kotlin.UInt): U256Wrapper { - return FfiConverterTypeU256Wrapper.lift( - uniffiRustCall() { _status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_constructor_u256wrapper_from_u32( - FfiConverterUInt.lower(`value`),_status) -} - ) - } - - - - /** - * Creates a `U256` value from a `u64` value. - * - * Logically this will only support values up to 64 bits. For larger values a different initialization should be used. - */ fun `fromU64`(`value`: kotlin.ULong): U256Wrapper { - return FfiConverterTypeU256Wrapper.lift( - uniffiRustCall() { _status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_constructor_u256wrapper_from_u64( - FfiConverterULong.lower(`value`),_status) -} - ) - } - - - - /** - * Attempts to parse a hex string as a `U256` value (wrapped). - * - * # Errors - * Will return an `Error::InvalidNumber` if the input is not a valid hex-string-presented number up to 256 bits. - */ - @Throws(WalletKitException::class) fun `tryFromHexString`(`hexString`: kotlin.String): U256Wrapper { - return FfiConverterTypeU256Wrapper.lift( - uniffiRustCallWithError(WalletKitException) { _status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_constructor_u256wrapper_try_from_hex_string( - FfiConverterString.lower(`hexString`),_status) -} - ) - } - - - - } - -} - -/** - * @suppress - */ -public object FfiConverterTypeU256Wrapper: FfiConverter { - - override fun lower(value: U256Wrapper): Pointer { - return value.uniffiClonePointer() - } - - override fun lift(value: Pointer): U256Wrapper { - return U256Wrapper(value) - } - - override fun read(buf: ByteBuffer): U256Wrapper { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: U256Wrapper) = 8UL - - override fun write(value: U256Wrapper, buf: ByteBuffer) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - - -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - - -/** - * A base World ID identity which can be used to generate World ID Proofs for different credentials. - * - * Most essential primitive for World ID. - */ -public interface WorldIdInterface { - - /** - * Generates a nullifier hash for a particular context (i.e. app + action) and the identity. - * The nullifier hash is a unique pseudo-random number for the particular identity and context. - * More information can be found [here](https://docs.world.org/world-id/concepts#vocabulary) - * - * [Protocol Reference](https://docs.semaphore.pse.dev/V2/technical-reference/circuits#nullifier-hash). - */ - fun `generateNullifierHash`(`context`: ProofContext): U256Wrapper - - /** - * Generates a World ID Zero-knowledge proof (ZKP) for a specific context (i.e. app + action) and the identity. - * This is equivalent to the user presenting their credential to a verifying party. - * - * **Requires the `semaphore` feature flag.** - * - * # Errors - * Will error if the Merkle Tree inclusion proof cannot be retrieved from the sign up sequencer or if - * something fails with the proof generation. - * - * # Example - * ```rust - * use walletkit_core::{proof::ProofContext, CredentialType, Environment, world_id::WorldId}; - * use std::sync::Arc; - * - * # tokio_test::block_on(async { - * let world_id = WorldId::new(b"not_a_real_secret", &Environment::Staging); - * let context = ProofContext::new("app_ce4cb73cb75fc3b73b71ffb4de178410", Some("my_action".to_string()), None, CredentialType::Device); - * let proof = world_id.generate_proof(&context).await.unwrap(); - * assert_eq!(proof.nullifier_hash.to_hex_string(), "0x302e253346d2b41a0fd71562ffc6e5ddcbab6d8ea3dd6d68e6a695b5639b1c37") - * # }) - * ``` - * note: running the doctest example above requires an HTTP connection to the sequencer. - */ - suspend fun `generateProof`(`context`: ProofContext): ProofOutput - - /** - * Generates the `identity_commitment` for a specific World ID identity and for a specific credential. - * For the same World ID, each credential will generate a different `identity_commitment` for privacy reasons. This is - * accomplished by using a different `identity_trapdoor` internally. - * - * The identity commitment is the public part of a World ID. It is what gets inserted into the membership set on-chain. Identity commitments - * are not directly used in proof verification. - */ - fun `getIdentityCommitment`(`credentialType`: CredentialType): U256Wrapper - - /** - * Compares two `WorldId`s for equality. - * - * This function uses constant-time comparison to prevent timing attacks, but should be performant enough. - * - * Exposed for foreign use. Use `PartialEq` if comparing within Rust. - * - * # Returns - * - * `true` if the two `WorldId`s are equal, `false` otherwise. - */ - fun `isEqualTo`(`other`: WorldId): kotlin.Boolean - - companion object -} - -/** - * A base World ID identity which can be used to generate World ID Proofs for different credentials. - * - * Most essential primitive for World ID. - */ -open class WorldId: Disposable, AutoCloseable, WorldIdInterface -{ - - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - /** - * Initializes a new `Identity` from a World ID secret. The identity is initialized for a specific environment. - */ - constructor(`secret`: kotlin.ByteArray, `environment`: Environment) : - this( - uniffiRustCall() { _status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_constructor_worldid_new( - FfiConverterByteArray.lower(`secret`),FfiConverterTypeEnvironment.lower(`environment`),_status) -} - ) - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (! this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction(private val pointer: Pointer?) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_free_worldid(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer { - return uniffiRustCall() { status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_clone_worldid(pointer!!, status) - } - } - - - /** - * Generates a nullifier hash for a particular context (i.e. app + action) and the identity. - * The nullifier hash is a unique pseudo-random number for the particular identity and context. - * More information can be found [here](https://docs.world.org/world-id/concepts#vocabulary) - * - * [Protocol Reference](https://docs.semaphore.pse.dev/V2/technical-reference/circuits#nullifier-hash). - */override fun `generateNullifierHash`(`context`: ProofContext): U256Wrapper { - return FfiConverterTypeU256Wrapper.lift( - callWithPointer { - uniffiRustCall() { _status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_worldid_generate_nullifier_hash( - it, FfiConverterTypeProofContext.lower(`context`),_status) -} - } - ) - } - - - - /** - * Generates a World ID Zero-knowledge proof (ZKP) for a specific context (i.e. app + action) and the identity. - * This is equivalent to the user presenting their credential to a verifying party. - * - * **Requires the `semaphore` feature flag.** - * - * # Errors - * Will error if the Merkle Tree inclusion proof cannot be retrieved from the sign up sequencer or if - * something fails with the proof generation. - * - * # Example - * ```rust - * use walletkit_core::{proof::ProofContext, CredentialType, Environment, world_id::WorldId}; - * use std::sync::Arc; - * - * # tokio_test::block_on(async { - * let world_id = WorldId::new(b"not_a_real_secret", &Environment::Staging); - * let context = ProofContext::new("app_ce4cb73cb75fc3b73b71ffb4de178410", Some("my_action".to_string()), None, CredentialType::Device); - * let proof = world_id.generate_proof(&context).await.unwrap(); - * assert_eq!(proof.nullifier_hash.to_hex_string(), "0x302e253346d2b41a0fd71562ffc6e5ddcbab6d8ea3dd6d68e6a695b5639b1c37") - * # }) - * ``` - * note: running the doctest example above requires an HTTP connection to the sequencer. - */ - @Throws(WalletKitException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `generateProof`(`context`: ProofContext) : ProofOutput { - return uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_worldid_generate_proof( - thisPtr, - FfiConverterTypeProofContext.lower(`context`), - ) - }, - { future, callback, continuation -> UniffiLib.INSTANCE.ffi_walletkit_core_rust_future_poll_pointer(future, callback, continuation) }, - { future, continuation -> UniffiLib.INSTANCE.ffi_walletkit_core_rust_future_complete_pointer(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_walletkit_core_rust_future_free_pointer(future) }, - // lift function - { FfiConverterTypeProofOutput.lift(it) }, - // Error FFI converter - WalletKitException.ErrorHandler, - ) - } - - - /** - * Generates the `identity_commitment` for a specific World ID identity and for a specific credential. - * For the same World ID, each credential will generate a different `identity_commitment` for privacy reasons. This is - * accomplished by using a different `identity_trapdoor` internally. - * - * The identity commitment is the public part of a World ID. It is what gets inserted into the membership set on-chain. Identity commitments - * are not directly used in proof verification. - */override fun `getIdentityCommitment`(`credentialType`: CredentialType): U256Wrapper { - return FfiConverterTypeU256Wrapper.lift( - callWithPointer { - uniffiRustCall() { _status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_worldid_get_identity_commitment( - it, FfiConverterTypeCredentialType.lower(`credentialType`),_status) -} - } - ) - } - - - - /** - * Compares two `WorldId`s for equality. - * - * This function uses constant-time comparison to prevent timing attacks, but should be performant enough. - * - * Exposed for foreign use. Use `PartialEq` if comparing within Rust. - * - * # Returns - * - * `true` if the two `WorldId`s are equal, `false` otherwise. - */override fun `isEqualTo`(`other`: WorldId): kotlin.Boolean { - return FfiConverterBoolean.lift( - callWithPointer { - uniffiRustCall() { _status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_method_worldid_is_equal_to( - it, FfiConverterTypeWorldId.lower(`other`),_status) -} - } - ) - } - - - - - - - companion object - -} - -/** - * @suppress - */ -public object FfiConverterTypeWorldId: FfiConverter { - - override fun lower(value: WorldId): Pointer { - return value.uniffiClonePointer() - } - - override fun lift(value: Pointer): WorldId { - return WorldId(value) - } - - override fun read(buf: ByteBuffer): WorldId { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: WorldId) = 8UL - - override fun write(value: WorldId, buf: ByteBuffer) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - - - -/** - * FFI-friendly credential record. - */ -data class CredentialRecordFfi ( - /** - * Credential identifier. - */ - var `credentialId`: kotlin.ByteArray, - /** - * Issuer schema identifier. - */ - var `issuerSchemaId`: kotlin.ULong, - /** - * Current credential status. - */ - var `status`: CredentialStatus, - /** - * Subject blinding factor tied to the credential subject. - */ - var `subjectBlindingFactor`: kotlin.ByteArray, - /** - * Genesis issuance timestamp (seconds). - */ - var `genesisIssuedAt`: kotlin.ULong, - /** - * Optional expiry timestamp (seconds). - */ - var `expiresAt`: kotlin.ULong?, - /** - * Last updated timestamp (seconds). - */ - var `updatedAt`: kotlin.ULong, - /** - * Raw credential blob bytes. - */ - var `credentialBlob`: kotlin.ByteArray, - /** - * Optional associated data blob bytes. - */ - var `associatedData`: kotlin.ByteArray? -) { - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeCredentialRecordFfi: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): CredentialRecordFfi { - return CredentialRecordFfi( - FfiConverterByteArray.read(buf), - FfiConverterULong.read(buf), - FfiConverterTypeCredentialStatus.read(buf), - FfiConverterByteArray.read(buf), - FfiConverterULong.read(buf), - FfiConverterOptionalULong.read(buf), - FfiConverterULong.read(buf), - FfiConverterByteArray.read(buf), - FfiConverterOptionalByteArray.read(buf), - ) - } - - override fun allocationSize(value: CredentialRecordFfi) = ( - FfiConverterByteArray.allocationSize(value.`credentialId`) + - FfiConverterULong.allocationSize(value.`issuerSchemaId`) + - FfiConverterTypeCredentialStatus.allocationSize(value.`status`) + - FfiConverterByteArray.allocationSize(value.`subjectBlindingFactor`) + - FfiConverterULong.allocationSize(value.`genesisIssuedAt`) + - FfiConverterOptionalULong.allocationSize(value.`expiresAt`) + - FfiConverterULong.allocationSize(value.`updatedAt`) + - FfiConverterByteArray.allocationSize(value.`credentialBlob`) + - FfiConverterOptionalByteArray.allocationSize(value.`associatedData`) - ) - - override fun write(value: CredentialRecordFfi, buf: ByteBuffer) { - FfiConverterByteArray.write(value.`credentialId`, buf) - FfiConverterULong.write(value.`issuerSchemaId`, buf) - FfiConverterTypeCredentialStatus.write(value.`status`, buf) - FfiConverterByteArray.write(value.`subjectBlindingFactor`, buf) - FfiConverterULong.write(value.`genesisIssuedAt`, buf) - FfiConverterOptionalULong.write(value.`expiresAt`, buf) - FfiConverterULong.write(value.`updatedAt`, buf) - FfiConverterByteArray.write(value.`credentialBlob`, buf) - FfiConverterOptionalByteArray.write(value.`associatedData`, buf) - } -} - - - -/** - * FFI-friendly proof disclosure result. - */ -data class ProofDisclosureResultFfi ( - /** - * Result kind. - */ - var `kind`: ProofDisclosureKind, - /** - * Stored proof package bytes. - */ - var `bytes`: kotlin.ByteArray -) { - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeProofDisclosureResultFfi: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): ProofDisclosureResultFfi { - return ProofDisclosureResultFfi( - FfiConverterTypeProofDisclosureKind.read(buf), - FfiConverterByteArray.read(buf), - ) - } - - override fun allocationSize(value: ProofDisclosureResultFfi) = ( - FfiConverterTypeProofDisclosureKind.allocationSize(value.`kind`) + - FfiConverterByteArray.allocationSize(value.`bytes`) - ) - - override fun write(value: ProofDisclosureResultFfi, buf: ByteBuffer) { - FfiConverterTypeProofDisclosureKind.write(value.`kind`, buf) - FfiConverterByteArray.write(value.`bytes`, buf) - } -} - - - -/** - * Kind of blob stored in the vault. - */ - -enum class BlobKind(val value: kotlin.UByte) { - - /** - * Credential blob payload. - */ - CREDENTIAL_BLOB(1u), - /** - * Associated data payload. - */ - ASSOCIATED_DATA(2u); - companion object -} - - -/** - * @suppress - */ -public object FfiConverterTypeBlobKind: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer) = try { - BlobKind.values()[buf.getInt() - 1] - } catch (e: IndexOutOfBoundsException) { - throw RuntimeException("invalid enum value, something is very wrong!!", e) - } - - override fun allocationSize(value: BlobKind) = 4UL - - override fun write(value: BlobKind, buf: ByteBuffer) { - buf.putInt(value.ordinal + 1) - } -} - - - - - -/** - * Status of a stored credential. - */ - -enum class CredentialStatus(val value: kotlin.UByte) { - - /** - * Credential is active and can be used. - */ - ACTIVE(1u), - /** - * Credential has been revoked. - */ - REVOKED(2u), - /** - * Credential has expired. - */ - EXPIRED(3u); - companion object -} - - -/** - * @suppress - */ -public object FfiConverterTypeCredentialStatus: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer) = try { - CredentialStatus.values()[buf.getInt() - 1] - } catch (e: IndexOutOfBoundsException) { - throw RuntimeException("invalid enum value, something is very wrong!!", e) - } - - override fun allocationSize(value: CredentialStatus) = 4UL - - override fun write(value: CredentialStatus, buf: ByteBuffer) { - buf.putInt(value.ordinal + 1) - } -} - - - - - -/** - * A `CredentialType` represents a specific credential which can be presented by a World ID holder. - * - * For example, if a World ID is Orb-verified, the holder can use their `Orb` credential to prove they have a - * valid Orb-verified credential. - * - * More details in `https://docs.world.org/world-id/concepts#proof-of-personhood` - */ - -enum class CredentialType { - - /** - * Represents persons who have been biometrically verified at an Orb. Highest level of proof of personhood verification. - */ - ORB, - /** - * Verified biometric ICAO-9303 government-issued document holder - */ - DOCUMENT, - /** - * Verified biometric ICAO-9303 government-issued document holder with additional presence checks - * such as Chip Authentication or Active Authentication. - * - * - * The identity trapdoor is `secure_passport` but it's serialized as `secure_document` to match `idkit-js` and the Developer Portal. - * Reference: - */ - SECURE_DOCUMENT, - /** - * Represents a semi-unique device - */ - DEVICE; - companion object -} - - -/** - * @suppress - */ -public object FfiConverterTypeCredentialType: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer) = try { - CredentialType.values()[buf.getInt() - 1] - } catch (e: IndexOutOfBoundsException) { - throw RuntimeException("invalid enum value, something is very wrong!!", e) - } - - override fun allocationSize(value: CredentialType) = 4UL - - override fun write(value: CredentialType, buf: ByteBuffer) { - buf.putInt(value.ordinal + 1) - } -} - - - - - -/** - * Represents the environment in which a World ID is being presented and used. - * - * Each environment uses different sources of truth for the World ID credentials. - * - * More information on testing for the World ID Protocol can be found in: `https://docs.world.org/world-id/quick-start/testing` - */ - -enum class Environment { - - /** - * For testing purposes ONLY. - */ - STAGING, - /** - * Live production environment. World ID Tree: `id.worldcoin.eth` - */ - PRODUCTION; - companion object -} - - -/** - * @suppress - */ -public object FfiConverterTypeEnvironment: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer) = try { - Environment.values()[buf.getInt() - 1] - } catch (e: IndexOutOfBoundsException) { - throw RuntimeException("invalid enum value, something is very wrong!!", e) - } - - override fun allocationSize(value: Environment) = 4UL - - override fun write(value: Environment, buf: ByteBuffer) { - buf.putInt(value.ordinal + 1) - } -} - - - - - -/** - * Enumeration of possible log levels. - * - * This enum represents the severity levels that can be used when logging messages. - */ - -enum class LogLevel { - - /** - * Designates very low priority, often extremely detailed messages. - */ - TRACE, - /** - * Designates lower priority debugging information. - */ - DEBUG, - /** - * Designates informational messages that highlight the progress of the application. - */ - INFO, - /** - * Designates potentially harmful situations. - */ - WARN, - /** - * Designates error events that might still allow the application to continue running. - */ - ERROR; - companion object -} - - -/** - * @suppress - */ -public object FfiConverterTypeLogLevel: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer) = try { - LogLevel.values()[buf.getInt() - 1] - } catch (e: IndexOutOfBoundsException) { - throw RuntimeException("invalid enum value, something is very wrong!!", e) - } - - override fun allocationSize(value: LogLevel) = 4UL - - override fun write(value: LogLevel, buf: ByteBuffer) { - buf.putInt(value.ordinal + 1) - } -} - - - - - -/** - * FFI-friendly proof disclosure result kind. - */ - -enum class ProofDisclosureKind { - - /** - * Stored bytes for the first disclosure of a request. - */ - FRESH, - /** - * Stored bytes replayed for an existing request. - */ - REPLAY; - companion object -} - - -/** - * @suppress - */ -public object FfiConverterTypeProofDisclosureKind: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer) = try { - ProofDisclosureKind.values()[buf.getInt() - 1] - } catch (e: IndexOutOfBoundsException) { - throw RuntimeException("invalid enum value, something is very wrong!!", e) - } - - override fun allocationSize(value: ProofDisclosureKind) = 4UL - - override fun write(value: ProofDisclosureKind, buf: ByteBuffer) { - buf.putInt(value.ordinal + 1) - } -} - - - - - - - -/** - * Errors raised by credential storage primitives. - */ -sealed class StorageException: kotlin.Exception() { - - /** - * Errors coming from the device keystore. - */ - class Keystore( - - val v1: kotlin.String - ) : StorageException() { - override val message - get() = "v1=${ v1 }" - } - - /** - * Errors coming from the blob store. - */ - class BlobStore( - - val v1: kotlin.String - ) : StorageException() { - override val message - get() = "v1=${ v1 }" - } - - /** - * Errors coming from the storage lock. - */ - class Lock( - - val v1: kotlin.String - ) : StorageException() { - override val message - get() = "v1=${ v1 }" - } - - /** - * Serialization/deserialization failures. - */ - class Serialization( - - val v1: kotlin.String - ) : StorageException() { - override val message - get() = "v1=${ v1 }" - } - - /** - * Cryptographic failures (AEAD, HKDF, etc.). - */ - class Crypto( - - val v1: kotlin.String - ) : StorageException() { - override val message - get() = "v1=${ v1 }" - } - - /** - * Invalid or malformed account key envelope. - */ - class InvalidEnvelope( - - val v1: kotlin.String - ) : StorageException() { - override val message - get() = "v1=${ v1 }" - } - - /** - * Unsupported envelope version. - */ - class UnsupportedEnvelopeVersion( - - val v1: kotlin.UInt - ) : StorageException() { - override val message - get() = "v1=${ v1 }" - } - - /** - * Errors coming from the vault database. - */ - class VaultDb( - - val v1: kotlin.String - ) : StorageException() { - override val message - get() = "v1=${ v1 }" - } - - /** - * Errors coming from the cache database. - */ - class CacheDb( - - val v1: kotlin.String - ) : StorageException() { - override val message - get() = "v1=${ v1 }" - } - - /** - * Leaf index mismatch during initialization. - */ - class InvalidLeafIndex( - - /** - * Leaf index stored in the vault. - */ - val `expected`: kotlin.ULong, - - /** - * Leaf index provided by the caller. - */ - val `provided`: kotlin.ULong - ) : StorageException() { - override val message - get() = "expected=${ `expected` }, provided=${ `provided` }" - } - - /** - * Vault database integrity check failed. - */ - class CorruptedVault( - - val v1: kotlin.String - ) : StorageException() { - override val message - get() = "v1=${ v1 }" - } - - /** - * Storage has not been initialized yet. - */ - class NotInitialized( - ) : StorageException() { - override val message - get() = "" - } - - /** - * Nullifier already disclosed for a different request. - */ - class NullifierAlreadyDisclosed( - ) : StorageException() { - override val message - get() = "" - } - - /** - * Credential not found in the vault. - */ - class CredentialNotFound( - ) : StorageException() { - override val message - get() = "" - } - - /** - * Unexpected `UniFFI` callback error. - */ - class UnexpectedUniFfiCallbackException( - - val v1: kotlin.String - ) : StorageException() { - override val message - get() = "v1=${ v1 }" - } - - - companion object ErrorHandler : UniffiRustCallStatusErrorHandler { - override fun lift(error_buf: RustBuffer.ByValue): StorageException = FfiConverterTypeStorageError.lift(error_buf) - } - - -} - -/** - * @suppress - */ -public object FfiConverterTypeStorageError : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): StorageException { - - - return when(buf.getInt()) { - 1 -> StorageException.Keystore( - FfiConverterString.read(buf), - ) - 2 -> StorageException.BlobStore( - FfiConverterString.read(buf), - ) - 3 -> StorageException.Lock( - FfiConverterString.read(buf), - ) - 4 -> StorageException.Serialization( - FfiConverterString.read(buf), - ) - 5 -> StorageException.Crypto( - FfiConverterString.read(buf), - ) - 6 -> StorageException.InvalidEnvelope( - FfiConverterString.read(buf), - ) - 7 -> StorageException.UnsupportedEnvelopeVersion( - FfiConverterUInt.read(buf), - ) - 8 -> StorageException.VaultDb( - FfiConverterString.read(buf), - ) - 9 -> StorageException.CacheDb( - FfiConverterString.read(buf), - ) - 10 -> StorageException.InvalidLeafIndex( - FfiConverterULong.read(buf), - FfiConverterULong.read(buf), - ) - 11 -> StorageException.CorruptedVault( - FfiConverterString.read(buf), - ) - 12 -> StorageException.NotInitialized() - 13 -> StorageException.NullifierAlreadyDisclosed() - 14 -> StorageException.CredentialNotFound() - 15 -> StorageException.UnexpectedUniFfiCallbackException( - FfiConverterString.read(buf), - ) - else -> throw RuntimeException("invalid error enum value, something is very wrong!!") - } - } - - override fun allocationSize(value: StorageException): ULong { - return when(value) { - is StorageException.Keystore -> ( - // Add the size for the Int that specifies the variant plus the size needed for all fields - 4UL - + FfiConverterString.allocationSize(value.v1) - ) - is StorageException.BlobStore -> ( - // Add the size for the Int that specifies the variant plus the size needed for all fields - 4UL - + FfiConverterString.allocationSize(value.v1) - ) - is StorageException.Lock -> ( - // Add the size for the Int that specifies the variant plus the size needed for all fields - 4UL - + FfiConverterString.allocationSize(value.v1) - ) - is StorageException.Serialization -> ( - // Add the size for the Int that specifies the variant plus the size needed for all fields - 4UL - + FfiConverterString.allocationSize(value.v1) - ) - is StorageException.Crypto -> ( - // Add the size for the Int that specifies the variant plus the size needed for all fields - 4UL - + FfiConverterString.allocationSize(value.v1) - ) - is StorageException.InvalidEnvelope -> ( - // Add the size for the Int that specifies the variant plus the size needed for all fields - 4UL - + FfiConverterString.allocationSize(value.v1) - ) - is StorageException.UnsupportedEnvelopeVersion -> ( - // Add the size for the Int that specifies the variant plus the size needed for all fields - 4UL - + FfiConverterUInt.allocationSize(value.v1) - ) - is StorageException.VaultDb -> ( - // Add the size for the Int that specifies the variant plus the size needed for all fields - 4UL - + FfiConverterString.allocationSize(value.v1) - ) - is StorageException.CacheDb -> ( - // Add the size for the Int that specifies the variant plus the size needed for all fields - 4UL - + FfiConverterString.allocationSize(value.v1) - ) - is StorageException.InvalidLeafIndex -> ( - // Add the size for the Int that specifies the variant plus the size needed for all fields - 4UL - + FfiConverterULong.allocationSize(value.`expected`) - + FfiConverterULong.allocationSize(value.`provided`) - ) - is StorageException.CorruptedVault -> ( - // Add the size for the Int that specifies the variant plus the size needed for all fields - 4UL - + FfiConverterString.allocationSize(value.v1) - ) - is StorageException.NotInitialized -> ( - // Add the size for the Int that specifies the variant plus the size needed for all fields - 4UL - ) - is StorageException.NullifierAlreadyDisclosed -> ( - // Add the size for the Int that specifies the variant plus the size needed for all fields - 4UL - ) - is StorageException.CredentialNotFound -> ( - // Add the size for the Int that specifies the variant plus the size needed for all fields - 4UL - ) - is StorageException.UnexpectedUniFfiCallbackException -> ( - // Add the size for the Int that specifies the variant plus the size needed for all fields - 4UL - + FfiConverterString.allocationSize(value.v1) - ) - } - } - - override fun write(value: StorageException, buf: ByteBuffer) { - when(value) { - is StorageException.Keystore -> { - buf.putInt(1) - FfiConverterString.write(value.v1, buf) - Unit - } - is StorageException.BlobStore -> { - buf.putInt(2) - FfiConverterString.write(value.v1, buf) - Unit - } - is StorageException.Lock -> { - buf.putInt(3) - FfiConverterString.write(value.v1, buf) - Unit - } - is StorageException.Serialization -> { - buf.putInt(4) - FfiConverterString.write(value.v1, buf) - Unit - } - is StorageException.Crypto -> { - buf.putInt(5) - FfiConverterString.write(value.v1, buf) - Unit - } - is StorageException.InvalidEnvelope -> { - buf.putInt(6) - FfiConverterString.write(value.v1, buf) - Unit - } - is StorageException.UnsupportedEnvelopeVersion -> { - buf.putInt(7) - FfiConverterUInt.write(value.v1, buf) - Unit - } - is StorageException.VaultDb -> { - buf.putInt(8) - FfiConverterString.write(value.v1, buf) - Unit - } - is StorageException.CacheDb -> { - buf.putInt(9) - FfiConverterString.write(value.v1, buf) - Unit - } - is StorageException.InvalidLeafIndex -> { - buf.putInt(10) - FfiConverterULong.write(value.`expected`, buf) - FfiConverterULong.write(value.`provided`, buf) - Unit - } - is StorageException.CorruptedVault -> { - buf.putInt(11) - FfiConverterString.write(value.v1, buf) - Unit - } - is StorageException.NotInitialized -> { - buf.putInt(12) - Unit - } - is StorageException.NullifierAlreadyDisclosed -> { - buf.putInt(13) - Unit - } - is StorageException.CredentialNotFound -> { - buf.putInt(14) - Unit - } - is StorageException.UnexpectedUniFfiCallbackException -> { - buf.putInt(15) - FfiConverterString.write(value.v1, buf) - Unit - } - }.let { /* this makes the `when` an expression, which ensures it is exhaustive */ } - } - -} - - - - - -/** - * Error outputs from `WalletKit` - */ -sealed class WalletKitException: kotlin.Exception() { - - /** - * Invalid input provided (e.g., incorrect length, format, etc.) - */ - class InvalidInput( - - /** - * The attribute that is invalid - */ - val `attribute`: kotlin.String, - - /** - * The reason the input is invalid - */ - val `reason`: kotlin.String - ) : WalletKitException() { - override val message - get() = "attribute=${ `attribute` }, reason=${ `reason` }" - } - - /** - * The presented data is not a valid U256 integer - */ - class InvalidNumber( - ) : WalletKitException() { - override val message - get() = "" - } - - /** - * Unexpected error serializing information - */ - class SerializationException( - - /** - * The error message from the serialization - */ - val `error`: kotlin.String - ) : WalletKitException() { - override val message - get() = "error=${ `error` }" - } - - /** - * Network connection error with details - */ - class NetworkException( - - /** - * The URL of the request - */ - val `url`: kotlin.String, - - /** - * The error message from the request - */ - val `error`: kotlin.String, - - /** - * The HTTP status code of the request, if available - */ - val `status`: kotlin.UShort? - ) : WalletKitException() { - override val message - get() = "url=${ `url` }, error=${ `error` }, status=${ `status` }" - } - - /** - * HTTP request failure - */ - class Reqwest( - - /** - * The error message from the request - */ - val `error`: kotlin.String - ) : WalletKitException() { - override val message - get() = "error=${ `error` }" - } - - /** - * Unhandled error generating a Zero-Knowledge Proof - */ - class ProofGeneration( - - /** - * The error message from the proof generation - */ - val `error`: kotlin.String - ) : WalletKitException() { - override val message - get() = "error=${ `error` }" - } - - /** - * The `semaphore` feature flag is not enabled - */ - class SemaphoreNotEnabled( - ) : WalletKitException() { - override val message - get() = "" - } - - /** - * The requested credential is not issued for this World ID - */ - class CredentialNotIssued( - ) : WalletKitException() { - override val message - get() = "" - } - - /** - * The requested credential has not been submitted on-chain - */ - class CredentialNotMined( - ) : WalletKitException() { - override val message - get() = "" - } - - /** - * This operation requires a registered account and an account is not registered - * for this authenticator. Call `create_account` first to register it. - */ - class AccountDoesNotExist( - ) : WalletKitException() { - override val message - get() = "" - } - - /** - * The account already exists for this authenticator. Call `account_index` to get the account index. - */ - class AccountAlreadyExists( - ) : WalletKitException() { - override val message - get() = "" - } - - /** - * The public key was not found in the batch, i.e. the authenticator is not authorized to sign for this action - */ - class UnauthorizedAuthenticator( - ) : WalletKitException() { - override val message - get() = "" - } - - /** - * An unexpected error occurred with the Authenticator - */ - class AuthenticatorException( - - /** - * The error message from the authenticator - */ - val `error`: kotlin.String - ) : WalletKitException() { - override val message - get() = "error=${ `error` }" - } - - /** - * An unexpected error occurred - */ - class Generic( - - /** - * The details of the error - */ - val `error`: kotlin.String - ) : WalletKitException() { - override val message - get() = "error=${ `error` }" - } - - - companion object ErrorHandler : UniffiRustCallStatusErrorHandler { - override fun lift(error_buf: RustBuffer.ByValue): WalletKitException = FfiConverterTypeWalletKitError.lift(error_buf) - } - - -} - -/** - * @suppress - */ -public object FfiConverterTypeWalletKitError : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): WalletKitException { - - - return when(buf.getInt()) { - 1 -> WalletKitException.InvalidInput( - FfiConverterString.read(buf), - FfiConverterString.read(buf), - ) - 2 -> WalletKitException.InvalidNumber() - 3 -> WalletKitException.SerializationException( - FfiConverterString.read(buf), - ) - 4 -> WalletKitException.NetworkException( - FfiConverterString.read(buf), - FfiConverterString.read(buf), - FfiConverterOptionalUShort.read(buf), - ) - 5 -> WalletKitException.Reqwest( - FfiConverterString.read(buf), - ) - 6 -> WalletKitException.ProofGeneration( - FfiConverterString.read(buf), - ) - 7 -> WalletKitException.SemaphoreNotEnabled() - 8 -> WalletKitException.CredentialNotIssued() - 9 -> WalletKitException.CredentialNotMined() - 10 -> WalletKitException.AccountDoesNotExist() - 11 -> WalletKitException.AccountAlreadyExists() - 12 -> WalletKitException.UnauthorizedAuthenticator() - 13 -> WalletKitException.AuthenticatorException( - FfiConverterString.read(buf), - ) - 14 -> WalletKitException.Generic( - FfiConverterString.read(buf), - ) - else -> throw RuntimeException("invalid error enum value, something is very wrong!!") - } - } - - override fun allocationSize(value: WalletKitException): ULong { - return when(value) { - is WalletKitException.InvalidInput -> ( - // Add the size for the Int that specifies the variant plus the size needed for all fields - 4UL - + FfiConverterString.allocationSize(value.`attribute`) - + FfiConverterString.allocationSize(value.`reason`) - ) - is WalletKitException.InvalidNumber -> ( - // Add the size for the Int that specifies the variant plus the size needed for all fields - 4UL - ) - is WalletKitException.SerializationException -> ( - // Add the size for the Int that specifies the variant plus the size needed for all fields - 4UL - + FfiConverterString.allocationSize(value.`error`) - ) - is WalletKitException.NetworkException -> ( - // Add the size for the Int that specifies the variant plus the size needed for all fields - 4UL - + FfiConverterString.allocationSize(value.`url`) - + FfiConverterString.allocationSize(value.`error`) - + FfiConverterOptionalUShort.allocationSize(value.`status`) - ) - is WalletKitException.Reqwest -> ( - // Add the size for the Int that specifies the variant plus the size needed for all fields - 4UL - + FfiConverterString.allocationSize(value.`error`) - ) - is WalletKitException.ProofGeneration -> ( - // Add the size for the Int that specifies the variant plus the size needed for all fields - 4UL - + FfiConverterString.allocationSize(value.`error`) - ) - is WalletKitException.SemaphoreNotEnabled -> ( - // Add the size for the Int that specifies the variant plus the size needed for all fields - 4UL - ) - is WalletKitException.CredentialNotIssued -> ( - // Add the size for the Int that specifies the variant plus the size needed for all fields - 4UL - ) - is WalletKitException.CredentialNotMined -> ( - // Add the size for the Int that specifies the variant plus the size needed for all fields - 4UL - ) - is WalletKitException.AccountDoesNotExist -> ( - // Add the size for the Int that specifies the variant plus the size needed for all fields - 4UL - ) - is WalletKitException.AccountAlreadyExists -> ( - // Add the size for the Int that specifies the variant plus the size needed for all fields - 4UL - ) - is WalletKitException.UnauthorizedAuthenticator -> ( - // Add the size for the Int that specifies the variant plus the size needed for all fields - 4UL - ) - is WalletKitException.AuthenticatorException -> ( - // Add the size for the Int that specifies the variant plus the size needed for all fields - 4UL - + FfiConverterString.allocationSize(value.`error`) - ) - is WalletKitException.Generic -> ( - // Add the size for the Int that specifies the variant plus the size needed for all fields - 4UL - + FfiConverterString.allocationSize(value.`error`) - ) - } - } - - override fun write(value: WalletKitException, buf: ByteBuffer) { - when(value) { - is WalletKitException.InvalidInput -> { - buf.putInt(1) - FfiConverterString.write(value.`attribute`, buf) - FfiConverterString.write(value.`reason`, buf) - Unit - } - is WalletKitException.InvalidNumber -> { - buf.putInt(2) - Unit - } - is WalletKitException.SerializationException -> { - buf.putInt(3) - FfiConverterString.write(value.`error`, buf) - Unit - } - is WalletKitException.NetworkException -> { - buf.putInt(4) - FfiConverterString.write(value.`url`, buf) - FfiConverterString.write(value.`error`, buf) - FfiConverterOptionalUShort.write(value.`status`, buf) - Unit - } - is WalletKitException.Reqwest -> { - buf.putInt(5) - FfiConverterString.write(value.`error`, buf) - Unit - } - is WalletKitException.ProofGeneration -> { - buf.putInt(6) - FfiConverterString.write(value.`error`, buf) - Unit - } - is WalletKitException.SemaphoreNotEnabled -> { - buf.putInt(7) - Unit - } - is WalletKitException.CredentialNotIssued -> { - buf.putInt(8) - Unit - } - is WalletKitException.CredentialNotMined -> { - buf.putInt(9) - Unit - } - is WalletKitException.AccountDoesNotExist -> { - buf.putInt(10) - Unit - } - is WalletKitException.AccountAlreadyExists -> { - buf.putInt(11) - Unit - } - is WalletKitException.UnauthorizedAuthenticator -> { - buf.putInt(12) - Unit - } - is WalletKitException.AuthenticatorException -> { - buf.putInt(13) - FfiConverterString.write(value.`error`, buf) - Unit - } - is WalletKitException.Generic -> { - buf.putInt(14) - FfiConverterString.write(value.`error`, buf) - Unit - } - }.let { /* this makes the `when` an expression, which ensures it is exhaustive */ } - } - -} - - - - -/** - * @suppress - */ -public object FfiConverterOptionalUShort: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): kotlin.UShort? { - if (buf.get().toInt() == 0) { - return null - } - return FfiConverterUShort.read(buf) - } - - override fun allocationSize(value: kotlin.UShort?): ULong { - if (value == null) { - return 1UL - } else { - return 1UL + FfiConverterUShort.allocationSize(value) - } - } - - override fun write(value: kotlin.UShort?, buf: ByteBuffer) { - if (value == null) { - buf.put(0) - } else { - buf.put(1) - FfiConverterUShort.write(value, buf) - } - } -} - - - - -/** - * @suppress - */ -public object FfiConverterOptionalULong: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): kotlin.ULong? { - if (buf.get().toInt() == 0) { - return null - } - return FfiConverterULong.read(buf) - } - - override fun allocationSize(value: kotlin.ULong?): ULong { - if (value == null) { - return 1UL - } else { - return 1UL + FfiConverterULong.allocationSize(value) - } - } - - override fun write(value: kotlin.ULong?, buf: ByteBuffer) { - if (value == null) { - buf.put(0) - } else { - buf.put(1) - FfiConverterULong.write(value, buf) - } - } -} - - - - -/** - * @suppress - */ -public object FfiConverterOptionalString: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): kotlin.String? { - if (buf.get().toInt() == 0) { - return null - } - return FfiConverterString.read(buf) - } - - override fun allocationSize(value: kotlin.String?): ULong { - if (value == null) { - return 1UL - } else { - return 1UL + FfiConverterString.allocationSize(value) - } - } - - override fun write(value: kotlin.String?, buf: ByteBuffer) { - if (value == null) { - buf.put(0) - } else { - buf.put(1) - FfiConverterString.write(value, buf) - } - } -} - - - - -/** - * @suppress - */ -public object FfiConverterOptionalByteArray: FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): kotlin.ByteArray? { - if (buf.get().toInt() == 0) { - return null - } - return FfiConverterByteArray.read(buf) - } - - override fun allocationSize(value: kotlin.ByteArray?): ULong { - if (value == null) { - return 1UL - } else { - return 1UL + FfiConverterByteArray.allocationSize(value) - } - } - - override fun write(value: kotlin.ByteArray?, buf: ByteBuffer) { - if (value == null) { - buf.put(0) - } else { - buf.put(1) - FfiConverterByteArray.write(value, buf) - } - } -} - - - - -/** - * @suppress - */ -public object FfiConverterSequenceULong: FfiConverterRustBuffer> { - override fun read(buf: ByteBuffer): List { - val len = buf.getInt() - return List(len) { - FfiConverterULong.read(buf) - } - } - - override fun allocationSize(value: List): ULong { - val sizeForLength = 4UL - val sizeForItems = value.map { FfiConverterULong.allocationSize(it) }.sum() - return sizeForLength + sizeForItems - } - - override fun write(value: List, buf: ByteBuffer) { - buf.putInt(value.size) - value.iterator().forEach { - FfiConverterULong.write(it, buf) - } - } -} - - - - -/** - * @suppress - */ -public object FfiConverterSequenceTypeCredentialRecordFfi: FfiConverterRustBuffer> { - override fun read(buf: ByteBuffer): List { - val len = buf.getInt() - return List(len) { - FfiConverterTypeCredentialRecordFfi.read(buf) - } - } - - override fun allocationSize(value: List): ULong { - val sizeForLength = 4UL - val sizeForItems = value.map { FfiConverterTypeCredentialRecordFfi.allocationSize(it) }.sum() - return sizeForLength + sizeForItems - } - - override fun write(value: List, buf: ByteBuffer) { - buf.putInt(value.size) - value.iterator().forEach { - FfiConverterTypeCredentialRecordFfi.write(it, buf) - } - } -} - - - - - - - - - /** - * Sets the global logger. - * - * This function allows you to provide your own implementation of the `Logger` trait. - * It initializes the logging system and should be called before any logging occurs. - * - * # Arguments - * - * * `logger` - An `Arc` containing your logger implementation. - * - * # Panics - * - * Panics if the logger has already been set. - * - * # Note - * - * If the logger has already been set, this function will print a message and do nothing. - */ fun `setLogger`(`logger`: Logger) - = - uniffiRustCall() { _status -> - UniffiLib.INSTANCE.uniffi_walletkit_core_fn_func_set_logger( - FfiConverterTypeLogger.lower(`logger`),_status) -} - - - - From 74a6557bfc14491130f3a0fd0d4195718e34deeb Mon Sep 17 00:00:00 2001 From: Luke Mann Date: Sun, 25 Jan 2026 13:19:49 -0800 Subject: [PATCH 09/10] update gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index ba3c1b70a..ead5b6b9c 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,7 @@ swift/tests/.build/ # Kotlin bindings and native libs kotlin/libs/ kotlin/walletkit/src/main/java/uniffi/ +kotlin/walletkit-android/src/main/java/uniffi/ kotlin/walletkit-tests/build/ .build/ From 5c7e43565f8174ebb25334fdfc7c9c6b5e610df5 Mon Sep 17 00:00:00 2001 From: Luke Mann Date: Sun, 25 Jan 2026 13:21:14 -0800 Subject: [PATCH 10/10] revert gitignore change --- .gitignore | 2 -- 1 file changed, 2 deletions(-) diff --git a/.gitignore b/.gitignore index ead5b6b9c..e00b85c21 100644 --- a/.gitignore +++ b/.gitignore @@ -22,9 +22,7 @@ swift/tests/.build/ # Kotlin bindings and native libs kotlin/libs/ kotlin/walletkit/src/main/java/uniffi/ -kotlin/walletkit-android/src/main/java/uniffi/ kotlin/walletkit-tests/build/ - .build/ .env